Repository navigation
161 lines (150 loc) · 5.79 KB
/
Copy pathci.yml
File metadata and controls
161 lines (150 loc) · 5.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
name: CI Pipeline
on:
push:
branches: [main, develop, 'feature/**']
pull_request:
branches: [main, develop]
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
NODE_VERSION: '22'
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci --prefer-offline --no-audit --no-fund
- run: npm run lint
typecheck:
name: Type Check
runs-on: ubuntu-latest
needs: lint
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci --prefer-offline --no-audit --no-fund
- run: npm run typecheck
build:
name: Build
runs-on: ubuntu-latest
needs: [lint, typecheck]
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci --prefer-offline --no-audit --no-fund
- run: npm run build
- uses: actions/upload-artifact@v7
with:
name: dist
path: dist/
retention-days: 7
test:
name: Tests
runs-on: ubuntu-latest
needs: build
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
# Verification runs each command inside an OS sandbox and fails closed
# when it cannot get one. On Linux that sandbox is bubblewrap, which the
# runner image does not ship, so without this every verify test reports
# "OS verification sandbox is unavailable". macOS uses built-in
# sandbox-exec and needs no install.
- uses: ./.github/actions/install-bubblewrap
- name: Lift the AppArmor user-namespace restriction
run: |
# ubuntu-24.04 images restrict unprivileged user namespaces through
# AppArmor; without lifting it bwrap cannot set up the network
# namespace and every sandboxed command dies with
# "loopback: Failed RTM_NEWADDR: Operation not permitted".
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 || true
bwrap --ro-bind / / --unshare-net --dev /dev --proc /proc -- /bin/sh -lc 'echo sandbox-ok'
- run: npm ci --prefer-offline --no-audit --no-fund
# Coverage thresholds live in vitest.config.ts (ratcheted floor below the
# measured baseline — see that file's comment). Previously `npm test` had
# no --coverage flag anywhere in CI, so the threshold was configured but
# never actually enforced.
- run: npm run test:coverage
windows-process-tree:
# POSIX process groups cannot exercise the Windows ownership contract. This
# integration test proves the Job Object kills supervised descendants while
# an outside sibling remains alive, guarding against PID-reuse regressions.
name: Windows process-tree ownership
runs-on: windows-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci --prefer-offline --no-audit --no-fund
- run: npm test -- --run src/adapters/processTree.windows.test.ts
loc-gate:
name: LOC Warning (800 lines)
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- name: Check file sizes
run: |
for file in $(find src -name "*.ts" -type f); do
lines=$(wc -l < "$file")
if [ "$lines" -gt 800 ]; then
echo "::warning file=$file::File exceeds 800 lines ($lines)"
fi
done
verify-sandbox:
# Deterministic verification refuses to run without an OS sandbox, and
# whether one is available is a property of the runner, not of our code — no
# unit test can answer it. This job asserts the recipe the README gives
# operators, so that recipe cannot go stale silently. (INT-3103)
#
# Measured on ubuntu-latest 2026-08-01: unprivileged_userns_clone=1 and
# user.max_user_namespaces=63838, but apparmor_restrict_unprivileged_userns=1,
# and with that set even `bwrap --unshare-user` fails at "setting up uid map:
# Permission denied". Installing bubblewrap alone is NOT enough there.
name: Verify sandbox (ubuntu-latest)
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/install-bubblewrap
- name: Bubblewrap alone must not be assumed sufficient
run: |
set -uo pipefail
if bwrap --ro-bind / / --unshare-net --dev /dev --proc /proc -- /usr/bin/true 2>/dev/null; then
echo "::warning::The sandbox now works without lifting the AppArmor restriction — the README's sysctl step may be obsolete on this image"
else
echo "Confirmed: installing bubblewrap alone does not give a working sandbox on this runner"
fi
- name: The documented recipe must work
run: |
set -euo pipefail
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
# The same namespaces src/verify/runner.ts sets up. Probing a subset
# would pass on a host that denies one of the others.
bwrap --ro-bind / / --unshare-net --dev /dev --proc /proc -- /usr/bin/true
echo "OS verification sandbox is available on ubuntu-latest with bubblewrap + the AppArmor sysctl"