128 lines
5.0 KiB
YAML
128 lines
5.0 KiB
YAML
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
|