mirror of
https://codeberg.org/guix/guix.git
synced 2026-09-04 21:01:43 +00:00
Authors were parsed from git history with the following command:
git log --all --grep="gnu:.*oniguruma" \
--pretty=format:";;; Copyright © %as %aN <%aE>"
* gnu/packages/textutils.scm (oniguruma): Move from here ...
* gnu/packages/regex.scm: ... to here.
* gnu/packages/admin.scm: Replace (gnu packages textutils) with (gnu
packages regex).
* gnu/packages/codex.scm: Likewise.
* gnu/packages/php.scm: Likewise.
* gnu/packages/rust-apps.scm: Likewise.
* gnu/packages/rust-sources.scm: Likewise.
* gnu/packages/machine-learning.scm: Remove (gnu packages textutils) to
resolve import warning.
* gnu/packages/web.scm: Adding (gnu packages regex) introduces import
warning, selecting just utf8proc from (gnu packages textutils) breaks
build.
* gnu/packages/textutils.scm: Add (guix deprecation).
Merges: guix/guix!9845
1117 lines
70 KiB
Scheme
1117 lines
70 KiB
Scheme
;;; GNU Guix --- Functional package management for GNU
|
|
;;; Copyright © 2026 Danny Milosavljevic <dannym@friendly-machines.com>
|
|
;;;
|
|
;;; This file is part of GNU Guix.
|
|
;;;
|
|
;;; GNU Guix is free software; you can redistribute it and/or modify it
|
|
;;; under the terms of the GNU General Public License as published by
|
|
;;; the Free Software Foundation; either version 3 of the License, or (at
|
|
;;; your option) any later version.
|
|
;;;
|
|
;;; GNU Guix is distributed in the hope that it will be useful, but
|
|
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
;;; GNU General Public License for more details.
|
|
;;;
|
|
;;; You should have received a copy of the GNU General Public License
|
|
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
;;; This module is separate from (gnu packages rust-apps) to avoid a
|
|
;;; circular module dependency: (gnu packages rust-sources), which
|
|
;;; defines rust-codex packages used by codex-acp, transitively loads
|
|
;;; (gnu packages rust-apps) through its #:use-module chain. If the
|
|
;;; codex-acp package lived in rust-apps.scm, loading rust-sources would
|
|
;;; trigger loading rust-apps before those rust-codex packages are
|
|
;;; defined, causing an unbound variable error.
|
|
|
|
(define-module (gnu packages codex)
|
|
#:use-module ((guix licenses) #:prefix license:)
|
|
#:use-module (guix gexp)
|
|
#:use-module (guix packages)
|
|
#:use-module (guix download)
|
|
#:use-module (guix git-download)
|
|
#:use-module (guix search-paths)
|
|
#:use-module (guix utils)
|
|
#:use-module (guix build-system cargo)
|
|
#:use-module (srfi srfi-1)
|
|
#:use-module (gnu packages)
|
|
#:use-module (gnu packages bash)
|
|
#:use-module (gnu packages base)
|
|
#:use-module (gnu packages cmake)
|
|
#:use-module (gnu packages compression)
|
|
#:use-module (gnu packages curl)
|
|
#:use-module (gnu packages gdb)
|
|
#:use-module (gnu packages linux)
|
|
#:use-module (gnu packages libunwind)
|
|
#:use-module (gnu packages llvm)
|
|
#:use-module (gnu packages llvm-meta)
|
|
#:use-module (gnu packages lsof)
|
|
#:use-module (gnu packages perl)
|
|
#:use-module (gnu packages pkg-config)
|
|
#:use-module (gnu packages python)
|
|
#:use-module (gnu packages regex)
|
|
#:use-module (gnu packages rust)
|
|
#:use-module (gnu packages rust-sources)
|
|
#:use-module (gnu packages sqlite)
|
|
#:use-module (gnu packages nss)
|
|
#:use-module (gnu packages tls)
|
|
#:use-module (gnu packages version-control)
|
|
#:use-module (gnu packages virtualization))
|
|
|
|
(define-public codex
|
|
(package
|
|
(name "codex")
|
|
(version "0.144.1")
|
|
(source
|
|
(origin
|
|
(method git-fetch)
|
|
(uri (git-reference
|
|
(url "https://github.com/openai/codex")
|
|
(commit "44918ea10c0f99151c6710411b4322c2f5c96bea")))
|
|
(file-name (git-file-name name version))
|
|
(sha256
|
|
(base32 "0icwnvss0zswsp0vz2cffzr4xq46zfv634cl9phn40kjhsl2ny18"))
|
|
(modules '((guix build utils)))
|
|
(snippet '(begin
|
|
;; Bundled bubblewrap source tree; includes a
|
|
;; compiled BPF blob (demos/flatpak.bpf).
|
|
(delete-file-recursively "codex-rs/vendor/bubblewrap")))
|
|
(patches (search-patches
|
|
"codex-0.144.1-disable-code-mode.patch"
|
|
"codex-0.144.1-remove-patch-sections.patch"
|
|
"codex-0.144.1-remove-libwebrtc.patch"
|
|
"codex-0.144.1-test-disable-network-model-refresh.patch"
|
|
"codex-0.144.1-test-selected-capability-readiness.patch"
|
|
"codex-0.144.1-test-shebangs.patch"
|
|
"codex-0.144.1-test-timeout.patch"))))
|
|
(build-system cargo-build-system)
|
|
(arguments
|
|
(list
|
|
#:rust rust-1.94
|
|
#:install-source? #f
|
|
;; exec-server is library-only in this release (no [[bin]] in
|
|
;; exec-server/Cargo.toml); cargo install --path exec-server fails
|
|
;; with "no packages found with binaries or examples". Drop it
|
|
;; from the install paths -- the library is consumed via the
|
|
;; workspace by other binaries here, not installed standalone.
|
|
#:cargo-install-paths '(list "cli" "exec"
|
|
"linux-sandbox" "mcp-server"
|
|
"app-server" "tui")
|
|
;; schema_fixtures_match_generated (upstream fixture is stale:
|
|
;; FileChange::Update in codex-protocol gained old_content,
|
|
;; new_content, move_path fields but the committed JSON schema
|
|
;; fixture was not regenerated).
|
|
#:cargo-test-flags '(list "--workspace"
|
|
"--exclude" "codex-app-server-protocol"
|
|
"--exclude" "codex-code-mode"
|
|
"--exclude" "codex-code-mode-host"
|
|
;;; BEGIN rustdoc absent from rust-1.94
|
|
;; rust-1.94 installs rustc and cargo but
|
|
;; not rustdoc. Select the ordinary test
|
|
;; targets explicitly, excluding doctests.
|
|
"--lib" "--bins" "--tests"
|
|
;;; END rustdoc absent from rust-1.94
|
|
"--"
|
|
;;; BEGIN Landlock returns NotEnforced
|
|
;;; in the build container; the sandbox
|
|
;;; binary panics instead of cleanly
|
|
;;; denying filesystem access.
|
|
;; Sandbox denial and escalation tests.
|
|
"--skip" "sandbox_denied_shell_returns_original_output"
|
|
;; Sandbox denial and escalation tests.
|
|
"--skip" "shell_escalated_permissions_rejected_then_ok"
|
|
;; Sandbox denial and escalation tests.
|
|
"--skip" "unified_exec_runs_under_sandbox"
|
|
;; codex-exec: spawn_command_under_linux_sandbox
|
|
;; panics with LandlockRestrict (exit 101).
|
|
"--skip" "python_getpwuid_works_under_sandbox"
|
|
;; Same as above.
|
|
"--skip" "python_multiprocessing_lock_works_under_sandbox"
|
|
;; Same as above.
|
|
"--skip" "sandbox_distinguishes_command_and_policy_cwds"
|
|
;; linux-sandbox: process_exec_tool_call
|
|
;; invokes Landlock; same panic.
|
|
"--skip" "test_writable_root"
|
|
;; Same as above.
|
|
"--skip" "test_timeout"
|
|
;; Same as above.
|
|
"--skip" "test_root_read"
|
|
;; Same as above.
|
|
"--skip" "test_dev_null_write"
|
|
;; Same as above.
|
|
"--skip" "test_no_new_privs_is_enabled"
|
|
;; One scenario runs through Landlock
|
|
;; which panics; cargo --skip cannot
|
|
;; target individual scenarios.
|
|
"--skip" "approval_matrix_covers_all_modes"
|
|
;; Landlock panic triggers escalation-retry
|
|
;; which interferes with the approval cache.
|
|
"--skip" "approving_apply_patch_for_session_skips_future_prompts_for_same_file"
|
|
;; These tests expect to interrupt a
|
|
;; long-running 'sleep 60' and receive
|
|
;; TurnAborted. What happens:
|
|
;;
|
|
;; 1. ReadOnly wraps the command with
|
|
;; codex-linux-sandbox (Landlock-based).
|
|
;; 2. Landlock is unavailable, so the
|
|
;; sandbox binary exits instantly.
|
|
;; 3. Orchestrator gets SandboxErr::Denied.
|
|
;; wants_no_sandbox_approval(OnRequest)
|
|
;; returns false, so no escalation --
|
|
;; denial returned directly.
|
|
;; 4. ToolEmitter::finish sends the error
|
|
;; to the mock model.
|
|
;; 5. Second mock SSE response fires,
|
|
;; turn finishes with TurnComplete.
|
|
;; 6. Op::Interrupt arrives 100 ms later,
|
|
;; but the turn is already done --
|
|
;; TurnAborted is never emitted,
|
|
;; test times out.
|
|
;;
|
|
;; The similar interrupt_long_running_tool_
|
|
;; emits_turn_aborted passes because it
|
|
;; sends the interrupt with no delay and
|
|
;; has only one mock response (so the turn
|
|
;; cannot complete first).
|
|
"--skip" "interrupt_persists_turn_aborted_marker_in_next_request"
|
|
;; Same causal chain as above.
|
|
"--skip" "interrupt_tool_records_history_entries"
|
|
;; WorkspaceWrite sandbox policy wraps
|
|
;; commands with codex-linux-sandbox;
|
|
;; Landlock panics, command never runs,
|
|
;; exit_code is None instead of Some(0).
|
|
"--skip" "request_permissions_grants_apply_to_later_exec_command_calls"
|
|
;; Same root cause.
|
|
"--skip" "request_permissions_grants_apply_to_later_shell_command_calls"
|
|
;; Same root cause.
|
|
"--skip" "partial_request_permissions_grants_do_not_preapprove_new_permissions"
|
|
;;; END Landlock
|
|
;;; BEGIN mcp_process.rs initialize
|
|
;;; assertion expects "version":"0.0.0"
|
|
;;; but the server returns "0.120.0".
|
|
"--skip" "test_codex_tool_passes_base_instructions"
|
|
"--skip" "test_shell_command_approval_triggers_elicitation"
|
|
"--skip" "test_patch_approval_triggers_elicitation"
|
|
;;; END version mismatch
|
|
;;; BEGIN Test isolation bug:
|
|
;;; unique_temp_dir() in
|
|
;;; state/src/runtime.rs names dirs
|
|
;;; using nanosecond timestamps, so
|
|
;;; parallel tests can collide. The
|
|
;;; SQLite migration runs CREATE TABLE
|
|
;;; threads (without IF NOT EXISTS),
|
|
;;; so the second init panics with
|
|
;;; "table threads already exists".
|
|
"--skip" "init_removes_legacy_state_db_files"
|
|
"--skip" "upsert_and_get_thread_memory"
|
|
"--skip" "get_last_n_thread_memories_for_cwd_matches_exactly"
|
|
"--skip" "upsert_thread_memory_errors_for_unknown_thread"
|
|
"--skip" "get_last_n_thread_memories_for_cwd_zero_returns_empty"
|
|
"--skip" "get_last_n_thread_memories_for_cwd_does_not_prefix_match"
|
|
"--skip" "deleting_thread_cascades_thread_memory"
|
|
;;; END SQLite test isolation
|
|
;;; BEGIN unchanged filesystem mtime
|
|
;; This test overwrites an imported
|
|
;; session immediately and expects
|
|
;; detection to hash it again. When
|
|
;; both writes receive the same mtime,
|
|
;; the mtime fast path treats the source
|
|
;; as unchanged and returns no session.
|
|
"--skip" "redetects_sessions_when_source_contents_change_after_import"
|
|
;;; END unchanged filesystem mtime
|
|
;;; BEGIN bare 'sh' after env_clear().
|
|
;; These tests spawn 'sh' after
|
|
;; env_clear() with an empty env map;
|
|
;; glibc execvp uses confstr(_CS_PATH)
|
|
;; ("/bin:/usr/bin") when PATH is
|
|
;; unset, but /bin/sh does not exist
|
|
;; in the build container.
|
|
"--skip" "cancellation_expiration_keeps_process_alive_until_terminated"
|
|
"--skip" "timeout_or_cancellation_reports_cancellation_without_timeout_exit_code"
|
|
;;; END bare 'sh' after env_clear()
|
|
;;; BEGIN Guile 3.0.9 system* inherits ignored SIGINT
|
|
;; Guile 3.0.9's system* temporarily sets
|
|
;; SIGINT to SIG_IGN while it waits for the
|
|
;; command ("Make sure the child can't kill
|
|
;; us"). The test command inherits that
|
|
;; disposition through cargo, and Codex's
|
|
;; pipe backend does not reset it before
|
|
;; exec. Its shell SIGINT trap cannot run.
|
|
"--skip" "exec_process_signal_interrupts_process"
|
|
;;; END Guile 3.0.9 system* inherits ignored SIGINT
|
|
;; Proxy baseline_policy returns 403
|
|
;; "not_allowed_local" for example.com;
|
|
;; test expects 200.
|
|
"--skip" "managed_network_proxy_decider_survives_full_access_start"
|
|
;;; BEGIN bash_snapshot_script() uses
|
|
;;; "compgen -e" to list exports but
|
|
;;; bash-minimal lacks programmable
|
|
;;; completion; export section is empty.
|
|
;; stdout.contains("VALID_NAME") fails.
|
|
"--skip" "bash_snapshot_filters_invalid_exports"
|
|
;; stdout.contains("MULTILINE_CERT") fails.
|
|
"--skip" "bash_snapshot_preserves_multiline_exports"
|
|
;; snapshot.contains("PATH") fails.
|
|
"--skip" "linux_bash_snapshot_includes_sections"
|
|
;;; END compgen
|
|
;; substitute* patches PAGER to store
|
|
;; path of cat; test expects bare "cat".
|
|
"--skip" "unified_exec_env_injects_defaults"
|
|
;;; BEGIN V8 disabled (codex-code-mode
|
|
;;; default-features = false).
|
|
;; codex-core: missing process-host fallback
|
|
;; executes JavaScript in the V8 session.
|
|
"--skip" "missing_process_host_falls_back_to_in_process_session"
|
|
;; codex-core: wait tracing terminates a cell
|
|
;; through the code-mode runtime.
|
|
"--skip" "missing_code_mode_wait_traces_only_the_wait_tool_call"
|
|
"--skip" "suite::code_mode::"
|
|
"--skip" "suite::code_mode_elicitation::"
|
|
"--skip" "suite::v2::imagegen_extension::standalone_image_generation_is_callable_from_code_mode_only"
|
|
;;; END V8 disabled
|
|
;;; BEGIN same compgen root cause:
|
|
;;; assert_posix_snapshot_sections
|
|
;;; asserts snapshot.contains("PATH").
|
|
;;; END compgen / shell snapshot
|
|
;;; BEGIN inaccessible snapshot under sh
|
|
;;; bash-minimal lacks "compgen -e", so the
|
|
;;; generated snapshot has no PATH section
|
|
;;; and these tests fail their snapshot
|
|
;;; assertions. They also expose a separate
|
|
;;; restricted-bwrap failure.
|
|
"--skip" "linux_shell_command_uses_shell_snapshot"
|
|
"--skip" "linux_unified_exec_uses_shell_snapshot"
|
|
"--skip" "shell_command_snapshot_still_intercepts_apply_patch"
|
|
;;; END inaccessible snapshot under sh
|
|
;;; BEGIN These sandbox tests set
|
|
;;; exclude_slash_tmp: true which makes
|
|
;;; bwrap exclude /tmp from its mount
|
|
;;; namespace. Since Guix builds under
|
|
;;; /tmp, the codex-linux-sandbox binary
|
|
;;; becomes invisible to bwrap even though
|
|
;;; the test adds the helper dir as a
|
|
;;; filesystem carve-out -- the parent /tmp
|
|
;;; bind-mount is absent so the carve-out
|
|
;;; has nothing to carve out of.
|
|
"--skip" "sandbox_reenables_writable_subpaths_under_unreadable_parents"
|
|
"--skip" "managed_proxy_mode_routes_through_bridge_and_blocks_direct_egress"
|
|
;;; END exclude_slash_tmp
|
|
;; Expects 3 TerminalInteraction events
|
|
;; across timed poll windows; under CPU
|
|
;; contention events merge into 2.
|
|
"--skip" "unified_exec_terminal_interaction_captures_delayed_output"
|
|
;;; BEGIN assert_parallel_duration
|
|
;;; requires < 1600 ms for parallel ops.
|
|
;; Two 300 ms test_sync_tool calls.
|
|
"--skip" "read_file_tools_run_in_parallel"
|
|
;; Two "sleep 0.25" shell commands.
|
|
"--skip" "shell_tools_run_in_parallel"
|
|
;; One test_sync_tool + one shell sleep.
|
|
"--skip" "mixed_parallel_tools_run_in_parallel"
|
|
;;; END assert_parallel_duration
|
|
;; Asserts elapsed >= 4500 ms and
|
|
;; < 5800 ms for a 5 s timeout; under
|
|
;; contention elapsed drifts past 5800.
|
|
"--skip" "remote_models_request_times_out_after_5s"
|
|
;;; BEGIN delayed auto-environment filesystem probes
|
|
;; This test adds a one-second delay in each
|
|
;; direction to every exec-server WebSocket
|
|
;; RPC, but allows only 60 seconds for
|
|
;; thread/start. Auto-environment startup
|
|
;; serially probes .git, AGENTS files, and
|
|
;; plugin metadata through many ancestors;
|
|
;; each missing-path request/response costs
|
|
;; about two seconds, so it cannot finish.
|
|
"--skip" "builder_interposes_fixed_delay_for_auto_env"
|
|
;;; END delayed auto-environment filesystem probes
|
|
;; Queued inter-agent mail must be
|
|
;; injected between the reasoning/
|
|
;; commentary item and the gate release;
|
|
;; under contention the gate fires before
|
|
;; the mail is processed, so the snapshot
|
|
;; shows the stale function_call instead
|
|
;; of the expected mail message.
|
|
"--skip" "queued_inter_agent_mail_triggers_follow_up_after_commentary_message_item"
|
|
;; Same root cause: snapshot mismatch at
|
|
;; pending_input.rs assert_two_responses_
|
|
;; input_snapshot; expects mail at position
|
|
;; 04 but gets function_call/shell instead.
|
|
"--skip" "queued_inter_agent_mail_triggers_follow_up_after_reasoning_item"
|
|
;;; BEGIN tracing callsite cache race
|
|
;; These tests install a thread-local tracing
|
|
;; subscriber, then rebuild tracing's
|
|
;; process-global callsite-interest cache.
|
|
;; Parallel tests can disable the request span,
|
|
;; causing the parent trace context to be sent.
|
|
"--skip" "process_start_propagates_caller_trace_context_across_background_task"
|
|
"--skip" "rpc_client_propagates_current_trace_context"
|
|
;;; END tracing callsite cache race
|
|
;;; BEGIN DNS for hostnames like
|
|
;;; example.com fails in the build
|
|
;;; sandbox. host_resolves_to_non_
|
|
;;; public_ip() in runtime.rs treats
|
|
;;; failed DNS as non-public and
|
|
;;; returns NotAllowedLocal, so tests
|
|
;;; expecting Allowed or Decider get
|
|
;;; Deny/BaselinePolicy instead.
|
|
"--skip" "host_blocked_requires_allowlist_match"
|
|
"--skip" "add_allowed_domain_removes_matching_deny_entry"
|
|
"--skip" "host_blocked_subdomain_wildcards_exclude_apex"
|
|
"--skip" "host_blocked_global_wildcard_allowlist_allows_public_hosts_except_denylist"
|
|
"--skip" "evaluate_host_policy_emits_domain_event_for_decider_ask"
|
|
"--skip" "evaluate_host_policy_emits_domain_event_for_decider_allow_override"
|
|
"--skip" "http_connect_accept_blocks_in_limited_mode"
|
|
"--skip" "http_connect_accept_allows_allowlisted_host_in_full_mode"
|
|
"--skip" "mitm_policy_blocks_disallowed_method_and_records_telemetry"
|
|
"--skip" "http_connect_accept_blocks_hooked_host_in_full_mode_without_mitm_state"
|
|
"--skip" "http_connect_accept_defers_brokered_host_mitm_until_protocol_detection"
|
|
"--skip" "http_connect_accept_passes_environment_id_to_decider"
|
|
"--skip" "mitm_policy_allows_matching_hooked_write_in_full_mode"
|
|
"--skip" "mitm_policy_blocks_hook_miss_for_hooked_host_and_records_telemetry_in_full_mode"
|
|
"--skip" "mitm_policy_blocks_matching_hooked_write_in_limited_mode"
|
|
"--skip" "evaluate_host_policy_emits_execution_id_for_baseline_allow"
|
|
"--skip" "handle_socks5_tcp_blocks_hooked_non_https_host_in_full_mode"
|
|
"--skip" "handle_socks5_tcp_blocks_limited_mode_without_mitm_state"
|
|
"--skip" "handle_socks5_tcp_detects_tls_for_brokered_nonstandard_port_in_full_mode"
|
|
"--skip" "handle_socks5_tcp_uses_mitm_for_hooked_host_in_full_mode"
|
|
"--skip" "handle_socks5_tcp_uses_mitm_in_limited_mode"
|
|
;; These tests bind their servers to
|
|
;; 127.0.0.1 but construct WebSocket URLs
|
|
;; using the hostname "localhost".
|
|
;; connect_tcp() passes that hostname to
|
|
;; tokio::net::lookup_host, which requires
|
|
;; a name resolver unavailable in the
|
|
;; build container.
|
|
"--skip" "public_connector_uses_factory_and_exposes_stream_and_sink"
|
|
"--skip" "direct_route_connects_secure_websocket"
|
|
"--skip" "http_proxy_tunnels_secure_websocket_before_handshake"
|
|
"--skip" "https_proxy_tunnels_secure_websocket_before_handshake"
|
|
;;; END DNS failure / NotAllowedLocal
|
|
;;; BEGIN Flaky: wiremock mock expects
|
|
;;; 1 POST to /codex/safety/arc but
|
|
;;; receives 0. Passes in some builds,
|
|
;;; fails in others with identical code
|
|
;;; and env. The identical custom_
|
|
;;; variant always passes; only the
|
|
;;; CODEX_APPS_MCP_SERVER_NAME variant
|
|
;;; flakes, suggesting a timing race
|
|
;;; in the async POST under contention.
|
|
"--skip" "approve_mode_blocks_when_arc_returns_interrupt_for_model"
|
|
"--skip" "approve_mode_blocks_when_arc_returns_interrupt_without_annotations"
|
|
;;; END ARC monitor flake
|
|
;;; BEGIN nss-certs sets SSL_CERT_FILE
|
|
;;; which makes build_reqwest_client load
|
|
;;; 171 CA certs via add_root_certificate.
|
|
;;; This changes the reqwest TLS backend
|
|
;;; configuration; the ARC safety POST to
|
|
;;; the localhost mock server never arrives
|
|
;;; (wiremock expects 1 request, gets 0).
|
|
"--skip" "approve_mode_blocks_when_arc_returns_interrupt_for_model"
|
|
"--skip" "approve_mode_blocks_when_arc_returns_interrupt_without_annotations"
|
|
;;; END nss-certs / ARC monitor
|
|
;; Upstream bug: JsonRpcConnection has
|
|
;; no websocket keepalive or read timeout.
|
|
;; When the exec-server is killed, the
|
|
;; client's reader task blocks on
|
|
;; websocket_reader.next() forever because
|
|
;; TCP doesn't detect the dead peer without
|
|
;; a write or keepalive probe. Previously
|
|
;; masked because empty env made "sleep 10"
|
|
;; fail instantly; with PATH the process
|
|
;; lives long enough to hit the deadlock.
|
|
"--skip" "remote_exec_process_reports_transport_disconnect"
|
|
;; 500 ms timeout on MCP list response
|
|
;; (mcp_server_status.rs timeout at
|
|
;; Duration::from_millis(500)). With
|
|
;; nss-certs TLS now works, so the
|
|
;; startup plugin sync HTTP requests
|
|
;; (chatgpt.com, api.github.com) wait
|
|
;; for TCP timeout instead of failing
|
|
;; instantly at TLS, eating the budget.
|
|
"--skip" "mcp_server_status_list_tools_and_auth_only_skips_slow_inventory_calls"
|
|
;;; BEGIN Stack overflow in 'current_thread'
|
|
;;; tokio runtime. The sibling tests in
|
|
;;; tracing_tests.rs use the local helper
|
|
;;; run_current_thread_test_with_stack
|
|
;;; (4 MiB) but this one uses bare
|
|
;;; #[tokio::test] on the 2 MiB default
|
|
;;; test-thread stack, and TurnStart's
|
|
;;; future graph exceeds it. With
|
|
;;; RUST_MIN_STACK raised the overflow is
|
|
;;; gone but the test then deterministically
|
|
;;; times out waiting for a span tagged
|
|
;;; codex.op = "user_input" on the remote
|
|
;;; trace that upstream never emits on that
|
|
;;; trace in our environment.
|
|
"--skip" "turn_start_jsonrpc_span_parents_core_turn_spans"
|
|
;;; END tracing test stack/timeout
|
|
;;; BEGIN The upstream loader resolves the
|
|
;;; MCP OAuth credentials store mode by
|
|
;;; calling
|
|
;;; resolve_mcp_oauth_credentials_store_mode
|
|
;;; with env!("CARGO_PKG_VERSION") -- which
|
|
;;; in the released 0.142.0 tarball is
|
|
;;; "0.142.0". These fixture tests
|
|
;;; construct the expected Config by
|
|
;;; passing the LOCAL_DEV_BUILD_VERSION
|
|
;;; constant ("0.0.0") to the same
|
|
;;; resolver, and the resolver only returns
|
|
;;; File for "0.0.0" -- Auto otherwise.
|
|
;;; Upstream CI builds from a checkout
|
|
;;; still carrying version = "0.0.0" so the
|
|
;;; tests pass there; they cannot pass
|
|
;;; against a released tag.
|
|
"--skip" "test_precedence_fixture_with_gpt3_profile"
|
|
"--skip" "test_precedence_fixture_with_gpt5_profile"
|
|
"--skip" "test_precedence_fixture_with_o3_profile"
|
|
"--skip" "test_precedence_fixture_with_zdr_profile"
|
|
;;; END LOCAL_DEV_BUILD_VERSION fixture mismatch
|
|
;;; BEGIN Verifies that Codex's user-shell
|
|
;;; spawn produces a child where
|
|
;;; CODEX_SANDBOX_NETWORK_DISABLED is unset
|
|
;;; (asserts stdout == "not-set"). We
|
|
;;; deliberately set that env var at the
|
|
;;; check phase to trigger upstream's
|
|
;;; skip_if_no_network! guards; the var then
|
|
;;; leaks into the spawned shell and the
|
|
;;; assertion fails. The guix skip is a
|
|
;;; direct cost of the network-gate fix --
|
|
;;; trading one broken test for two that
|
|
;;; now skip cleanly.
|
|
"--skip" "user_shell_command_does_not_set_network_sandbox_env_var"
|
|
;;; END CODEX_SANDBOX_NETWORK_DISABLED leak
|
|
;;; BEGIN The exec-server/tests/file_system
|
|
;;; integration tests invoke the real
|
|
;;; system bwrap, which tries to execvp the
|
|
;;; test binary at the cargo target path
|
|
;;; under the build dir. Same root cause
|
|
;;; as the existing
|
|
;;; sandbox_reenables_writable_subpaths_under_unreadable_parents
|
|
;;; skip: guix builds live under /tmp but
|
|
;;; bwrap's mount namespace excludes /tmp,
|
|
;;; making the binary invisible inside the
|
|
;;; namespace.
|
|
"--skip" "file_system_copy_preserves_symlink_source"
|
|
"--skip" "file_system_copy_rejects_symlink_escape_destination"
|
|
"--skip" "file_system_copy_rejects_symlink_escape_source"
|
|
"--skip" "file_system_create_directory_rejects_symlink_escape"
|
|
"--skip" "file_system_read_directory_rejects_symlink_escape"
|
|
"--skip" "file_system_remove_rejects_symlink_escape"
|
|
"--skip" "file_system_remove_removes_symlink_not_target"
|
|
"--skip" "file_system_sandboxed_read_allows_readable_root"
|
|
"--skip" "file_system_sandboxed_read_rejects_symlink_escape"
|
|
"--skip" "file_system_sandboxed_read_rejects_symlink_parent_dotdot_escape"
|
|
"--skip" "file_system_sandboxed_write_allows_additional_write_root"
|
|
"--skip" "file_system_sandboxed_write_rejects_symlink_escape"
|
|
"--skip" "file_system_sandboxed_write_rejects_unwritable_path"
|
|
;;; END bwrap-cant-see-/tmp file_system tests
|
|
;;; BEGIN Landlock is unavailable on Guix,
|
|
;;; so the sandbox cannot deny network
|
|
;;; syscalls. These tests expect
|
|
;;; SandboxErr::Denied for the nc, ping,
|
|
;;; ssh, getent, and dev_tcp_redirection
|
|
;;; commands but those commands instead
|
|
;;; block on connect() until the test's 2s
|
|
;;; timeout fires (Timeout, exit 124).
|
|
;;; Same root cause as the Landlock skips
|
|
;;; at the top of this list; newer test
|
|
;;; names.
|
|
"--skip" "sandbox_blocks_nc"
|
|
"--skip" "sandbox_blocks_ping"
|
|
"--skip" "sandbox_blocks_dev_tcp_redirection"
|
|
"--skip" "sandbox_blocks_getent"
|
|
"--skip" "sandbox_blocks_ssh"
|
|
;;; END Landlock network block
|
|
;;; BEGIN Requires github.com network access.
|
|
;;; With no "extraKnownMarketplaces" entry
|
|
;;; in settings.json,
|
|
;;; collect_marketplace_import_sources
|
|
;;; inserts a fallback entry for the
|
|
;;; "claude-plugins-official" marketplace
|
|
;;; pointing at the github shorthand
|
|
;;; "anthropics/claude-plugins-official".
|
|
;;; add_marketplace -> parse_marketplace_source
|
|
;;; expands that to a github.com/.git URL
|
|
;;; and clone_git_source runs 'git clone'.
|
|
;;; The Guix build sandbox has no network,
|
|
;;; so the clone fails and the marketplace
|
|
;;; lands in failed_marketplaces. No mock
|
|
;;; cloner is injected via
|
|
;;; ExternalAgentConfigService::new_for_test,
|
|
;;; so there is no in-tree way to satisfy
|
|
;;; the test offline.
|
|
"--skip" "import_plugins_infers_external_official_marketplace_when_missing_from_settings"
|
|
;;; END github.com network access
|
|
;;; BEGIN Async race between the rollout
|
|
;;; writer task and a direct SQLite read.
|
|
;;; The test calls
|
|
;;; update_memory_settings_with_app_server,
|
|
;;; which routes through the in-memory
|
|
;;; thread path on the app-server's
|
|
;;; ThreadMemoryModeSet handler ->
|
|
;;; Session::set_thread_memory_mode ->
|
|
;;; persist_thread_memory_mode_update,
|
|
;;; which writes a RolloutItem::SessionMeta
|
|
;;; with the new mode to the rollout file
|
|
;;; via recorder.record_items + flush. The
|
|
;;; rollout writer task then asynchronously
|
|
;;; calls sync_thread_state_after_write,
|
|
;;; which calls state_db::apply_rollout_items
|
|
;;; to mirror the SessionMeta into SQLite.
|
|
;;; flush() only awaits the rollout-file
|
|
;;; write, not the subsequent SQLite sync.
|
|
;;; The test opens a fresh StateRuntime and
|
|
;;; reads get_thread_memory_mode
|
|
;;; immediately, so it observes the creation
|
|
;;; default of "enabled" written by
|
|
;;; upsert_thread_with_creation_memory_mode
|
|
;;; instead of the new "disabled". Upstream
|
|
;;; CI wins this race; we lose.
|
|
"--skip" "update_memory_settings_updates_current_thread_memory_mode"
|
|
;;; END memory-mode rollout/sqlite race
|
|
;;; BEGIN The Stopwatch::new constructor in
|
|
;;; codex_shell_escalation anchors
|
|
;;; T0 = Instant::now() at construction;
|
|
;;; the spawned cancellation task fires at
|
|
;;; T0 + limit. The test captures
|
|
;;; start = Instant::now() at T1 > T0 (after
|
|
;;; cancellation_token() returns) and
|
|
;;; asserts start.elapsed() >= limit,
|
|
;;; i.e. (T0 + limit) - T1 >= limit, which
|
|
;;; is always false by the offset T1 - T0.
|
|
;;; The test only passes when scheduler
|
|
;;; jitter on the sleep wakeup happens to
|
|
;;; exceed that offset. Upstream wins the
|
|
;;; race; we don't always.
|
|
"--skip" "cancellation_receiver_fires_after_limit"
|
|
;;; END Stopwatch construction/start offset race
|
|
;;; BEGIN invalid remote-control auth-wakeup test
|
|
;;; remote_control_waits_for_account_id_before_enrolling
|
|
;;; tries to prove that auth_manager.reload()
|
|
;;; wakes the remote-control worker before
|
|
;;; REMOTE_CONTROL_ACCOUNT_ID_RETRY_INTERVAL.
|
|
;;; It does this by first waiting 100 ms for
|
|
;;; "no request", then rewriting auth.json,
|
|
;;; calling reload(), and waiting another
|
|
;;; 100 ms for the enroll request.
|
|
;;;
|
|
;;; That is not a valid assertion of the
|
|
;;; intended behavior. The retry sleep starts
|
|
;;; when the worker first observes the missing
|
|
;;; account id, before the test's first
|
|
;;; 100 ms wait completes. A larger second
|
|
;;; timeout can therefore overlap the existing
|
|
;;; one-second fallback retry and pass even if
|
|
;;; the auth-change watch never woke the task.
|
|
;;; The test also has no synchronization point
|
|
;;; proving the worker is actually parked on
|
|
;;; auth_change_rx.changed() before reload().
|
|
"--skip" "remote_control_waits_for_account_id_before_enrolling"
|
|
;;; END invalid remote-control auth-wakeup test
|
|
;;; BEGIN invalid remote-control timeout tests
|
|
;;; These are tests, not production behavior.
|
|
;;; request_timeout_before_response_headers_is_transient
|
|
;;; tries to prove that a reqwest timeout before
|
|
;;; response headers is classified as a
|
|
;;; transient remote-control server error.
|
|
;;; response_body_timeout_is_transient tries to
|
|
;;; prove the same classification for a timeout
|
|
;;; while reading the response body after a
|
|
;;; partial HTTP 200 response.
|
|
;;;
|
|
;;; Both tests use timed_out_request. That
|
|
;;; helper starts a fake TCP server and races
|
|
;;; the intended server setup against a 100 ms
|
|
;;; client timeout. The client is supposed to
|
|
;;; time out; the invalid test assumption is
|
|
;;; that the spawned fake server task has always
|
|
;;; reached the later wait point before that
|
|
;;; timeout fires. For the header-timeout test
|
|
;;; that means after listener.accept(); for the
|
|
;;; body-timeout test that means after
|
|
;;; listener.accept() and
|
|
;;; stream.write_all(partial_response).
|
|
;;;
|
|
;;; If the client times out before the fake
|
|
;;; server reaches that point, timed_out_request
|
|
;;; has already received the timeout error the
|
|
;;; test wants to assert. It then tries to
|
|
;;; clean up by sending request_done_tx and
|
|
;;; awaiting server_task. But request_done_tx
|
|
;;; is only observed after the fake server has
|
|
;;; completed the accept/partial-response setup;
|
|
;;; it cannot wake listener.accept() or an
|
|
;;; earlier write. server_task.await can
|
|
;;; therefore hang before either test reaches
|
|
;;; assert_transient_timeout.
|
|
"--skip" "request_timeout_before_response_headers_is_transient"
|
|
"--skip" "response_body_timeout_is_transient"
|
|
;;; END invalid remote-control timeout tests
|
|
;;; BEGIN Order-brittle thread-name websocket
|
|
;;; notification test.
|
|
;;; thread_name_updated_broadcasts_for_loaded_threads
|
|
;;; resumes a stored thread, reads only the
|
|
;;; thread/resume response, renames the thread,
|
|
;;; reads the thread/name/updated notifications,
|
|
;;; and then asserts both websocket clients are
|
|
;;; silent.
|
|
;;;
|
|
;;; The resume path can emit an additional
|
|
;;; thread/goal/cleared snapshot notification
|
|
;;; after the resume response when the resumed
|
|
;;; thread has no stored goal.
|
|
;;; The test does not drain or expect that
|
|
;;; valid post-resume notification, so the
|
|
;;; final silence assertion can fail on
|
|
;;; thread/goal/cleared even though the thread
|
|
;;; rename broadcast itself succeeded.
|
|
"--skip" "thread_name_updated_broadcasts_for_loaded_threads"
|
|
;;; END Order-brittle thread-name websocket notification test
|
|
;;; BEGIN Order-brittle app-list notification
|
|
;;; tests. Codex fetches its apps list from
|
|
;;; two sources in parallel (installed apps
|
|
;;; via MCP, available apps via HTTP)
|
|
;;; and emits an "app/list/updated"
|
|
;;; notification as each source finishes.
|
|
;;; The tests use server-side delays
|
|
;;; (0 ms for MCP, 300 ms for HTTP) to force
|
|
;;; the MCP source to finish first, then
|
|
;;; assert a strict two-notification
|
|
;;; sequence.
|
|
;;; The implementation is order-agnostic
|
|
;;; (apps_processor.rs suppresses the
|
|
;;; HTTP-only interim; final merged
|
|
;;; state is identical either way).
|
|
;;; When the HTTP source wins the race, only
|
|
;;; the final merged notification fires, so
|
|
;;; the test sees one notification instead of
|
|
;;; two and fails the assertion against the
|
|
;;; expected first (accessible-only)
|
|
;;; notification.
|
|
"--skip" "list_apps_emits_updates_and_returns_after_both_lists_load"
|
|
"--skip" "list_apps_force_refetch_patches_updates_from_cached_snapshots"
|
|
;;; END Order-brittle app-list notification tests
|
|
;;; BEGIN Skip .git creators. Otherwise other
|
|
;;; 8 tests would have their context poisoned.
|
|
;;; The other tests care about .git not being
|
|
;;; there.
|
|
"--skip" "turn_interrupt_aborts_running_turn"
|
|
"--skip" "sandbox_with_network_proxy_blocks_direct_loopback_access"
|
|
"--skip" "zsh_fork_inner_allowed_script_inherits_active_permission_profile"
|
|
"--skip" "permission_request_hook_allows_network_approval_without_prompt"
|
|
"--skip" "guardian_receives_exact_triggers_for_concurrent_network_requests"
|
|
"--skip" "guardian_receives_exact_trigger_for_single_network_request"
|
|
"--skip" "approved_network_host_for_one_environment_still_prompts_in_another"
|
|
"--skip" "unified_exec_network_denial_emits_failed_background_end_event"
|
|
"--skip" "unified_exec_short_lived_network_denial_emits_failed_end_event"
|
|
;;; END Skip .git creators.
|
|
)
|
|
#:phases
|
|
#~(modify-phases %standard-phases
|
|
(add-after 'unpack 'chdir-to-workspace
|
|
(lambda _
|
|
(chdir "codex-rs")))
|
|
(add-after 'chdir-to-workspace 'update-version-in-snapshots
|
|
(lambda _
|
|
;; Snapshot test files contain hardcoded 0.0.0 version strings.
|
|
;; Update them to match the actual package version.
|
|
(let ((snap-files (find-files "." "\\.snap$")))
|
|
(substitute* snap-files
|
|
(("\\(v0\\.0\\.0\\) ") "(v0.144.1) ")
|
|
(("Update available! 0\\.0\\.0 -> 9\\.9\\.9 ")
|
|
"Update available! 0.144.1 -> 9.9.9")
|
|
(("Update available! 0\\.0\\.0 -> 9\\.9\\.9")
|
|
"Update available! 0.144.1 -> 9.9.9")))))
|
|
(add-after 'chdir-to-workspace 'patch-git-deps-to-vendor
|
|
(lambda _
|
|
;; Replace git dependencies with version references so cargo
|
|
;; resolves them from the vendored sources.
|
|
(substitute* "Cargo.toml"
|
|
(("crossterm = \\{ git = [^}]+\\}")
|
|
"crossterm = { version = \"0.28.1\" }")
|
|
(("ratatui = \\{ git = [^}]+\\}")
|
|
"ratatui = { version = \"0.29.0\" }")
|
|
(("tokio-tungstenite = \\{ git = [^}]+\\}")
|
|
"tokio-tungstenite = { version = \"0.28.0\" }")
|
|
(("tungstenite = \\{ git = [^}]+\\}")
|
|
"tungstenite = { version = \"0.27.0\" }")
|
|
(("nucleo = \\{ git = [^}]+\\}")
|
|
"nucleo = { version = \"0.5.0\" }")
|
|
(("runfiles = \\{ git = [^}]+\\}")
|
|
"runfiles = { version = \"0.1.0\" }"))
|
|
;; Remove workspace members that have unbuildable deps
|
|
;; (v8-poc requires V8). code-mode stays a workspace member
|
|
;; so its codex-protocol resolves to the same local copy as
|
|
;; the rest of the build; disabling its default features
|
|
;; (below) avoids pulling in V8.
|
|
(substitute* "Cargo.toml"
|
|
(("\"v8-poc\",") ""))
|
|
;; Disable V8 runtime in codex-code-mode by turning off
|
|
;; default features. Keep path= so code-mode is resolved
|
|
;; from the local source tree and shares codex-protocol
|
|
;; (and other workspace crates) with the rest of the build;
|
|
;; resolving it from the vendor instead introduces a
|
|
;; duplicate codex-protocol and causes E0308 type
|
|
;; mismatches in codex-tools.
|
|
(substitute* "Cargo.toml"
|
|
(("codex-code-mode = \\{ path = \"code-mode\" \\}")
|
|
"codex-code-mode = { path = \"code-mode\", default-features = false }"))))
|
|
(add-after 'patch-git-deps-to-vendor 'add-version-to-workspace-deps
|
|
(lambda _
|
|
;; Keep internal workspace path dependencies local, while giving
|
|
;; Cargo explicit versions for resolver/vendor normalization.
|
|
(let ((cargo-files (find-files "." "^Cargo\\.toml$")))
|
|
(substitute* cargo-files
|
|
;; Handle inline deps: name = { path = "..." }
|
|
(("(codex[_-][a-z0-9_-]+|app_test_support|core_test_support|mcp_test_support) = \\{ path = " all name)
|
|
(string-append name " = { version = \"0.144.1\", path = "))
|
|
;; Handle inline deps with package: name = { package = "...", path = "..." }
|
|
(("(codex[_-][a-z0-9_-]+|app_test_support|core_test_support|mcp_test_support) = \\{ package = " all name)
|
|
(string-append name " = { version = \"0.144.1\", package = "))
|
|
;; Handle section deps: [dependencies.X] with path = "..."
|
|
(("^(path = \"\\.\\./[^\"]*\")" all path-line)
|
|
(string-append path-line "\nversion = \"0.144.1\""))))))
|
|
(add-after 'chdir-to-workspace 'use-gnu-store-in-sandbox
|
|
(lambda _
|
|
;; LINUX_PLATFORM_DEFAULT_READ_ROOTS in linux-sandbox/src/
|
|
;; bwrap.rs is the read-only baseline that codex's bwrap
|
|
;; sandbox bind-mounts so commands can read /usr/bin/ls,
|
|
;; libc, etc. Upstream lists "/nix/store" for NixOS;
|
|
;; Guix's equivalent is "/gnu/store". Without this swap,
|
|
;; the codex sandbox cannot locate any binaries on a Guix
|
|
;; system because every "system" path resolves into
|
|
;; /gnu/store/<hash>-pkg/bin/...
|
|
(substitute* "linux-sandbox/src/bwrap.rs"
|
|
(("\"/nix/store\"") "\"/gnu/store\""))))
|
|
(add-after 'chdir-to-workspace 'patch-hardcoded-paths
|
|
(lambda* (#:key inputs #:allow-other-keys)
|
|
(let ((bash-bin (string-append
|
|
(assoc-ref inputs "bash-minimal") "/bin"))
|
|
(coreutils-bin (string-append
|
|
(assoc-ref inputs "coreutils") "/bin"))
|
|
(git-bin (string-append
|
|
(assoc-ref inputs "git-minimal") "/bin"))
|
|
(sed-bin (string-append
|
|
(assoc-ref inputs "sed") "/bin"))
|
|
;; Include .policy files: the execpolicy-legacy
|
|
;; crate embeds default.policy via include_str!
|
|
;; at compile time, so its paths must also be
|
|
;; patched.
|
|
(rs-files (find-files "." "\\.(rs|policy)$")))
|
|
(substitute* rs-files
|
|
(("\"/bin/bash\"")
|
|
(string-append "\"" bash-bin "/bash\""))
|
|
(("\"/bin/sh\"")
|
|
(string-append "\"" bash-bin "/sh\""))
|
|
(("\"/usr/bin/bash\"")
|
|
(string-append "\"" bash-bin "/bash\""))
|
|
(("\"/usr/bin/sh\"")
|
|
(string-append "\"" bash-bin "/sh\""))
|
|
;;; bash/sh with inline arguments, e.g. "/bin/bash -i".
|
|
(("\"/bin/bash ")
|
|
(string-append "\"" bash-bin "/bash "))
|
|
(("\"/bin/sh ")
|
|
(string-append "\"" bash-bin "/sh "))
|
|
;; Single-quoted paths in test assertion strings,
|
|
;; e.g. "exec '/bin/bash' -c ...". Must match the
|
|
;; double-quoted substitutions above so both sides
|
|
;; of assert_eq!/contains use the store path.
|
|
(("'/bin/bash'")
|
|
(string-append "'" bash-bin "/bash'"))
|
|
(("'/bin/sh'")
|
|
(string-append "'" bash-bin "/sh'"))
|
|
;; coreutils.
|
|
(("\"/bin/(cat|cp|date|echo|head|ls|rm|sleep|true|touch)\"" all cmd)
|
|
(string-append "\"" coreutils-bin "/" cmd "\""))
|
|
;; coreutils.
|
|
(("\"/usr/bin/(cat|cp|head|ls|touch|true)\"" all cmd)
|
|
(string-append "\"" coreutils-bin "/" cmd "\""))
|
|
;; coreutils with inline arguments
|
|
;; like "/bin/echo END-EVENT".
|
|
(("\"/bin/(cat|cp|date|echo|head|ls|rm|sleep|true|touch) " all cmd)
|
|
(string-append "\"" coreutils-bin "/" cmd " "))
|
|
(("\"/usr/bin/git\"")
|
|
(string-append "\"" git-bin "/git\""))
|
|
(("\"/usr/bin/sed\"")
|
|
(string-append "\"" sed-bin "/sed\"")))
|
|
;; Bare Command::new("git") and Command::new("ls")
|
|
;; in codex source files that exec internally.
|
|
(substitute* (find-files "git-utils" "\\.rs$")
|
|
(("Command::new\\(\"git\"\\)")
|
|
(string-append "Command::new(\"" git-bin "/git\")")))
|
|
(substitute* "tui/src/get_git_diff.rs"
|
|
(("Command::new\\(\"git\"\\)")
|
|
(string-append "Command::new(\"" git-bin "/git\")")))
|
|
(substitute* "core/src/turn_diff_tracker.rs"
|
|
(("Command::new\\(\"git\"\\)")
|
|
(string-append "Command::new(\"" git-bin "/git\")")))
|
|
(substitute* "cloud-tasks/src/env_detect.rs"
|
|
(("Command::new\\(\"git\"\\)")
|
|
(string-append "Command::new(\"" git-bin "/git\")")))
|
|
(substitute* "core-plugins/src/startup_sync.rs"
|
|
(("\"git\",")
|
|
(string-append "\"" git-bin "/git\",")))
|
|
(substitute* "file-search/src/lib.rs"
|
|
(("Command::new\\(\"ls\"\\)")
|
|
(string-append "Command::new(\"" coreutils-bin "/ls\")")))
|
|
;; Neutralize pager with absolute path to cat.
|
|
(substitute* "core/src/unified_exec/process_manager.rs"
|
|
(("\"PAGER\", \"cat\"")
|
|
(string-append "\"PAGER\", \"" coreutils-bin "/cat\""))
|
|
(("\"GIT_PAGER\", \"cat\"")
|
|
(string-append "\"GIT_PAGER\", \"" coreutils-bin "/cat\""))
|
|
(("\"GH_PAGER\", \"cat\"")
|
|
(string-append "\"GH_PAGER\", \"" coreutils-bin "/cat\"")))
|
|
;; Bare /bin/sleep in the exec-server test (without
|
|
;; surrounding double quotes, so the main pattern does
|
|
;; not match).
|
|
(substitute*
|
|
"exec-server/tests/exec_process.rs"
|
|
(("/bin/sleep")
|
|
(string-append coreutils-bin "/sleep")))
|
|
;; The encrypted-relay test clears the child
|
|
;; environment, so its bare "true" has no PATH
|
|
;; through which it could be resolved.
|
|
(substitute*
|
|
"exec-server/tests/relay.rs"
|
|
(("\"true\"")
|
|
(string-append "\"" coreutils-bin "/true\"")))
|
|
;; Bare /bin/sleep inside a format! string
|
|
;; (no surrounding double quotes, so the main
|
|
;; pattern does not match).
|
|
(substitute*
|
|
"shell-escalation/src/unix/escalate_server.rs"
|
|
(("/bin/sleep")
|
|
(string-append coreutils-bin "/sleep")))
|
|
;; @SHELL@ placeholder from test-shebangs patch
|
|
(substitute*
|
|
(list "rmcp-client/src/program_resolver.rs"
|
|
"tui/src/external_editor.rs")
|
|
(("@SHELL@")
|
|
(string-append bash-bin "/sh")))
|
|
;; Shebangs inside raw string literals (r#"..."#)
|
|
;; that are written to scripts at runtime. The
|
|
;; main "/bin/sh" patterns only match double-quoted
|
|
;; occurrences; these shebangs have no surrounding
|
|
;; double quotes.
|
|
(substitute*
|
|
(list "cli/src/doctor.rs"
|
|
"core-plugins/src/npm_source_tests.rs"
|
|
"core-plugins/src/remote_bundle.rs"
|
|
"core-plugins/src/startup_sync_tests.rs"
|
|
"core/src/git_info_tests.rs"
|
|
"core/src/tools/runtimes/shell/unix_escalation_tests.rs"
|
|
"core/tests/suite/client.rs"
|
|
"core/tests/suite/guardian_review.rs"
|
|
"core/tests/suite/skill_approval.rs"
|
|
"core/tests/suite/user_notification.rs"
|
|
"exec-server/tests/file_system_unix.rs"
|
|
"git-utils/src/baseline.rs"
|
|
"git-utils/src/info.rs"
|
|
"login/src/auth/auth_tests.rs"
|
|
"models-manager/src/manager_tests.rs"
|
|
"sandboxing/src/bwrap_tests.rs"
|
|
"shell-command/src/command_safety/is_safe_command.rs"
|
|
"tui/src/get_git_diff.rs")
|
|
(("#!/bin/bash")
|
|
(string-append "#!" bash-bin "/bash"))
|
|
(("#!/bin/sh")
|
|
(string-append "#!" bash-bin "/sh")))
|
|
;; Test helpers pass env: Default::default() to the
|
|
;; spawn code which calls env_clear(), leaving
|
|
;; processes with no PATH. Inject the build-time
|
|
;; PATH so tests find coreutils, bash, etc.
|
|
(substitute* (find-files "." "\\.rs$")
|
|
(("env: Default::default\\(\\)")
|
|
(string-append
|
|
"env: std::env::var(\"PATH\")"
|
|
".into_iter()"
|
|
".map(|p| (\"PATH\".to_string(), p))"
|
|
".collect()")))
|
|
;; Test-only absolute path; bare name works via PATH.
|
|
(substitute* "exec-server/tests/exec_process.rs"
|
|
(("\"/usr/bin/python3\"")
|
|
"\"python3\""))
|
|
;; Same empty-env issue but in JSON literals
|
|
;; (serde_json::json! macro).
|
|
(substitute* "exec-server/tests/process.rs"
|
|
(("\"env\": \\{\\}")
|
|
(string-append
|
|
"\"env\": {\"PATH\": \""
|
|
(getenv "PATH")
|
|
"\"}"))))))
|
|
(add-before 'build 'set-bubblewrap-source
|
|
(lambda* (#:key inputs #:allow-other-keys)
|
|
(setenv "CODEX_BWRAP_SOURCE_DIR"
|
|
(string-append
|
|
(assoc-ref inputs "bubblewrap-source")
|
|
"/"))))
|
|
(add-before 'build 'create-node-version-file
|
|
(lambda _
|
|
(call-with-output-file "guix-vendor/node-version.txt"
|
|
(lambda (port)
|
|
(display "22.22.0" port)))))
|
|
(add-before 'check 'increase-open-file-limit
|
|
(lambda _
|
|
;; The app-server integration suite runs hundreds of Tokio
|
|
;; runtimes, mock HTTP servers, and app-server subprocesses under
|
|
;; libtest. On large builders, the default soft nofile limit of
|
|
;; 1024 is not enough and failures cascade as EMFILE.
|
|
(call-with-values (lambda () (getrlimit 'nofile))
|
|
(lambda (soft hard)
|
|
(let ((target (if hard (min hard 65536) 65536)))
|
|
(when (and soft (< soft target))
|
|
(setrlimit 'nofile target hard)
|
|
(format #t
|
|
"increased maximum number of open files from ~d to ~d~%"
|
|
soft target)))))))
|
|
(add-before 'check 'set-home
|
|
(lambda _
|
|
;; HOME must not be a prefix of /tmp, otherwise
|
|
;; the TUI's path shortener replaces /tmp/... with
|
|
;; ~/... and 30 snapshot tests fail.
|
|
(setenv "HOME" "/tmp/guix-home")
|
|
(mkdir-p "/tmp/guix-home")
|
|
(setenv "USER" "nixbld")
|
|
;; fetch_ide_context_uses_unregistered_request_route:
|
|
;; IDE-context IPC rejects sockets in group-writable
|
|
;; temporary directories.
|
|
(umask #o077)
|
|
;; Default libtest thread stack is 2 MiB, which is not
|
|
;; enough for tokio current_thread tests that drive
|
|
;; codex-core's full turn pipeline. Upstream gates such
|
|
;; tests through run_current_thread_test_with_stack (4 MiB)
|
|
;; in app-server/src/message_processor/tracing_tests.rs;
|
|
;; raise the global default so future additions that
|
|
;; forget the wrapper still pass.
|
|
(setenv "RUST_MIN_STACK" "8388608")
|
|
;; Prevent fanout on huge servers.
|
|
(setenv "TOKIO_WORKER_THREADS" "3")
|
|
;; Set Codex's no-network marker so upstream tests using
|
|
;; skip_if_no_network! return early.
|
|
(setenv "CODEX_SANDBOX_NETWORK_DISABLED" "1")))
|
|
(replace 'check
|
|
(lambda* (#:key tests? parallel-build? cargo-test-flags
|
|
#:allow-other-keys)
|
|
(when tests?
|
|
;; Do not let libtest use all build cores here. Most
|
|
;; app-server integration tests start a real app-server
|
|
;; process, and each initialized server creates two
|
|
;; notify/inotify watchers (SkillsWatcher and FsWatchManager).
|
|
;; Linux's default fs.inotify.max_user_instances is often 128
|
|
;; per real UID, so --test-threads=128 can exhaust that
|
|
;; per-user inotify instance limit even after raising
|
|
;; RLIMIT_NOFILE.
|
|
(let ((test-threads (min 64 (parallel-job-count))))
|
|
(apply invoke
|
|
`("cargo" "test" "--offline"
|
|
,@(if parallel-build?
|
|
(list "-j"
|
|
(number->string
|
|
(parallel-job-count)))
|
|
(list "-j" "1"))
|
|
,@cargo-test-flags
|
|
,@(if (member "--" cargo-test-flags)
|
|
'()
|
|
'("--"))
|
|
"--test-threads"
|
|
,(number->string test-threads))))))))))
|
|
(native-inputs `(("bubblewrap" ,bubblewrap) ;tests need bwrap on PATH
|
|
("clang" ,clang)
|
|
("cmake-minimal" ,cmake-minimal)
|
|
("curl" ,curl) ;local loopback test
|
|
("libunwind" ,libunwind)
|
|
("lsof" ,lsof) ;app-server tests
|
|
("nss-certs-for-test" ,nss-certs-for-test) ;OTLP gRPC TLS
|
|
("perl" ,perl)
|
|
("procps" ,procps) ;tests use ps
|
|
("python-minimal" ,python-minimal)
|
|
("pkg-config" ,pkg-config)
|
|
("bubblewrap-source" ,(package-source bubblewrap))))
|
|
(inputs (cons* bash-minimal coreutils git-minimal sed
|
|
libcap oniguruma openssl sqlite zlib `(,zstd "lib")
|
|
(cargo-inputs 'codex)))
|
|
(properties '((timeout . 108000) ; 30 hours
|
|
(max-silent-time . 54000))) ; 15 hours
|
|
(home-page "https://github.com/openai/codex")
|
|
(synopsis "AI-assisted coding CLI and TUI")
|
|
(description
|
|
"Codex is an AI-powered coding assistant that runs in the terminal.
|
|
It provides an interactive TUI for conversations with AI models, with
|
|
support for shell command execution, file editing, and code generation.
|
|
Configure providers via @file{~/.codex/config.toml}.
|
|
|
|
codex-code-mode's V8 Javascript executor is disabled. That means if
|
|
you want to use the GPT 5.6 models you need to add a custom
|
|
@code{model_catalog_json} into @code{~/.codex/config.toml} that
|
|
makes them NOT force code (i.e. V8) mode.")
|
|
(license license:asl2.0)))
|
|
|
|
(define-public codex-acp
|
|
(package
|
|
(name "codex-acp")
|
|
(version "0.11.1")
|
|
(source
|
|
(origin
|
|
(method git-fetch)
|
|
(uri (git-reference
|
|
(url "https://github.com/zed-industries/codex-acp")
|
|
(commit (string-append "v" version))))
|
|
(file-name (git-file-name name version))
|
|
(sha256
|
|
(base32 "1rlgkbvrbdl5jhf49sjn2mifn2rq6z0vwf0gard23y1bz6sr3wmr"))
|
|
(patches (search-patches "codex-acp-0.11.1-remove-patch-sections.patch"))))
|
|
(build-system cargo-build-system)
|
|
(arguments
|
|
(list
|
|
#:install-source? #f
|
|
#:phases
|
|
#~(modify-phases %standard-phases
|
|
(add-after 'unpack 'patch-codex-deps
|
|
(lambda* (#:key inputs #:allow-other-keys)
|
|
;; Rewrite git dependencies to use vendored sources from rust-codex
|
|
(substitute* "Cargo.toml"
|
|
(("git = \"https://github.com/openai/codex\", tag = \"rust-v0.117.0\"")
|
|
"version = \"0.117.0\""))
|
|
;; Disable V8 runtime in codex-code-mode (transitive dep).
|
|
(let ((port (open-file "Cargo.toml" "a")))
|
|
(display "
|
|
[dependencies.codex-code-mode]
|
|
version = \"0.117.0\"
|
|
default-features = false
|
|
" port)
|
|
(close-port port))
|
|
;; Point codex-linux-sandbox at system bubblewrap source.
|
|
(setenv "CODEX_BWRAP_SOURCE_DIR"
|
|
(string-append
|
|
(assoc-ref inputs "bubblewrap-source")
|
|
"/"))))
|
|
(add-before 'check 'set-home
|
|
(lambda _
|
|
(setenv "HOME" "/tmp")
|
|
(setenv "USER" "nixbld")))
|
|
(add-before 'build 'create-node-version-file
|
|
(lambda _
|
|
;; codex-core's js_repl uses include_str! to read
|
|
;; node-version.txt four directories up from its source;
|
|
;; create it where the vendored crate expects it.
|
|
(call-with-output-file "guix-vendor/node-version.txt"
|
|
(lambda (port)
|
|
(display "22.22.0" port))))))))
|
|
(native-inputs
|
|
`(("cmake-minimal" ,cmake-minimal)
|
|
("clang" ,clang)
|
|
("pkg-config" ,pkg-config)
|
|
("bubblewrap-source" ,(package-source bubblewrap))))
|
|
(inputs (cons* libcap openssl sqlite zlib `(,zstd "lib")
|
|
(cargo-inputs 'codex-acp)))
|
|
(home-page "https://github.com/zed-industries/codex-acp")
|
|
(synopsis "ACP-compatible agent bridging Zed Codex with ACP clients")
|
|
(description
|
|
"This package provides an Agent Client Protocol (ACP) compatible agent
|
|
that bridges the Zed Codex runtime with ACP clients over stdio. It
|
|
supports multiple LLM providers through configuration in
|
|
@file{~/.codex/config.toml} and integrates with MCP servers for filesystem
|
|
operations.")
|
|
(license license:asl2.0)))
|