fix(cli/windows): always print bootstrap error; remove debug gate

Also:
- chore(release): delete legacy stage_release.sh and remove references
- ci(release): add ESM integrity guard for bin/coder.js in npm pack tarball
- docs(npm): clarify prepublishOnly guidance
This commit is contained in:
James Peter
2025-09-11 22:18:38 +10:00
parent 742ddc152c
commit 74785d58b3
5 changed files with 38 additions and 141 deletions
+22
View File
@@ -523,6 +523,28 @@ jobs:
fi
echo "tag=v${NEW_VERSION}" >> "$GITHUB_OUTPUT"
- name: Verify ESM bin integrity (no require in coder.js)
shell: bash
working-directory: codex-cli
run: |
set -euo pipefail
tgz=$(npm pack --json | jq -r '.[0].filename')
mkdir -p .pack && tar -xzf "$tgz" -C .pack
BIN=".pack/package/bin/coder.js"
test -f "$BIN" || { echo "coder.js missing in packed tarball" >&2; exit 1; }
if grep -q "require(\"fs\").createWriteStream" "$BIN"; then
echo "Found CommonJS require in coder.js; expected ESM. Failing." >&2
sed -n '1,60p' "$BIN" >&2 || true
exit 1
fi
# Sanity: ensure ESM import of fs functions exists
if ! grep -q "import .*from \"fs\"" "$BIN"; then
echo "Missing ESM import from fs in coder.js; unexpected content." >&2
sed -n '1,80p' "$BIN" >&2 || true
exit 1
fi
echo "ESM integrity OK for coder.js"
# Generate CHANGELOG and release notes by running our own `code` CLI in headless mode.
# It will:
# - Review changes between the previous tag and the new version
+7 -1
View File
@@ -132,6 +132,8 @@ const getCachedBinaryPath = (version) => {
return path.join(cacheDir, `code-${targetTriple}${ext}`);
};
let lastBootstrapError = null;
const httpsDownload = (url, dest) => new Promise((resolve, reject) => {
const req = httpsGet(url, (res) => {
const status = res.statusCode || 0;
@@ -255,7 +257,7 @@ const tryBootstrapBinary = async () => {
if (platform !== "win32") try { chmodSync(binaryPath, 0o755); } catch {}
return true;
})
.catch((_e) => false);
.catch((e) => { lastBootstrapError = e; return false; });
} catch {
return false;
}
@@ -301,6 +303,10 @@ if (existsSync(binaryPath)) {
}
} else {
console.error(`Binary not found: ${binaryPath}`);
if (lastBootstrapError) {
const msg = (lastBootstrapError && (lastBootstrapError.message || String(lastBootstrapError))) || 'unknown bootstrap error';
console.error(`Bootstrap error: ${msg}`);
}
console.error(`Please try reinstalling the package:`);
console.error(` npm uninstall -g @just-every/code`);
console.error(` npm install -g @just-every/code`);
+1 -1
View File
@@ -21,7 +21,7 @@
"scripts": {
"preinstall": "node scripts/preinstall.js",
"postinstall": "node postinstall.js",
"prepublishOnly": "node -e \"const fs=require('fs'),path=require('path'); const repoGit=path.join(__dirname,'..','.git'); const inCi=process.env.GITHUB_ACTIONS==='true'||process.env.CI==='true'; if(fs.existsSync(repoGit) && !inCi){ console.error('Refusing to publish from codex-cli. Use codex-cli/scripts/stage_release.sh to stage a release.'); process.exit(1);} else { console.log(inCi ? 'CI publish detected.' : 'Publishing staged package...'); }\""
"prepublishOnly": "node -e \"const fs=require('fs'),path=require('path'); const repoGit=path.join(__dirname,'..','.git'); const inCi=process.env.GITHUB_ACTIONS==='true'||process.env.CI==='true'; if(fs.existsSync(repoGit) && !inCi){ console.error('Refusing to publish from codex-cli. Publishing happens via release.yml.'); process.exit(1);} else { console.log(inCi ? 'CI publish detected.' : 'Publishing staged package...'); }\""
},
"repository": {
"type": "git",
-120
View File
@@ -1,120 +0,0 @@
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# stage_release.sh
# -----------------------------------------------------------------------------
# Stages an npm release for @openai/codex.
#
# Usage:
#
# --tmp <dir> : Use <dir> instead of a freshly created temp directory.
# -h|--help : Print usage.
#
# -----------------------------------------------------------------------------
set -euo pipefail
# Helper - usage / flag parsing
usage() {
cat <<EOF
Usage: $(basename "$0") [--tmp DIR] [--version VERSION]
Options
--tmp DIR Use DIR to stage the release (defaults to a fresh mktemp dir)
--version Specify the version to release (defaults to a timestamp-based version)
-h, --help Show this help
Legacy positional argument: the first non-flag argument is still interpreted
as the temporary directory (for backwards compatibility) but is deprecated.
EOF
exit "${1:-0}"
}
TMPDIR=""
# Default to a timestamp-based version (keep same scheme as before)
VERSION="$(printf '0.1.%d' "$(date +%y%m%d%H%M)")"
WORKFLOW_URL=""
# Manual flag parser - Bash getopts does not handle GNU long options well.
while [[ $# -gt 0 ]]; do
case "$1" in
--tmp)
shift || { echo "--tmp requires an argument"; usage 1; }
TMPDIR="$1"
;;
--tmp=*)
TMPDIR="${1#*=}"
;;
--version)
shift || { echo "--version requires an argument"; usage 1; }
VERSION="$1"
;;
--workflow-url)
shift || { echo "--workflow-url requires an argument"; exit 1; }
WORKFLOW_URL="$1"
;;
-h|--help)
usage 0
;;
--*)
echo "Unknown option: $1" >&2
usage 1
;;
*)
echo "Unexpected extra argument: $1" >&2
usage 1
;;
esac
shift
done
# Fallback when the caller did not specify a directory.
# If no directory was specified create a fresh temporary one.
if [[ -z "$TMPDIR" ]]; then
TMPDIR="$(mktemp -d)"
fi
# Ensure the directory exists, then resolve to an absolute path.
mkdir -p "$TMPDIR"
TMPDIR="$(cd "$TMPDIR" && pwd)"
# Main build logic
echo "Staging release in $TMPDIR"
# The script lives in codex-cli/scripts/ - change into codex-cli root so that
# relative paths keep working.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CODEX_CLI_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
pushd "$CODEX_CLI_ROOT" >/dev/null
# 1. Build the JS artifacts ---------------------------------------------------
# Paths inside the staged package
mkdir -p "$TMPDIR/bin"
cp -r bin/codex.js "$TMPDIR/bin/codex.js"
cp ../README.md "$TMPDIR" || true # README is one level up - ignore if missing
# Modify package.json - bump version and optionally add the native directory to
# the files array so that the binaries are published to npm.
jq --arg version "$VERSION" \
'.version = $version' \
package.json > "$TMPDIR/package.json"
# 2. Native runtime deps - SKIPPED
# Binaries are now fetched on-demand via postinstall.js to reduce npm package size
# ./scripts/install_native_deps.sh --workflow-url "$WORKFLOW_URL" "$TMPDIR"
popd >/dev/null
echo "Staged version $VERSION for release in $TMPDIR"
echo "Verify the CLI:"
echo " node ${TMPDIR}/bin/codex.js --version"
echo " node ${TMPDIR}/bin/codex.js --help"
# Print final hint for convenience
echo "Next: cd \"$TMPDIR\" && npm publish"
+8 -19
View File
@@ -14,17 +14,14 @@ def main() -> int:
Run this after the GitHub Release has been created and use
`--release-version` to specify the version to release.
Optionally pass `--tmp` to control the temporary staging directory that will be
forwarded to stage_release.sh.
Optionally pass `--tmp` to control the temporary staging directory used by
release CI to build the npm package.
"""
)
parser.add_argument(
"--release-version", required=True, help="Version to release, e.g., 0.3.0"
)
parser.add_argument(
"--tmp",
help="Optional path to stage the npm package; forwarded to stage_release.sh",
)
parser.add_argument("--tmp", help="Optional path to stage the npm package")
args = parser.parse_args()
version = args.release_version
@@ -49,19 +46,11 @@ forwarded to stage_release.sh.
print(f"should `git checkout {sha}`")
current_dir = Path(__file__).parent.resolve()
cmd = [
str(current_dir / "stage_release.sh"),
"--version",
version,
"--workflow-url",
workflow["url"],
]
if args.tmp:
cmd.extend(["--tmp", args.tmp])
stage_release = subprocess.run(cmd)
stage_release.check_returncode()
# The release pipeline is handled by GitHub Actions (release.yml). This
# helper now only locates the rust-release workflow run for the given
# version and prints the checkout hint. Staging and npm publish are driven
# by CI.
print("Release staging is handled by release.yml; no local staging step.")
return 0