Skip to content

Commit 73fe138

Browse files
authored
chore: introduce new --native flag to Node module release process (#844)
This PR introduces an optional build flag, `--native`, that will build a version of the Codex npm module that: - Includes both the Node.js and native Rust versions (for Mac and Linux) - Will run the native version if `CODEX_RUST=1` is set - Runs the TypeScript version otherwise Note this PR also updates the workflow URL to https://github.com/openai/codex/actions/runs/14872557396, as that is a build from today that includes everything up through #843. Test Plan: In `~/code/codex/codex-cli`, I ran: ``` pnpm stage-release --native ``` The end of the output was: ``` Staged version 0.1.2505121317 for release in /var/folders/wm/f209bc1n2bd_r0jncn9s6j_00000gp/T/tmp.xd2p5ETYGN Test Node: node /var/folders/wm/f209bc1n2bd_r0jncn9s6j_00000gp/T/tmp.xd2p5ETYGN/bin/codex.js --help Test Rust: CODEX_RUST=1 node /var/folders/wm/f209bc1n2bd_r0jncn9s6j_00000gp/T/tmp.xd2p5ETYGN/bin/codex.js --help Next: cd "/var/folders/wm/f209bc1n2bd_r0jncn9s6j_00000gp/T/tmp.xd2p5ETYGN" && npm publish --tag native ``` I verified that running each of these commands ran the expected version of Codex. While here, I also added `bin` to the `files` list in `package.json`, which should have been done as part of #757, as that added new entries to `bin` that were matched by `.gitignore` but should have been included in a release.
1 parent f3bd143 commit 73fe138

File tree

6 files changed

+279
-48
lines changed

6 files changed

+279
-48
lines changed

README.md

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -652,17 +652,21 @@ The **DCO check** blocks merges until every commit in the PR carries the footer
652652

653653
### Releasing `codex`
654654

655-
To publish a new version of the CLI, run the following in the `codex-cli` folder to stage the release in a temporary directory:
655+
To publish a new version of the CLI you first need to stage the npm package. A
656+
helper script in `codex-cli/scripts/` does all the heavy lifting. Inside the
657+
`codex-cli` folder run:
656658

657-
```
659+
```bash
660+
# Classic, JS implementation that includes small, native binaries for Linux sandboxing.
658661
pnpm stage-release
659-
```
660662

661-
Note you can specify the folder for the staged release:
662-
663-
```
663+
# Optionally specify the temp directory to reuse between runs.
664664
RELEASE_DIR=$(mktemp -d)
665-
pnpm stage-release "$RELEASE_DIR"
665+
pnpm stage-release --tmp "$RELEASE_DIR"
666+
667+
# "Fat" package that additionally bundles the native Rust CLI binaries for
668+
# Linux. End-users can then opt-in at runtime by setting CODEX_RUST=1.
669+
pnpm stage-release --native
666670
```
667671

668672
Go to the folder where the release is staged and verify that it works as intended. If so, run the following from the temp folder:

codex-cli/.eslintrc.cjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module.exports = {
22
root: true,
3-
env: { browser: true, es2020: true },
3+
env: { browser: true, node: true, es2020: true },
44
extends: [
55
"eslint:recommended",
66
"plugin:@typescript-eslint/recommended",

codex-cli/bin/codex.js

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,89 @@
11
#!/usr/bin/env node
2+
// Unified entry point for the Codex CLI.
3+
/*
4+
* Behavior
5+
* =========
6+
* 1. By default we import the JavaScript implementation located in
7+
* dist/cli.js.
8+
*
9+
* 2. Developers can opt-in to a pre-compiled Rust binary by setting the
10+
* environment variable CODEX_RUST to a truthy value (`1`, `true`, etc.).
11+
* When that variable is present we resolve the correct binary for the
12+
* current platform / architecture and execute it via child_process.
13+
*
14+
* If the CODEX_RUST=1 is specified and there is no native binary for the
15+
* current platform / architecture, an error is thrown.
16+
*/
217

3-
// Unified entry point for Codex CLI on all platforms
4-
// Dynamically loads the compiled ESM bundle in dist/cli.js
18+
import { spawnSync } from "child_process";
19+
import path from "path";
20+
import { fileURLToPath, pathToFileURL } from "url";
521

6-
import path from 'path';
7-
import { fileURLToPath, pathToFileURL } from 'url';
22+
// Determine whether the user explicitly wants the Rust CLI.
23+
const wantsNative =
24+
process.env.CODEX_RUST != null
25+
? ["1", "true", "yes"].includes(process.env.CODEX_RUST.toLowerCase())
26+
: false;
27+
28+
// Try native binary if requested.
29+
if (wantsNative) {
30+
const { platform, arch } = process;
31+
32+
let targetTriple = null;
33+
switch (platform) {
34+
case "linux":
35+
switch (arch) {
36+
case "x64":
37+
targetTriple = "x86_64-unknown-linux-musl";
38+
break;
39+
case "arm64":
40+
targetTriple = "aarch64-unknown-linux-gnu";
41+
break;
42+
default:
43+
break;
44+
}
45+
break;
46+
case "darwin":
47+
switch (arch) {
48+
case "x64":
49+
targetTriple = "x86_64-apple-darwin";
50+
break;
51+
case "arm64":
52+
targetTriple = "aarch64-apple-darwin";
53+
break;
54+
default:
55+
break;
56+
}
57+
break;
58+
default:
59+
break;
60+
}
61+
62+
if (!targetTriple) {
63+
throw new Error(`Unsupported platform: ${platform} (${arch})`);
64+
}
65+
66+
// __dirname equivalent in ESM
67+
const __filename = fileURLToPath(import.meta.url);
68+
const __dirname = path.dirname(__filename);
69+
70+
const binaryPath = path.join(__dirname, "..", "bin", `codex-${targetTriple}`);
71+
const result = spawnSync(binaryPath, process.argv.slice(2), {
72+
stdio: "inherit",
73+
});
74+
75+
const exitCode = typeof result.status === "number" ? result.status : 1;
76+
process.exit(exitCode);
77+
}
78+
79+
// Fallback: execute the original JavaScript CLI.
880

981
// Determine this script's directory
1082
const __filename = fileURLToPath(import.meta.url);
1183
const __dirname = path.dirname(__filename);
1284

1385
// Resolve the path to the compiled CLI bundle
14-
const cliPath = path.resolve(__dirname, '../dist/cli.js');
86+
const cliPath = path.resolve(__dirname, "../dist/cli.js");
1587
const cliUrl = pathToFileURL(cliPath).href;
1688

1789
// Load and execute the CLI
@@ -21,7 +93,6 @@ const cliUrl = pathToFileURL(cliPath).href;
2193
} catch (err) {
2294
// eslint-disable-next-line no-console
2395
console.error(err);
24-
// eslint-disable-next-line no-undef
2596
process.exit(1);
2697
}
2798
})();

codex-cli/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"stage-release": "./scripts/stage_release.sh"
2424
},
2525
"files": [
26+
"bin",
2627
"dist"
2728
],
2829
"dependencies": {

codex-cli/scripts/install_native_deps.sh

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,44 @@
1-
#!/bin/bash
1+
#!/usr/bin/env bash
22

3-
# Copy the Linux sandbox native binaries into the bin/ subfolder of codex-cli/.
3+
# Install native runtime dependencies for codex-cli.
44
#
5-
# Usage:
6-
# ./scripts/install_native_deps.sh [CODEX_CLI_ROOT]
5+
# By default the script copies the sandbox binaries that are required at
6+
# runtime. When called with the --full-native flag, it additionally
7+
# bundles pre-built Rust CLI binaries so that the resulting npm package can run
8+
# the native implementation when users set CODEX_RUST=1.
79
#
8-
# Arguments
9-
# [CODEX_CLI_ROOT] – Optional. If supplied, it should be the codex-cli
10-
# folder that contains the package.json for @openai/codex.
10+
# Usage
11+
# install_native_deps.sh [RELEASE_ROOT] [--full-native]
1112
#
12-
# When no argument is given we assume the script is being run directly from a
13-
# development checkout. In that case we install the binaries into the
14-
# repository’s own `bin/` directory so that the CLI can run locally.
13+
# The optional RELEASE_ROOT is the path that contains package.json. Omitting
14+
# it installs the binaries into the repository's own bin/ folder to support
15+
# local development.
1516

1617
set -euo pipefail
1718

19+
# ------------------
20+
# Parse arguments
21+
# ------------------
22+
23+
DEST_DIR=""
24+
INCLUDE_RUST=0
25+
26+
for arg in "$@"; do
27+
case "$arg" in
28+
--full-native)
29+
INCLUDE_RUST=1
30+
;;
31+
*)
32+
if [[ -z "$DEST_DIR" ]]; then
33+
DEST_DIR="$arg"
34+
else
35+
echo "Unexpected argument: $arg" >&2
36+
exit 1
37+
fi
38+
;;
39+
esac
40+
done
41+
1842
# ----------------------------------------------------------------------------
1943
# Determine where the binaries should be installed.
2044
# ----------------------------------------------------------------------------
@@ -41,7 +65,7 @@ mkdir -p "$BIN_DIR"
4165
# Until we start publishing stable GitHub releases, we have to grab the binaries
4266
# from the GitHub Action that created them. Update the URL below to point to the
4367
# appropriate workflow run:
44-
WORKFLOW_URL="https://github.com/openai/codex/actions/runs/14763725716"
68+
WORKFLOW_URL="https://github.com/openai/codex/actions/runs/14950726936"
4569
WORKFLOW_ID="${WORKFLOW_URL##*/}"
4670

4771
ARTIFACTS_DIR="$(mktemp -d)"
@@ -50,12 +74,26 @@ trap 'rm -rf "$ARTIFACTS_DIR"' EXIT
5074
# NB: The GitHub CLI `gh` must be installed and authenticated.
5175
gh run download --dir "$ARTIFACTS_DIR" --repo openai/codex "$WORKFLOW_ID"
5276

53-
# Decompress the two target architectures.
77+
# Decompress the artifacts for Linux sandboxing.
5478
zstd -d "$ARTIFACTS_DIR/x86_64-unknown-linux-musl/codex-linux-sandbox-x86_64-unknown-linux-musl.zst" \
5579
-o "$BIN_DIR/codex-linux-sandbox-x64"
5680

5781
zstd -d "$ARTIFACTS_DIR/aarch64-unknown-linux-gnu/codex-linux-sandbox-aarch64-unknown-linux-gnu.zst" \
5882
-o "$BIN_DIR/codex-linux-sandbox-arm64"
5983

60-
echo "Installed native dependencies into $BIN_DIR"
84+
if [[ "$INCLUDE_RUST" -eq 1 ]]; then
85+
# x64 Linux
86+
zstd -d "$ARTIFACTS_DIR/x86_64-unknown-linux-musl/codex-x86_64-unknown-linux-musl.zst" \
87+
-o "$BIN_DIR/codex-x86_64-unknown-linux-musl"
88+
# ARM64 Linux
89+
zstd -d "$ARTIFACTS_DIR/aarch64-unknown-linux-gnu/codex-aarch64-unknown-linux-gnu.zst" \
90+
-o "$BIN_DIR/codex-aarch64-unknown-linux-gnu"
91+
# x64 macOS
92+
zstd -d "$ARTIFACTS_DIR/x86_64-apple-darwin/codex-x86_64-apple-darwin.zst" \
93+
-o "$BIN_DIR/codex-x86_64-apple-darwin"
94+
# ARM64 macOS
95+
zstd -d "$ARTIFACTS_DIR/aarch64-apple-darwin/codex-aarch64-apple-darwin.zst" \
96+
-o "$BIN_DIR/codex-aarch64-apple-darwin"
97+
fi
6198

99+
echo "Installed native dependencies into $BIN_DIR"

0 commit comments

Comments
 (0)