From 7b9f26d2b1f52deec2ef1b43ab28b54806be0745 Mon Sep 17 00:00:00 2001 From: theDancingDeveloper Date: Fri, 9 Oct 2026 09:52:49 +0000 Subject: [PATCH 1/7] Add fork-ci: fmt, clippy and tests for the crates a PR changes The upstream blocking-ci, CLA Assistant and v8-canary workflows are disabled on the fork, so fork pull requests need their own check. It diffs the PR against fork/main, takes the codex-rs crates whose src changed, and runs cargo fmt --check, cargo clippy -D warnings and cargo test for each. A PR that changes no crate runs nothing. Both actions are pinned to commit SHAs verified to exist. --- .github/workflows/fork-ci.yml | 71 +++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 .github/workflows/fork-ci.yml diff --git a/.github/workflows/fork-ci.yml b/.github/workflows/fork-ci.yml new file mode 100644 index 000000000000..3beeb4bd81b2 --- /dev/null +++ b/.github/workflows/fork-ci.yml @@ -0,0 +1,71 @@ +# Fork-only CI. The upstream blocking-ci, CLA and v8-canary workflows are +# disabled on this fork, so this is the check that runs on fork pull requests. +# New file with a fork-unique name; no upstream workflow is edited. +name: fork-ci + +on: + pull_request: + branches: [fork/main] + workflow_dispatch: + +permissions: + contents: read + +jobs: + check: + name: fmt, clippy, test + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.0.1 + with: + persist-credentials: false + fetch-depth: 0 + + - name: Set up Rust toolchain + uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0 + with: + components: rustfmt, clippy + + # Only the crates a fork patch touches are checked. Docs and workflow + # changes test nothing. + - name: Crates changed against fork/main + id: crates + shell: bash + run: | + set -euo pipefail + base="$(git merge-base HEAD origin/fork/main 2>/dev/null || git merge-base HEAD fork/main)" + mapfile -t crates < <(git diff --name-only "$base"...HEAD \ + | awk -F/ '/^codex-rs\/[^/]+\/src\//{print $2}' | sort -u) + echo "changed: ${crates[*]:-none}" + { + echo "list<> "$GITHUB_OUTPUT" + + - name: cargo fmt + shell: bash + run: | + set -euo pipefail + while IFS= read -r crate; do + [[ -z "$crate" ]] && continue + cargo fmt -p "$crate" -- --check + done <<< "${{ steps.crates.outputs.list }}" + + - name: cargo clippy + shell: bash + run: | + set -euo pipefail + while IFS= read -r crate; do + [[ -z "$crate" ]] && continue + cargo clippy -p "$crate" --all-targets -- -D warnings + done <<< "${{ steps.crates.outputs.list }}" + + - name: cargo test + shell: bash + run: | + set -euo pipefail + while IFS= read -r crate; do + [[ -z "$crate" ]] && continue + cargo test -p "$crate" + done <<< "${{ steps.crates.outputs.list }}" From a57a0e2b0a08644994edbcdca1d4a2fc694cc048 Mon Sep 17 00:00:00 2001 From: theDancingDeveloper Date: Fri, 9 Oct 2026 09:52:59 +0000 Subject: [PATCH 2/7] Detect a changed crate from any file under it, not only src A change to a crate's Cargo.toml, build.rs or tests changes the crate too. --- .github/workflows/fork-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/fork-ci.yml b/.github/workflows/fork-ci.yml index 3beeb4bd81b2..b6778a5835f5 100644 --- a/.github/workflows/fork-ci.yml +++ b/.github/workflows/fork-ci.yml @@ -35,7 +35,7 @@ jobs: set -euo pipefail base="$(git merge-base HEAD origin/fork/main 2>/dev/null || git merge-base HEAD fork/main)" mapfile -t crates < <(git diff --name-only "$base"...HEAD \ - | awk -F/ '/^codex-rs\/[^/]+\/src\//{print $2}' | sort -u) + | awk -F/ '/^codex-rs\/[^/]+\/.+/{print $2}' | sort -u) echo "changed: ${crates[*]:-none}" { echo "list< Date: Fri, 9 Oct 2026 09:56:43 +0000 Subject: [PATCH 3/7] Map changed files to packages and check the whole workspace Directory names are not package names and crates nest, so each changed file maps to the package whose manifest directory is the longest prefix of its path, from cargo metadata --no-deps. The mapping lives in .github/scripts/fork-changed-packages.py rather than an inline heredoc, which cannot be indented inside a workflow step. A change to the workspace Cargo.toml or Cargo.lock checks every package. The job runs in codex-rs/, installs libcap-dev and pkg-config (linux-sandbox links libcap), and runs cargo check --workspace --all-targets so a struct-field patch that breaks a dependent fails here rather than only in the package that changed. --- .github/scripts/fork-changed-packages.py | 29 ++++++++++++++++ .github/workflows/fork-ci.yml | 43 ++++++++++++++++++++---- 2 files changed, 65 insertions(+), 7 deletions(-) create mode 100644 .github/scripts/fork-changed-packages.py diff --git a/.github/scripts/fork-changed-packages.py b/.github/scripts/fork-changed-packages.py new file mode 100644 index 000000000000..2210358d262c --- /dev/null +++ b/.github/scripts/fork-changed-packages.py @@ -0,0 +1,29 @@ +"""Map changed file paths to their cargo package names. + +Reads `cargo metadata --no-deps --format-version 1` on stdin and file paths, +one per line, on argv. A file belongs to the package whose manifest directory +is the longest prefix of its path, so a nested crate wins over its parent and +the directory name never has to equal the package name. +""" + +import json +import os +import sys + +meta = json.load(sys.stdin) +root = os.getcwd() +dirs = [] +for package in meta["packages"]: + manifest = os.path.relpath(package["manifest_path"], root) + dirs.append((os.path.dirname(manifest), package["name"])) +dirs.sort(key=lambda entry: len(entry[0]), reverse=True) + +seen = [] +for argument in sys.argv[1:]: + path = os.path.normpath(argument.removeprefix("codex-rs/")) + for directory, name in dirs: + if path == directory or path.startswith(directory + os.sep): + if name not in seen: + seen.append(name) + break +print("\n".join(seen)) diff --git a/.github/workflows/fork-ci.yml b/.github/workflows/fork-ci.yml index b6778a5835f5..359f865566bd 100644 --- a/.github/workflows/fork-ci.yml +++ b/.github/workflows/fork-ci.yml @@ -15,6 +15,10 @@ jobs: check: name: fmt, clippy, test runs-on: ubuntu-24.04 + # The workspace Cargo.toml is codex-rs/Cargo.toml; the repo root has none. + defaults: + run: + working-directory: codex-rs steps: - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.0.1 with: @@ -26,23 +30,48 @@ jobs: with: components: rustfmt, clippy - # Only the crates a fork patch touches are checked. Docs and workflow - # changes test nothing. - - name: Crates changed against fork/main + # linux-sandbox's build.rs links libcap through pkg-config. + - name: Install build libraries + working-directory: ${{ github.workspace }} + run: sudo apt-get update && sudo apt-get install -y libcap-dev pkg-config + + # Directory names are not package names (app-server-protocol is the + # package codex-app-server-protocol) and crates nest (codex-rs/ext/skills), + # so each changed file maps to the package whose manifest directory is + # the longest prefix of its path. A workspace manifest change rebuilds + # every package. + - name: Packages changed against fork/main id: crates shell: bash run: | set -euo pipefail base="$(git merge-base HEAD origin/fork/main 2>/dev/null || git merge-base HEAD fork/main)" - mapfile -t crates < <(git diff --name-only "$base"...HEAD \ - | awk -F/ '/^codex-rs\/[^/]+\/.+/{print $2}' | sort -u) - echo "changed: ${crates[*]:-none}" + mapfile -t files < <(git diff --name-only "$base"...HEAD) + all=false + for f in "${files[@]}"; do + case "$f" in + codex-rs/Cargo.toml|codex-rs/Cargo.lock) all=true ;; + esac + done + if $all; then + mapfile -t packages < <(cargo metadata --no-deps --format-version 1 \ + | jq -r '.packages[].name' | sort -u) + else + mapfile -t packages < <(cargo metadata --no-deps --format-version 1 \ + | python3 "${GITHUB_WORKSPACE}/.github/scripts/fork-changed-packages.py" "${files[@]}") + fi + echo "changed: ${packages[*]:-none}" { echo "list<> "$GITHUB_OUTPUT" + # A whole-workspace check, because a struct-field patch breaks dependents + # that a per-package test never compiles. + - name: cargo check + run: cargo check --workspace --all-targets + - name: cargo fmt shell: bash run: | From 9d098f96088c49462d238daa786248cbacb14cc3 Mon Sep 17 00:00:00 2001 From: theDancingDeveloper Date: Fri, 9 Oct 2026 09:57:04 +0000 Subject: [PATCH 4/7] Strip the codex-rs prefix without str.removeprefix --- .github/scripts/fork-changed-packages.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/scripts/fork-changed-packages.py b/.github/scripts/fork-changed-packages.py index 2210358d262c..b7cdcf082705 100644 --- a/.github/scripts/fork-changed-packages.py +++ b/.github/scripts/fork-changed-packages.py @@ -20,7 +20,10 @@ seen = [] for argument in sys.argv[1:]: - path = os.path.normpath(argument.removeprefix("codex-rs/")) + path = os.path.normpath(argument) + prefix = "codex-rs" + os.sep + if path.startswith(prefix): + path = path[len(prefix):] for directory, name in dirs: if path == directory or path.startswith(directory + os.sep): if name not in seen: From 472cc140070e0e77178c60b041131d5cca6ae81e Mon Sep 17 00:00:00 2001 From: theDancingDeveloper Date: Fri, 9 Oct 2026 10:17:54 +0000 Subject: [PATCH 5/7] Install the GLib and GStreamer libraries the workspace check needs cargo check --workspace fails in the glib-sys build script because the voice and GStreamer crates (glib-sys, gstreamer-*-sys, gio-sys, gobject-sys) link system libraries the runner does not have. Install libglib2.0-dev, libgstreamer1.0-dev, libgstreamer-plugins-base1.0-dev and libasound2-dev alongside libcap-dev, matching what upstream rust-ci-full installs, so the full-workspace check stays honest instead of excluding those crates. --- .github/workflows/fork-ci.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/fork-ci.yml b/.github/workflows/fork-ci.yml index 359f865566bd..2bf6ba4fc26f 100644 --- a/.github/workflows/fork-ci.yml +++ b/.github/workflows/fork-ci.yml @@ -30,10 +30,18 @@ jobs: with: components: rustfmt, clippy - # linux-sandbox's build.rs links libcap through pkg-config. + # linux-sandbox's build.rs links libcap through pkg-config. The voice and + # GStreamer crates (glib-sys, gstreamer-*-sys, gio-sys, gobject-sys) need + # the system libraries too, or `cargo check --workspace` fails in their + # build scripts. libasound2-dev matches what upstream rust-ci-full installs. - name: Install build libraries working-directory: ${{ github.workspace }} - run: sudo apt-get update && sudo apt-get install -y libcap-dev pkg-config + run: | + sudo apt-get update + sudo apt-get install -y \ + libcap-dev pkg-config \ + libglib2.0-dev libgstreamer1.0-dev \ + libgstreamer-plugins-base1.0-dev libasound2-dev # Directory names are not package names (app-server-protocol is the # package codex-app-server-protocol) and crates nest (codex-rs/ext/skills), From 8e7029cfaf7fca65b36a3d4f64d640544fa96481 Mon Sep 17 00:00:00 2001 From: theDancingDeveloper Date: Fri, 9 Oct 2026 10:57:28 +0000 Subject: [PATCH 6/7] Exclude codex-voice-host from the workspace check It is the only package that pulls in GStreamer, and it requires gstreamer-1.0 >= 1.28 while Ubuntu 24.04 ships 1.24, so no apt package can satisfy the build script. Upstream does not build it with cargo either: the release workflows build //codex-rs/voice-host with Bazel against a bundled GStreamer. libasound2-dev stays, matching rust-ci-full. --- .github/workflows/fork-ci.yml | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/.github/workflows/fork-ci.yml b/.github/workflows/fork-ci.yml index 2bf6ba4fc26f..160933ec09ed 100644 --- a/.github/workflows/fork-ci.yml +++ b/.github/workflows/fork-ci.yml @@ -30,18 +30,10 @@ jobs: with: components: rustfmt, clippy - # linux-sandbox's build.rs links libcap through pkg-config. The voice and - # GStreamer crates (glib-sys, gstreamer-*-sys, gio-sys, gobject-sys) need - # the system libraries too, or `cargo check --workspace` fails in their - # build scripts. libasound2-dev matches what upstream rust-ci-full installs. + # linux-sandbox's build.rs links libcap through pkg-config. - name: Install build libraries working-directory: ${{ github.workspace }} - run: | - sudo apt-get update - sudo apt-get install -y \ - libcap-dev pkg-config \ - libglib2.0-dev libgstreamer1.0-dev \ - libgstreamer-plugins-base1.0-dev libasound2-dev + run: sudo apt-get update && sudo apt-get install -y libcap-dev pkg-config libasound2-dev # Directory names are not package names (app-server-protocol is the # package codex-app-server-protocol) and crates nest (codex-rs/ext/skills), @@ -75,10 +67,12 @@ jobs: echo "EOF" } >> "$GITHUB_OUTPUT" - # A whole-workspace check, because a struct-field patch breaks dependents - # that a per-package test never compiles. + # codex-voice-host is excluded: it is the only package pulling in GStreamer, + # and it requires gstreamer-1.0 >= 1.28. Ubuntu 24.04 ships 1.24, so no + # apt package satisfies it. Upstream never builds it with cargo either; it + # builds //codex-rs/voice-host with Bazel against a bundled GStreamer. - name: cargo check - run: cargo check --workspace --all-targets + run: cargo check --workspace --all-targets --exclude codex-voice-host - name: cargo fmt shell: bash From 27bc6403b0432819c51dc72693e6de05a9f1c77c Mon Sep 17 00:00:00 2001 From: theDancingDeveloper Date: Fri, 9 Oct 2026 11:09:50 +0000 Subject: [PATCH 7/7] Point the workspace check at the Codex-built rusty_v8 artifacts cargo check --workspace builds v8, whose build.rs downloads librusty_v8_ptrcomp_sandbox_release from denoland and gets a 404: denoland does not publish the ptrcomp_sandbox profile this tree expects. Run setup-rusty-v8 for x86_64-unknown-linux-gnu first, as fork-release does, so RUSTY_V8_ARCHIVE and RUSTY_V8_SRC_BINDING_PATH come from the openai/codex rusty-v8 release and are verified against the in-tree checksum manifest. --- .github/workflows/fork-ci.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/fork-ci.yml b/.github/workflows/fork-ci.yml index 160933ec09ed..693d41cfcf35 100644 --- a/.github/workflows/fork-ci.yml +++ b/.github/workflows/fork-ci.yml @@ -67,6 +67,15 @@ jobs: echo "EOF" } >> "$GITHUB_OUTPUT" + # v8's build.rs otherwise downloads librusty_v8 from denoland, which does + # not publish the ptrcomp_sandbox profile this tree expects. This fetches + # the Codex-built artifacts from the upstream release and verifies them + # against the in-tree checksum manifest, the same as fork-release. + - name: Configure rusty_v8 artifact overrides and verify checksums + uses: ./.github/actions/setup-rusty-v8 + with: + target: x86_64-unknown-linux-gnu + # codex-voice-host is excluded: it is the only package pulling in GStreamer, # and it requires gstreamer-1.0 >= 1.28. Ubuntu 24.04 ships 1.24, so no # apt package satisfies it. Upstream never builds it with cargo either; it