Files
claude-skills-reference/.github/workflows/pr-issue-auto-close.yml
Alireza Rezvani 7fde8a90e9 Dev (#218)
* Improve senior-fullstack skill description and workflow validation

- Expand frontmatter description with concrete actions and trigger clauses
- Add validation steps to scaffolding workflow (verify scaffold succeeded)
- Add re-run verification step to audit workflow (confirm P0 fixes)

* chore: sync codex skills symlinks [automated]

* fix(skill): normalize senior-fullstack frontmatter to inline format

Normalize YAML description from block scalar (>) to inline single-line
format matching all other 50+ skills. Align frontmatter trigger phrases
with the body's Trigger Phrases section to eliminate duplication.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ci): add GITHUB_TOKEN to checkout + restore corrupted skill descriptions

- Add token: ${{ secrets.GITHUB_TOKEN }} to actions/checkout@v4 in
  sync-codex-skills.yml so git-auto-commit-action can push back to branch
  (fixes: fatal: could not read Username, exit 128)
- Restore correct description for incident-commander (was: 'Skill from engineering-team')
- Restore correct description for senior-fullstack (was: '>')

* fix(ci): pass PROJECTS_TOKEN to fix automated commits + remove duplicate checkout

Fixes PROJECTS_TOKEN passthrough for git-auto-commit-action and removes duplicate checkout step in pr-issue-auto-close workflow.

* fix(ci): remove stray merge conflict marker in sync-codex-skills.yml (#221)

Co-authored-by: Leo <leo@leo-agent-server>

* fix(ci): fix workflow errors + add OpenClaw support (#222)

---------

Co-authored-by: Baptiste Fernandez <fernandez.baptiste1@gmail.com>
Co-authored-by: alirezarezvani <5697919+alirezarezvani@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Leo <leo@leo-agent-server>
2026-02-25 10:58:52 +01:00

201 lines
7.1 KiB
YAML

---
name: Auto-Close Issues on PR Merge
'on':
pull_request:
types: [closed]
permissions:
issues: write
pull-requests: read
contents: read
jobs:
close-linked-issues:
name: Close Issues Linked in PR
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Check Workflow Kill Switch
run: |
if [ -f ".github/WORKFLOW_KILLSWITCH" ]; then
STATUS=$(grep "STATUS:" .github/WORKFLOW_KILLSWITCH | awk '{print $2}')
if [ "$STATUS" = "DISABLED" ]; then
echo "🛑 Workflows disabled by kill switch"
exit 0
fi
fi
- name: Extract linked issues from PR body
id: extract_issues
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const prBody = context.payload.pull_request.body || '';
const prNumber = context.payload.pull_request.number;
// Patterns to detect linked issues
// Supports: Fixes #123, Closes #456, Resolves #789, Related to #111, See #222
const patterns = [
/(?:fix|fixes|fixed|close|closes|closed|resolve|resolves|resolved)\s+#(\d+)/gi,
/(?:related\s+to|see|ref|references)\s+#(\d+)/gi
];
const issueNumbers = new Set();
// Extract issue numbers
patterns.forEach(pattern => {
let match;
while ((match = pattern.exec(prBody)) !== null) {
issueNumbers.add(match[1]);
}
});
// Also check PR title
const prTitle = context.payload.pull_request.title || '';
patterns.forEach(pattern => {
let match;
while ((match = pattern.exec(prTitle)) !== null) {
issueNumbers.add(match[1]);
}
});
// Also check commit messages in PR
const commits = await github.rest.pulls.listCommits({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber
});
commits.data.forEach(commit => {
const message = commit.commit.message;
patterns.forEach(pattern => {
let match;
while ((match = pattern.exec(message)) !== null) {
issueNumbers.add(match[1]);
}
});
});
const issues = Array.from(issueNumbers);
console.log(`Found linked issues: ${issues.join(', ')}`);
return issues;
- name: Close linked issues
if: steps.extract_issues.outputs.result != '[]'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const issueNumbers = ${{ steps.extract_issues.outputs.result }};
const prNumber = context.payload.pull_request.number;
const prTitle = context.payload.pull_request.title;
const prUrl = context.payload.pull_request.html_url;
const merger = context.payload.pull_request.merged_by.login;
console.log(`Processing ${issueNumbers.length} linked issue(s)`);
for (const issueNumber of issueNumbers) {
try {
// Get issue details first
const issue = await github.rest.issues.get({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: parseInt(issueNumber)
});
// Skip if already closed
if (issue.data.state === 'closed') {
console.log(`Issue #${issueNumber} already closed, skipping`);
continue;
}
// Add comment explaining closure
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: parseInt(issueNumber),
body: `Completed via PR #${prNumber}\n\nPR: ${prTitle}\nURL: ${prUrl}\nMerged by: ${merger}\n\nThis issue has been resolved and the changes have been merged into main.\n\nAutomatically closed via PR merge automation`
});
// Close the issue
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: parseInt(issueNumber),
state: 'closed',
state_reason: 'completed'
});
console.log(`✅ Closed issue #${issueNumber}`);
} catch (error) {
console.error(`❌ Failed to close issue #${issueNumber}: ${error.message}`);
}
}
- name: Update project board status
if: steps.extract_issues.outputs.result != '[]'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const issueNumbers = ${{ steps.extract_issues.outputs.result }};
for (const issueNumber of issueNumbers) {
try {
// Add status: done label
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: parseInt(issueNumber),
labels: ['status: done']
});
// Remove in-progress and in-review labels
const labelsToRemove = ['status: in-progress', 'status: in-review'];
for (const label of labelsToRemove) {
try {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: parseInt(issueNumber),
name: label
});
} catch (e) {
// Label might not exist, ignore error
}
}
console.log(`✅ Updated project status for issue #${issueNumber}`);
} catch (error) {
console.error(`❌ Failed to update status for issue #${issueNumber}: ${error.message}`);
}
}
- name: Summary
if: steps.extract_issues.outputs.result != '[]'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const issueNumbers = ${{ steps.extract_issues.outputs.result }};
const prNumber = context.payload.pull_request.number;
console.log(`
✅ PR #${prNumber} Merge Automation Complete
Closed issues: ${issueNumbers.join(', ')}
Updated project board: status → done
Comments added: Linked to PR #${prNumber}
All linked issues have been automatically closed and marked as done.
`);