ci: run argument-comment-lint through bazel package tests

This commit is contained in:
Michael Bolin
2026-03-28 02:00:57 -07:00
parent e39ddc61b1
commit 335f56b5ed
11 changed files with 356 additions and 26 deletions
+19
View File
@@ -0,0 +1,19 @@
load("@rules_rust//rust:defs.bzl", "rust_binary")
filegroup(
name = "runtime-files",
srcs = [
"argument-comment-lint",
"run-prebuilt-linter.py",
"wrapper_common.py",
],
visibility = ["//visibility:public"],
)
rust_binary(
name = "argument-comment-lint-bazel-runner",
crate_name = "argument_comment_lint_bazel_runner",
crate_root = "bazel_runner.rs",
srcs = ["bazel_runner.rs"],
visibility = ["//visibility:public"],
)
+4 -1
View File
@@ -85,7 +85,9 @@ rustup toolchain install nightly-2025-09-18 \
The checked-in DotSlash file lives at `tools/argument-comment-lint/argument-comment-lint`.
`run-prebuilt-linter.py` resolves that file via `dotslash` and is the path used by
`just clippy`, `just argument-comment-lint`, and the Rust CI job. The
`just clippy` and `just argument-comment-lint`. Bazel-backed CI now drives the
same wrapper through package-owned test targets tagged `argument-comment-lint`.
The
source-build path remains available in `run.py` for people
iterating on the lint crate itself.
@@ -128,6 +130,7 @@ Run the lint against `codex-rs` from the repo root:
```bash
./tools/argument-comment-lint/run-prebuilt-linter.py -p codex-core
just argument-comment-lint -p codex-core
bazel test --build_tests_only --test_tag_filters=argument-comment-lint //codex-rs/...
```
If no package selection is provided, `run-prebuilt-linter.py` defaults to checking the
+148
View File
@@ -0,0 +1,148 @@
use std::env;
use std::ffi::OsString;
use std::hash::DefaultHasher;
use std::hash::Hash;
use std::hash::Hasher;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use std::process::ExitCode;
const TOOLCHAIN_CHANNEL: &str = "nightly-2025-09-18";
#[cfg(target_os = "linux")]
const TOOLCHAIN_TRIPLE: &str = "x86_64-unknown-linux-gnu";
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
const TOOLCHAIN_TRIPLE: &str = "aarch64-apple-darwin";
#[cfg(all(target_os = "macos", target_arch = "x86_64"))]
const TOOLCHAIN_TRIPLE: &str = "x86_64-apple-darwin";
#[cfg(target_os = "windows")]
const TOOLCHAIN_TRIPLE: &str = "x86_64-pc-windows-msvc";
fn main() -> ExitCode {
match run() {
Ok(code) => code,
Err(err) => {
eprintln!("{err}");
ExitCode::from(1)
}
}
}
fn run() -> Result<ExitCode, String> {
let manifest = env::var("ARGUMENT_COMMENT_LINT_MANIFEST")
.map_err(|_| "ARGUMENT_COMMENT_LINT_MANIFEST must be set".to_string())?;
let workspace_dir = env::current_dir().map_err(|err| format!("failed to get cwd: {err}"))?;
let wrapper = find_repo_root(&workspace_dir)?
.join("tools")
.join("argument-comment-lint")
.join("run-prebuilt-linter.py");
let python = if cfg!(windows) { "python" } else { "python3" };
let mut command = Command::new(python);
command.arg(&wrapper);
command.arg("--manifest-path");
command.arg(&manifest);
// Keep Linux on the narrower target set for now to match the current CI
// rollout, while macOS and Windows continue to exercise all targets.
if cfg!(target_os = "linux") {
command.args(["--", "--lib", "--bins"]);
}
if env::var_os("CARGO_TARGET_DIR").is_none() {
command.env("CARGO_TARGET_DIR", shared_target_dir(&workspace_dir));
}
command.env("RUSTUP_TOOLCHAIN", TOOLCHAIN_CHANNEL);
command.env("RUSTUP_AUTO_INSTALL", "0");
if env::var_os("RUSTUP_HOME").is_none()
&& let Some(rustup_home) = infer_rustup_home()
{
command.env("RUSTUP_HOME", rustup_home);
}
let status = command
.status()
.map_err(|err| format!("failed to execute {python}: {err}"))?;
Ok(status
.code()
.and_then(|code| u8::try_from(code).ok())
.map_or_else(|| ExitCode::from(1), ExitCode::from))
}
fn find_repo_root(cwd: &Path) -> Result<&Path, String> {
if cwd
.join("tools")
.join("argument-comment-lint")
.join("run-prebuilt-linter.py")
.is_file()
{
return Ok(cwd);
}
let Some(parent) = cwd.parent() else {
return Err(format!(
"argument-comment wrapper not found relative to {}",
cwd.display()
));
};
if parent
.join("tools")
.join("argument-comment-lint")
.join("run-prebuilt-linter.py")
.is_file()
{
return Ok(parent);
}
Err(format!(
"argument-comment wrapper not found relative to {}",
cwd.display()
))
}
fn shared_target_dir(workspace_dir: &Path) -> PathBuf {
let namespace = if let Some(run_id) = env::var_os("GITHUB_RUN_ID") {
let mut namespace = run_id;
if let Some(run_attempt) = env::var_os("GITHUB_RUN_ATTEMPT") {
namespace.push("-");
namespace.push(run_attempt);
}
namespace
} else {
let mut hasher = DefaultHasher::new();
workspace_dir.hash(&mut hasher);
OsString::from(format!("{:016x}", hasher.finish()))
};
env::temp_dir()
.join("argument-comment-lint")
.join(namespace)
.join(format!("{TOOLCHAIN_CHANNEL}-{TOOLCHAIN_TRIPLE}"))
}
fn infer_rustup_home() -> Option<OsString> {
if let Some(rustup_home) = env::var_os("RUSTUP_HOME") {
return Some(rustup_home);
}
if let Some(cargo_home) = env::var_os("CARGO_HOME")
&& let Some(home_dir) = Path::new(&cargo_home).parent()
{
let rustup_home = home_dir.join(".rustup");
if rustup_home.is_dir() {
return Some(rustup_home.into_os_string());
}
}
for var in ["HOME", "USERPROFILE"] {
if let Some(home) = env::var_os(var) {
let rustup_home = Path::new(&home).join(".rustup");
if rustup_home.is_dir() {
return Some(rustup_home.into_os_string());
}
}
}
None
}
@@ -83,6 +83,26 @@ class WrapperCommonTest(unittest.TestCase):
],
)
def test_explicit_package_manifest_does_not_force_workspace(self) -> None:
parsed = wrapper_common.parse_wrapper_args(
[
"--manifest-path",
"/tmp/custom/Cargo.toml",
]
)
final_args = wrapper_common.build_final_args(parsed, Path("/repo/codex-rs/Cargo.toml"))
self.assertEqual(
final_args,
[
"--no-deps",
"--manifest-path",
"/tmp/custom/Cargo.toml",
"--",
"--all-targets",
],
)
if __name__ == "__main__":
unittest.main()
@@ -107,7 +107,7 @@ def build_final_args(parsed: ParsedWrapperArgs, manifest_path: Path) -> list[str
if not parsed.has_manifest_path:
final_args.extend(["--manifest-path", str(manifest_path)])
if not parsed.has_package_selection:
if not parsed.has_package_selection and not parsed.has_manifest_path:
final_args.append("--workspace")
if not parsed.has_no_deps:
final_args.append("--no-deps")
@@ -205,6 +205,9 @@ def ensure_source_prerequisites(env: MutableMapping[str, str]) -> None:
def prefer_rustup_shims(env: MutableMapping[str, str]) -> None:
if env.get("CODEX_ARGUMENT_COMMENT_LINT_SKIP_RUSTUP_SHIMS") == "1":
return
rustup = shutil.which("rustup", path=env.get("PATH"))
if rustup is None:
return