fix|ci: added additional headers. #599
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Fix for ci-staging.yml - Handle "No commits between" error | |
| name: CI - Staging | |
| on: | |
| push: | |
| branches: | |
| - 'staging' | |
| pull_request: | |
| branches: | |
| - 'main' | |
| types: [opened, synchronize, reopened] | |
| jobs: | |
| staging_to_main_pr: | |
| name: 'Create/Update Staging to Main PR' | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'push' && github.ref_name == 'staging' | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| issues: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Check if staging has changes to release | |
| id: check_changes | |
| uses: actions/github-script@v8 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| // Compare staging with main to see if there are changes | |
| try { | |
| const comparison = await github.rest.repos.compareCommits({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| base: 'main', | |
| head: 'staging' | |
| }); | |
| const commitCount = comparison.data.commits.length; | |
| console.log(`Found ${commitCount} commits between main and staging`); | |
| if (commitCount === 0) { | |
| console.log('Staging is up-to-date with main - no PR needed'); | |
| return { | |
| hasChanges: false, | |
| commitCount: 0, | |
| message: 'Staging is already up-to-date with main' | |
| }; | |
| } | |
| console.log(`Staging has ${commitCount} commits ready for release`); | |
| return { | |
| hasChanges: true, | |
| commitCount: commitCount, | |
| commits: comparison.data.commits | |
| }; | |
| } catch (error) { | |
| console.log(`Error comparing branches: ${error.message}`); | |
| return { | |
| hasChanges: false, | |
| commitCount: 0, | |
| error: error.message | |
| }; | |
| } | |
| - name: Skip PR creation - no changes | |
| if: fromJson(steps.check_changes.outputs.result).hasChanges == false | |
| run: | | |
| echo "Staging to Main PR Status: UP TO DATE" | |
| echo "Staging branch is already synchronized with main" | |
| echo "No new changes to release" | |
| echo "" | |
| echo "This is normal and indicates:" | |
| echo "- Previous release was successfully merged" | |
| echo "- No new features have been promoted to staging yet" | |
| echo "- System is in a clean state" | |
| - name: Check if staging→main PR exists | |
| if: fromJson(steps.check_changes.outputs.result).hasChanges == true | |
| id: check_pr | |
| uses: actions/github-script@v8 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const existingPRs = await github.rest.pulls.list({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| head: `${context.repo.owner}:staging`, | |
| base: 'main', | |
| state: 'open' | |
| }); | |
| if (existingPRs.data.length > 0) { | |
| console.log(`Found existing PR: #${existingPRs.data[0].number}`); | |
| return { | |
| exists: true, | |
| number: existingPRs.data[0].number, | |
| url: existingPRs.data[0].html_url | |
| }; | |
| } else { | |
| console.log('No existing staging→main PR found'); | |
| return { exists: false }; | |
| } | |
| - name: Create new staging→main PR | |
| if: fromJson(steps.check_changes.outputs.result).hasChanges == true && fromJson(steps.check_pr.outputs.result).exists == false | |
| id: create_pr | |
| uses: actions/github-script@v8 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const commitCount = ${{ fromJson(steps.check_changes.outputs.result).commitCount }}; | |
| const pr = await github.rest.pulls.create({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| title: `Production Release: Staging → Main (${commitCount} commits)`, | |
| head: 'staging', | |
| base: 'main', | |
| body: `## Production Release from Staging | |
| This PR contains **${commitCount} commits** tested and validated in staging, ready for production deployment. | |
| ### Pre-merge Checklist | |
| - [ ] All tests passing | |
| - [ ] No merge conflicts | |
| - [ ] Changes reviewed and approved | |
| - [ ] Version bumped (if applicable) | |
| - [ ] Documentation updated | |
| ### Merge Strategy | |
| **This PR will be MERGE COMMITTED to maintain history** | |
| ### Changes will be categorized below... | |
| --- | |
| *This PR is automatically maintained by CI*`, | |
| draft: false | |
| }); | |
| // Add labels | |
| await github.rest.issues.addLabels({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr.data.number, | |
| labels: ['production', 'staging→main', 'auto-pr'] | |
| }); | |
| console.log(`Created PR #${pr.data.number}`); | |
| return { number: pr.data.number, url: pr.data.html_url }; | |
| # Only run the update step if we have changes and a PR | |
| - name: Update PR with detailed changes | |
| if: fromJson(steps.check_changes.outputs.result).hasChanges == true | |
| id: update_pr | |
| uses: actions/github-script@v8 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| // Get PR number from either existing or newly created PR | |
| let prNumber = null; | |
| const checkResult = ${{ toJson(steps.check_pr.outputs.result) }}; | |
| const createResult = ${{ toJson(steps.create_pr.outputs.result) }}; | |
| if (checkResult && checkResult.exists) { | |
| prNumber = checkResult.number; | |
| } else if (createResult && createResult.number) { | |
| prNumber = createResult.number; | |
| } | |
| if (!prNumber) { | |
| console.log('No PR to update'); | |
| return; | |
| } | |
| // Get commits between main and staging | |
| const comparison = await github.rest.repos.compareCommits({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| base: 'main', | |
| head: 'staging' | |
| }); | |
| const commits = comparison.data.commits; | |
| console.log(`Found ${commits.length} commits`); | |
| // Categorize commits | |
| const categories = { | |
| feat: [], | |
| fix: [], | |
| docs: [], | |
| perf: [], | |
| refactor: [], | |
| test: [], | |
| ci: [], | |
| chore: [], | |
| other: [] | |
| }; | |
| // Track merged PRs for better context | |
| const mergedPRs = new Set(); | |
| commits.forEach(commit => { | |
| const message = commit.commit.message; | |
| const firstLine = message.split('\n')[0]; | |
| const sha = commit.sha.substring(0, 7); | |
| // Check if this is a squashed PR merge | |
| const prMatch = message.match(/#(\d+)/); | |
| if (prMatch) { | |
| mergedPRs.add(prMatch[1]); | |
| } | |
| const commitLine = `- ${firstLine} (\`${sha}\`)`; | |
| if (message.match(/^feat(\(.+\))?:/i)) { | |
| categories.feat.push(commitLine); | |
| } else if (message.match(/^fix(\(.+\))?:/i)) { | |
| categories.fix.push(commitLine); | |
| } else if (message.match(/^docs(\(.+\))?:/i)) { | |
| categories.docs.push(commitLine); | |
| } else if (message.match(/^perf(\(.+\))?:/i)) { | |
| categories.perf.push(commitLine); | |
| } else if (message.match(/^refactor(\(.+\))?:/i)) { | |
| categories.refactor.push(commitLine); | |
| } else if (message.match(/^test(\(.+\))?:/i)) { | |
| categories.test.push(commitLine); | |
| } else if (message.match(/^ci(\(.+\))?:/i)) { | |
| categories.ci.push(commitLine); | |
| } else if (message.match(/^chore(\(.+\))?:/i)) { | |
| categories.chore.push(commitLine); | |
| } else { | |
| categories.other.push(commitLine); | |
| } | |
| }); | |
| // Build comprehensive PR body | |
| let prBody = `## Production Release from Staging\n\n`; | |
| prBody += `### Summary\n`; | |
| prBody += `- **Total Commits:** ${commits.length}\n`; | |
| prBody += `- **Merged PRs:** ${mergedPRs.size}\n`; | |
| prBody += `- **Target Branch:** main\n`; | |
| prBody += `- **Merge Strategy:** Merge Commit\n\n`; | |
| prBody += `### Pre-merge Checklist\n`; | |
| prBody += `- [ ] All tests passing\n`; | |
| prBody += `- [ ] No merge conflicts\n`; | |
| prBody += `- [ ] Changes reviewed and approved\n`; | |
| prBody += `- [ ] Version bumped (if applicable)\n`; | |
| prBody += `- [ ] Documentation updated\n\n`; | |
| prBody += `### Changes by Category\n\n`; | |
| if (categories.feat.length > 0) { | |
| prBody += `#### New Features\n${categories.feat.join('\n')}\n\n`; | |
| } | |
| if (categories.fix.length > 0) { | |
| prBody += `#### Bug Fixes\n${categories.fix.join('\n')}\n\n`; | |
| } | |
| if (categories.perf.length > 0) { | |
| prBody += `#### Performance Improvements\n${categories.perf.join('\n')}\n\n`; | |
| } | |
| if (categories.refactor.length > 0) { | |
| prBody += `#### Code Refactoring\n${categories.refactor.join('\n')}\n\n`; | |
| } | |
| if (categories.docs.length > 0) { | |
| prBody += `#### Documentation\n${categories.docs.join('\n')}\n\n`; | |
| } | |
| if (categories.test.length > 0) { | |
| prBody += `#### Tests\n${categories.test.join('\n')}\n\n`; | |
| } | |
| if (categories.ci.length > 0) { | |
| prBody += `#### CI/CD\n${categories.ci.join('\n')}\n\n`; | |
| } | |
| if (categories.chore.length > 0) { | |
| prBody += `#### Chores\n${categories.chore.join('\n')}\n\n`; | |
| } | |
| if (categories.other.length > 0) { | |
| prBody += `#### Other Changes\n${categories.other.join('\n')}\n\n`; | |
| } | |
| prBody += `### Important Notes\n`; | |
| prBody += `- This PR should be **MERGE COMMITTED** to maintain history\n`; | |
| prBody += `- Ensure all checks pass before merging\n`; | |
| prBody += `- After merge, staging will be automatically synced with main\n\n`; | |
| prBody += `---\n`; | |
| prBody += `*This PR is automatically maintained by CI • Last updated: ${new Date().toISOString()}*`; | |
| // Update the PR | |
| await github.rest.pulls.update({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: prNumber, | |
| body: prBody | |
| }); | |
| console.log(`Updated PR #${prNumber} with detailed categorized commits`); | |
| - name: Summary | |
| run: | | |
| if [[ "${{ fromJson(steps.check_changes.outputs.result).hasChanges }}" == "true" ]]; then | |
| echo "Staging→Main PR: CREATED/UPDATED" | |
| echo "Commits ready for release: ${{ fromJson(steps.check_changes.outputs.result).commitCount }}" | |
| else | |
| echo "Staging→Main PR: NOT NEEDED" | |
| echo "Staging is already up-to-date with main" | |
| fi | |
| validate_staging_pr: | |
| name: 'Validate Staging→Main PR' | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' && github.base_ref == 'main' && github.head_ref == 'staging' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check for conflicts | |
| run: | | |
| echo "Checking for merge conflicts..." | |
| # Try to merge main into staging (dry run) | |
| git config user.name "CI Bot" | |
| git config user.email "[email protected]" | |
| if git merge --no-commit --no-ff origin/main; then | |
| echo "No merge conflicts detected" | |
| git merge --abort | |
| else | |
| echo "Merge conflicts detected! Please resolve before merging." | |
| git merge --abort | |
| exit 1 | |
| fi | |
| - name: Summary | |
| run: | | |
| echo "Staging→Main PR Validation Summary" | |
| echo "======================================" | |
| echo "PR validation completed" | |
| echo "Remember to:" | |
| echo " - Review all changes carefully" | |
| echo " - Ensure all tests pass" | |
| echo " - Use MERGE COMMIT to maintain history" | |
| echo " - Update version numbers if needed" |