diff --git a/CHANGELOG.md b/CHANGELOG.md index 8835178..7f7d083 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,4 +37,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Includes comprehensive error handling and logging - Provides detailed outputs for integration with other workflows - +## [1.1.0] - August 2025 + +Introduces improved changelog management with a dedicated marker system for automated insertions, along with streamlined documentation and configuration options for better usability. + +### Added +- New CONTEXT_LEDGER_MARKER system for precise changelog entry insertion +- Enhanced configuration options for changelog suggestion handling + +### Changed +- Streamlined documentation structure with improved README clarity +- Simplified action configuration with better defaults +- Optimized changelog generation process + +### Fixed +- Improved handling of changelog suggestions and marker removal +- Enhanced action stability and reliability + +### Removed +- Legacy migration files and outdated documentation +- Deprecated example configurations + +### Technical Details +- Refactored changelog generation logic for better maintainability +- Improved marker-based insertion system for more reliable updates diff --git a/EXTRACTION_SUMMARY.md b/EXTRACTION_SUMMARY.md deleted file mode 100644 index 6ae4655..0000000 --- a/EXTRACTION_SUMMARY.md +++ /dev/null @@ -1,334 +0,0 @@ -# GitHub Action Extraction Summary - -## 🎯 What Was Extracted - -This standalone GitHub Action was extracted from the TripSnag monorepo's complex changelog automation workflow. Here's what was transformed: - -### Original Workflow Complexity - -- **File**: `.github/workflows/update-changelog.yml` -- **Size**: 854 lines of complex YAML and embedded JavaScript -- **Functionality**: AI-powered changelog generation with Claude API -- **Scope**: Single repository, tightly coupled to TripSnag structure - -### Extracted Action Benefits - -- **Reusable**: Can be used in any repository -- **Maintainable**: Clear separation of concerns -- **Testable**: Dedicated CI/CD pipeline -- **Documented**: Comprehensive usage guides -- **Configurable**: Flexible input parameters - -## πŸ“ Project Structure - -``` -context-ledger/ -β”œβ”€β”€ action.yml # GitHub Action definition -β”œβ”€β”€ lib/ -β”‚ └── generate-changelog.js # Core changelog generation logic -β”œβ”€β”€ package.json # Node.js dependencies -β”œβ”€β”€ README.md # Main documentation -β”œβ”€β”€ USAGE.md # Detailed usage examples -β”œβ”€β”€ MIGRATION_GUIDE.md # Migration instructions -β”œβ”€β”€ CHANGELOG.md # Project changelog -β”œβ”€β”€ LICENSE # MIT License -β”œβ”€β”€ .gitignore # Git ignore rules -β”œβ”€β”€ .github/ -β”‚ β”œβ”€β”€ workflows/ -β”‚ β”‚ └── test.yml # CI/CD pipeline -β”‚ β”œβ”€β”€ ISSUE_TEMPLATE/ -β”‚ β”‚ β”œβ”€β”€ bug_report.md # Bug report template -β”‚ β”‚ └── feature_request.md # Feature request template -β”‚ β”œβ”€β”€ pull_request_template.md # PR template -β”‚ └── dependabot.yml # Dependency updates -└── examples/ - └── migration-example.yml # Migration example -``` - -## πŸ”§ Core Components Extracted - -### 1. Action Definition (`action.yml`) - -- **Inputs**: 11 configurable parameters -- **Outputs**: 5 result variables -- **Runs**: Composite action using bash and Node.js -- **Features**: Loop prevention, AI generation, GitHub integration - -### 2. Core Logic (`lib/generate-changelog.js`) - -- **Functions**: 8 modular functions for different aspects -- **Features**: - - Commit parsing and categorization - - Semantic versioning logic - - Claude AI integration - - Git operations - - Error handling - -### 3. CI/CD Pipeline (`.github/workflows/test.yml`) - -- **Jobs**: 3 different test scenarios -- **Tests**: Syntax validation, integration testing, linting -- **Triggers**: Push, PR, and manual dispatch - -## πŸš€ Key Features - -### Smart Loop Prevention - -```yaml -- name: Check for suggestion commits - # Prevents infinite loops from GitHub suggestions - # Detects changelog-only commits - # Skips when appropriate -``` - -### AI-Powered Analysis - -```javascript -// Categorizes commits using conventional commit patterns -// Determines semantic version increments -// Generates meaningful changelog entries -// Uses Claude API for natural language processing -``` - -### GitHub Integration - -```yaml -- name: Create GitHub PR suggestions - # Creates one-click apply suggestions - # Handles diff positioning - # Works with new and modified files -``` - -### Semantic Versioning - -```javascript -// Automatically determines version increments: -// Breaking changes β†’ major (1.0.0 β†’ 2.0.0) -// New features β†’ minor (1.0.0 β†’ 1.1.0) -// Bug fixes β†’ patch (1.0.0 β†’ 1.0.1) -``` - -## πŸ“Š Migration Benefits - -| Aspect | Before | After | -| ------------------- | ----------- | --------------- | -| **Workflow Size** | 854 lines | ~100 lines | -| **Maintainability** | Single repo | Reusable action | -| **Testing** | Manual | Automated CI/CD | -| **Documentation** | Minimal | Comprehensive | -| **Error Handling** | Basic | Robust | -| **Customization** | Hard-coded | Configurable | -| **Updates** | Manual | Dependabot | - -## πŸ› οΈ Usage Examples - -### Basic Usage - -```yaml -- name: Generate Changelog - uses: lukemun/context-ledger@v1 - with: - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} -``` - -### Advanced Configuration - -```yaml -- name: Generate Changelog - uses: lukemun/context-ledger@v1 - with: - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - changelog_path: "docs/CHANGES.md" - target_name: "api-service" - version_increment: "minor" - auto_commit: true -``` - -### Monorepo Usage - -```yaml -strategy: - matrix: - project: - - { name: "frontend", path: "packages/frontend/CHANGELOG.md" } - - { name: "backend", path: "packages/backend/CHANGELOG.md" } - -steps: - - name: Generate Changelog for ${{ matrix.project.name }} - uses: lukemun/context-ledger@v1 - with: - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - changelog_path: ${{ matrix.project.path }} - target_name: ${{ matrix.project.name }} -``` - -## πŸ”’ Security Features - -### API Key Protection - -- Stored in GitHub Secrets -- Never logged or exposed -- Supports organization-level secrets - -### Minimal Permissions - -```yaml -permissions: - contents: write # Read/write changelog files - pull-requests: write # Create suggestions -``` - -### Input Validation - -- Validates all input parameters -- Sanitizes file paths -- Prevents injection attacks - -## πŸ“ˆ Publishing Options - -### GitHub Marketplace - -1. Create public repository -2. Add proper `action.yml` metadata -3. Create release with tag -4. Submit to marketplace - -### Private Distribution - -1. Create private repository -2. Use in workflows via: - ```yaml - uses: organization/context-ledger@v1 - ``` - -### Fork and Customize - -1. Fork this repository -2. Customize for your needs -3. Use your fork in workflows - -## πŸ”§ Customization Points - -### Prompt Engineering - -Modify `lib/generate-changelog.js` to adjust: - -- AI prompt structure -- Domain-specific terminology -- Output formatting -- Analysis depth - -### Commit Categorization - -Customize commit analysis: - -- Conventional commit patterns -- Category mappings -- Version increment rules -- Breaking change detection - -### GitHub Integration - -Adjust suggestion behavior: - -- Diff positioning logic -- Comment formatting -- Review creation -- Error handling - -## πŸ§ͺ Testing Strategy - -### Local Development - -```bash -# Install dependencies -npm install - -# Test syntax -node -c lib/generate-changelog.js - -# Local testing with act -act pull_request -s ANTHROPIC_API_KEY=test_key -``` - -### Integration Testing - -1. Create test repository -2. Configure action workflow -3. Create test PRs -4. Verify changelog generation - -### CI/CD Pipeline - -- Automatic testing on push/PR -- Syntax validation -- Integration tests -- Dependency security checks - -## πŸ“š Documentation Structure - -### User-Facing - -- **README.md**: Overview and quick start -- **USAGE.md**: Detailed examples and configuration -- **examples/**: Real-world workflow examples - -### Developer-Facing - -- **MIGRATION_GUIDE.md**: How to extract and customize -- **EXTRACTION_SUMMARY.md**: This document -- **CHANGELOG.md**: Version history - -### Community - -- **Issue templates**: Bug reports and feature requests -- **PR template**: Contribution guidelines -- **LICENSE**: MIT license for open source use - -## 🎯 Next Steps - -### For Original Repository (TripSnag) - -1. Replace existing workflow with action usage -2. Test with a sample PR -3. Monitor for any regressions -4. Enjoy simplified maintenance - -### For Action Repository - -1. Publish to GitHub (public or private) -2. Set up Dependabot for security updates -3. Add integration tests -4. Consider contributing back improvements - -### For Other Users - -1. Fork or use the published action -2. Customize for your domain -3. Create your own workflows -4. Share improvements with community - -## πŸ’‘ Innovation Opportunities - -### Enhanced AI Features - -- Multi-model support (GPT, Gemini) -- Custom training data -- Domain-specific prompts -- Automated testing suggestions - -### Advanced Integrations - -- Jira ticket linking -- Slack notifications -- Release automation -- Documentation updates - -### Community Features - -- Shared prompt library -- Best practices database -- Template marketplace -- Plugin ecosystem - -This extraction transforms a complex, single-use workflow into a powerful, reusable tool that can benefit developers across the entire GitHub ecosystem! diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md deleted file mode 100644 index 0dbf81f..0000000 --- a/MIGRATION_GUIDE.md +++ /dev/null @@ -1,304 +0,0 @@ -# Migration Guide: From Inline Workflow to Standalone Action - -This guide helps you extract your existing GitHub Actions workflow into this standalone, reusable action. - -## πŸ“Š Before vs After Comparison - -| Aspect | Before (Inline Workflow) | After (Standalone Action) | -| ------------------- | ------------------------ | ------------------------- | -| **Lines of Code** | ~850 lines | ~100 lines | -| **Maintainability** | Complex, single-repo | Simple, reusable | -| **Testing** | Manual, in-production | Automated CI/CD | -| **Updates** | Manual copy-paste | Automated via Dependabot | -| **Error Handling** | Basic | Comprehensive | -| **Documentation** | Minimal | Extensive | -| **Reusability** | Single repository | Multiple repositories | - -## πŸš€ Migration Process - -### Step 1: Extract Your Current Workflow - -Your existing workflow likely contains several key components: - -1. **Loop Prevention Logic** - Detecting suggestion commits -2. **Change Analysis** - Extracting PR commits and file changes -3. **Claude AI Integration** - API calls and prompt engineering -4. **GitHub Suggestions** - Creating PR review suggestions -5. **Version Management** - Semantic versioning logic - -### Step 2: Create the Standalone Action Repository - -1. **Create a new repository** for your action: - - ```bash - git clone https://github.com/lukemun/context-ledger.git - cd context-ledger - ``` - -2. **Copy the extracted structure** from this directory to your new repo: - ``` - context-ledger/ - β”œβ”€β”€ action.yml # Action definition - β”œβ”€β”€ lib/generate-changelog.js # Core logic - β”œβ”€β”€ package.json # Dependencies - β”œβ”€β”€ README.md # Documentation - β”œβ”€β”€ USAGE.md # Usage examples - β”œβ”€β”€ LICENSE # MIT License - β”œβ”€β”€ .github/workflows/test.yml # CI/CD - └── examples/ # Example workflows - ``` - -### Step 3: Customize for Your Needs - -1. **Update action.yml**: - - - Change author information - - Adjust input defaults - - Modify branding - -2. **Customize generate-changelog.js**: - - - Adjust prompt engineering for your domain - - Modify commit categorization logic - - Update version increment rules - -3. **Update documentation**: - - Replace placeholder URLs with your repository - - Add specific examples for your use case - - Document any custom configurations - -### Step 4: Publish Your Action - -1. **Tag and release**: - - ```bash - git tag v1.0.0 - git push origin v1.0.0 - ``` - -2. **Publish to GitHub Marketplace** (optional): - - Go to your repository on GitHub - - Click "Releases" β†’ "Create a new release" - - Fill in release details - - Check "Publish this Action to the GitHub Marketplace" - -### Step 5: Update Your Original Repository - -Replace your existing workflow with a simplified version: - -```yaml -name: Update Changelog - -on: - pull_request: - branches: [main] - types: [opened, synchronize, reopened, ready_for_review] - paths-ignore: - - "**/CHANGELOG.md" - -permissions: - contents: write - pull-requests: write - -jobs: - update-changelog: - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.ref }} - fetch-depth: 0 - - - name: Generate Changelog - uses: lukemun/context-ledger@v1 - with: - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - changelog_path: "CHANGELOG.md" - target_name: "project" -``` - -## πŸ”§ Customization Examples - -### For Monorepos - -Create a matrix strategy to handle multiple changelogs: - -```yaml -strategy: - matrix: - project: - - { name: "frontend", path: "packages/frontend/CHANGELOG.md" } - - { name: "backend", path: "packages/backend/CHANGELOG.md" } - -steps: - - name: Generate Changelog for ${{ matrix.project.name }} - uses: lukemun/context-ledger@v1 - with: - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - changelog_path: ${{ matrix.project.path }} - target_name: ${{ matrix.project.name }} -``` - -### For Different Environments - -Handle different changelog files based on target branch: - -```yaml -- name: Determine changelog path - id: changelog-path - run: | - if [ "${{ github.base_ref }}" = "main" ]; then - echo "path=CHANGELOG.md" >> $GITHUB_OUTPUT - echo "target=production" >> $GITHUB_OUTPUT - else - echo "path=CHANGELOG-DEV.md" >> $GITHUB_OUTPUT - echo "target=development" >> $GITHUB_OUTPUT - fi - -- name: Generate Changelog - uses: yourusername/claude-changelog-action@v1 - with: - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - changelog_path: ${{ steps.changelog-path.outputs.path }} - target_name: ${{ steps.changelog-path.outputs.target }} -``` - -## πŸ§ͺ Testing Your Action - -### Local Testing with Act - -```bash -# Install act -brew install act - -# Test pull request trigger -act pull_request \ - -s ANTHROPIC_API_KEY=your_test_key \ - -e test-event.json - -# Test with custom payload -echo '{"pull_request":{"head":{"ref":"feature-branch"},"base":{"ref":"main"}}}' > test-event.json -act pull_request -e test-event.json -``` - -### Integration Testing - -1. Create a test repository -2. Add your action as a workflow -3. Create a test PR with meaningful commits -4. Verify the changelog generation works correctly - -### Unit Testing (Future Enhancement) - -Consider adding Jest or similar for testing individual functions: - -```bash -npm install --save-dev jest -# Add tests for commit categorization, version logic, etc. -``` - -## πŸ”’ Security Considerations - -### API Key Management - -1. **Repository Secrets**: Store in GitHub repository secrets -2. **Organization Secrets**: Share across multiple repos -3. **Key Rotation**: Regularly rotate your Anthropic API keys -4. **Least Privilege**: Use minimal required permissions - -### Permissions - -The action requires these permissions: - -```yaml -permissions: - contents: write # To read/write changelog files - pull-requests: write # To create suggestions -``` - -### Dependencies - -Keep dependencies up to date: - -```yaml -# .github/dependabot.yml -version: 2 -updates: - - package-ecosystem: "npm" - directory: "/" - schedule: - interval: "weekly" -``` - -## πŸ“ˆ Maintenance and Updates - -### Versioning Strategy - -Use semantic versioning for your action: - -- **v1.0.0**: Initial release -- **v1.1.0**: New features (backward compatible) -- **v1.0.1**: Bug fixes -- **v2.0.0**: Breaking changes - -### Update Process - -1. **Make changes** in a feature branch -2. **Test thoroughly** with integration tests -3. **Update documentation** and examples -4. **Create release** with appropriate version tag -5. **Notify users** of breaking changes - -### Support Multiple Versions - -Maintain multiple major versions: - -``` -v1.2.3 -> v1 (auto-updates to latest v1.x) -v2.1.0 -> v2 (auto-updates to latest v2.x) -``` - -## 🎯 Best Practices - -### Action Design - -1. **Single Responsibility**: Focus on changelog generation only -2. **Configurable**: Make behavior configurable via inputs -3. **Defensive**: Handle errors gracefully -4. **Documented**: Provide comprehensive documentation - -### Workflow Design - -1. **Minimal Permissions**: Only request what you need -2. **Error Handling**: Use `continue-on-error` where appropriate -3. **Conditional Logic**: Skip unnecessary runs -4. **Clear Outputs**: Provide useful outputs for chaining - -### Community - -1. **Open Source**: Consider making your action open source -2. **Examples**: Provide real-world usage examples -3. **Support**: Respond to issues and feature requests -4. **Documentation**: Keep documentation up to date - -## πŸ”— Resources - -- [GitHub Actions Documentation](https://docs.github.com/en/actions) -- [Creating Actions](https://docs.github.com/en/actions/creating-actions) -- [Action Marketplace](https://github.com/marketplace?type=actions) -- [Anthropic API Documentation](https://docs.anthropic.com/) -- [Semantic Versioning](https://semver.org/) - -## 🀝 Contributing Back - -If you create improvements to this action: - -1. **Fork** the original repository -2. **Create** a feature branch -3. **Test** your changes thoroughly -4. **Submit** a pull request with clear description -5. **Maintain** backward compatibility when possible - -This migration transforms a complex, single-use workflow into a maintainable, reusable GitHub Action that can benefit the entire community! diff --git a/README.md b/README.md index d9373a4..f368541 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,60 @@ A GitHub Action that maintains a source of truth for LLM context across your cod > **Installation**: Use `lukemun/context-ledger@v1` in your workflows. For the latest features, use `@main`. +## πŸ› οΈ Quick Setup + +- **Add secret**: In GitHub β†’ Settings β†’ Secrets and variables β†’ Actions β†’ New repository secret + + - Name: `ANTHROPIC_API_KEY` + - Value: your Anthropic API key + +- **Add workflow**: Create `.github/workflows/changelog.yml` + +```yaml +name: Update Changelog + +on: + pull_request: + branches: [main] + types: [opened, synchronize, reopened, ready_for_review] + paths-ignore: + - "**/CHANGELOG.md" + - "CHANGELOG.md" + +permissions: + contents: write + pull-requests: write + +jobs: + update-changelog: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Generate Changelog + uses: lukemun/context-ledger@v1 + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + # Optional: changelog_path, target_name, commit_range, version_increment, create_pr_suggestions, auto_commit +``` + +- **Create changelog**: Add `CHANGELOG.md` to your repo root + +```markdown +# Changelog + +All notable changes to this project will be documented in this file. + +## [Unreleased] + + +``` + +- **Open a PR**: The action analyzes your changes and suggests entries for `CHANGELOG.md`. + ## ✨ Features - πŸ€– **AI-Powered Analysis**: Uses Claude AI to understand commit patterns and generate meaningful changelog entries @@ -27,7 +81,7 @@ A GitHub Action that maintains a source of truth for LLM context across your cod - **Faster onboarding for new engineers and contractors (founder benefit)**: Give an LLM the precise, up‑to‑date context to answer β€œhow does this work?” based on real, recent changes. Reduce ramp‑up time without long knowledge dumps. - **Sales enablement with up‑to‑date product information**: Keep customer‑facing docs, release summaries, and collateral aligned with what actually shipped so sales can speak confidently and accurately. -## πŸš€ Quick Start +## 🧩 In-Depth Setup ### Simple Setup (single project) @@ -157,8 +211,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - - +### Added + +- Initial features and functionality ``` #### Step 3: Get Anthropic API Key @@ -196,42 +251,6 @@ Create a new pull request and watch Context Ledger analyze your changes and sugg - **Permissions**: Make sure your repository allows Actions to write to PRs (enabled by default) - **Customize target options**: Update the `target_changelog` options in `workflow_dispatch` to match your project structure (e.g., service names in a monorepo) -### Basic Usage (Alternative) - -```yaml -name: Update Changelog - -on: - pull_request: - branches: [main] - types: [opened, synchronize, reopened, ready_for_review] - paths-ignore: - - "**/CHANGELOG.md" - - "CHANGELOG.md" - -jobs: - update-changelog: - runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.ref }} - fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Generate Changelog - uses: lukemun/context-ledger@v1 - with: - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - changelog_path: "CHANGELOG.md" - target_name: "project" -``` - ### Required Setup 1. **Get an Anthropic API Key**: @@ -241,6 +260,7 @@ jobs: - Add it to your repository secrets as `ANTHROPIC_API_KEY` 2. **Configure Repository Permissions**: + - Ensure your workflow has `contents: write` and `pull-requests: write` permissions - For private repositories, you may need to adjust branch protection rules @@ -371,7 +391,17 @@ jobs: 3. **Change Analysis**: Extracts PR commits, changed files, and git diffs 4. **AI Processing**: Claude AI analyzes changes and generates categorized changelog entries 5. **Version Management**: Automatically determines semantic version increments -6. **GitHub Integration**: Creates suggestions for one-click application in PRs +6. **Changelog Updates**: Simply appends new entries to the end of your changelog file +7. **GitHub Integration**: Creates suggestions for one-click application in PRs + +### Changelog Generation + +The action uses a simple marker-based approach: + +- Looks for the `` marker at the end of your changelog +- Inserts new entries just before this marker +- The marker always stays at the bottom of the file +- This ensures clean insertion points and prevents overwriting content ### Commit Analysis @@ -396,7 +426,7 @@ The action intelligently categorizes commits based on conventional commit patter ### Project Structure ``` -claude-changelog-action/ +context-ledger/ β”œβ”€β”€ action.yml # Action definition β”œβ”€β”€ lib/ β”‚ └── generate-changelog.js # Core logic @@ -437,6 +467,7 @@ act pull_request -s ANTHROPIC_API_KEY=your_test_key Context Ledger provides two workflows for maximum flexibility: 1. **Production Workflow** (`changelog.yml`) - Uses the published version (`@v1`) + - Stable, tested version - What your users will experience - Runs automatically on PRs and releases @@ -458,6 +489,7 @@ When ready to release a new version: ``` This will: + - Create a new version tag (e.g., `v1.0.19`) - Update the floating major tag (e.g., `v1`) - Push both tags to GitHub diff --git a/action.yml b/action.yml index 457f898..058f67e 100644 --- a/action.yml +++ b/action.yml @@ -287,19 +287,23 @@ runs: // Create placeholder content if no existing content found if (!existingContent) { - existingContent = '# Changelog\n\n## [Unreleased]\n- Placeholder for AI-generated changelog\n\n\n'; + existingContent = '# Changelog\n\nAll notable changes to this project will be documented in this file.\n\n\n'; } - // Ensure AI_APPEND_HERE marker is always present at the end - if (!existingContent.includes('')) { - existingContent += '\n\n'; - console.log('πŸ“ Added AI_APPEND_HERE marker'); + // Ensure Context Ledger marker exists + if (!existingContent.includes('')) { + existingContent = existingContent.trimEnd() + '\n\n\n'; + console.log('πŸ“ Added Context Ledger marker'); } - // If file exists but not in PR diff, add timestamp to trigger PR inclusion + // If file exists but not in PR diff, add a small change to trigger PR inclusion if (fileExistsOnTarget) { + // Add a timestamp comment before the marker to ensure it shows in diff const timestamp = new Date().toISOString(); - existingContent += `\n\n`; + existingContent = existingContent.replace( + '', + `\n` + ); console.log('πŸ“ Adding timestamp to trigger PR diff inclusion'); } @@ -449,21 +453,24 @@ runs: echo "EOF" >> $GITHUB_OUTPUT fi - # For PRs, compare against the base branch + # For PRs, check if we should create suggestions if [ "${{ github.event_name }}" = "pull_request" ]; then echo "Fetching base branch for comparison..." BASE_BRANCH="${{ steps.analyze-changes.outputs.base_branch }}" git fetch origin "$BASE_BRANCH" - echo "Comparing with origin/$BASE_BRANCH..." - if git diff --quiet "origin/$BASE_BRANCH..HEAD" -- "${{ inputs.changelog_path }}"; then - echo "has_changes=false" >> $GITHUB_OUTPUT - echo "DEBUG: No differences from base branch" - else + echo "Checking for non-changelog changes in PR..." + # Get all changed files excluding changelog + NON_CHANGELOG_CHANGES=$(git diff --name-only "origin/$BASE_BRANCH..HEAD" | grep -v -E "(CHANGELOG\.md|.*CHANGELOG\.md)$" || true) + + if [ -n "$NON_CHANGELOG_CHANGES" ]; then echo "has_changes=true" >> $GITHUB_OUTPUT - echo "DEBUG: Changelog differs from base branch" - echo "Diff preview:" - git diff "origin/$BASE_BRANCH..HEAD" -- "${{ inputs.changelog_path }}" || true + echo "DEBUG: PR has non-changelog changes, suggestions should be created" + echo "Non-changelog files changed:" + echo "$NON_CHANGELOG_CHANGES" + else + echo "has_changes=false" >> $GITHUB_OUTPUT + echo "DEBUG: PR only has changelog changes, skipping suggestions" fi else # For non-PR events, check local changes @@ -501,6 +508,11 @@ runs: git push - name: Create GitHub PR suggestions + # Only create suggestions if: + # - Changelog was generated (status == 'UPDATED') + # - This is a PR event + # - PR has non-changelog changes (has_changes == 'true') + # - User wants suggestions (create_pr_suggestions == 'true') if: | steps.check-status.outputs.status == 'UPDATED' && github.event_name == 'pull_request' && @@ -548,13 +560,12 @@ runs: if (changelogFile.status === 'added') { core.info('Changelog file is newly added - creating file replacement suggestion'); - // For new files, suggest inserting before the AI_APPEND_HERE marker - let fullNewChangelog; - if (currentChangelog.includes('')) { - fullNewChangelog = currentChangelog.replace(//, newContent); - } else { - fullNewChangelog = currentChangelog + '\n\n' + newContent; + // For new files, append content to existing changelog + let fullNewChangelog = currentChangelog.trimEnd(); + if (fullNewChangelog) { + fullNewChangelog += '\n\n'; } + fullNewChangelog += newContent + '\n'; await github.rest.pulls.createReview({ owner: context.repo.owner, @@ -578,12 +589,13 @@ runs: throw new Error('No patch available for modified file'); } - // Parse patch to find lines we can target + // Parse patch to find a suitable position for the suggestion const patchLines = patch.split('\n'); let bestPosition = null; let currentPosition = 0; + let lastContextLine = null; - // Look for the AI_APPEND_HERE marker in the patch + // Find the AI_CONTENT marker or the last added/context line for (let i = 0; i < patchLines.length; i++) { const line = patchLines[i]; if (line.startsWith('@@')) { @@ -594,36 +606,37 @@ runs: } } else if (line.startsWith('+')) { currentPosition++; - if (line.includes('AI_APPEND_HERE')) { + if (line.includes('CONTEXT_LEDGER_MARKER')) { bestPosition = currentPosition; - break; + break; // Found our marker, stop searching } + bestPosition = currentPosition; // Keep track of added lines } else if (line.startsWith(' ')) { currentPosition++; + if (line.includes('CONTEXT_LEDGER_MARKER')) { + bestPosition = currentPosition; + break; // Found our marker, stop searching + } + lastContextLine = currentPosition; // Track context lines as fallback } } - // If no marker found in patch, find last added line - if (!bestPosition) { - currentPosition = 0; - for (let i = 0; i < patchLines.length; i++) { - const line = patchLines[i]; - if (line.startsWith('@@')) { - const match = line.match(/@@ -\d+,?\d* \+(\d+),?\d* @@/); - if (match) { - currentPosition = parseInt(match[1]) - 1; - } - } else if (line.startsWith('+')) { - currentPosition++; - bestPosition = currentPosition; // Keep updating to get the last one - } else if (line.startsWith(' ')) { - currentPosition++; - } - } + // Use the best position found, or fall back to last context line + if (!bestPosition && lastContextLine) { + bestPosition = lastContextLine; } if (!bestPosition) { - throw new Error('Could not find suitable position in diff'); + core.warning('Could not find ideal position in diff, attempting to create comment without line suggestion'); + // Instead of throwing, create a general PR comment + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: `πŸ€– **AI Changelog Ready!**\n\nAdd this to your \`${targetPath}\`:\n\n\`\`\`markdown\n${newContent}\n\`\`\`\n\n*Note: Could not create an inline suggestion due to diff limitations. Please copy and paste the above content manually.*` + }); + core.info('βœ… Posted changelog as PR comment (could not create inline suggestion)'); + return; } // Create suggestion targeting the found position diff --git a/examples/migration-example.yml b/examples/migration-example.yml deleted file mode 100644 index 1b95100..0000000 --- a/examples/migration-example.yml +++ /dev/null @@ -1,155 +0,0 @@ -# Migration Example: Converting from inline workflow to the action -# -# BEFORE: Your existing .github/workflows/update-changelog.yml -# AFTER: This simplified version using the action - -name: Update Changelog with Claude - -on: - # Same triggers as your original workflow - pull_request: - branches: - - main - types: [opened, synchronize, reopened, ready_for_review] - paths-ignore: - - "docs/**" - - "**/CHANGELOG.md" - - "CHANGELOG.md" - - release: - types: [published] - - workflow_dispatch: - inputs: - target_changelog: - description: "Target changelog to update" - required: false - default: "project-wide" - type: choice - options: - - "project-wide" - - "decipher" - - "gf-crawler" - - "nextjs" - commit_range: - description: "Number of recent commits to analyze (default: 10)" - required: false - default: "10" - version_increment: - description: "Version increment type" - required: false - default: "auto" - type: choice - options: - - "auto" - - "patch" - - "minor" - - "major" - -permissions: - contents: write - pull-requests: write - issues: write - -jobs: - # Single job instead of complex multi-step workflow - update-changelog: - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.ref }} - fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} - - # Determine which changelog to update (same logic as before) - - name: Determine target changelog - id: determine-target - run: | - TARGET="${{ github.event.inputs.target_changelog || 'project-wide' }}" - - case "$TARGET" in - "decipher") - CHANGELOG_PATH="decipher/documentation/CHANGELOG.md" - ;; - "gf-crawler") - CHANGELOG_PATH="gf-crawler/CHANGELOG.md" - ;; - "nextjs") - CHANGELOG_PATH="nextjs/CHANGELOG.md" - ;; - "project-wide") - CHANGELOG_PATH="CHANGELOG.md" - ;; - *) - CHANGELOG_PATH="CHANGELOG.md" - ;; - esac - - echo "changelog_path=$CHANGELOG_PATH" >> $GITHUB_OUTPUT - echo "target=$TARGET" >> $GITHUB_OUTPUT - - # This replaces ~500 lines of complex workflow logic! - - name: Generate Changelog with Claude - id: changelog - uses: lukemun/context-ledger@v1 - with: - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - changelog_path: ${{ steps.determine-target.outputs.changelog_path }} - target_name: ${{ steps.determine-target.outputs.target }} - commit_range: ${{ github.event.inputs.commit_range || '10' }} - version_increment: ${{ github.event.inputs.version_increment || 'auto' }} - # For non-PR events, auto-commit the changes - auto_commit: ${{ github.event_name != 'pull_request' }} - - # Optional: Create summary (replaces the complex summary logic) - - name: Create summary - if: always() - run: | - echo "## Changelog Update Summary" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - - STATUS="${{ steps.changelog.outputs.status }}" - TARGET="${{ steps.determine-target.outputs.target }}" - CHANGELOG_PATH="${{ steps.determine-target.outputs.changelog_path }}" - - case "$STATUS" in - "UPDATED") - if [ "${{ steps.changelog.outputs.has_changes }}" = "true" ]; then - echo "βœ… **Changelog updated successfully**" >> $GITHUB_STEP_SUMMARY - echo "- Target: \`$TARGET\`" >> $GITHUB_STEP_SUMMARY - echo "- File: \`$CHANGELOG_PATH\`" >> $GITHUB_STEP_SUMMARY - echo "- Version: \`${{ steps.changelog.outputs.version_generated }}\`" >> $GITHUB_STEP_SUMMARY - else - echo "ℹ️ **No changes detected in changelog**" >> $GITHUB_STEP_SUMMARY - fi - ;; - "NO_UPDATE_NEEDED"|"SKIPPED") - echo "ℹ️ **No changelog update needed**" >> $GITHUB_STEP_SUMMARY - echo "- Claude determined recent changes don't warrant changelog entry" >> $GITHUB_STEP_SUMMARY - ;; - "ERROR") - echo "❌ **Error updating changelog**" >> $GITHUB_STEP_SUMMARY - echo "- Check the workflow logs for details" >> $GITHUB_STEP_SUMMARY - ;; - esac - -# Benefits of this migration: -# -# 1. βœ… Reduced from ~850 lines to ~100 lines -# 2. βœ… All complex logic moved to reusable action -# 3. βœ… Same functionality with better error handling -# 4. βœ… Easier to maintain and debug -# 5. βœ… Can be used across multiple repositories -# 6. βœ… Regular updates via Dependabot -# 7. βœ… Better testing and validation -# 8. βœ… Cleaner, more readable workflow -# -# Migration steps: -# 1. Add ANTHROPIC_API_KEY to repository secrets -# 2. Replace your existing workflow with this one -# 3. Update the action reference to: lukemun/context-ledger@v1 -# 4. Test with a sample PR -# 5. Customize inputs as needed for your use case diff --git a/lib/generate-changelog.js b/lib/generate-changelog.js index b21da62..76196df 100644 --- a/lib/generate-changelog.js +++ b/lib/generate-changelog.js @@ -145,33 +145,18 @@ function getGitDiff(commitCount, isPR, baseSha, headSha) { */ function getCurrentChangelog(changelogPath, maxLines = 100) { let currentChangelog = ''; - + try { // Try to get from main branch first to avoid conflicts const fullChangelog = execSync(`git show origin/main:${changelogPath}`, { encoding: 'utf8' }); console.log('Using latest changelog from main branch to avoid conflicts'); - + // Extract only recent entries for context (last N lines) const lines = fullChangelog.split('\n'); - - // Find the AI_APPEND_HERE marker or get last maxLines - const markerIndex = lines.findIndex(line => line.includes('')); - - if (markerIndex !== -1) { - // Get lines from beginning up to marker (usually recent entries are at top) - const startIndex = Math.max(0, markerIndex - maxLines); - currentChangelog = lines.slice(startIndex, markerIndex + 1).join('\n'); - - // Also include the header if we're cutting it off - if (startIndex > 10) { - const header = lines.slice(0, 10).join('\n'); - currentChangelog = header + '\n\n... [earlier entries omitted for context] ...\n\n' + currentChangelog; - } - } else { - // No marker, just get last maxLines - currentChangelog = lines.slice(-maxLines).join('\n'); - } - + + // Just get last maxLines for context + currentChangelog = lines.slice(-maxLines).join('\n'); + console.log(`Using ${currentChangelog.split('\n').length} lines of changelog for context`); } catch (error) { console.log('Could not get changelog from main branch, using local version'); @@ -352,27 +337,39 @@ async function updateChangelog() { newChangelogContent = newChangelogContent.replace(versionHeaderMatch[1], ''); } - // Insert new content before AI_APPEND_HERE marker or append to end + // Get the full current changelog from main branch or local file + let fullChangelog; + try { + fullChangelog = execSync(`git show origin/main:${changelogPath}`, { encoding: 'utf8' }); + } catch (error) { + if (fs.existsSync(changelogPath)) { + fullChangelog = fs.readFileSync(changelogPath, 'utf8'); + } else { + fullChangelog = ''; + } + } + + // Look for the Context Ledger marker + const marker = ''; let finalChangelog; - if (currentChangelog.includes('')) { - // Insert before the marker to preserve it - finalChangelog = currentChangelog.replace( - //, - `${newChangelogContent}\n\n` - ); + + if (fullChangelog.includes(marker)) { + // Insert new content before the marker + finalChangelog = fullChangelog.replace(marker, newChangelogContent + '\n\n' + marker); } else { - // No marker found, append to end and add marker - finalChangelog = currentChangelog; - if (!finalChangelog.endsWith('\n')) { - finalChangelog += '\n'; + // No marker found, append content and add marker + fullChangelog = fullChangelog.trimEnd(); + if (fullChangelog) { + finalChangelog = fullChangelog + '\n\n' + newChangelogContent + '\n\n' + marker + '\n'; + } else { + finalChangelog = newChangelogContent + '\n\n' + marker + '\n'; } - finalChangelog += '\n' + newChangelogContent + '\n\n'; } // Write files fs.writeFileSync(changelogPath, finalChangelog); - // Save the new content WITHOUT the AI_APPEND_HERE marker (it will be added during file write) + // Save the new content for the action to use fs.writeFileSync('new_content.txt', newChangelogContent); fs.writeFileSync('changelog_status.txt', 'UPDATED');