fix: use portable grep alternation in pulse test (#23676) #30801
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: 🔍 Code Review Monitoring & Auto-Fix | |
| on: | |
| push: | |
| branches: [ main, develop ] | |
| pull_request: | |
| branches: [ main ] | |
| schedule: | |
| # Run daily at 2 AM UTC | |
| - cron: '0 2 * * *' | |
| workflow_dispatch: | |
| # Top-level permissions required for GITHUB_TOKEN to push auto-fixes and | |
| # post PR comments. Job-level permissions alone are insufficient when the | |
| # repository default token permissions are set to read-only (GitHub Actions | |
| # ignores job-level elevation in that case). | |
| # See: github.com/marcusquinn/aidevops/issues/3848, GH#5069 | |
| permissions: | |
| contents: write | |
| issues: write | |
| pull-requests: write | |
| security-events: write | |
| # Cancel in-progress runs when a new run is triggered | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| code-review-monitoring: | |
| name: 🤖 Monitor & Auto-Fix Code Quality | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| issues: write | |
| pull-requests: write | |
| security-events: write | |
| steps: | |
| - name: 📥 Checkout Repository | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: 🐍 Setup Python | |
| uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 | |
| with: | |
| python-version: '3.11' | |
| - name: 📦 Install Dependencies | |
| run: | | |
| # Install jq for JSON processing | |
| sudo apt-get update && sudo apt-get install -y jq curl | |
| # Install Python dependencies for quality tools | |
| pip install --upgrade pip pyyaml | |
| - name: 🔍 Install Quality Tools | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| # Install Qlty CLI via gh release download (uses GITHUB_TOKEN, avoids | |
| # redirect/rate-limit issues with curl on /releases/latest/download/ URLs). | |
| # Fail-open: transient failures must not block CI (root cause of GH#4216). | |
| if gh release download --repo qltysh/qlty --pattern "qlty-x86_64-unknown-linux-gnu.tar.xz" --output qlty.tar.xz 2>&1; then | |
| if tar xf qlty.tar.xz 2>&1; then | |
| sudo mv qlty-x86_64-unknown-linux-gnu/qlty /usr/local/bin/ 2>/dev/null || true | |
| echo "Qlty CLI installed: $(qlty --version 2>/dev/null || echo 'version unknown')" | |
| else | |
| echo "::warning::Qlty tar extraction failed — skipping qlty analysis" | |
| fi | |
| else | |
| echo "::warning::Qlty download failed — skipping qlty analysis" | |
| fi | |
| rm -rf qlty.tar.xz qlty-x86_64-unknown-linux-gnu 2>/dev/null || true | |
| # Codacy CLI: the Linux binary was removed from releases (only JAR remains). | |
| # Skip install — codacy-analysis-cli is not used in monitor-code-review.sh. | |
| echo "Codacy CLI binary no longer available as a release asset; skipping." | |
| - name: 🏃 Run Code Review Monitoring | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }} | |
| run: | | |
| # Run monitoring with full analysis | |
| bash ./.agents/scripts/monitor-code-review.sh monitor | |
| - name: 🔧 Apply Auto-Fixes | |
| run: | | |
| # NOTE: Qlty fmt disabled - it introduces invalid shell syntax | |
| # (adds "|| exit" after "then" in if statements) | |
| # See: https://github.com/marcusquinn/aidevops/issues/333 | |
| # Apply framework-specific fixes only | |
| bash ./.agents/scripts/monitor-code-review.sh fix | |
| - name: 📊 Generate Quality Report | |
| run: | | |
| # Generate comprehensive quality report | |
| bash ./.agents/scripts/monitor-code-review.sh report > quality-report.md | |
| # Add current metrics | |
| echo "## 📈 Current Quality Metrics" >> quality-report.md | |
| echo "" >> quality-report.md | |
| # SonarCloud metrics | |
| if curl -s "https://sonarcloud.io/api/measures/component?component=marcusquinn_aidevops&metricKeys=bugs,vulnerabilities,code_smells" | jq -r '.component.measures[] | "- **\(.metric | gsub("_"; " ") | ascii_upcase)**: \(.value)"' >> quality-report.md 2>/dev/null; then | |
| echo "SonarCloud metrics added" | |
| fi | |
| echo "" >> quality-report.md | |
| echo "Generated on: $(date)" >> quality-report.md | |
| - name: 📤 Upload Quality Report | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: quality-report | |
| path: quality-report.md | |
| retention-days: 30 | |
| - name: 📤 Upload SARIF Results | |
| if: always() && hashFiles('codacy-results.sarif') != '' | |
| uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4 | |
| with: | |
| sarif_file: codacy-results.sarif | |
| category: codacy | |
| continue-on-error: true | |
| - name: Validate Auto-Fixes | |
| id: validate | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| run: | | |
| # Check if there are any changes to validate | |
| if git diff --quiet; then | |
| echo "No changes to validate" | |
| echo "has_changes=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| # chmod-only changes are a workflow artifact, not a code quality fix. | |
| # They previously produced 257-file mode-only commits and then noisy | |
| # push failures when main advanced concurrently. | |
| if git -c core.fileMode=false diff --quiet; then | |
| echo "::notice::Only file mode changes detected; discarding auto-fix artifact" | |
| git diff --summary || true | |
| git reset --hard HEAD | |
| echo "has_changes=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| errors=0 | |
| reverted_files="" | |
| # Validate each modified file by type | |
| for file in $(git diff --name-only); do | |
| case "$file" in | |
| *.sh) | |
| # ShellCheck validation (error severity) | |
| if ! shellcheck -S error "$file" 2>/dev/null; then | |
| echo "FAIL: ShellCheck rejected $file" | |
| git checkout -- "$file" | |
| reverted_files="$reverted_files $file" | |
| errors=$((errors + 1)) | |
| # Bash syntax check | |
| elif ! bash -n "$file" 2>/dev/null; then | |
| echo "FAIL: bash -n syntax check rejected $file" | |
| git checkout -- "$file" | |
| reverted_files="$reverted_files $file" | |
| errors=$((errors + 1)) | |
| else | |
| echo "OK: $file passed validation" | |
| fi | |
| ;; | |
| *.json) | |
| if ! python3 -m json.tool "$file" > /dev/null 2>&1; then | |
| echo "FAIL: Invalid JSON in $file" | |
| git checkout -- "$file" | |
| reverted_files="$reverted_files $file" | |
| errors=$((errors + 1)) | |
| else | |
| echo "OK: $file passed validation" | |
| fi | |
| ;; | |
| *.yml|*.yaml) | |
| if ! python3 -c "import yaml; yaml.safe_load(open('$file'))" 2>/dev/null; then | |
| echo "FAIL: Invalid YAML in $file" | |
| git checkout -- "$file" | |
| reverted_files="$reverted_files $file" | |
| errors=$((errors + 1)) | |
| else | |
| echo "OK: $file passed validation" | |
| fi | |
| ;; | |
| *) | |
| echo "OK: $file (no specific validator)" | |
| ;; | |
| esac | |
| done | |
| if [[ $errors -gt 0 ]]; then | |
| echo "::warning::Reverted $errors file(s) that failed validation:$reverted_files" | |
| fi | |
| # After reverting bad files, check if valid changes remain | |
| if git diff --quiet; then | |
| echo "All changes failed validation and were reverted" | |
| echo "has_changes=false" >> "$GITHUB_OUTPUT" | |
| else | |
| remaining=$(git diff --name-only | wc -l | tr -d ' ') | |
| echo "Validated changes ready for PR ($remaining file(s) passed)" | |
| echo "has_changes=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Commit Auto-Fixes to Main | |
| # Commit validated fixes directly to main instead of creating a PR. | |
| # Why: GitHub Actions is not permitted to create PRs in this repo | |
| # (Settings > Actions > General > "Allow GitHub Actions to create and | |
| # approve pull requests" is disabled). Since all changes have already | |
| # passed ShellCheck, bash -n, JSON, and YAML validation in the prior | |
| # step, committing directly is safe. See: GH#5069 | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' && steps.validate.outputs.has_changes == 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| git config --local user.email "action@github.com" | |
| git config --local user.name "GitHub Action" | |
| git add . | |
| if git diff --cached --quiet; then | |
| echo "No staged auto-fix changes remain after validation" | |
| exit 0 | |
| fi | |
| git commit -m "fix: apply validated code quality improvements [skip ci] | |
| Automated fixes applied by Code Review Monitoring workflow. | |
| All changes passed validation (ShellCheck, syntax, JSON/YAML). | |
| Generated by: GitHub Actions Code Review Monitoring" | |
| PUSH_SUCCEEDED=false | |
| for i in 1 2 3; do | |
| echo "Push attempt $i..." | |
| git fetch origin main | |
| if ! git rebase origin/main; then | |
| echo "::warning::Auto-fix rebase conflicted with current main; aborting and exiting neutral" | |
| git rebase --abort || true | |
| exit 0 | |
| fi | |
| if git push origin HEAD:main; then | |
| echo "Push succeeded on attempt $i" | |
| PUSH_SUCCEEDED=true | |
| break | |
| fi | |
| echo "Push failed, retrying after refreshing from origin/main..." | |
| sleep $((i * 3)) | |
| done | |
| if [[ "$PUSH_SUCCEEDED" != "true" ]]; then | |
| echo "::warning::Auto-fix push did not land after retries; exiting neutral to avoid benign race notification noise" | |
| exit 0 | |
| fi | |
| echo "Auto-fix changes committed directly to main" | |
| - name: 📝 Comment on PR | |
| if: github.event_name == 'pull_request' | |
| uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7 | |
| continue-on-error: true | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| if (fs.existsSync('quality-report.md')) { | |
| const report = fs.readFileSync('quality-report.md', 'utf8'); | |
| try { | |
| await github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: `## 🔍 Code Quality Report\n\n${report}\n\n---\n*Generated by AI DevOps Framework Code Review Monitoring*` | |
| }); | |
| } catch (error) { | |
| // Fork PRs using pull_request trigger lack write permissions. | |
| // Log the error but don't fail the job — the quality report is | |
| // still available as a build artifact. | |
| core.warning(`Could not post PR comment: ${error.message}`); | |
| } | |
| } | |
| - name: 🎯 Summary | |
| if: always() | |
| run: | | |
| echo "## 🎉 Code Review Monitoring Complete!" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### 📊 Quality Status:" >> $GITHUB_STEP_SUMMARY | |
| # Add SonarCloud status | |
| if curl -s "https://sonarcloud.io/api/measures/component?component=marcusquinn_aidevops&metricKeys=bugs,vulnerabilities,code_smells" | jq -r '"- **Bugs**: " + (.component.measures[] | select(.metric=="bugs") | .value) + "\n- **Vulnerabilities**: " + (.component.measures[] | select(.metric=="vulnerabilities") | .value) + "\n- **Code Smells**: " + (.component.measures[] | select(.metric=="code_smells") | .value)' >> $GITHUB_STEP_SUMMARY 2>/dev/null; then | |
| echo "Quality metrics added to summary" | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### 🛠️ Tools Used:" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✅ SonarCloud Analysis" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✅ Codacy Analysis with Auto-Fix" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✅ Qlty Universal Linting" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✅ TOON Format Validation" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "*Automated by AI DevOps Framework v1.5.0*" >> $GITHUB_STEP_SUMMARY |