Skip to content
70 changes: 57 additions & 13 deletions justfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
set working-directory := "codex-rs"
set positional-arguments
export JUST_SHELL := justfile_directory() / "scripts/just-shell.py"
set shell := ["python3", "-c", 'import os, runpy; runpy.run_path(os.environ["JUST_SHELL"], run_name="__main__")']
set windows-shell := ["python", "-c", 'import os, runpy; runpy.run_path(os.environ["JUST_SHELL"], run_name="__main__")']

rust_min_stack := "8388608" # 8 MiB
python := if os_family() == "windows" { "python" } else { "python3" }

# Display help
help:
Expand All @@ -10,81 +14,115 @@ help:
# `codex`
alias c := codex
codex *args:
cargo run --bin codex -- "$@"
cargo run --bin codex -- {args}

# `codex exec`
exec *args:
cargo run --bin codex -- exec "$@"
cargo run --bin codex -- exec {args}

# Start `codex exec-server` and run codex-tui.
[unix]
[no-cd]
[positional-arguments]
tui-with-exec-server *args:
{{ justfile_directory() }}/scripts/run_tui_with_exec_server.sh "$@"

# Run the CLI version of the file-search crate.
file-search *args:
cargo run --bin codex-file-search -- "$@"
cargo run --bin codex-file-search -- {args}

# Build the CLI and run the app-server test client
app-server-test-client *args:
cargo build -p codex-cli
cargo run -p codex-app-server-test-client -- --codex-bin ./target/debug/codex "$@"
cargo run -p codex-app-server-test-client -- --codex-bin ./target/debug/codex {args}

# Format Rust and Python SDK code.
fmt:
cargo fmt -- --config imports_granularity=Item 2>/dev/null
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

fix *args:
cargo clippy --fix --tests --allow-dirty "$@"
cargo clippy --fix --tests --allow-dirty {args}

clippy *args:
cargo clippy --tests "$@"
cargo clippy --tests {args}

[unix]
install:
rustup show active-toolchain
cargo fetch

[windows]
install:
#!powershell.exe -File
$pwsh = Get-Command pwsh.exe -ErrorAction SilentlyContinue
if (-not $pwsh) {
winget install --exact --id Microsoft.PowerShell --source winget --accept-package-agreements --accept-source-agreements
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
}
rustup show active-toolchain
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
cargo fetch
exit $LASTEXITCODE

# Run nextest with --no-fail-fast so all tests are run.
#
# Run `cargo install --locked cargo-nextest` if you don't have it installed.
# Prefer this for routine local runs. Workspace crate features are banned, so
# there should be no need to add `--all-features`.
[unix]
test *args:
RUST_MIN_STACK={{ rust_min_stack }} cargo nextest run --no-fail-fast "$@"
just bench-smoke

[windows]
test *args:
$env:RUST_MIN_STACK = "{{ rust_min_stack }}"; cargo nextest run --no-fail-fast @($args | Select-Object -Skip 1)
just bench-smoke

# Run explicit workspace benchmark targets.
bench *args:
cargo bench --workspace --bench '*' "$@"
cargo bench --workspace --bench '*' {args}

# Run benchmark targets once to ensure they start successfully.
bench-smoke:
just bench -- --test

# Build and run Codex from source using Bazel.
# Note we have to use the combination of `[no-cd]` and `--run_under="cd $PWD &&"`
# to ensure that Bazel runs the command in the current working directory.
# On Unix, use `[no-cd]` and `--run_under="cd $PWD &&"` to ensure Bazel runs
# the command in the current working directory.
[unix]
[no-cd]
bazel-codex *args:
bazel run //codex-rs/cli:codex --run_under="cd $PWD &&" -- "$@"

[windows]
bazel-codex *args:
bazel run //codex-rs/cli:codex --run_under='cd /d "{{invocation_directory_native()}}" &&' -- @($args | Select-Object -Skip 1)

[no-cd]
bazel-lock-update:
bazel mod deps --lockfile_mode=update

[unix]
[no-cd]
bazel-lock-check:
{{ justfile_directory() }}/scripts/check-module-bazel-lock.sh
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be done in a follow-up, but maybe this should be a platform-independent Python script?


[windows]
bazel-lock-check:
bazel mod deps --lockfile_mode=error; if ($LASTEXITCODE -ne 0) { Write-Error "MODULE.bazel.lock is out of date. Run 'just bazel-lock-update' and commit the updated lockfile."; exit 1 }

bazel-test:
bazel test --test_tag_filters=-argument-comment-lint //... --keep_going

[unix]
[no-cd]
bazel-clippy:
bazel_targets="$({{ justfile_directory() }}/scripts/list-bazel-clippy-targets.sh)" && bazel build --config=clippy -- ${bazel_targets}

[unix]
[no-cd]
bazel-argument-comment-lint:
bazel build --config=argument-comment-lint -- $({{ justfile_directory() }}/tools/argument-comment-lint/list-bazel-targets.sh)
Expand All @@ -97,21 +135,22 @@ build-for-release:

# Run the MCP server
mcp-server-run *args:
cargo run -p codex-mcp-server -- "$@"
cargo run -p codex-mcp-server -- {args}

# Regenerate the json schema for config.toml from the current config types.
write-config-schema:
cargo run -p codex-core --bin codex-write-config-schema

# Regenerate vendored app-server protocol schema artifacts.
write-app-server-schema *args:
cargo run -p codex-app-server-protocol --bin write_schema_fixtures -- "$@"
cargo run -p codex-app-server-protocol --bin write_schema_fixtures -- {args}

[no-cd]
write-hooks-schema:
cargo run --manifest-path {{ justfile_directory() }}/codex-rs/Cargo.toml -p codex-hooks --bin write_hooks_schema_fixtures

# Run the argument-comment Dylint checks across codex-rs.
[unix]
[no-cd]
argument-comment-lint *args:
if [ "$#" -eq 0 ]; then \
Expand All @@ -122,8 +161,13 @@ argument-comment-lint *args:

[no-cd]
argument-comment-lint-from-source *args:
{{ justfile_directory() }}/tools/argument-comment-lint/run.py "$@"
{{ python }} {{ justfile_directory() }}/tools/argument-comment-lint/run.py {args}

# Tail logs from the state SQLite database
[unix]
log *args:
if [ "${1:-}" = "--" ]; then shift; fi; cargo run -p codex-state --bin logs_client -- "$@"

[windows]
log *args:
$forwarded_args = @($args | Select-Object -Skip 1); if ($forwarded_args.Count -gt 0 -and $forwarded_args[0] -eq "--") { $forwarded_args = @($forwarded_args | Select-Object -Skip 1) }; cargo run -p codex-state --bin logs_client -- @forwarded_args
75 changes: 75 additions & 0 deletions scripts/just-shell.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/usr/bin/env python3
"""Cross-platform shell launcher for `just` recipes.

This keeps recipe bodies as normal shell snippets while giving the justfile one
portable placeholder, `{args}`, for forwarding variadic recipe arguments.
"""

from __future__ import annotations

import os
import shutil
import subprocess
import sys


ARGS_TOKEN = "{args}"
STDERR_NULL_TOKEN = "{stderr-null}"
POWERSHELL_ARGS = "@($args | Select-Object -Skip 1)"
POWERSHELL_STDERR_NULL = '2>$null; exit $LASTEXITCODE'
SH_ARGS = '"$@"'
SH_STDERR_NULL = "2>/dev/null"


def main() -> int:
if len(sys.argv) < 2:
print("just shell adapter expected a recipe command.", file=sys.stderr)
return 1

command = sys.argv[1]
recipe_name = sys.argv[2] if len(sys.argv) > 2 else ""
recipe_args = sys.argv[3:]

if os.name == "nt":
return run_powershell(command, recipe_name, recipe_args)
else:
return run_sh(command, recipe_name, recipe_args)


def run_sh(command: str, recipe_name: str, recipe_args: list[str]) -> int:
command = command.replace(ARGS_TOKEN, SH_ARGS)
command = command.replace(STDERR_NULL_TOKEN, SH_STDERR_NULL)
return subprocess.run(
["sh", "-cu", command, recipe_name, *recipe_args],
check=False,
).returncode
Comment on lines +42 to +45
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use exec so it replaces the process?

Suggested change
return subprocess.run(
["sh", "-cu", command, recipe_name, *recipe_args],
check=False,
).returncode
os.execvp("sh", ["sh", "-cu", command, recipe_name, *recipe_args])



def run_powershell(command: str, recipe_name: str, recipe_args: list[str]) -> int:
pwsh = shutil.which("pwsh.exe") or shutil.which("pwsh")
if pwsh is None:
print(
"PowerShell ('pwsh') is required for Windows just recipes. "
"Run 'just install' to install it.",
file=sys.stderr,
)
return 1

command = command.replace(ARGS_TOKEN, POWERSHELL_ARGS)
command = command.replace(STDERR_NULL_TOKEN, POWERSHELL_STDERR_NULL)
return subprocess.run(
[
pwsh,
"-NoLogo",
"-NoProfile",
"-CommandWithArgs",
command,
recipe_name,
*recipe_args,
],
check=False,
).returncode


if __name__ == "__main__":
raise SystemExit(main())
8 changes: 5 additions & 3 deletions sdk/python/tests/test_artifact_workflow_and_binaries.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,16 @@ def test_root_fmt_recipe_formats_rust_and_python_sdk() -> None:
fmt_recipe = lines[fmt_index:next_recipe_index]
actual = {
"working_directory": lines[0],
"previous_attribute": lines[fmt_index - 1],
"previous_comment": next(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this file have to change because of changes to the justfile?

line for line in reversed(lines[:fmt_index]) if line.startswith("#")
),
"commands": [line.strip() for line in fmt_recipe[1:] if line.strip()],
}
expected = {
"working_directory": 'set working-directory := "codex-rs"',
"previous_attribute": "# Format Rust and Python SDK code.",
"previous_comment": "# Format Rust and Python SDK code.",
"commands": [
"cargo fmt -- --config imports_granularity=Item 2>/dev/null",
"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",
],
Expand Down
Loading