diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 85a690a..352059f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: outputs: code: ${{ steps.filter.outputs.code }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 with: fetch-depth: 0 - id: filter @@ -60,8 +60,8 @@ jobs: if: needs.changes.outputs.code == 'true' runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v5 - - uses: actions/setup-python@v6 + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.12" - run: pip install -e '.[dev]' @@ -71,18 +71,16 @@ jobs: types: needs: changes if: needs.changes.outputs.code == 'true' - # ADVISORY type-checking (R36): pyright `basic` on passiveworkers/net only. Non-blocking - # (continue-on-error) — it surfaces type signal without gating merges; promote to a required - # check once the scoped surface is clean and the include list has widened. See [tool.pyright]. + # BLOCKING type-checking (R36): pyright `basic` on passiveworkers/net only. A pyright error + # fails this job and therefore `ci-ok`. Widen the include list module-by-module. See [tool.pyright]. runs-on: ubuntu-24.04 - continue-on-error: true steps: - - uses: actions/checkout@v5 - - uses: actions/setup-python@v6 + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.12" - run: pip install -e . pyright # -e . gives pyright the real fastapi/pydantic types - - name: Pyright (basic, passiveworkers/net — advisory) + - name: Pyright (basic, passiveworkers/net) run: pyright test: @@ -95,8 +93,8 @@ jobs: matrix: python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] steps: - - uses: actions/checkout@v5 - - uses: actions/setup-python@v6 + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: ${{ matrix.python-version }} - name: Install (all extras + dev tools) @@ -120,11 +118,11 @@ jobs: # the served inline JS is Python-version-independent, so run the guards once runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v5 - - uses: actions/setup-python@v6 + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.12" - - uses: actions/setup-node@v5 + - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version: "24" - run: pip install -e . @@ -141,8 +139,8 @@ jobs: # never CI-checked — every extra import must stay lazy/optional, or this job fails. runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v5 - - uses: actions/setup-python@v6 + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.12" - name: Install core only (no extras) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..20c563f --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,36 @@ +name: CodeQL + +on: + push: + branches: [main] + paths-ignore: ['docs/**', '**.md'] + pull_request: + schedule: + - cron: "27 4 * * 1" # weekly (Monday 04:27 UTC): picks up new queries on unchanged code + workflow_dispatch: + +# Read-only token by default; only the analyze job gets the one write scope it needs. +permissions: + contents: read + +concurrency: + group: codeql-${{ github.ref }} + # Same rule as ci.yml: never cancel a main run, only superseded feature-branch pushes. + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} + +jobs: + # Not a required check (only ci.yml's `ci-ok` is); results land in the Security tab. + analyze: + runs-on: ubuntu-24.04 + permissions: + contents: read + security-events: write # upload SARIF results to code scanning + steps: + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + - uses: github/codeql-action/init@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3 + with: + languages: python + build-mode: none # interpreted language: no build step + - uses: github/codeql-action/analyze@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3 + with: + category: "/language:python" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7bfab47..0ae60b1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,8 +15,8 @@ jobs: # green `main`, but a tag is a one-shot action, so re-verify rather than trust that. runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v5 - - uses: actions/setup-python@v6 + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.12" - name: Install (all extras + dev tools) @@ -30,8 +30,8 @@ jobs: needs: test runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v5 - - uses: actions/setup-python@v6 + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.12" - run: python -m pip install --upgrade pip build twine @@ -39,7 +39,7 @@ jobs: run: python -m build - name: twine check run: twine check dist/* - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: dist path: dist/ @@ -53,12 +53,12 @@ jobs: permissions: id-token: write # OIDC — no stored PyPI token steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: dist path: dist/ - name: Publish to PyPI (Trusted Publishing, OIDC) - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2 with: # A retried run after a partial success (e.g. sdist uploaded, wheel upload dropped) # would otherwise have PyPI reject the already-accepted file, fail this job again, @@ -71,13 +71,13 @@ jobs: permissions: contents: write steps: - - uses: actions/checkout@v5 - - uses: actions/download-artifact@v4 + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: dist path: dist/ - name: Create GitHub Release with build provenance - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2 with: files: dist/* generate_release_notes: true diff --git a/AGENTS.md b/AGENTS.md index 3ab5c62..16bf6ad 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -58,11 +58,11 @@ CI gate is scoped to `select = ["E9", "F"]` (pyflakes + syntax errors only — s ```bash python -m py_compile $(git ls-files '*.py') ``` -This is a syntax-compile check, not a real type checker. CI separately runs an -**advisory** `pyright` pass scoped to `passiveworkers/net` only -(`continue-on-error: true`, non-blocking — see `[tool.pyright]` in `pyproject.toml` -and the `types` job in `.github/workflows/ci.yml`); it surfaces signal but doesn't -gate merges yet. +This is a syntax-compile check, not a real type checker. CI separately runs a +**blocking** `pyright` pass scoped to `passiveworkers/net` only (the `types` job in +`.github/workflows/ci.yml`, configured by `[tool.pyright]` in `pyproject.toml`): a +pyright error fails `types` and so the required `ci-ok`. Run `pyright` locally +before pushing a change under `passiveworkers/net`. **Test** @@ -163,7 +163,7 @@ entry in `billed_repos.json`. force-pushing your own branch here is fine. Its `required_checks` is `[ci-ok]`: the aggregate job in `ci.yml` that needs `changes`, `lint`, `types`, `test` (Python 3.10-3.14), `frontend` and `core-install`, always runs, and fails if any needed job failed or was -cancelled (skipped is fine; `types` is advisory). GitHub enforces `ci-ok` through a +cancelled (skipped is fine). GitHub enforces `ci-ok` through a `required_status_checks` rule on the "fleet: protect default branch" ruleset (alongside deletion and non-fast-forward). `ci.yml` has no `paths-ignore` on `pull_request`: a `changes` job skips the heavy jobs for docs-only PRs (every file under `docs/` or `*.md`), diff --git a/pyproject.toml b/pyproject.toml index fd72ac5..d449a9f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -107,10 +107,10 @@ filterwarnings = [ ] [tool.pyright] -# Scoped, ADVISORY type-checking (R36/D52). Starts on the federation layer only — the highest-value, +# Scoped, BLOCKING type-checking (R36/D52). Starts on the federation layer only — the highest-value, # most-tested surface, where the pydantic request/response models already carry real types. `basic` mode # is Any-tolerant, so the dynamic `sqlite3.Row["col"]` / `json.loads(...)` patterns don't explode. The CI -# `types` job runs this but does NOT gate the build (continue-on-error) — signal now, enforcement later. +# `types` job runs this and gates the build: a pyright error fails `types` and so the required `ci-ok`. # Widen `include` module-by-module as the surface is annotated; NOT strict, NOT repo-wide by design. include = ["passiveworkers/net"] typeCheckingMode = "basic"