Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,15 @@ jobs:
- name: Check root README ToC
run: python3 scripts/readme_toc.py README.md

- uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2.62.49
with:
tool: just
- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
version: "0.11.3"
- name: Ruff format Python scripts (run `just fmt` to fix)
run: just fmt-scripts-check

- name: Prettier (run `pnpm run format:fix` to fix)
run: pnpm run format
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ In the codex-rs folder where the rust code lives:
trivial; prefer new modules/files and keep `chatwidget.rs` focused on orchestration.
- When running Rust commands (e.g. `just fix` or `just test`) be patient with the command and never try to kill them using the PID. Rust lock can make the execution slow, this is expected.

Run `just fmt` (in `codex-rs` directory) automatically after you have finished making Rust code changes; do not ask for approval to run it. Additionally, run the tests:
Run `just fmt` (in the `codex-rs` directory) automatically after you have finished making code changes anywhere in this repository; do not ask for approval to run it. Additionally, run the tests:

1. Do not run `cargo test` directly. Use `just test` so test execution follows the repo defaults.
2. Run the test for the specific project that was changed. For example, if changes were made in `codex-rs/tui`, run `just test -p codex-tui`.
Expand Down
7 changes: 6 additions & 1 deletion justfile
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,16 @@ app-server-test-client *args:
cargo build -p codex-cli
cargo run -p codex-app-server-test-client -- --codex-bin ./target/debug/codex {args}

# Format Rust and Python SDK code.
# Format Rust, Python SDK code, and Python scripts.
Comment thread
anp-oai marked this conversation as resolved.
fmt:
cargo fmt -- --config imports_granularity=Item {stderr-null}
uv run --frozen --project ../sdk/python --extra dev ruff check --fix --fix-only ../sdk/python
uv run --frozen --project ../sdk/python --extra dev ruff format ../sdk/python
# Root scripts have their own locked Ruff environment.
uv run --frozen --project ../scripts ruff format ../scripts

fmt-scripts-check:
uv run --frozen --project ../scripts ruff format --check ../scripts

fix *args:
cargo clippy --fix --tests --allow-dirty {args}
Expand Down
16 changes: 12 additions & 4 deletions scripts/check_blob_size.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ def blob_size(commit: str, path: str) -> int:
return int(run_git("cat-file", "-s", f"{commit}:{path}").strip())


def collect_changed_blobs(base: str, head: str, allowlist: set[str]) -> list[ChangedBlob]:
def collect_changed_blobs(
base: str, head: str, allowlist: set[str]
) -> list[ChangedBlob]:
blobs: list[ChangedBlob] = []
for path in get_changed_paths(base, head):
blobs.append(
Expand Down Expand Up @@ -137,7 +139,9 @@ def main() -> int:
parser = argparse.ArgumentParser(
description="Fail if changed blobs exceed the configured size budget."
)
parser.add_argument("--base", required=True, help="Base git revision to diff against.")
parser.add_argument(
"--base", required=True, help="Base git revision to diff against."
)
parser.add_argument("--head", required=True, help="Head git revision to inspect.")
parser.add_argument(
"--max-bytes",
Expand All @@ -156,7 +160,9 @@ def main() -> int:
allowlist = load_allowlist(args.allowlist)
blobs = collect_changed_blobs(args.base, args.head, allowlist)
violations = [
blob for blob in blobs if blob.size_bytes > args.max_bytes and not blob.is_allowlisted
blob
for blob in blobs
if blob.size_bytes > args.max_bytes and not blob.is_allowlisted
]

write_step_summary(args.max_bytes, blobs, violations)
Expand All @@ -165,7 +171,9 @@ def main() -> int:
print("No changed files were detected.")
return 0

print(f"Checked {len(blobs)} changed file(s) against the {args.max_bytes}-byte limit.")
print(
f"Checked {len(blobs)} changed file(s) against the {args.max_bytes}-byte limit."
)
for blob in blobs:
status = "allowlisted" if blob.is_allowlisted else "ok"
if blob in violations:
Expand Down
9 changes: 7 additions & 2 deletions scripts/codex_package/archive.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,9 @@ def resolve_zstd_command(


def write_zip_archive(package_dir: Path, archive_path: Path) -> None:
with zipfile.ZipFile(archive_path, "w", compression=zipfile.ZIP_DEFLATED) as archive:
with zipfile.ZipFile(
archive_path, "w", compression=zipfile.ZIP_DEFLATED
) as archive:
for path in package_entries(package_dir):
relative_path = path.relative_to(package_dir)
if path.is_dir():
Expand All @@ -109,4 +111,7 @@ def write_zip_archive(package_dir: Path, archive_path: Path) -> None:


def package_entries(package_dir: Path) -> list[Path]:
return sorted(package_dir.rglob("*"), key=lambda path: path.relative_to(package_dir).as_posix())
return sorted(
package_dir.rglob("*"),
key=lambda path: path.relative_to(package_dir).as_posix(),
)
7 changes: 4 additions & 3 deletions scripts/codex_package/cargo.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ def build_source_binaries(
variant,
build_entrypoint=entrypoint_bin is None,
build_bwrap=spec.is_linux and bwrap_bin is None,
build_codex_command_runner=spec.is_windows
and codex_command_runner_bin is None,
build_codex_command_runner=spec.is_windows and codex_command_runner_bin is None,
build_codex_windows_sandbox_setup=spec.is_windows
and codex_windows_sandbox_setup_bin is None,
)
Expand Down Expand Up @@ -138,7 +137,9 @@ def validate_prebuilt_resource_inputs(
)


def resolve_output_path(explicit_path: Path | None, default_path: Path | None) -> Path | None:
def resolve_output_path(
explicit_path: Path | None, default_path: Path | None
) -> Path | None:
if explicit_path is not None:
return explicit_path.resolve()

Expand Down
10 changes: 5 additions & 5 deletions scripts/codex_package/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ def parse_args() -> argparse.Namespace:
type=Path,
default=argparse.SUPPRESS,
help=(
"Output directory to create as the package root. Defaults to a new "
"temporary directory."
"Output directory to create as the package root. Defaults to a new temporary directory."
),
)
parser.add_argument(
Expand All @@ -72,8 +71,7 @@ def parse_args() -> argparse.Namespace:
"--cargo-profile",
default="dev-small",
help=(
"Cargo profile for source-built package artifacts. Use release for "
"release packages."
"Cargo profile for source-built package artifacts. Use release for release packages."
),
)
parser.add_argument(
Expand Down Expand Up @@ -169,7 +167,9 @@ def main() -> int:
)
prepare_package_dir(package_dir, force=args.force)
build_package_dir(package_dir, version, variant, spec, inputs)
validate_package_dir(package_dir, variant, spec, include_zsh=inputs.zsh_bin is not None)
validate_package_dir(
package_dir, variant, spec, include_zsh=inputs.zsh_bin is not None
)

for archive_output in args.archive_output:
archive_path = archive_output.resolve()
Expand Down
4 changes: 3 additions & 1 deletion scripts/codex_package/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
def prepare_package_dir(package_dir: Path, *, force: bool) -> None:
if package_dir.exists():
if not package_dir.is_dir():
raise RuntimeError(f"Package output exists and is not a directory: {package_dir}")
raise RuntimeError(
f"Package output exists and is not a directory: {package_dir}"
)
if any(package_dir.iterdir()):
if not force:
raise RuntimeError(
Expand Down
8 changes: 6 additions & 2 deletions scripts/codex_package/test_cargo.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ def test_macos_package_with_prebuilt_entrypoint_builds_nothing(self) -> None:
[],
)

def test_linux_package_with_prebuilt_entrypoint_and_bwrap_builds_nothing(self) -> None:
def test_linux_package_with_prebuilt_entrypoint_and_bwrap_builds_nothing(
self,
) -> None:
self.assertEqual(
source_binaries_for_target(
TARGET_SPECS["x86_64-unknown-linux-musl"],
Expand All @@ -40,7 +42,9 @@ def test_linux_package_with_prebuilt_entrypoint_and_bwrap_builds_nothing(self) -
[],
)

def test_windows_package_with_prebuilt_entrypoint_and_helpers_builds_nothing(self) -> None:
def test_windows_package_with_prebuilt_entrypoint_and_helpers_builds_nothing(
self,
) -> None:
self.assertEqual(
source_binaries_for_target(
TARGET_SPECS["x86_64-pc-windows-msvc"],
Expand Down
29 changes: 18 additions & 11 deletions scripts/codex_package/v8.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ def resolve_codex_v8_cargo_env(
return {}
if archive_override or binding_override:
raise RuntimeError(
"Cargo package builds need RUSTY_V8_ARCHIVE and "
"RUSTY_V8_SRC_BINDING_PATH set together."
"Cargo package builds need RUSTY_V8_ARCHIVE and RUSTY_V8_SRC_BINDING_PATH set together."
)

artifacts = fetch_codex_v8_artifacts(spec, cache_root=cache_root)
Expand All @@ -61,12 +60,13 @@ def fetch_codex_v8_artifacts(
cache_root: Path | None = None,
) -> RustyV8ArtifactPair:
if spec.is_windows:
raise RuntimeError(f"No Codex-built V8 release artifacts for target: {spec.target}")
raise RuntimeError(
f"No Codex-built V8 release artifacts for target: {spec.target}"
)

version = version or resolved_v8_crate_version()
release_url = (
"https://github.com/openai/codex/releases/download/"
f"rusty-v8-v{version}"
f"https://github.com/openai/codex/releases/download/rusty-v8-v{version}"
)
target = spec.target
cache_dir = (cache_root or default_cache_root()) / f"rusty-v8-{version}-{target}"
Expand Down Expand Up @@ -98,7 +98,9 @@ def resolved_v8_crate_version() -> str:
}
)
if len(versions) != 1:
raise RuntimeError(f"Expected exactly one resolved v8 version, found: {versions}")
raise RuntimeError(
f"Expected exactly one resolved v8 version, found: {versions}"
)
return versions[0]


Expand All @@ -111,18 +113,21 @@ def load_checksums(checksums_path: Path, artifact_names: set[str]) -> dict[str,
lines = checksums_path.read_text(encoding="utf-8").splitlines()
if len(lines) != len(artifact_names):
raise RuntimeError(
f"Expected {len(artifact_names)} V8 checksums in {checksums_path}, "
f"found {len(lines)}."
f"Expected {len(artifact_names)} V8 checksums in {checksums_path}, found {len(lines)}."
)

for line in lines:
parts = line.split(maxsplit=1)
if len(parts) != 2:
raise RuntimeError(f"Invalid V8 checksum line in {checksums_path}: {line!r}")
raise RuntimeError(
f"Invalid V8 checksum line in {checksums_path}: {line!r}"
)

digest, artifact_name = parts[0], parts[1].strip()
if len(digest) != 64 or any(char not in "0123456789abcdef" for char in digest):
raise RuntimeError(f"Invalid V8 checksum digest in {checksums_path}: {digest}")
raise RuntimeError(
f"Invalid V8 checksum digest in {checksums_path}: {digest}"
)
if artifact_name not in artifact_names:
raise RuntimeError(
f"Unexpected V8 checksum artifact in {checksums_path}: {artifact_name}"
Expand All @@ -146,7 +151,9 @@ def ensure_valid_artifact(artifact: Path, checksum: str, url: str) -> None:
return

artifact.unlink(missing_ok=True)
raise RuntimeError(f"Codex-built V8 artifact {artifact} failed checksum validation.")
raise RuntimeError(
f"Codex-built V8 artifact {artifact} failed checksum validation."
)


def has_checksum(path: Path, expected: str) -> bool:
Expand Down
2 changes: 1 addition & 1 deletion scripts/just-shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
ARGS_TOKEN = "{args}"
STDERR_NULL_TOKEN = "{stderr-null}"
POWERSHELL_ARGS = "@($args | Select-Object -Skip 1)"
POWERSHELL_STDERR_NULL = '2>$null; exit $LASTEXITCODE'
POWERSHELL_STDERR_NULL = "2>$null; exit $LASTEXITCODE"
SH_ARGS = '"$@"'
SH_STDERR_NULL = "2>/dev/null"

Expand Down
21 changes: 17 additions & 4 deletions scripts/mock_responses_websocket_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,23 @@ def _event_response_done() -> dict[str, Any]:


def _event_response_completed(response_id: str) -> dict[str, Any]:
return {"type": "response.completed", "response": {"id": response_id, "usage": _default_usage()}}
return {
"type": "response.completed",
"response": {"id": response_id, "usage": _default_usage()},
}


def _event_function_call(call_id: str, name: str, arguments_json: str) -> dict[str, Any]:
def _event_function_call(
call_id: str, name: str, arguments_json: str
) -> dict[str, Any]:
return {
"type": "response.output_item.done",
"item": {"type": "function_call", "call_id": call_id, "name": name, "arguments": arguments_json},
"item": {
"type": "function_call",
"call_id": call_id,
"name": name,
"arguments": arguments_json,
},
}


Expand All @@ -75,6 +85,7 @@ def _print_request(prefix: str, payload: Any) -> None:
sys.stdout.write(f"{prefix} {_utc_iso()}\n{pretty}\n")
sys.stdout.flush()


async def _handle_connection(
websocket: Any,
*,
Expand All @@ -91,7 +102,9 @@ async def _handle_connection(

path_no_qs = path.split("?", 1)[0] if path != "(unknown)" else path
if path_no_qs != "(unknown)" and path_no_qs != expected_path:
sys.stdout.write(f"[conn] {_utc_iso()} rejecting unexpected path (expected {expected_path})\n")
sys.stdout.write(
f"[conn] {_utc_iso()} rejecting unexpected path (expected {expected_path})\n"
)
sys.stdout.flush()
await websocket.close(code=1008, reason="unexpected websocket path")
return
Expand Down
9 changes: 9 additions & 0 deletions scripts/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[project]
name = "codex-scripts"
version = "0.0.0"
requires-python = ">=3.10"
dependencies = ["ruff>=0.15.8"]

[tool.uv]
exclude-newer = "7 days"
index-strategy = "first-index"
4 changes: 2 additions & 2 deletions scripts/readme_toc.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def check_or_fix(readme_path: Path, fix: bool) -> int:
current_block = lines[begin_idx + 1 : end_idx]
current = [l for l in current_block if l.lstrip().startswith("- [")]
# generate expected ToC from content without current ToC
toc_content = lines[:begin_idx] + lines[end_idx+1:]
toc_content = lines[:begin_idx] + lines[end_idx + 1 :]
expected = generate_toc_lines("\n".join(toc_content))
if current == expected:
return 0
Expand All @@ -109,7 +109,7 @@ def check_or_fix(readme_path: Path, fix: bool) -> int:
return 1
# rebuild file with updated ToC
prefix = lines[: begin_idx + 1]
suffix = lines[end_idx+1:]
suffix = lines[end_idx + 1 :]
new_lines = prefix + [""] + expected + [""] + suffix
readme_path.write_text("\n".join(new_lines) + "\n", encoding="utf-8")
print(f"Updated ToC in {readme_path}.")
Expand Down
Loading
Loading