ci: fail jobs that dirty the worktree (#29720)

## Why

CI jobs should not silently leave tracked changes or untracked files in
the repository worktree.

## What

- Add a shared final worktree-cleanliness action to 19 checkout-bearing
PR and main CI jobs.
- Ignore the intentional SDK scratch directory and nested V8 checkout.
- Pin Bazelisk in shared CI setup so `.bazelversion` remains
authoritative, avoiding `MODULE.bazel.lock` deltas on Windows runners.
- Leave `rust-ci-full` and release-only workflows unchanged.
- Update `AGENTS.md` to discourage review bots from asking for
`MODULE.bazel.lock` changes.
This commit is contained in:
Adam Perry @ OpenAI
2026-06-24 11:06:35 -07:00
committed by GitHub
parent 390b73133b
commit 93c79046d6
12 changed files with 103 additions and 3 deletions

View File

@@ -0,0 +1,19 @@
name: check-clean-worktree
description: Fail when a CI job leaves tracked changes or untracked files in the repository worktree.
runs:
using: composite
steps:
- name: Check for a clean worktree
shell: bash
run: |
set -euo pipefail
status="$(git -C "${GITHUB_WORKSPACE}" status --porcelain=v1 --untracked-files=normal --ignore-submodules=none)"
if [[ -z "${status}" ]]; then
exit 0
fi
echo "::error::CI job left tracked changes or untracked files in the repository worktree"
printf '%s\n' "${status}"
exit 1

View File

@@ -34,6 +34,10 @@ runs:
- name: Set up Bazel - name: Set up Bazel
uses: bazel-contrib/setup-bazel@c5acdfb288317d0b5c0bbd7a396a3dc868bb0f86 # 0.19.0 uses: bazel-contrib/setup-bazel@c5acdfb288317d0b5c0bbd7a396a3dc868bb0f86 # 0.19.0
# Without an explicit Bazelisk version, setup-bazel leaves PATH unchanged
# and Windows can use the runner's standalone Bazel, ignoring .bazelversion.
with:
bazelisk-version: 1.28.1
- name: Configure Bazel repository cache - name: Configure Bazel repository cache
id: configure_bazel_repository_cache id: configure_bazel_repository_cache

View File

@@ -134,6 +134,10 @@ jobs:
path: ${{ steps.prepare_bazel.outputs.repository-cache-path }} path: ${{ steps.prepare_bazel.outputs.repository-cache-path }}
key: ${{ steps.prepare_bazel.outputs.repository-cache-key }} key: ${{ steps.prepare_bazel.outputs.repository-cache-key }}
- name: Check for a clean worktree
if: always() && !cancelled()
uses: ./.github/actions/check-clean-worktree
test-windows-shard: test-windows-shard:
# Split the Windows Bazel test leg across separate Windows hosts. Jobs with # Split the Windows Bazel test leg across separate Windows hosts. Jobs with
# BuildBuddy credentials use Linux RBE for build actions; test execution # BuildBuddy credentials use Linux RBE for build actions; test execution
@@ -239,6 +243,10 @@ jobs:
path: ${{ runner.temp }}/bazel-execution-logs path: ${{ runner.temp }}/bazel-execution-logs
if-no-files-found: ignore if-no-files-found: ignore
- name: Check for a clean worktree
if: always() && !cancelled()
uses: ./.github/actions/check-clean-worktree
test-windows: test-windows:
# Preserve the existing required-check surface while the real work happens # Preserve the existing required-check surface while the real work happens
# in the sharded Windows jobs above. # in the sharded Windows jobs above.
@@ -333,6 +341,10 @@ jobs:
path: ${{ steps.prepare_bazel.outputs.repository-cache-path }} path: ${{ steps.prepare_bazel.outputs.repository-cache-path }}
key: ${{ steps.prepare_bazel.outputs.repository-cache-key }} key: ${{ steps.prepare_bazel.outputs.repository-cache-key }}
- name: Check for a clean worktree
if: always() && !cancelled()
uses: ./.github/actions/check-clean-worktree
clippy: clippy:
timeout-minutes: 30 timeout-minutes: 30
strategy: strategy:
@@ -433,6 +445,10 @@ jobs:
path: ${{ steps.prepare_bazel.outputs.repository-cache-path }} path: ${{ steps.prepare_bazel.outputs.repository-cache-path }}
key: ${{ steps.prepare_bazel.outputs.repository-cache-key }} key: ${{ steps.prepare_bazel.outputs.repository-cache-key }}
- name: Check for a clean worktree
if: always() && !cancelled()
uses: ./.github/actions/check-clean-worktree
verify-release-build: verify-release-build:
timeout-minutes: 30 timeout-minutes: 30
strategy: strategy:
@@ -545,3 +561,7 @@ jobs:
with: with:
path: ${{ steps.prepare_bazel.outputs.repository-cache-path }} path: ${{ steps.prepare_bazel.outputs.repository-cache-path }}
key: ${{ steps.prepare_bazel.outputs.repository-cache-key }} key: ${{ steps.prepare_bazel.outputs.repository-cache-key }}
- name: Check for a clean worktree
if: always() && !cancelled()
uses: ./.github/actions/check-clean-worktree

View File

@@ -32,3 +32,7 @@ jobs:
--head "$HEAD_SHA" \ --head "$HEAD_SHA" \
--max-bytes 512000 \ --max-bytes 512000 \
--allowlist .github/blob-size-allowlist.txt --allowlist .github/blob-size-allowlist.txt
- name: Check for a clean worktree
if: always() && !cancelled()
uses: ./.github/actions/check-clean-worktree

View File

@@ -32,3 +32,7 @@ jobs:
with: with:
rust-version: 1.95.0 rust-version: 1.95.0
manifest-path: ./codex-rs/Cargo.toml manifest-path: ./codex-rs/Cargo.toml
- name: Check for a clean worktree
if: always() && !cancelled()
uses: ./.github/actions/check-clean-worktree

View File

@@ -88,3 +88,7 @@ jobs:
- name: Prettier (run `pnpm run format:fix` to fix) - name: Prettier (run `pnpm run format:fix` to fix)
run: pnpm run format run: pnpm run format
- name: Check for a clean worktree
if: always() && !cancelled()
uses: ./.github/actions/check-clean-worktree

View File

@@ -28,3 +28,7 @@ jobs:
uses: codespell-project/actions-codespell@8f01853be192eb0f849a5c7d721450e7a467c579 # v2.2 uses: codespell-project/actions-codespell@8f01853be192eb0f849a5c7d721450e7a467c579 # v2.2
with: with:
ignore_words_file: .codespellignore ignore_words_file: .codespellignore
- name: Check for a clean worktree
if: always() && !cancelled()
uses: ./.github/actions/check-clean-worktree

View File

@@ -58,6 +58,10 @@ jobs:
echo "codex=$codex" >> "$GITHUB_OUTPUT" echo "codex=$codex" >> "$GITHUB_OUTPUT"
echo "workflows=$workflows" >> "$GITHUB_OUTPUT" echo "workflows=$workflows" >> "$GITHUB_OUTPUT"
- name: Check for a clean worktree
if: always() && !cancelled()
uses: ./.github/actions/check-clean-worktree
# --- Fast Cargo-native PR checks ------------------------------------------- # --- Fast Cargo-native PR checks -------------------------------------------
general: general:
name: Format / etc name: Format / etc
@@ -83,6 +87,10 @@ jobs:
- name: Rust benchmark smoke test - name: Rust benchmark smoke test
run: just bench-smoke run: just bench-smoke
- name: Check for a clean worktree
if: always() && !cancelled()
uses: ./.github/actions/check-clean-worktree
cargo_shear: cargo_shear:
name: cargo shear name: cargo shear
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
@@ -103,6 +111,10 @@ jobs:
- name: cargo shear - name: cargo shear
run: cargo shear --deny-warnings run: cargo shear --deny-warnings
- name: Check for a clean worktree
if: always() && !cancelled()
uses: ./.github/actions/check-clean-worktree
argument_comment_lint_package: argument_comment_lint_package:
name: Argument comment lint package name: Argument comment lint package
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
@@ -154,6 +166,10 @@ jobs:
env: env:
RUST_MIN_STACK: "8388608" # 8 MiB RUST_MIN_STACK: "8388608" # 8 MiB
- name: Check for a clean worktree
if: always() && !cancelled()
uses: ./.github/actions/check-clean-worktree
argument_comment_lint_prebuilt: argument_comment_lint_prebuilt:
name: Argument comment lint - ${{ matrix.name }} name: Argument comment lint - ${{ matrix.name }}
runs-on: ${{ matrix.runs_on || matrix.runner }} runs-on: ${{ matrix.runs_on || matrix.runner }}
@@ -205,6 +221,10 @@ jobs:
target: ${{ runner.os }} target: ${{ runner.os }}
buildbuddy-api-key: ${{ secrets.BUILDBUDDY_API_KEY }} buildbuddy-api-key: ${{ secrets.BUILDBUDDY_API_KEY }}
- name: Check for a clean worktree
if: always() && !cancelled() && steps.argument_comment_lint_gate.outputs.run == 'true'
uses: ./.github/actions/check-clean-worktree
# --- Gatherer job that you mark as the ONLY required status ----------------- # --- Gatherer job that you mark as the ONLY required status -----------------
results: results:
name: CI results (required) name: CI results (required)

View File

@@ -41,6 +41,10 @@ jobs:
/tmp/uv/bin/uv run --frozen --no-sync pytest /tmp/uv/bin/uv run --frozen --no-sync pytest
' '
- name: Check for a clean worktree
if: always() && !cancelled()
uses: ./.github/actions/check-clean-worktree
sdks: sdks:
runs-on: runs-on:
group: ${{ github.event.repository.name }}-runners group: ${{ github.event.repository.name }}-runners
@@ -161,3 +165,7 @@ jobs:
path: | path: |
~/.cache/bazel-repo-cache ~/.cache/bazel-repo-cache
key: bazel-cache-x86_64-unknown-linux-gnu-${{ hashFiles('MODULE.bazel', 'codex-rs/Cargo.lock', 'codex-rs/Cargo.toml') }} key: bazel-cache-x86_64-unknown-linux-gnu-${{ hashFiles('MODULE.bazel', 'codex-rs/Cargo.lock', 'codex-rs/Cargo.toml') }}
- name: Check for a clean worktree
if: always() && !cancelled()
uses: ./.github/actions/check-clean-worktree

View File

@@ -97,6 +97,10 @@ jobs:
echo "${output}" echo "${output}"
echo "${output}" >> "${GITHUB_OUTPUT}" echo "${output}" >> "${GITHUB_OUTPUT}"
- name: Check for a clean worktree
if: always() && !cancelled()
uses: ./.github/actions/check-clean-worktree
build: build:
name: Build ${{ matrix.variant }} ${{ matrix.target }} name: Build ${{ matrix.variant }} ${{ matrix.target }}
needs: metadata needs: metadata
@@ -322,6 +326,10 @@ jobs:
name: v8-canary-${{ needs.metadata.outputs.v8_version }}-${{ matrix.variant }}-${{ matrix.target }} name: v8-canary-${{ needs.metadata.outputs.v8_version }}-${{ matrix.variant }}-${{ matrix.target }}
path: dist/${{ matrix.target }}/* path: dist/${{ matrix.target }}/*
- name: Check for a clean worktree
if: always() && !cancelled()
uses: ./.github/actions/check-clean-worktree
build-windows-source: build-windows-source:
name: Build ptrcomp-sandbox ${{ matrix.target }} from source name: Build ptrcomp-sandbox ${{ matrix.target }} from source
needs: metadata needs: metadata
@@ -459,3 +467,7 @@ jobs:
with: with:
name: v8-canary-${{ needs.metadata.outputs.v8_version }}-ptrcomp-sandbox-${{ matrix.target }} name: v8-canary-${{ needs.metadata.outputs.v8_version }}-ptrcomp-sandbox-${{ matrix.target }}
path: dist/${{ matrix.target }}/* path: dist/${{ matrix.target }}/*
- name: Check for a clean worktree
if: always() && !cancelled()
uses: ./.github/actions/check-clean-worktree

2
.gitignore vendored
View File

@@ -60,6 +60,8 @@ yarn-error.log*
# ci # ci
.vercel/ .vercel/
.netlify/ .netlify/
/.tmp/sdk-ci/
/upstream-rusty-v8/
# patches # patches
apply_patch/ apply_patch/

View File

@@ -35,9 +35,8 @@ In the codex-rs folder where the rust code lives:
- When working with MCP tool calls, prefer using `codex-rs/codex-mcp/src/mcp_connection_manager.rs` to handle mutation of tools and tool calls. Aim to minimize the footprint of changes and leverage existing abstractions rather than plumbing code through multiple levels of function calls. - When working with MCP tool calls, prefer using `codex-rs/codex-mcp/src/mcp_connection_manager.rs` to handle mutation of tools and tool calls. Aim to minimize the footprint of changes and leverage existing abstractions rather than plumbing code through multiple levels of function calls.
- Do not call `reset_client_session` unnecessarily; let the incremental check logic decide whether to reuse the previous request. - Do not call `reset_client_session` unnecessarily; let the incremental check logic decide whether to reuse the previous request.
- If you change Rust dependencies (`Cargo.toml` or `Cargo.lock`), run `just bazel-lock-update` from the - If you change Rust dependencies (`Cargo.toml` or `Cargo.lock`), run `just bazel-lock-update` from the
repo root to refresh `MODULE.bazel.lock`, and include that lockfile update in the same change. repo root to refresh `MODULE.bazel.lock`, and include that lockfile update in the same change. CI
- After dependency changes, run `just bazel-lock-check` from the repo root so lockfile drift is caught verifies lockfile drift.
locally before CI.
- Bazel does not automatically make source-tree files available to compile-time Rust file access. If - Bazel does not automatically make source-tree files available to compile-time Rust file access. If
you add `include_str!`, `include_bytes!`, `sqlx::migrate!`, or similar build-time file or you add `include_str!`, `include_bytes!`, `sqlx::migrate!`, or similar build-time file or
directory reads, update the crate's `BUILD.bazel` (`compile_data`, `build_script_data`, or test directory reads, update the crate's `BUILD.bazel` (`compile_data`, `build_script_data`, or test