Skip to content

Commit 4357b1e

Browse files
fix: harden Node CI and Windows package smoke (#84)
1 parent b26d4c3 commit 4357b1e

7 files changed

Lines changed: 109 additions & 14 deletions

File tree

.github/workflows/container-ci.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ on:
2525
permissions:
2626
contents: read
2727

28+
concurrency:
29+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
30+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
31+
2832
jobs:
2933
container:
3034
name: linux-amd64
@@ -96,7 +100,8 @@ jobs:
96100
mkdir -p results state
97101
chmod 700 results state
98102
printf 'id,repository,revision\n' > repositories.csv
99-
export CODEX_SECURITY_USER="$(id -u):$(id -g)"
103+
CODEX_SECURITY_USER="$(id -u):$(id -g)"
104+
export CODEX_SECURITY_USER
100105
docker compose config --quiet
101106
docker compose run --rm codex-security --version
102107

.github/workflows/node-ci.yml

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,35 +8,51 @@ on:
88
permissions:
99
contents: read
1010

11+
concurrency:
12+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
13+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
14+
1115
jobs:
1216
test:
13-
name: ${{ matrix.os }} / node-22
17+
name: ${{ matrix.os }} / node-${{ matrix.node }}
1418
runs-on: ${{ matrix.os }}
19+
timeout-minutes: 20
1520
strategy:
1621
fail-fast: false
1722
matrix:
1823
os: [ubuntu-latest, macos-latest, windows-latest]
24+
node: ["22"]
25+
include:
26+
- os: ubuntu-latest
27+
node: "24"
28+
- os: ubuntu-latest
29+
node: "26"
1930

2031
steps:
2132
- name: Checkout repository
2233
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
2334
with:
2435
persist-credentials: false
2536

37+
- name: Set up pnpm
38+
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
39+
with:
40+
package_json_file: sdk/typescript/package.json
41+
cache: true
42+
cache_dependency_path: sdk/typescript/pnpm-lock.yaml
43+
2644
- name: Set up Node.js
2745
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
2846
with:
29-
node-version: "22"
47+
node-version: ${{ matrix.node }}
48+
cache: npm
49+
cache-dependency-path: sdk/typescript/pnpm-lock.yaml
3050

3151
- name: Set up Bun
3252
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
3353
with:
3454
bun-version: "1.3.14"
3555

36-
- name: Enable pnpm
37-
shell: bash
38-
run: corepack enable && corepack prepare "$(node -p 'require("./sdk/typescript/package.json").packageManager')" --activate
39-
4056
- name: Install dependencies
4157
run: pnpm --dir sdk/typescript install --frozen-lockfile
4258

@@ -65,3 +81,18 @@ jobs:
6581
working-directory: sdk/typescript
6682
shell: bash
6783
run: pnpm run check:package ../../dist/*.tgz
84+
85+
- name: Smoke-test Node.js runtime
86+
working-directory: sdk/typescript
87+
shell: bash
88+
run: |
89+
set -euo pipefail
90+
node --input-type=module --eval '
91+
import { CodexSecurity } from "@openai/codex-security";
92+
93+
if (typeof CodexSecurity !== "function") {
94+
throw new Error("The SDK does not export CodexSecurity.");
95+
}
96+
'
97+
node bin/codex-security.mjs --version
98+
node bin/codex-security.mjs --help

.github/workflows/node-release.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ jobs:
6767
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
6868
with:
6969
bun-version: "1.3.14"
70+
no-cache: true
7071

7172
- name: Enable pnpm
7273
shell: bash

sdk/typescript/scripts/check-package.mjs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import { readFileSync } from "node:fs";
44
import { fileURLToPath } from "node:url";
55
import { brotliDecompressSync, gunzipSync } from "node:zlib";
66
import { assertExpectedGitHead } from "./package-provenance.mjs";
7+
import { packageSmokeTimeouts } from "./package-smoke-timeouts.mjs";
8+
9+
const PACKAGE_SMOKE_PROCESS_TIMEOUT_MS =
10+
packageSmokeTimeouts().processTimeoutMs;
711

812
const args = process.argv.slice(2);
913
if (args[0] === "--") args.shift();
@@ -279,15 +283,16 @@ if (args.length === 1) {
279283
[fileURLToPath(new URL("./smoke-package.mjs", import.meta.url)), archive],
280284
{
281285
stdio: "inherit",
282-
timeout: 150_000,
286+
timeout: PACKAGE_SMOKE_PROCESS_TIMEOUT_MS,
283287
killSignal: "SIGKILL",
284288
windowsHide: true,
285289
},
286290
);
287291
if (smoke.error?.code === "ETIMEDOUT") {
288-
throw new Error("Installed npm package smoke timed out after 150000 ms.", {
289-
cause: smoke.error,
290-
});
292+
throw new Error(
293+
`Installed npm package smoke timed out after ${PACKAGE_SMOKE_PROCESS_TIMEOUT_MS} ms.`,
294+
{ cause: smoke.error },
295+
);
291296
}
292297
if (smoke.error !== undefined) throw smoke.error;
293298
if (smoke.status !== 0) {
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export function packageSmokeTimeouts(platform = process.platform) {
2+
const commandTimeoutMs = platform === "win32" ? 180_000 : 120_000;
3+
4+
return {
5+
commandTimeoutMs,
6+
processTimeoutMs: commandTimeoutMs + 30_000,
7+
};
8+
}

sdk/typescript/scripts/smoke-package.mjs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ import {
1111
import { tmpdir } from "node:os";
1212
import { basename, dirname, join, relative, resolve, sep } from "node:path";
1313
import { fileURLToPath } from "node:url";
14+
import { packageSmokeTimeouts } from "./package-smoke-timeouts.mjs";
1415

15-
const PACKAGE_SMOKE_TIMEOUT_MS = 120_000;
16+
const PACKAGE_SMOKE_TIMEOUT_MS = packageSmokeTimeouts().commandTimeoutMs;
1617
const packageRoot = fileURLToPath(new URL("../", import.meta.url));
1718
const packageManifest = JSON.parse(
1819
await readFile(new URL("../package.json", import.meta.url), "utf8"),
@@ -191,12 +192,11 @@ try {
191192
[
192193
...npm.args,
193194
"install",
195+
"--prefer-offline",
194196
"--include=optional",
195197
"--ignore-scripts",
196198
"--no-audit",
197199
"--no-fund",
198-
"--cache",
199-
join(consumer, ".npm-cache"),
200200
archive,
201201
],
202202
{ cwd: consumer },
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { describe, expect, test } from "bun:test";
2+
3+
type PackageSmokeTimeouts = {
4+
packageSmokeTimeouts: (platform?: NodeJS.Platform) => {
5+
commandTimeoutMs: number;
6+
processTimeoutMs: number;
7+
};
8+
};
9+
10+
const { packageSmokeTimeouts } = (await import(
11+
new URL("../scripts/package-smoke-timeouts.mjs", import.meta.url).href
12+
)) as PackageSmokeTimeouts;
13+
14+
describe("npm package smoke timeouts", () => {
15+
test("preserves the existing timeout on Linux and macOS", () => {
16+
for (const platform of ["linux", "darwin"] as const) {
17+
expect(packageSmokeTimeouts(platform)).toEqual({
18+
commandTimeoutMs: 120_000,
19+
processTimeoutMs: 150_000,
20+
});
21+
}
22+
});
23+
24+
test("allows the Windows npm install to complete", () => {
25+
expect(packageSmokeTimeouts("win32")).toEqual({
26+
commandTimeoutMs: 180_000,
27+
processTimeoutMs: 210_000,
28+
});
29+
});
30+
31+
test("keeps the parent smoke timeout above the command timeout", () => {
32+
for (const platform of ["linux", "darwin", "win32"] as const) {
33+
const { commandTimeoutMs, processTimeoutMs } =
34+
packageSmokeTimeouts(platform);
35+
36+
expect(processTimeoutMs).toBe(commandTimeoutMs + 30_000);
37+
}
38+
});
39+
40+
test("uses the active runtime platform by default", () => {
41+
expect(packageSmokeTimeouts()).toEqual(
42+
packageSmokeTimeouts(process.platform),
43+
);
44+
});
45+
});

0 commit comments

Comments
 (0)