Merge branch 'main' into copilot/fix-github-actions-job-failure #87
Workflow file for this run
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: "Autofix (Maintenance)" | ||
| on: | ||
| push: | ||
| branches: ["main"] | ||
| paths: | ||
| - "**/*.py" | ||
| - "**/*.{js,jsx,ts,tsx,mjs,cjs,json,yml,yaml,md,css,scss,html}" | ||
| - "**/*.{c,cc,cpp,cxx,h,hh,hpp,hxx}" | ||
| - "package.json" | ||
| - "package-lock.json" | ||
| - "requirements*.txt" | ||
| - "pyproject.toml" | ||
| - ".ruff.toml" | ||
| - "ruff.toml" | ||
| - ".eslintrc*" | ||
| - "eslint.config.*" | ||
| - ".prettierrc*" | ||
| - ".prettierignore" | ||
| schedule: | ||
| - cron: "15 4 * * 2" | ||
| workflow_dispatch: | ||
| inputs: | ||
| allow_unsafe_fixes: | ||
| description: "Allow potentially unsafe fixes (ruff --unsafe-fixes) for manual/scheduled runs" | ||
| type: boolean | ||
| default: false | ||
| open_autofix_pr: | ||
| description: "Open PR for push/schedule/manual runs when fixes exist" | ||
| type: boolean | ||
| default: true | ||
| run_autofix: | ||
| description: "Run autofixers" | ||
| type: boolean | ||
| default: true | ||
| permissions: | ||
| contents: read | ||
| concurrency: | ||
| group: autofix-maintenance-${{ github.workflow }}-${{ github.ref }} | ||
| cancel-in-progress: false | ||
| jobs: | ||
| autofix: | ||
| if: >- | ||
| github.event_name != 'workflow_dispatch' || inputs.run_autofix | ||
| name: Autofix maintenance | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 30 | ||
| steps: | ||
| - name: Harden runner | ||
| uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 | ||
| with: | ||
| disable-sudo: false | ||
| egress-policy: audit | ||
| - name: Checkout repository | ||
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | ||
| with: | ||
| fetch-depth: 0 | ||
| persist-credentials: true | ||
| - name: Set up Python | ||
| uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 | ||
| with: | ||
| python-version: "3.12" | ||
| cache: "pip" | ||
| - name: Set up Node.js | ||
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 | ||
| with: | ||
| node-version: "20" | ||
| - name: Cache npm | ||
| uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 | ||
| with: | ||
| path: | | ||
| ~/.npm | ||
| ~/.cache | ||
| key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-node- | ||
| - id: scope | ||
| name: Build maintenance file scope | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| : > .changed_files.txt | ||
| : > .changed_py.txt | ||
| : > .changed_js_like.txt | ||
| : > .changed_c_like.txt | ||
| git ls-files > .changed_files.txt || true | ||
| grep -E '\.py$' .changed_files.txt > .changed_py.txt || true | ||
| grep -E '\.(js|jsx|ts|tsx|mjs|cjs|json|yml|yaml|md|css|scss|html)$' .changed_files.txt > .changed_js_like.txt || true | ||
| grep -E '\.(c|cc|cpp|cxx|h|hh|hpp|hxx)$' .changed_files.txt > .changed_c_like.txt || true | ||
| echo "all_count=$(wc -l < .changed_files.txt | tr -d ' ')" >> "$GITHUB_OUTPUT" | ||
| echo "py_count=$(wc -l < .changed_py.txt | tr -d ' ')" >> "$GITHUB_OUTPUT" | ||
| echo "js_count=$(wc -l < .changed_js_like.txt | tr -d ' ')" >> "$GITHUB_OUTPUT" | ||
| echo "c_count=$(wc -l < .changed_c_like.txt | tr -d ' ')" >> "$GITHUB_OUTPUT" | ||
| - name: Install autofix tooling | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| python -m pip install --upgrade pip | ||
| pip install "ruff>=0.6,<1.0" | ||
| if [ -f package.json ]; then | ||
| if [ -f package-lock.json ]; then | ||
| npm ci --no-audit --no-fund || npm install --no-audit --no-fund | ||
| else | ||
| npm install --no-audit --no-fund | ||
| fi | ||
| npx --no-install prettier --version >/dev/null 2>&1 || npm install --no-audit --no-fund --no-save prettier@3 | ||
| else | ||
| npm install --global --no-fund --no-audit prettier@3 | ||
| fi | ||
| if [ "${{ steps.scope.outputs.c_count }}" != "0" ]; then | ||
| sudo DEBIAN_FRONTEND=noninteractive apt-get update -qq | ||
| sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends clang-format | ||
| fi | ||
| - name: Log tool versions | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| python --version | ||
| node --version | ||
| npm --version | ||
| ruff --version | ||
| if [ -f package.json ]; then | ||
| npx --no-install prettier --version || true | ||
| npx --no-install eslint --version || true | ||
| else | ||
| prettier --version || true | ||
| fi | ||
| - name: Run ruff fixes | ||
| continue-on-error: true | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| if [ "${{ steps.scope.outputs.py_count }}" = "0" ]; then | ||
| echo "No Python files; skipping ruff." | ||
| exit 0 | ||
| fi | ||
| RUN_UNSAFE="false" | ||
| if [ "${{ github.event_name }}" = "workflow_dispatch" ] || [ "${{ github.event_name }}" = "schedule" ]; then | ||
| if [ "${{ inputs.allow_unsafe_fixes || 'false' }}" = "true" ]; then | ||
| RUN_UNSAFE="true" | ||
| fi | ||
| fi | ||
| if [ "$RUN_UNSAFE" = "true" ]; then | ||
| xargs -r ruff check --fix --unsafe-fixes --exit-zero < .changed_py.txt | ||
| else | ||
| xargs -r ruff check --fix --exit-zero < .changed_py.txt | ||
| fi | ||
| xargs -r ruff format < .changed_py.txt | ||
| - name: Run eslint fixes (if configured) | ||
| continue-on-error: true | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| if [ "${{ steps.scope.outputs.js_count }}" = "0" ]; then | ||
| echo "No JS/TS/etc files; skipping eslint." | ||
| exit 0 | ||
| fi | ||
| if [ -f package.json ] && npx --no-install eslint --version >/dev/null 2>&1; then | ||
| xargs -r npx eslint --fix --ext .js,.jsx,.ts,.tsx < .changed_js_like.txt || true | ||
| else | ||
| echo "eslint not available; skipping." | ||
| fi | ||
| - name: Run prettier write | ||
| continue-on-error: true | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| if [ "${{ steps.scope.outputs.all_count }}" = "0" ]; then | ||
| echo "No repository files found; skipping prettier." | ||
| exit 0 | ||
| fi | ||
| if [ -f package.json ]; then | ||
| xargs -r npx --no-install prettier --write --ignore-unknown < .changed_files.txt || true | ||
| else | ||
| xargs -r prettier --write --ignore-unknown < .changed_files.txt || true | ||
| fi | ||
| - name: Run clang-format (C/C++) | ||
| continue-on-error: true | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| if [ "${{ steps.scope.outputs.c_count }}" = "0" ]; then | ||
| echo "No C/C++ files; skipping clang-format." | ||
| exit 0 | ||
| fi | ||
| if command -v clang-format >/dev/null 2>&1; then | ||
| mapfile -t files < .changed_c_like.txt | ||
| if [ "${#files[@]}" -gt 0 ]; then | ||
| clang-format -i --style=file --fallback-style=LLVM "${files[@]}" || true | ||
| fi | ||
| else | ||
| echo "clang-format not installed; skipping." | ||
| fi | ||
| - name: Revert workflow file changes | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| git restore --staged --worktree -- .github/workflows || true | ||
| - name: Verify formatter/linter state | ||
| continue-on-error: true | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| if [ "${{ steps.scope.outputs.py_count }}" != "0" ]; then | ||
| xargs -r ruff check < .changed_py.txt || true | ||
| xargs -r ruff format --check < .changed_py.txt || true | ||
| fi | ||
| if [ "${{ steps.scope.outputs.js_count }}" != "0" ]; then | ||
| if [ -f package.json ] && npx --no-install eslint --version >/dev/null 2>&1; then | ||
| xargs -r npx eslint --ext .js,.jsx,.ts,.tsx < .changed_js_like.txt || true | ||
| fi | ||
| if [ -f package.json ]; then | ||
| xargs -r npx --no-install prettier --check --ignore-unknown < .changed_files.txt || true | ||
| else | ||
| xargs -r prettier --check --ignore-unknown < .changed_files.txt || true | ||
| fi | ||
| fi | ||
| - id: diff | ||
| name: Detect changes | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| if git diff --quiet --exit-code; then | ||
| echo "changed=false" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "changed=true" >> "$GITHUB_OUTPUT" | ||
| git --no-pager diff --stat | ||
| fi | ||
| - name: Create follow-up PR for autofixes | ||
| if: >- | ||
| steps.diff.outputs.changed == 'true' && | ||
| github.repository == 'Bryan-Roe/Aria' && | ||
| (github.event_name == 'push' || | ||
| github.event_name == 'schedule' || | ||
| (github.event_name == 'workflow_dispatch' && inputs.open_autofix_pr)) | ||
| permissions: | ||
| contents: write | ||
| pull-requests: write | ||
| uses: step-security/create-pull-request@50c103da2b9ca12cd5bc013fc6931051a5aa872b # v8.1.1 | ||
| with: | ||
| base: ${{ github.ref_name }} | ||
| branch: chore/autofix-${{ github.run_id }} | ||
| commit-message: "ci(style): apply automated autofixes" | ||
| title: "ci(style): automated lint/format autofixes" | ||
| body: | | ||
| Automated lint/format updates from workflow run #${{ github.run_id }}. | ||
| Applied tools: | ||
| - ruff check --fix (+ optional --unsafe-fixes on schedule/manual) | ||
| - ruff format | ||
| - eslint --fix (if configured) | ||
| - prettier --write | ||
| - clang-format (if C/C++ files exist) | ||
| labels: | | ||
| automated | ||
| autofix | ||
| chore | ||
| delete-branch: true | ||
| sign-commits: false | ||
| token: ${{ secrets.GITHUB_TOKEN }} | ||