diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..6810eeff --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,7 @@ +# Code owners are auto-requested as reviewers when a PR touches these paths. +# This is an advisory flag for release/CI/build infrastructure, not a merge +# gate (require_code_owner_reviews is intentionally off). + +/.github/ @igerber +/pyproject.toml @igerber +/rust/ @igerber diff --git a/.github/workflows/ai_pr_review.yml b/.github/workflows/ai_pr_review.yml index 991fddc4..2ac0ddf6 100644 --- a/.github/workflows/ai_pr_review.yml +++ b/.github/workflows/ai_pr_review.yml @@ -117,7 +117,7 @@ jobs: # ───────────────────────────────────────────────────────────────── - name: Resolve PR number + metadata id: pr - uses: actions/github-script@v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 with: script: | const prNumber = @@ -180,7 +180,7 @@ jobs: # durably even after the fork is deleted or branches removed. # The previous workflow used `refs/pull//merge`, which is # garbage-collected on closed PRs — this path replaces that. - - uses: actions/checkout@v7 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 if: steps.pr.outputs.state != 'open' && steps.pr.outputs.is_fork == 'false' with: ref: refs/pull/${{ steps.pr.outputs.number }}/head @@ -191,7 +191,7 @@ jobs: # has not yet mirrored a freshly API-created PR's head # (see .claude/commands/submit-pr.md:327-345). head_sha is # guaranteed to exist on the head repo for an open PR. - - uses: actions/checkout@v7 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 if: steps.pr.outputs.state == 'open' && steps.pr.outputs.is_fork == 'false' with: repository: ${{ steps.pr.outputs.head_repo_full_name }} @@ -211,7 +211,7 @@ jobs: - name: Fetch previous AI review (if any) id: prev_review if: steps.pr.outputs.is_fork == 'false' - uses: actions/github-script@v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 with: script: | const { owner, repo } = context.repo; @@ -509,7 +509,7 @@ jobs: - name: Run Codex id: run_codex if: steps.pr.outputs.is_fork == 'false' - uses: openai/codex-action@v1 + uses: openai/codex-action@10cb888d2ed3b99867f7e7ccff174a861a75aeb6 # v1 with: openai-api-key: ${{ secrets.OPENAI_API_KEY }} prompt-file: .github/codex/prompts/pr_review_compiled.md @@ -521,7 +521,7 @@ jobs: - name: Post PR comment (new on every event except initial open) if: steps.pr.outputs.is_fork == 'false' - uses: actions/github-script@v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 env: CODEX_FINAL_MESSAGE: ${{ steps.run_codex.outputs.final-message }} PR_NUMBER: ${{ steps.pr.outputs.number }} diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index 6cd90d6c..d4dae9b0 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -34,13 +34,15 @@ jobs: container: quay.io/pypa/manylinux_2_28_aarch64 artifact: wheels-linux-aarch64 steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - name: Install system dependencies run: dnf install -y openssl-devel perl-IPC-Cmd openblas-devel - name: Install Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable branch + with: + toolchain: stable - name: Install maturin run: /opt/python/cp312-cp312/bin/pip install maturin @@ -65,7 +67,7 @@ jobs: fi - name: Upload wheels - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: ${{ matrix.artifact }} path: dist/*.whl @@ -80,15 +82,17 @@ jobs: matrix: python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14'] steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 with: python-version: ${{ matrix.python-version }} - name: Install Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable branch + with: + toolchain: stable - name: Install maturin run: pip install maturin @@ -97,7 +101,7 @@ jobs: run: maturin build --release --out dist --features extension-module,accelerate - name: Upload wheels - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: wheels-macos-arm64-py${{ matrix.python-version }} path: dist/*.whl @@ -111,15 +115,17 @@ jobs: matrix: python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14'] steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 with: python-version: ${{ matrix.python-version }} - name: Install Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable branch + with: + toolchain: stable - name: Install maturin run: pip install maturin @@ -128,7 +134,7 @@ jobs: run: maturin build --release --out dist --features extension-module - name: Upload wheels - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: wheels-windows-py${{ matrix.python-version }} path: dist/*.whl @@ -139,10 +145,10 @@ jobs: if: ${{ !inputs.linux_only }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 with: python-version: '3.11' @@ -153,7 +159,7 @@ jobs: run: maturin sdist --out dist - name: Upload sdist - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: sdist path: dist/*.tar.gz diff --git a/.github/workflows/docs-tests.yml b/.github/workflows/docs-tests.yml index 3fee2631..b4681d34 100644 --- a/.github/workflows/docs-tests.yml +++ b/.github/workflows/docs-tests.yml @@ -52,10 +52,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 with: # 3.14 to mirror Pure Python Fallback (the only existing job # that actually ran these tests). notebooks.yml uses 3.11 for @@ -90,14 +90,14 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - name: Install pandoc # nbsphinx invokes pandoc to render notebook markdown cells. run: sudo apt-get update && sudo apt-get install -y pandoc - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 with: # Match the RTD build (.readthedocs.yaml:9) so CI catches drift # against what users actually see on diff-diff.readthedocs.io. @@ -128,10 +128,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - name: Set up Python 3.9 - uses: actions/setup-python@v6 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 with: # 3.9 is the project's declared floor (pyproject.toml requires-python # >=3.9). The sphinx-build job above runs on 3.11; this job verifies diff --git a/.github/workflows/notebooks.yml b/.github/workflows/notebooks.yml index 1ca9bb62..c28fc9c9 100644 --- a/.github/workflows/notebooks.yml +++ b/.github/workflows/notebooks.yml @@ -35,10 +35,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 with: python-version: '3.11' @@ -66,7 +66,7 @@ jobs: - name: Upload failed notebook outputs if: failure() - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: failed-notebook-outputs path: docs/tutorials/*.ipynb diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 303354b1..830d896a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -23,10 +23,10 @@ jobs: steps: - name: Download all artifacts - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 with: path: dist merge-multiple: true - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1 diff --git a/.github/workflows/rust-test.yml b/.github/workflows/rust-test.yml index 3908f293..7c414b69 100644 --- a/.github/workflows/rust-test.yml +++ b/.github/workflows/rust-test.yml @@ -78,10 +78,12 @@ jobs: os: [ubuntu-latest, macos-latest, windows-latest, ubuntu-24.04-arm] steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable branch + with: + toolchain: stable - name: Install OpenBLAS (Linux) if: runner.os == 'Linux' @@ -116,15 +118,17 @@ jobs: python-version: ['3.11', '3.13', '3.14'] steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 with: python-version: ${{ matrix.python-version }} - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable branch + with: + toolchain: stable - name: Install test dependencies # Keep in sync with pyproject.toml [project.dependencies] and [project.optional-dependencies.dev] @@ -220,10 +224,10 @@ jobs: || github.event.label.name == 'ready-for-ci')) runs-on: ubuntu-latest steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 with: python-version: '3.14' diff --git a/tests/test_openai_review.py b/tests/test_openai_review.py index 3ae27dde..ba4b8f96 100644 --- a/tests/test_openai_review.py +++ b/tests/test_openai_review.py @@ -2451,7 +2451,8 @@ class TestWorkflowCodexActionContract: gates), or ``TestWorkflowDoesNotExecutePRHeadCode`` (``sandbox: read-only``): - - the action pin (``openai/codex-action@v1``) + its ``prompt-file`` input + - the action pin (``openai/codex-action`` at a full commit SHA with a + ``# v1`` version comment) + its ``prompt-file`` input - the compiled-prompt path agreeing between the build step (``PROMPT=``) and the action input (``prompt-file:``) - the ``final-message`` output flowing from the ``id:``-tagged Codex step @@ -2520,11 +2521,14 @@ def _require_block(self, workflow_text, step_name): def test_run_codex_uses_pinned_action(self, workflow_text): block = self._require_block(workflow_text, self.RUN_CODEX_STEP) assert re.search( - r"^\s*uses:\s*openai/codex-action@v1\s*$", block, re.MULTILINE + r"^\s*uses:\s*openai/codex-action@[0-9a-f]{40}\s*#\s*v1(?:\.\d+)*\s*$", + block, + re.MULTILINE, ), ( - "the Run Codex step must invoke the pinned openai/codex-action@v1; " - "a floating tag or a different action silently changes the review " - "contract." + "the Run Codex step must invoke openai/codex-action pinned to a " + "full commit SHA with a `# v1` version comment; a floating ref, a " + "different action, or a major-version bump silently changes the " + "review contract." ) def test_run_codex_passes_prompt_file_input(self, workflow_text): @@ -3359,7 +3363,7 @@ def _extract_open_pr_checkout_block(workflow_text): import re pattern = re.compile( - r"^ - uses: actions/checkout@\S+\s*\n" + r"^ - uses: actions/checkout@\S+(?:\s+#[^\n]*)?\n" r" if: [^\n]*state == 'open'[^\n]*\n" r"((?:[ ]{8,}.*\n|[ ]*\n)*)", re.MULTILINE,