Merge pull request #98 from XAOSTECH:anglicise/20260316-032939 #238
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
| name: Bash Linting with Auto-Fix Suggestions | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - master | |
| - develop | |
| paths: | |
| - '**.sh' | |
| - '.github/workflows/bash-lint-advanced.yml' | |
| pull_request: | |
| branches: | |
| - main | |
| - master | |
| - develop | |
| paths: | |
| - '**.sh' | |
| workflow_dispatch: | |
| inputs: | |
| auto_fix: | |
| description: 'Attempt automatic fixes for common issues' | |
| required: false | |
| default: 'false' | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| checks: write | |
| actions: read | |
| jobs: | |
| shellcheck-analysis: | |
| name: ShellCheck Analysis & Suggestions | |
| runs-on: ubuntu-latest | |
| outputs: | |
| errors: ${{ steps.shellcheck.outputs.errors }} | |
| warnings: ${{ steps.shellcheck.outputs.warnings }} | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| checks: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Install tools | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y shellcheck | |
| - name: Run ShellCheck with JSON output | |
| id: shellcheck | |
| continue-on-error: true | |
| run: | | |
| mkdir -p /tmp/lint | |
| # Run ShellCheck for all scripts | |
| find . -type f -name "*.sh" ! -path "./.git/*" ! -path "./node_modules/*" ! -path "./vendor/*" \ | |
| -exec shellcheck -f json {} + > /tmp/lint/results.json 2>&1 || true | |
| # Count issues | |
| ERRORS=$(jq '[.[] | select(.level=="error")] | length' /tmp/lint/results.json 2>/dev/null || echo "0") | |
| WARNINGS=$(jq '[.[] | select(.level=="warning")] | length' /tmp/lint/results.json 2>/dev/null || echo "0") | |
| echo "errors=$ERRORS" >> $GITHUB_OUTPUT | |
| echo "warnings=$WARNINGS" >> $GITHUB_OUTPUT | |
| cat /tmp/lint/results.json | |
| - name: Parse and annotate issues | |
| if: always() | |
| run: | | |
| if [[ ! -f /tmp/lint/results.json ]]; then | |
| echo "No linting results found" | |
| exit 0 | |
| fi | |
| jq -r '.[] | "::notice file=\(.file),line=\(.line),col=\(.column)::[\(.code)] \(.message) - See: https://www.shellcheck.net/wiki/\(.code)"' /tmp/lint/results.json 2>/dev/null || true | |
| - name: Generate suggestions markdown | |
| id: suggestions | |
| run: | | |
| { | |
| echo "# 🔍 Bash Linting Report" | |
| echo "" | |
| echo "**Errors**: ${{ steps.shellcheck.outputs.errors }}" | |
| echo "**Warnings**: ${{ steps.shellcheck.outputs.warnings }}" | |
| echo "" | |
| if [[ -f /tmp/lint/results.json ]]; then | |
| echo "## Issues Found:" | |
| echo "" | |
| jq -r '.[] | | |
| "### [\(.code)] \(.message)\n" + | |
| "- **File**: `\(.file)` (line \(.line), col \(.column))\n" + | |
| "- **Severity**: \(.level)\n" + | |
| "- **Wiki**: https://www.shellcheck.net/wiki/\(.code)\n"' /tmp/lint/results.json 2>/dev/null || echo "No issues parsed" | |
| fi | |
| } > /tmp/lint/suggestions.md | |
| cat /tmp/lint/suggestions.md | |
| - name: Suggest fixes on PR | |
| if: github.event_name == 'pull_request' && (steps.shellcheck.outputs.errors > 0 || steps.shellcheck.outputs.warnings > 0) | |
| uses: actions/github-script@v8 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const results = JSON.parse(fs.readFileSync('/tmp/lint/results.json', 'utf8')); | |
| if (results.length === 0) return; | |
| // Group issues by file | |
| const issuesByFile = {}; | |
| results.forEach(issue => { | |
| if (!issuesByFile[issue.file]) { | |
| issuesByFile[issue.file] = []; | |
| } | |
| issuesByFile[issue.file].push(issue); | |
| }); | |
| // Create review with suggestions | |
| const reviewComments = []; | |
| Object.entries(issuesByFile).forEach(([file, issues]) => { | |
| issues.forEach(issue => { | |
| reviewComments.push({ | |
| path: file, | |
| line: issue.line, | |
| side: 'RIGHT', | |
| body: `**[SC${issue.code}]** ${issue.message}\n\n[📖 Read more](https://www.shellcheck.net/wiki/${issue.code})` | |
| }); | |
| }); | |
| }); | |
| if (reviewComments.length > 0) { | |
| github.rest.pulls.createReview({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: context.issue.number, | |
| event: 'COMMENT', | |
| comments: reviewComments.slice(0, 50) // GitHub limit: 50 comments per review | |
| }).catch(err => { | |
| console.log('Note: Could not create inline review (file may not be in diff)'); | |
| console.log(err.message); | |
| }); | |
| } | |
| create-fix-pr: | |
| name: Create Auto-Fix PR | |
| runs-on: ubuntu-latest | |
| if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Install tools | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y shellcheck shfmt jq | |
| - name: Generate security app token | |
| id: app_token | |
| uses: actions/create-github-app-token@v2 | |
| with: | |
| app-id: ${{ secrets['XSS_AI'] }} | |
| private-key: ${{ secrets['XSS_PK'] }} | |
| - name: Setup bot identity for signing | |
| id: identity | |
| uses: XAOSTECH/dev-control/.github/actions/identity@b067f72ca7f849b734a45634f23581a739d5146f # v2.0.0 | |
| with: | |
| gpg-private-key: ${{ secrets['XB_GK'] }} | |
| gpg-passphrase: ${{ secrets['XB_GP'] }} | |
| user-token: ${{ secrets['XB_UT'] }} | |
| bot-name: xaos-bot | |
| - name: Run ShellCheck to find issues | |
| id: check | |
| continue-on-error: true | |
| run: | | |
| find . -type f -name "*.sh" ! -path "./.git/*" ! -path "./node_modules/*" ! -path "./vendor/*" \ | |
| -exec shellcheck -f json {} + > /tmp/lint/results.json 2>&1 || true | |
| ERRORS=$(jq '[.[] | select(.level=="error")] | length' /tmp/lint/results.json 2>/dev/null || echo "0") | |
| echo "errors=$ERRORS" >> $GITHUB_OUTPUT | |
| [[ "$ERRORS" -gt 0 ]] && echo "has_fixes=true" >> $GITHUB_OUTPUT || echo "has_fixes=false" >> $GITHUB_OUTPUT | |
| - name: Apply style fixes with shfmt | |
| if: steps.check.outputs.has_fixes == 'true' | |
| run: | | |
| echo "Applying style fixes..." | |
| find . -type f -name "*.sh" ! -path "./.git/*" ! -path "./node_modules/*" ! -path "./vendor/*" \ | |
| -exec shfmt -i 4 -bn -ci -w {} + | |
| echo "Style fixes applied" | |
| - name: Create fix PR | |
| if: steps.check.outputs.has_fixes == 'true' && steps.identity.outputs.gpg-outcome == 'success' | |
| run: | | |
| set -euo pipefail | |
| # Configure git for commits | |
| git config user.name "xaos-bot" | |
| git config user.email "dev@xaostech.com" | |
| git config user.signingkey "$(git config user.signingkey)" | |
| # Check if there are changes | |
| if git diff --quiet; then | |
| echo "No changes to commit" | |
| exit 0 | |
| fi | |
| # Create feature branch | |
| BRANCH="fix/bash-linting-$(date +%s)" | |
| git checkout -b "$BRANCH" | |
| # Commit fixes with GPG signature | |
| git add -A | |
| git commit -S -m "fix: Apply bash linting auto-fixes\n\n- ShellCheck findings addressed via shellcheck analysis\n- Code style standardised with shfmt\n- Auto-generated by bash-lint-advanced workflow" | |
| # Push with security app token | |
| git push origin "$BRANCH" --force-with-lease | |
| # Create PR using security app token | |
| gh pr create \ | |
| --repo "$GITHUB_REPOSITORY" \ | |
| --base main \ | |
| --head "$BRANCH" \ | |
| --title "🔧 Bash Linting Auto-Fixes" \ | |
| --body "Automated fixes from bash linting analysis.\n\n## Changes\n- ShellCheck findings addressed\n- Code style standardised with shfmt\n\nAuto-generated by bash-lint-advanced workflow. Please review before merging." \ | |
| --label "automated" \ | |
| --label "bash-linting" \ | |
| || echo "PR may already exist for this branch" | |
| env: | |
| GH_TOKEN: ${{ steps.app_token.outputs.token }} | |
| summary: | |
| name: Linting Summary | |
| runs-on: ubuntu-latest | |
| needs: [shellcheck-analysis] | |
| if: always() | |
| steps: | |
| - name: Create summary | |
| run: | | |
| ERRORS="${{ needs.shellcheck-analysis.outputs.errors }}" | |
| WARNINGS="${{ needs.shellcheck-analysis.outputs.warnings }}" | |
| echo "## 📊 Bash Linting Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Errors**: ${ERRORS:-0}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Warnings**: ${WARNINGS:-0}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Next Steps:" >> $GITHUB_STEP_SUMMARY | |
| echo "1. Review issues in workflow annotations" >> $GITHUB_STEP_SUMMARY | |
| echo "2. Check PR comments for detailed suggestions" >> $GITHUB_STEP_SUMMARY | |
| echo "3. Follow linked wiki pages for explanations" >> $GITHUB_STEP_SUMMARY | |
| echo "4. Create PR with fixes" >> $GITHUB_STEP_SUMMARY | |
| # Fail if there are errors | |
| if [[ "${ERRORS:-0}" -gt 0 ]]; then | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "❌ **Errors found - please fix**" >> $GITHUB_STEP_SUMMARY | |
| exit 1 | |
| fi |