diff --git a/.bazelrc b/.bazelrc index ce7c1e1d43..331f8634d2 100644 --- a/.bazelrc +++ b/.bazelrc @@ -75,6 +75,11 @@ common:ci --disk_cache= common:ci-bazel --config=ci common:ci-bazel --build_metadata=TAG_workflow=bazel +# Shared config for Bazel-backed Rust linting. +build:clippy --aspects=@rules_rust//rust:defs.bzl%rust_clippy_aspect +build:clippy --output_groups=+clippy_checks +build:clippy --@rules_rust//rust/settings:clippy.toml=//codex-rs:clippy.toml + # Rearrange caches on Windows so they're on the same volume as the checkout. common:ci-windows --config=ci-bazel common:ci-windows --build_metadata=TAG_os=windows diff --git a/.github/workflows/bazel.yml b/.github/workflows/bazel.yml index 79d963a537..9a647903cc 100644 --- a/.github/workflows/bazel.yml +++ b/.github/workflows/bazel.yml @@ -231,3 +231,77 @@ jobs: path: | ~/.cache/bazel-repo-cache key: bazel-cache-${{ matrix.target }}-${{ hashFiles('MODULE.bazel', 'codex-rs/Cargo.lock', 'codex-rs/Cargo.toml') }} + + clippy: + runs-on: ubuntu-24.04 + name: Bazel clippy for codex-rs + + steps: + - uses: actions/checkout@v6 + + - name: Set up Bazel + uses: bazelbuild/setup-bazelisk@v3 + + # Restore bazel repository cache so we don't have to redownload all the external dependencies + # on every CI run. + - name: Restore bazel repository cache + id: cache_bazel_repository_restore + uses: actions/cache/restore@v5 + with: + path: | + ~/.cache/bazel-repo-cache + key: bazel-cache-x86_64-unknown-linux-gnu-${{ hashFiles('MODULE.bazel', 'codex-rs/Cargo.lock', 'codex-rs/Cargo.toml') }} + restore-keys: | + bazel-cache-x86_64-unknown-linux-gnu + + - name: bazel build --config=clippy //codex-rs/... + env: + BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }} + shell: bash + run: | + set -o pipefail + + bazel_args=( + build + --config=ci-linux + --config=clippy + --build_metadata=COMMIT_SHA=$(git rev-parse HEAD) + --build_metadata=TAG_job=clippy + ) + + bazel_targets=( + //codex-rs/... + # Keep the initial Bazel clippy scope on codex-rs and out of the + # V8 proof-of-concept target for now. + -//codex-rs/v8-poc:all + ) + + if [[ -n "${BUILDBUDDY_API_KEY:-}" ]]; then + echo "BuildBuddy API key is available; using remote Bazel configuration." + bazel $BAZEL_STARTUP_ARGS \ + --noexperimental_remote_repo_contents_cache \ + "${bazel_args[@]}" \ + --remote_header="x-buildbuddy-api-key=$BUILDBUDDY_API_KEY" \ + -- \ + "${bazel_targets[@]}" + else + echo "BuildBuddy API key is not available; using local Bazel configuration." + bazel $BAZEL_STARTUP_ARGS \ + --noexperimental_remote_repo_contents_cache \ + "${bazel_args[@]}" \ + --remote_cache= \ + --remote_executor= \ + -- \ + "${bazel_targets[@]}" + fi + + # Save bazel repository cache explicitly; make non-fatal so cache uploading + # never fails the overall job. Only save when key wasn't hit. + - name: Save bazel repository cache + if: always() && !cancelled() && steps.cache_bazel_repository_restore.outputs.cache-hit != 'true' + continue-on-error: true + uses: actions/cache/save@v5 + with: + path: | + ~/.cache/bazel-repo-cache + key: bazel-cache-x86_64-unknown-linux-gnu-${{ hashFiles('MODULE.bazel', 'codex-rs/Cargo.lock', 'codex-rs/Cargo.toml') }} diff --git a/codex-rs/BUILD.bazel b/codex-rs/BUILD.bazel index 47324dbdca..b6711d9b81 100644 --- a/codex-rs/BUILD.bazel +++ b/codex-rs/BUILD.bazel @@ -1,3 +1,4 @@ exports_files([ + "clippy.toml", "node-version.txt", ]) diff --git a/codex-rs/shell-escalation/src/unix/socket.rs b/codex-rs/shell-escalation/src/unix/socket.rs index 8325e940ff..aa97b5f379 100644 --- a/codex-rs/shell-escalation/src/unix/socket.rs +++ b/codex-rs/shell-escalation/src/unix/socket.rs @@ -60,8 +60,12 @@ fn extract_fds(control: &[u8]) -> Vec { if level == libc::SOL_SOCKET && ty == libc::SCM_RIGHTS { let data_ptr = unsafe { libc::CMSG_DATA(cmsg).cast::() }; let fd_count: usize = { - let cmsg_data_len = - unsafe { (*cmsg).cmsg_len as usize } - unsafe { libc::CMSG_LEN(0) as usize }; + // `cmsghdr::cmsg_len` is not typed consistently across targets, so normalize it + // before doing the size arithmetic. + #[allow(clippy::useless_conversion)] + let cmsg_data_len = usize::try_from(unsafe { (*cmsg).cmsg_len }) + .expect("cmsghdr length fits") + - unsafe { libc::CMSG_LEN(0) as usize }; cmsg_data_len / size_of::() }; for i in 0..fd_count { diff --git a/codex-rs/tui/src/app_event.rs b/codex-rs/tui/src/app_event.rs index b5e81edd9f..1fa27b6753 100644 --- a/codex-rs/tui/src/app_event.rs +++ b/codex-rs/tui/src/app_event.rs @@ -480,6 +480,7 @@ pub(crate) enum AppEvent { /// Voice transcription finished for the given placeholder id. #[cfg(not(target_os = "linux"))] + #[cfg_attr(not(feature = "voice-input"), allow(dead_code))] TranscriptionComplete { id: String, text: String, diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index e291480d0b..e8abd9cad1 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -167,6 +167,7 @@ mod voice { pub(crate) enum RealtimeInputBehavior { Ungated, PlaybackAware { + #[allow(dead_code)] playback_queued_samples: Arc, }, } diff --git a/codex-rs/tui_app_server/src/app_event.rs b/codex-rs/tui_app_server/src/app_event.rs index e6d8bdecda..e652cb2351 100644 --- a/codex-rs/tui_app_server/src/app_event.rs +++ b/codex-rs/tui_app_server/src/app_event.rs @@ -491,6 +491,7 @@ pub(crate) enum AppEvent { /// Voice transcription finished for the given placeholder id. #[cfg(not(target_os = "linux"))] + #[cfg_attr(not(feature = "voice-input"), allow(dead_code))] TranscriptionComplete { id: String, text: String, diff --git a/justfile b/justfile index 5c9fa5e6ab..08e77e5b38 100644 --- a/justfile +++ b/justfile @@ -69,6 +69,9 @@ bazel-lock-check: bazel-test: bazel test //... --keep_going +bazel-clippy: + bazel build --config=clippy -- //codex-rs/... -//codex-rs/v8-poc:all + bazel-remote-test: bazel test //... --config=remote --platforms=//:rbe --keep_going