name: Lint # Runs the project's pre-commit hooks (ruff, mypy, custom anti-pattern grep # hooks, etc.) on every non-draft PR and on push to main. Surfaces as the # `Pre-commit checks` check, a REQUIRED gate entry in merge-ready.yml. Draft PRs # are skipped; `ready_for_review` refires so the check doesn't strand pending. on: pull_request: types: [opened, synchronize, reopened, ready_for_review] push: branches: - main permissions: contents: read env: # No web SPA build during `uv sync` (setup.py _build_web_ui): this job never # serves the bundle, and the build otherwise times out on public npm. OMNIGENT_SKIP_WEB_UI: "true" # Route uv and pip at PyPI. pre-commit installs hook repos via pip (not uv), # so PIP_INDEX_URL is needed even though the workflow only invokes uv. UV_INDEX_URL: https://pypi.org/simple PIP_INDEX_URL: https://pypi.org/simple concurrency: group: lint-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: # Security precondition gate: untrusted PRs are held until the scan passes # (security-gate.yml); trusted authors and non-PR events pass through. gate: uses: ./.github/workflows/security-gate.yml pre-commit: name: Pre-commit checks needs: gate if: ${{ !github.event.pull_request.draft }} runs-on: ubuntu-latest timeout-minutes: 15 steps: - name: Check out repo uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version-file: ".python-version" # Must run BEFORE any `uv` command: `uv sync`/`uv run` would re-resolve and # rewrite a committed proxy URL to canonical, masking it. Checks the # committed file as-is (stdlib only, no venv). - name: Check uv.lock uses the public PyPI index run: python scripts/normalize_uv_lock_registry.py --check uv.lock - name: Set up uv uses: astral-sh/setup-uv@38f3f104447c67c051c4a08e39b64a148898af3a # v3 with: enable-cache: true - name: Cache virtualenv uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v4 with: path: .venv key: venv-${{ runner.os }}-${{ hashFiles('.python-version') }}-${{ hashFiles('uv.lock') }} - name: Install dependencies # `--locked` is the hard gate: fails if uv.lock is out of sync with # pyproject.toml (a bare `uv run pre-commit` would re-lock first and mask # a stale lockfile). Fix locally with `uv lock`. run: uv sync --locked --extra dev # Sets up Node 20 and pins npm to the same major that regenerates # the lockfile in the OSS-regen workflows, so the freshness gate # below doesn't flake on npm version-skew churn. - name: Set up Node.js uses: ./.github/actions/setup-node - name: Install web dependencies working-directory: web # Pin the npm registry to the npmjs default. env: NPM_CONFIG_REGISTRY: https://registry.npmjs.org/ run: npm ci --legacy-peer-deps # The npm equivalent of the `uv sync --locked` gate above. `npm ci` # only checks the lockfile is CONSISTENT with package.json; it # tolerates cosmetic drift (dev/extraneous flags, metadata) that a # fresh resolution would rewrite. Regenerate the lockfile and fail # if it differs from the committed one. - name: Check web/package-lock.json is up to date working-directory: web env: NPM_CONFIG_REGISTRY: https://registry.npmjs.org/ run: | npm install --package-lock-only --legacy-peer-deps --no-audit --no-fund git diff --exit-code package-lock.json || { echo "::error::web/package-lock.json is out of date. Run 'npm install --package-lock-only --legacy-peer-deps' in web/ and commit the result." exit 1 } # ktlint is invoked by the android-ktlint-* pre-commit hooks. The wrapper # script (web/android/bin/ktlint.sh) exits 0 if ktlint is absent, so we # install it here before pre-commit runs to ensure the check is enforced. # The binary is verified against a pinned SHA-256 so a corrupted or spoofed # download is caught before the binary is made executable. - name: Install ktlint env: KTLINT_VERSION: "1.8.0" KTLINT_SHA256: "a3fd620207d5c40da6ca789b95e7f823c54e854b7fade7f613e91096a3706d75" run: | curl -sSLf \ "https://github.com/ktlint/ktlint/releases/download/${KTLINT_VERSION}/ktlint" \ -o /tmp/ktlint echo "${KTLINT_SHA256} /tmp/ktlint" | sha256sum -c chmod +x /tmp/ktlint sudo mv /tmp/ktlint /usr/local/bin/ktlint - name: Run formatting, lint, and typing checks run: uv run pre-commit run --all-files --show-diff-on-failure - name: Type-check web working-directory: web run: npm run type-check