Skip to content

fix|ci: added additional headers. #599

fix|ci: added additional headers.

fix|ci: added additional headers. #599

Workflow file for this run

# 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"