From de68e871b58285875c31762c19838cfd87544854 Mon Sep 17 00:00:00 2001 From: Jeff Huber Date: Wed, 2 Sep 2026 13:49:14 -0700 Subject: [PATCH] Fix Codex runner smoke compatibility --- docs/build-loop-in-30-minutes.md | 11 ++++++- docs/self-hosted-mac-runner.md | 11 ++++++- .../doctor_checks/self_hosted_runner.py | 3 -- tests/test_doctor_self_hosted_runner.py | 31 +++++++++++++++++++ tests/test_release_hygiene.py | 12 +++++++ 5 files changed, 63 insertions(+), 5 deletions(-) diff --git a/docs/build-loop-in-30-minutes.md b/docs/build-loop-in-30-minutes.md index 8d42fef2..f6d07716 100644 --- a/docs/build-loop-in-30-minutes.md +++ b/docs/build-loop-in-30-minutes.md @@ -392,11 +392,20 @@ Run these smoke checks as the macOS user that owns the runner process: ```bash gh auth status codex --version -codex exec --skip-git-repo-check --sandbox read-only --ask-for-approval never --ephemeral "Reply with exactly: ok" +codex exec --skip-git-repo-check --sandbox read-only "Reply with exactly: ok" claude auth status claude -p "Reply with exactly: ok" --output-format json ``` +For non-interactive runner or hosted-agent setup, load the Codex API key from a +secret store and pipe it into the CLI without printing it: + +```bash +printf '%s\n' "$OPENAI_API_KEY" | codex login --with-api-key +codex login status +codex exec --skip-git-repo-check --sandbox read-only "Reply with exactly: ok" +``` + In service mode, set `USER`, `LOGNAME`, `SHELL`, `LANG`, and a PATH that can find `gh`, `git`, `codex`, `claude`, and `python3` in the runner `.env`, then fully restart the listener. diff --git a/docs/self-hosted-mac-runner.md b/docs/self-hosted-mac-runner.md index 92373558..287b490c 100644 --- a/docs/self-hosted-mac-runner.md +++ b/docs/self-hosted-mac-runner.md @@ -116,11 +116,20 @@ Verify auth from a runner job or from the same service user environment: ```bash gh auth status codex login status -codex exec --skip-git-repo-check --sandbox read-only --ask-for-approval never --ephemeral "Reply with exactly: ok" +codex exec --skip-git-repo-check --sandbox read-only "Reply with exactly: ok" claude auth status claude -p "Reply with exactly: ok" --output-format json ``` +For API-key runner auth, store `OPENAI_API_KEY` in the runner's secret manager +or private `.env`, then load it into the setup shell and run: + +```bash +printf '%s\n' "$OPENAI_API_KEY" | codex login --with-api-key +codex login status +codex exec --skip-git-repo-check --sandbox read-only "Reply with exactly: ok" +``` + `claude auth status` is not enough by itself. The prompt smoke is the useful signal because login-keychain or inherited-env failures often appear only when Claude makes a real non-interactive request. diff --git a/src/code_mower/doctor_checks/self_hosted_runner.py b/src/code_mower/doctor_checks/self_hosted_runner.py index 62d465be..a1e22cd0 100644 --- a/src/code_mower/doctor_checks/self_hosted_runner.py +++ b/src/code_mower/doctor_checks/self_hosted_runner.py @@ -215,9 +215,6 @@ def _check_codex_auth_probe( "--skip-git-repo-check", "--sandbox", "read-only", - "--ask-for-approval", - "never", - "--ephemeral", "--output-last-message", str(output_path), "Reply with exactly: ok", diff --git a/tests/test_doctor_self_hosted_runner.py b/tests/test_doctor_self_hosted_runner.py index f4c1da46..ef98370b 100644 --- a/tests/test_doctor_self_hosted_runner.py +++ b/tests/test_doctor_self_hosted_runner.py @@ -145,6 +145,37 @@ def fake_run(command: list[str], **_: object) -> subprocess.CompletedProcess[str self.assertEqual(codex_check.status, "fail") self.assertIn("codex auth prompt probe", codex_check.message) + def test_runner_cli_auth_codex_probe_omits_stale_smoke_flags(self) -> None: + config = _runner_config() + codex_lane = config["lanes"]["codex"] + calls: list[list[str]] = [] + + with tempfile.TemporaryDirectory() as tmp: + bin_dir = Path(tmp) / "bin" + bin_dir.mkdir() + codex = bin_dir / "codex" + codex.write_text("#!/bin/sh\nexit 0\n", encoding="utf-8") + codex.chmod(0o755) + + def fake_run(command: list[str], **_: object) -> subprocess.CompletedProcess[str]: + calls.append(command) + return subprocess.CompletedProcess(command, 0, "ok", "") + + with mock.patch.dict(os.environ, {"PATH": str(bin_dir)}, clear=False): + checks = check_runner_cli_auth( + [("codex", codex_lane)], + run=fake_run, + http_timeout=5, + ) + + codex_check = next(check for check in checks if check.lane == "codex") + self.assertEqual(codex_check.status, "pass") + self.assertEqual(len(calls), 1) + self.assertNotIn("--ephemeral", calls[0]) + self.assertNotIn("--ask-for-approval", calls[0]) + self.assertNotIn("--ephemeral", codex_check.detail["args"]) + self.assertNotIn("--ask-for-approval", codex_check.detail["args"]) + def test_runner_workflow_labels_fail_when_custom_label_is_missing(self) -> None: with tempfile.TemporaryDirectory() as tmp: repo_root = Path(tmp) diff --git a/tests/test_release_hygiene.py b/tests/test_release_hygiene.py index 67c8b25b..f4a6c07f 100644 --- a/tests/test_release_hygiene.py +++ b/tests/test_release_hygiene.py @@ -114,6 +114,18 @@ def test_ci_workflow_tests_supported_python_minors(self) -> None: self.assertIn(" - name: Unit tests\n", workflow) self.assertIn(" - name: Compile sources\n", workflow) + def test_codex_smoke_docs_use_supported_flags(self) -> None: + doc_paths = ( + ROOT / "docs/build-loop-in-30-minutes.md", + ROOT / "docs/self-hosted-mac-runner.md", + ) + + for path in doc_paths: + text = path.read_text(encoding="utf-8") + self.assertNotIn("--ephemeral", text, msg=str(path)) + self.assertNotIn("--ask-for-approval", text, msg=str(path)) + self.assertIn("codex login --with-api-key", text, msg=str(path)) + def test_ruff_static_rule_stage_is_intentional(self) -> None: pyproject = tomllib.loads((ROOT / "pyproject.toml").read_text(encoding="utf-8"))