Automate GitHub workflows with AI assistance. Includes PR reviews, issue triage, CI/CD integration, and Git operations. Use when automating GitHub workflows, setting up PR review automation, creating GitHub Actions, or triaging issues.
Add this skill
npx mdskills install sickn33/github-workflow-automationComprehensive GitHub automation patterns with well-structured workflows and practical examples
1---2name: github-workflow-automation3description: "Automate GitHub workflows with AI assistance. Includes PR reviews, issue triage, CI/CD integration, and Git operations. Use when automating GitHub workflows, setting up PR review automation, creating GitHub Actions, or triaging issues."4---56# ๐ง GitHub Workflow Automation78> Patterns for automating GitHub workflows with AI assistance, inspired by [Gemini CLI](https://github.com/google-gemini/gemini-cli) and modern DevOps practices.910## When to Use This Skill1112Use this skill when:1314- Automating PR reviews with AI15- Setting up issue triage automation16- Creating GitHub Actions workflows17- Integrating AI into CI/CD pipelines18- Automating Git operations (rebases, cherry-picks)1920---2122## 1. Automated PR Review2324### 1.1 PR Review Action2526```yaml27# .github/workflows/ai-review.yml28name: AI Code Review2930on:31 pull_request:32 types: [opened, synchronize]3334jobs:35 review:36 runs-on: ubuntu-latest37 permissions:38 contents: read39 pull-requests: write4041 steps:42 - uses: actions/checkout@v443 with:44 fetch-depth: 04546 - name: Get changed files47 id: changed48 run: |49 files=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)50 echo "files<<EOF" >> $GITHUB_OUTPUT51 echo "$files" >> $GITHUB_OUTPUT52 echo "EOF" >> $GITHUB_OUTPUT5354 - name: Get diff55 id: diff56 run: |57 diff=$(git diff origin/${{ github.base_ref }}...HEAD)58 echo "diff<<EOF" >> $GITHUB_OUTPUT59 echo "$diff" >> $GITHUB_OUTPUT60 echo "EOF" >> $GITHUB_OUTPUT6162 - name: AI Review63 uses: actions/github-script@v764 with:65 script: |66 const { Anthropic } = require('@anthropic-ai/sdk');67 const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });6869 const response = await client.messages.create({70 model: "claude-3-sonnet-20240229",71 max_tokens: 4096,72 messages: [{73 role: "user",74 content: `Review this PR diff and provide feedback:7576 Changed files: ${{ steps.changed.outputs.files }}7778 Diff:79 ${{ steps.diff.outputs.diff }}8081 Provide:82 1. Summary of changes83 2. Potential issues or bugs84 3. Suggestions for improvement85 4. Security concerns if any8687 Format as GitHub markdown.`88 }]89 });9091 await github.rest.pulls.createReview({92 owner: context.repo.owner,93 repo: context.repo.repo,94 pull_number: context.issue.number,95 body: response.content[0].text,96 event: 'COMMENT'97 });98 env:99 ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}100```101102### 1.2 Review Comment Patterns103104````markdown105# AI Review Structure106107## ๐ Summary108109Brief description of what this PR does.110111## โ What looks good112113- Well-structured code114- Good test coverage115- Clear naming conventions116117## โ ๏ธ Potential Issues1181191. **Line 42**: Possible null pointer exception120 ```javascript121 // Current122 user.profile.name;123 // Suggested124 user?.profile?.name ?? "Unknown";125 ```126````1271282. **Line 78**: Consider error handling129 ```javascript130 // Add try-catch or .catch()131 ```132133## ๐ก Suggestions134135- Consider extracting the validation logic into a separate function136- Add JSDoc comments for public methods137138## ๐ Security Notes139140- No sensitive data exposure detected141- API key handling looks correct142143````144145### 1.3 Focused Reviews146147```yaml148# Review only specific file types149- name: Filter code files150 run: |151 files=$(git diff --name-only origin/${{ github.base_ref }}...HEAD | \152 grep -E '\.(ts|tsx|js|jsx|py|go)$' || true)153 echo "code_files=$files" >> $GITHUB_OUTPUT154155# Review with context156- name: AI Review with context157 run: |158 # Include relevant context files159 context=""160 for file in ${{ steps.changed.outputs.files }}; do161 if [[ -f "$file" ]]; then162 context+="=== $file ===\n$(cat $file)\n\n"163 fi164 done165166 # Send to AI with full file context167````168169---170171## 2. Issue Triage Automation172173### 2.1 Auto-label Issues174175```yaml176# .github/workflows/issue-triage.yml177name: Issue Triage178179on:180 issues:181 types: [opened]182183jobs:184 triage:185 runs-on: ubuntu-latest186 permissions:187 issues: write188189 steps:190 - name: Analyze issue191 uses: actions/github-script@v7192 with:193 script: |194 const issue = context.payload.issue;195196 // Call AI to analyze197 const analysis = await analyzeIssue(issue.title, issue.body);198199 // Apply labels200 const labels = [];201202 if (analysis.type === 'bug') {203 labels.push('bug');204 if (analysis.severity === 'high') labels.push('priority: high');205 } else if (analysis.type === 'feature') {206 labels.push('enhancement');207 } else if (analysis.type === 'question') {208 labels.push('question');209 }210211 if (analysis.area) {212 labels.push(`area: ${analysis.area}`);213 }214215 await github.rest.issues.addLabels({216 owner: context.repo.owner,217 repo: context.repo.repo,218 issue_number: issue.number,219 labels: labels220 });221222 // Add initial response223 if (analysis.type === 'bug' && !analysis.hasReproSteps) {224 await github.rest.issues.createComment({225 owner: context.repo.owner,226 repo: context.repo.repo,227 issue_number: issue.number,228 body: `Thanks for reporting this issue!229230To help us investigate, could you please provide:231- Steps to reproduce the issue232- Expected behavior233- Actual behavior234- Environment (OS, version, etc.)235236This will help us resolve your issue faster. ๐`237 });238 }239```240241### 2.2 Issue Analysis Prompt242243```typescript244const TRIAGE_PROMPT = `245Analyze this GitHub issue and classify it:246247Title: {title}248Body: {body}249250Return JSON with:251{252 "type": "bug" | "feature" | "question" | "docs" | "other",253 "severity": "low" | "medium" | "high" | "critical",254 "area": "frontend" | "backend" | "api" | "docs" | "ci" | "other",255 "summary": "one-line summary",256 "hasReproSteps": boolean,257 "isFirstContribution": boolean,258 "suggestedLabels": ["label1", "label2"],259 "suggestedAssignees": ["username"] // based on area expertise260}261`;262```263264### 2.3 Stale Issue Management265266```yaml267# .github/workflows/stale.yml268name: Manage Stale Issues269270on:271 schedule:272 - cron: "0 0 * * *" # Daily273274jobs:275 stale:276 runs-on: ubuntu-latest277 steps:278 - uses: actions/stale@v9279 with:280 stale-issue-message: |281 This issue has been automatically marked as stale because it has not had282 recent activity. It will be closed in 14 days if no further activity occurs.283284 If this issue is still relevant:285 - Add a comment with an update286 - Remove the `stale` label287288 Thank you for your contributions! ๐289290 stale-pr-message: |291 This PR has been automatically marked as stale. Please update it or it292 will be closed in 14 days.293294 days-before-stale: 60295 days-before-close: 14296 stale-issue-label: "stale"297 stale-pr-label: "stale"298 exempt-issue-labels: "pinned,security,in-progress"299 exempt-pr-labels: "pinned,security"300```301302---303304## 3. CI/CD Integration305306### 3.1 Smart Test Selection307308```yaml309# .github/workflows/smart-tests.yml310name: Smart Test Selection311312on:313 pull_request:314315jobs:316 analyze:317 runs-on: ubuntu-latest318 outputs:319 test_suites: ${{ steps.analyze.outputs.suites }}320321 steps:322 - uses: actions/checkout@v4323 with:324 fetch-depth: 0325326 - name: Analyze changes327 id: analyze328 run: |329 # Get changed files330 changed=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)331332 # Determine which test suites to run333 suites="[]"334335 if echo "$changed" | grep -q "^src/api/"; then336 suites=$(echo $suites | jq '. + ["api"]')337 fi338339 if echo "$changed" | grep -q "^src/frontend/"; then340 suites=$(echo $suites | jq '. + ["frontend"]')341 fi342343 if echo "$changed" | grep -q "^src/database/"; then344 suites=$(echo $suites | jq '. + ["database", "api"]')345 fi346347 # If nothing specific, run all348 if [ "$suites" = "[]" ]; then349 suites='["all"]'350 fi351352 echo "suites=$suites" >> $GITHUB_OUTPUT353354 test:355 needs: analyze356 runs-on: ubuntu-latest357 strategy:358 matrix:359 suite: ${{ fromJson(needs.analyze.outputs.test_suites) }}360361 steps:362 - uses: actions/checkout@v4363364 - name: Run tests365 run: |366 if [ "${{ matrix.suite }}" = "all" ]; then367 npm test368 else369 npm test -- --suite ${{ matrix.suite }}370 fi371```372373### 3.2 Deployment with AI Validation374375```yaml376# .github/workflows/deploy.yml377name: Deploy with AI Validation378379on:380 push:381 branches: [main]382383jobs:384 validate:385 runs-on: ubuntu-latest386 steps:387 - uses: actions/checkout@v4388389 - name: Get deployment changes390 id: changes391 run: |392 # Get commits since last deployment393 last_deploy=$(git describe --tags --abbrev=0 2>/dev/null || echo "")394 if [ -n "$last_deploy" ]; then395 changes=$(git log --oneline $last_deploy..HEAD)396 else397 changes=$(git log --oneline -10)398 fi399 echo "changes<<EOF" >> $GITHUB_OUTPUT400 echo "$changes" >> $GITHUB_OUTPUT401 echo "EOF" >> $GITHUB_OUTPUT402403 - name: AI Risk Assessment404 id: assess405 uses: actions/github-script@v7406 with:407 script: |408 // Analyze changes for deployment risk409 const prompt = `410 Analyze these changes for deployment risk:411412 ${process.env.CHANGES}413414 Return JSON:415 {416 "riskLevel": "low" | "medium" | "high",417 "concerns": ["concern1", "concern2"],418 "recommendations": ["rec1", "rec2"],419 "requiresManualApproval": boolean420 }421 `;422423 // Call AI and parse response424 const analysis = await callAI(prompt);425426 if (analysis.riskLevel === 'high') {427 core.setFailed('High-risk deployment detected. Manual review required.');428 }429430 return analysis;431 env:432 CHANGES: ${{ steps.changes.outputs.changes }}433434 deploy:435 needs: validate436 runs-on: ubuntu-latest437 environment: production438 steps:439 - name: Deploy440 run: |441 echo "Deploying to production..."442 # Deployment commands here443```444445### 3.3 Rollback Automation446447```yaml448# .github/workflows/rollback.yml449name: Automated Rollback450451on:452 workflow_dispatch:453 inputs:454 reason:455 description: "Reason for rollback"456 required: true457458jobs:459 rollback:460 runs-on: ubuntu-latest461 steps:462 - uses: actions/checkout@v4463 with:464 fetch-depth: 0465466 - name: Find last stable version467 id: stable468 run: |469 # Find last successful deployment470 stable=$(git tag -l 'v*' --sort=-version:refname | head -1)471 echo "version=$stable" >> $GITHUB_OUTPUT472473 - name: Rollback474 run: |475 git checkout ${{ steps.stable.outputs.version }}476 # Deploy stable version477 npm run deploy478479 - name: Notify team480 uses: slackapi/slack-github-action@v1481 with:482 payload: |483 {484 "text": "๐ Production rolled back to ${{ steps.stable.outputs.version }}",485 "blocks": [486 {487 "type": "section",488 "text": {489 "type": "mrkdwn",490 "text": "*Rollback executed*\nโข Version: `${{ steps.stable.outputs.version }}`\nโข Reason: ${{ inputs.reason }}\nโข Triggered by: ${{ github.actor }}"491 }492 }493 ]494 }495```496497---498499## 4. Git Operations500501### 4.1 Automated Rebasing502503```yaml504# .github/workflows/auto-rebase.yml505name: Auto Rebase506507on:508 issue_comment:509 types: [created]510511jobs:512 rebase:513 if: github.event.issue.pull_request && contains(github.event.comment.body, '/rebase')514 runs-on: ubuntu-latest515516 steps:517 - uses: actions/checkout@v4518 with:519 fetch-depth: 0520 token: ${{ secrets.GITHUB_TOKEN }}521522 - name: Setup Git523 run: |524 git config user.name "github-actions[bot]"525 git config user.email "github-actions[bot]@users.noreply.github.com"526527 - name: Rebase PR528 run: |529 # Fetch PR branch530 gh pr checkout ${{ github.event.issue.number }}531532 # Rebase onto main533 git fetch origin main534 git rebase origin/main535536 # Force push537 git push --force-with-lease538 env:539 GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}540541 - name: Comment result542 uses: actions/github-script@v7543 with:544 script: |545 github.rest.issues.createComment({546 owner: context.repo.owner,547 repo: context.repo.repo,548 issue_number: context.issue.number,549 body: 'โ Successfully rebased onto main!'550 })551```552553### 4.2 Smart Cherry-Pick554555```typescript556// AI-assisted cherry-pick that handles conflicts557async function smartCherryPick(commitHash: string, targetBranch: string) {558 // Get commit info559 const commitInfo = await exec(`git show ${commitHash} --stat`);560561 // Check for potential conflicts562 const targetDiff = await exec(563 `git diff ${targetBranch}...HEAD -- ${affectedFiles}`564 );565566 // AI analysis567 const analysis = await ai.analyze(`568 I need to cherry-pick this commit to ${targetBranch}:569570 ${commitInfo}571572 Current state of affected files on ${targetBranch}:573 ${targetDiff}574575 Will there be conflicts? If so, suggest resolution strategy.576 `);577578 if (analysis.willConflict) {579 // Create branch for manual resolution580 await exec(581 `git checkout -b cherry-pick-${commitHash.slice(0, 7)} ${targetBranch}`582 );583 const result = await exec(`git cherry-pick ${commitHash}`, {584 allowFail: true,585 });586587 if (result.failed) {588 // AI-assisted conflict resolution589 const conflicts = await getConflicts();590 for (const conflict of conflicts) {591 const resolution = await ai.resolveConflict(conflict);592 await applyResolution(conflict.file, resolution);593 }594 }595 } else {596 await exec(`git checkout ${targetBranch}`);597 await exec(`git cherry-pick ${commitHash}`);598 }599}600```601602### 4.3 Branch Cleanup603604```yaml605# .github/workflows/branch-cleanup.yml606name: Branch Cleanup607608on:609 schedule:610 - cron: '0 0 * * 0' # Weekly611 workflow_dispatch:612613jobs:614 cleanup:615 runs-on: ubuntu-latest616 steps:617 - uses: actions/checkout@v4618 with:619 fetch-depth: 0620621 - name: Find stale branches622 id: stale623 run: |624 # Branches not updated in 30 days625 stale=$(git for-each-ref --sort=-committerdate refs/remotes/origin \626 --format='%(refname:short) %(committerdate:relative)' | \627 grep -E '[3-9][0-9]+ days|[0-9]+ months|[0-9]+ years' | \628 grep -v 'origin/main\|origin/develop' | \629 cut -d' ' -f1 | sed 's|origin/||')630631 echo "branches<<EOF" >> $GITHUB_OUTPUT632 echo "$stale" >> $GITHUB_OUTPUT633 echo "EOF" >> $GITHUB_OUTPUT634635 - name: Create cleanup PR636 if: steps.stale.outputs.branches != ''637 uses: actions/github-script@v7638 with:639 script: |640 const branches = `${{ steps.stale.outputs.branches }}`.split('\n').filter(Boolean);641642 const body = `## ๐งน Stale Branch Cleanup643644The following branches haven't been updated in over 30 days:645646${branches.map(b => `- \`${b}\``).join('\n')}647648### Actions:649- [ ] Review each branch650- [ ] Delete branches that are no longer needed651- Comment \`/keep branch-name\` to preserve specific branches652`;653654 await github.rest.issues.create({655 owner: context.repo.owner,656 repo: context.repo.repo,657 title: 'Stale Branch Cleanup',658 body: body,659 labels: ['housekeeping']660 });661```662663---664665## 5. On-Demand Assistance666667### 5.1 @mention Bot668669```yaml670# .github/workflows/mention-bot.yml671name: AI Mention Bot672673on:674 issue_comment:675 types: [created]676 pull_request_review_comment:677 types: [created]678679jobs:680 respond:681 if: contains(github.event.comment.body, '@ai-helper')682 runs-on: ubuntu-latest683684 steps:685 - uses: actions/checkout@v4686687 - name: Extract question688 id: question689 run: |690 # Extract text after @ai-helper691 question=$(echo "${{ github.event.comment.body }}" | sed 's/.*@ai-helper//')692 echo "question=$question" >> $GITHUB_OUTPUT693694 - name: Get context695 id: context696 run: |697 if [ "${{ github.event.issue.pull_request }}" != "" ]; then698 # It's a PR - get diff699 gh pr diff ${{ github.event.issue.number }} > context.txt700 else701 # It's an issue - get description702 gh issue view ${{ github.event.issue.number }} --json body -q .body > context.txt703 fi704 echo "context=$(cat context.txt)" >> $GITHUB_OUTPUT705 env:706 GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}707708 - name: AI Response709 uses: actions/github-script@v7710 with:711 script: |712 const response = await ai.chat(`713 Context: ${process.env.CONTEXT}714715 Question: ${process.env.QUESTION}716717 Provide a helpful, specific answer. Include code examples if relevant.718 `);719720 await github.rest.issues.createComment({721 owner: context.repo.owner,722 repo: context.repo.repo,723 issue_number: context.issue.number,724 body: response725 });726 env:727 CONTEXT: ${{ steps.context.outputs.context }}728 QUESTION: ${{ steps.question.outputs.question }}729```730731### 5.2 Command Patterns732733```markdown734## Available Commands735736| Command | Description |737| :------------------- | :-------------------------- |738| `@ai-helper explain` | Explain the code in this PR |739| `@ai-helper review` | Request AI code review |740| `@ai-helper fix` | Suggest fixes for issues |741| `@ai-helper test` | Generate test cases |742| `@ai-helper docs` | Generate documentation |743| `/rebase` | Rebase PR onto main |744| `/update` | Update PR branch from main |745| `/approve` | Mark as approved by bot |746| `/label bug` | Add 'bug' label |747| `/assign @user` | Assign to user |748```749750---751752## 6. Repository Configuration753754### 6.1 CODEOWNERS755756```757# .github/CODEOWNERS758759# Global owners760* @org/core-team761762# Frontend763/src/frontend/ @org/frontend-team764*.tsx @org/frontend-team765*.css @org/frontend-team766767# Backend768/src/api/ @org/backend-team769/src/database/ @org/backend-team770771# Infrastructure772/.github/ @org/devops-team773/terraform/ @org/devops-team774Dockerfile @org/devops-team775776# Docs777/docs/ @org/docs-team778*.md @org/docs-team779780# Security-sensitive781/src/auth/ @org/security-team782/src/crypto/ @org/security-team783```784785### 6.2 Branch Protection786787```yaml788# Set up via GitHub API789- name: Configure branch protection790 uses: actions/github-script@v7791 with:792 script: |793 await github.rest.repos.updateBranchProtection({794 owner: context.repo.owner,795 repo: context.repo.repo,796 branch: 'main',797 required_status_checks: {798 strict: true,799 contexts: ['test', 'lint', 'ai-review']800 },801 enforce_admins: true,802 required_pull_request_reviews: {803 required_approving_review_count: 1,804 require_code_owner_reviews: true,805 dismiss_stale_reviews: true806 },807 restrictions: null,808 required_linear_history: true,809 allow_force_pushes: false,810 allow_deletions: false811 });812```813814---815816## Best Practices817818### Security819820- [ ] Store API keys in GitHub Secrets821- [ ] Use minimal permissions in workflows822- [ ] Validate all inputs823- [ ] Don't expose sensitive data in logs824825### Performance826827- [ ] Cache dependencies828- [ ] Use matrix builds for parallel testing829- [ ] Skip unnecessary jobs with path filters830- [ ] Use self-hosted runners for heavy workloads831832### Reliability833834- [ ] Add timeouts to jobs835- [ ] Handle rate limits gracefully836- [ ] Implement retry logic837- [ ] Have rollback procedures838839---840841## Resources842843- [Gemini CLI GitHub Action](https://github.com/google-github-actions/run-gemini-cli)844- [GitHub Actions Documentation](https://docs.github.com/en/actions)845- [GitHub REST API](https://docs.github.com/en/rest)846- [CODEOWNERS Syntax](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners)847
Full transparency โ inspect the skill content before installing.