@norskvideo/ctl-dev-kit 0.1.85 → 0.1.87

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/flake.nix CHANGED
@@ -87,6 +87,15 @@
87
87
  buildTools = [
88
88
  biome
89
89
  pkgs.bun
90
+ pkgs.nodejs # @biomejs/biome's bin is a JS wrapper, not the binary —
91
+ # it is what reads BIOME_BINARY below and execs the nix
92
+ # build. Something has to *parse* that wrapper first, so a
93
+ # modern node has to be on PATH or the host's is used: on
94
+ # an older distro node (Ubuntu noble ships v12) `bun run
95
+ # lint` dies on the wrapper's optional chaining before
96
+ # biome ever starts. norsk-ctl carries nodejs for kubb and
97
+ # so never hit this; the split-out products had nothing
98
+ # putting node in the shell.
90
99
  pkgs.cargo # builds the native product-signer addon (probe/native)
91
100
  pkgs.rustc
92
101
  pkgs.git
@@ -43,36 +43,117 @@ jobs:
43
43
  build:
44
44
  runs-on: x64
45
45
  steps:
46
- # A guide's engine tier launches the same docker instances the integration
47
- # suite does, leaving root-owned bind-mount target dirs under test-temp/
48
- # that the non-root runner can't remove which fails actions/checkout's
49
- # own cleanup before anything runs. Nuke them from a throwaway root
50
- # container first. Best-effort. (Mirrors integration.yml.)
51
- - name: Clear stale root-owned test-temp (pre-checkout)
46
+ # Runs BEFORE checkout, and does two things: points the harness's temp base
47
+ # out of the workspace for every later step in this job, and clears whatever
48
+ # the old repo-local default left behind on this runner. The body carries the
49
+ # why. It fails the job only when legacy litter survives -- which would take
50
+ # the checkout down a step later anyway, but report nothing useful.
51
+ - name: Route test-temp out of the workspace (pre-checkout)
52
52
  run: |
53
53
  set -uo pipefail
54
+ # These runners are docker-OUTSIDE-of-docker, so a bind-mount source the
55
+ # HOST daemon has not seen before is created by dockerd, as ROOT, on the
56
+ # host -- even when the launch passes --container-user (reproduced
57
+ # 2026-09-04). The harness's default base is repo-local for an OrbStack
58
+ # inotify constraint that binds macOS dev and nothing here, so every run
59
+ # seeded the CHECKOUT with root-owned dirs a non-root `git clean -ffdx`
60
+ # cannot remove. That took commentary's checks AND integration red on
61
+ # 2026-09-03. Use the sanctioned override, per-run and outside the
62
+ # checkout -- as 8ff17a8d did in June for one debug workflow, and stopped.
63
+ base="$RUNNER_TEMP/nctt-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"
64
+ mkdir -p "$base"
65
+ echo "NORSK_CTL_TEST_TMP=$base" >> "$GITHUB_ENV"
66
+ # Yesterday's bases hold dockerd-created root dirs, so a non-root rm
67
+ # EACCESes. They cannot wedge a checkout, but they do fill the disk.
68
+ for old in $(find "$RUNNER_TEMP" -maxdepth 1 -name 'nctt-*' -mtime +0 -printf '%f\n' 2>/dev/null || true); do
69
+ docker run --rm --user 0:0 -v "$RUNNER_TEMP":/t alpine sh -c 'rm -rf -- "/t/$1"' sh "$old" >/dev/null 2>&1 || true
70
+ done
71
+ # LEGACY: a runner that ran the old default still carries a workspace
72
+ # test-temp, and a checkout with clean:true dies on it. Reap the holders
73
+ # first -- rm cannot unlink a live mountpoint even as root (EBUSY), and a
74
+ # RESTARTING leaked container re-creates the path as root seconds after
75
+ # any clean. Match on mount SOURCE, not a label: the holders are untracked
76
+ # by construction, because a killed run labels nothing.
54
77
  tt="$GITHUB_WORKSPACE/test-temp"
55
78
  [ -d "$tt" ] || exit 0
56
- docker run --rm --user 0:0 -v "$tt":/t alpine sh \
57
- -c 'rm -rf /t/* /t/.[!.]* 2>/dev/null || true' || rm -rf "$tt"/* 2>/dev/null || true
79
+ stuck=""
80
+ for c in $(docker ps -aq 2>/dev/null || true); do
81
+ if docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null | grep -q "^$tt/"; then
82
+ stuck="$stuck $c"
83
+ fi
84
+ done
85
+ if [ -n "$stuck" ]; then
86
+ echo "reaping containers holding mounts under $tt:$stuck"
87
+ docker rm -f $stuck || true
88
+ fi
89
+ # The DIRECTORY, not just its contents: an empty but root-owned base
90
+ # still EACCESes a later mkdtemp. Mounting the PARENT is what lets a root
91
+ # container unlink the leaf.
92
+ docker run --rm --user 0:0 -v "$GITHUB_WORKSPACE":/w alpine \
93
+ sh -c 'rm -rf /w/test-temp' || rm -rf "$tt" || true
94
+ # Fail HERE if it survived. The checkout fails on it either way, but
95
+ # reports only an EACCES rmdir with no clue what was holding the path.
96
+ if [ -e "$tt" ]; then
97
+ echo "::error::$tt survived the pre-checkout clean"
98
+ ls -lan "$tt" || true
99
+ exit 1
100
+ fi
58
101
 
59
- # Root-owned leftovers under test-temp/ are what `clean: false` used to route
60
- # around: the integration suite bind-mounts host dirs into containers, and a
61
- # non-root git clean then EACCESes on what they wrote -- taking down whatever
62
- # innocent job checked out next. Skipping the clean cured that symptom and
63
- # caused another: every gitignored artifact survived between runs, so a test
64
- # could pass on build output an earlier job left behind and go red the first
65
- # time it landed on a cold runner (probe, 2026-09-01). Clear the leftovers
66
- # from a root container FIRST, and the checkout can clean properly again.
67
- # Best-effort by construction: no docker, no test-temp, or a failed run all
68
- # fall through without failing the job.
69
- - name: Clear stale root-owned test-temp (pre-checkout)
102
+ # Runs BEFORE checkout, and does two things: points the harness's temp base
103
+ # out of the workspace for every later step in this job, and clears whatever
104
+ # the old repo-local default left behind on this runner. The body carries the
105
+ # why. It fails the job only when legacy litter survives -- which would take
106
+ # the checkout down a step later anyway, but report nothing useful.
107
+ - name: Route test-temp out of the workspace (pre-checkout)
70
108
  run: |
71
109
  set -uo pipefail
110
+ # These runners are docker-OUTSIDE-of-docker, so a bind-mount source the
111
+ # HOST daemon has not seen before is created by dockerd, as ROOT, on the
112
+ # host -- even when the launch passes --container-user (reproduced
113
+ # 2026-09-04). The harness's default base is repo-local for an OrbStack
114
+ # inotify constraint that binds macOS dev and nothing here, so every run
115
+ # seeded the CHECKOUT with root-owned dirs a non-root `git clean -ffdx`
116
+ # cannot remove. That took commentary's checks AND integration red on
117
+ # 2026-09-03. Use the sanctioned override, per-run and outside the
118
+ # checkout -- as 8ff17a8d did in June for one debug workflow, and stopped.
119
+ base="$RUNNER_TEMP/nctt-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"
120
+ mkdir -p "$base"
121
+ echo "NORSK_CTL_TEST_TMP=$base" >> "$GITHUB_ENV"
122
+ # Yesterday's bases hold dockerd-created root dirs, so a non-root rm
123
+ # EACCESes. They cannot wedge a checkout, but they do fill the disk.
124
+ for old in $(find "$RUNNER_TEMP" -maxdepth 1 -name 'nctt-*' -mtime +0 -printf '%f\n' 2>/dev/null || true); do
125
+ docker run --rm --user 0:0 -v "$RUNNER_TEMP":/t alpine sh -c 'rm -rf -- "/t/$1"' sh "$old" >/dev/null 2>&1 || true
126
+ done
127
+ # LEGACY: a runner that ran the old default still carries a workspace
128
+ # test-temp, and a checkout with clean:true dies on it. Reap the holders
129
+ # first -- rm cannot unlink a live mountpoint even as root (EBUSY), and a
130
+ # RESTARTING leaked container re-creates the path as root seconds after
131
+ # any clean. Match on mount SOURCE, not a label: the holders are untracked
132
+ # by construction, because a killed run labels nothing.
72
133
  tt="$GITHUB_WORKSPACE/test-temp"
73
134
  [ -d "$tt" ] || exit 0
74
- docker run --rm --user 0:0 -v "$tt":/t alpine sh \
75
- -c 'rm -rf /t/* /t/.[!.]* 2>/dev/null || true' || rm -rf "$tt"/* 2>/dev/null || true
135
+ stuck=""
136
+ for c in $(docker ps -aq 2>/dev/null || true); do
137
+ if docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null | grep -q "^$tt/"; then
138
+ stuck="$stuck $c"
139
+ fi
140
+ done
141
+ if [ -n "$stuck" ]; then
142
+ echo "reaping containers holding mounts under $tt:$stuck"
143
+ docker rm -f $stuck || true
144
+ fi
145
+ # The DIRECTORY, not just its contents: an empty but root-owned base
146
+ # still EACCESes a later mkdtemp. Mounting the PARENT is what lets a root
147
+ # container unlink the leaf.
148
+ docker run --rm --user 0:0 -v "$GITHUB_WORKSPACE":/w alpine \
149
+ sh -c 'rm -rf /w/test-temp' || rm -rf "$tt" || true
150
+ # Fail HERE if it survived. The checkout fails on it either way, but
151
+ # reports only an EACCES rmdir with no clue what was holding the path.
152
+ if [ -e "$tt" ]; then
153
+ echo "::error::$tt survived the pre-checkout clean"
154
+ ls -lan "$tt" || true
155
+ exit 1
156
+ fi
76
157
 
77
158
  - uses: actions/checkout@v5
78
159
  with:
@@ -66,23 +66,61 @@ jobs:
66
66
  # publish job gates on this output instead of on PUBLISH_IMAGE directly.
67
67
  publish: ${{ steps.gen.outputs.publish }}
68
68
  steps:
69
- # Root-owned leftovers under test-temp/ are what `clean: false` used to route
70
- # around: the integration suite bind-mounts host dirs into containers, and a
71
- # non-root git clean then EACCESes on what they wrote -- taking down whatever
72
- # innocent job checked out next. Skipping the clean cured that symptom and
73
- # caused another: every gitignored artifact survived between runs, so a test
74
- # could pass on build output an earlier job left behind and go red the first
75
- # time it landed on a cold runner (probe, 2026-09-01). Clear the leftovers
76
- # from a root container FIRST, and the checkout can clean properly again.
77
- # Best-effort by construction: no docker, no test-temp, or a failed run all
78
- # fall through without failing the job.
79
- - name: Clear stale root-owned test-temp (pre-checkout)
69
+ # Runs BEFORE checkout, and does two things: points the harness's temp base
70
+ # out of the workspace for every later step in this job, and clears whatever
71
+ # the old repo-local default left behind on this runner. The body carries the
72
+ # why. It fails the job only when legacy litter survives -- which would take
73
+ # the checkout down a step later anyway, but report nothing useful.
74
+ - name: Route test-temp out of the workspace (pre-checkout)
80
75
  run: |
81
76
  set -uo pipefail
77
+ # These runners are docker-OUTSIDE-of-docker, so a bind-mount source the
78
+ # HOST daemon has not seen before is created by dockerd, as ROOT, on the
79
+ # host -- even when the launch passes --container-user (reproduced
80
+ # 2026-09-04). The harness's default base is repo-local for an OrbStack
81
+ # inotify constraint that binds macOS dev and nothing here, so every run
82
+ # seeded the CHECKOUT with root-owned dirs a non-root `git clean -ffdx`
83
+ # cannot remove. That took commentary's checks AND integration red on
84
+ # 2026-09-03. Use the sanctioned override, per-run and outside the
85
+ # checkout -- as 8ff17a8d did in June for one debug workflow, and stopped.
86
+ base="$RUNNER_TEMP/nctt-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"
87
+ mkdir -p "$base"
88
+ echo "NORSK_CTL_TEST_TMP=$base" >> "$GITHUB_ENV"
89
+ # Yesterday's bases hold dockerd-created root dirs, so a non-root rm
90
+ # EACCESes. They cannot wedge a checkout, but they do fill the disk.
91
+ for old in $(find "$RUNNER_TEMP" -maxdepth 1 -name 'nctt-*' -mtime +0 -printf '%f\n' 2>/dev/null || true); do
92
+ docker run --rm --user 0:0 -v "$RUNNER_TEMP":/t alpine sh -c 'rm -rf -- "/t/$1"' sh "$old" >/dev/null 2>&1 || true
93
+ done
94
+ # LEGACY: a runner that ran the old default still carries a workspace
95
+ # test-temp, and a checkout with clean:true dies on it. Reap the holders
96
+ # first -- rm cannot unlink a live mountpoint even as root (EBUSY), and a
97
+ # RESTARTING leaked container re-creates the path as root seconds after
98
+ # any clean. Match on mount SOURCE, not a label: the holders are untracked
99
+ # by construction, because a killed run labels nothing.
82
100
  tt="$GITHUB_WORKSPACE/test-temp"
83
101
  [ -d "$tt" ] || exit 0
84
- docker run --rm --user 0:0 -v "$tt":/t alpine sh \
85
- -c 'rm -rf /t/* /t/.[!.]* 2>/dev/null || true' || rm -rf "$tt"/* 2>/dev/null || true
102
+ stuck=""
103
+ for c in $(docker ps -aq 2>/dev/null || true); do
104
+ if docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null | grep -q "^$tt/"; then
105
+ stuck="$stuck $c"
106
+ fi
107
+ done
108
+ if [ -n "$stuck" ]; then
109
+ echo "reaping containers holding mounts under $tt:$stuck"
110
+ docker rm -f $stuck || true
111
+ fi
112
+ # The DIRECTORY, not just its contents: an empty but root-owned base
113
+ # still EACCESes a later mkdtemp. Mounting the PARENT is what lets a root
114
+ # container unlink the leaf.
115
+ docker run --rm --user 0:0 -v "$GITHUB_WORKSPACE":/w alpine \
116
+ sh -c 'rm -rf /w/test-temp' || rm -rf "$tt" || true
117
+ # Fail HERE if it survived. The checkout fails on it either way, but
118
+ # reports only an EACCES rmdir with no clue what was holding the path.
119
+ if [ -e "$tt" ]; then
120
+ echo "::error::$tt survived the pre-checkout clean"
121
+ ls -lan "$tt" || true
122
+ exit 1
123
+ fi
86
124
 
87
125
  - uses: actions/checkout@v5
88
126
  with:
@@ -109,23 +147,61 @@ jobs:
109
147
  arch: arm64
110
148
  runs-on: ${{ matrix.runner }}
111
149
  steps:
112
- # Root-owned leftovers under test-temp/ are what `clean: false` used to route
113
- # around: the integration suite bind-mounts host dirs into containers, and a
114
- # non-root git clean then EACCESes on what they wrote -- taking down whatever
115
- # innocent job checked out next. Skipping the clean cured that symptom and
116
- # caused another: every gitignored artifact survived between runs, so a test
117
- # could pass on build output an earlier job left behind and go red the first
118
- # time it landed on a cold runner (probe, 2026-09-01). Clear the leftovers
119
- # from a root container FIRST, and the checkout can clean properly again.
120
- # Best-effort by construction: no docker, no test-temp, or a failed run all
121
- # fall through without failing the job.
122
- - name: Clear stale root-owned test-temp (pre-checkout)
150
+ # Runs BEFORE checkout, and does two things: points the harness's temp base
151
+ # out of the workspace for every later step in this job, and clears whatever
152
+ # the old repo-local default left behind on this runner. The body carries the
153
+ # why. It fails the job only when legacy litter survives -- which would take
154
+ # the checkout down a step later anyway, but report nothing useful.
155
+ - name: Route test-temp out of the workspace (pre-checkout)
123
156
  run: |
124
157
  set -uo pipefail
158
+ # These runners are docker-OUTSIDE-of-docker, so a bind-mount source the
159
+ # HOST daemon has not seen before is created by dockerd, as ROOT, on the
160
+ # host -- even when the launch passes --container-user (reproduced
161
+ # 2026-09-04). The harness's default base is repo-local for an OrbStack
162
+ # inotify constraint that binds macOS dev and nothing here, so every run
163
+ # seeded the CHECKOUT with root-owned dirs a non-root `git clean -ffdx`
164
+ # cannot remove. That took commentary's checks AND integration red on
165
+ # 2026-09-03. Use the sanctioned override, per-run and outside the
166
+ # checkout -- as 8ff17a8d did in June for one debug workflow, and stopped.
167
+ base="$RUNNER_TEMP/nctt-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"
168
+ mkdir -p "$base"
169
+ echo "NORSK_CTL_TEST_TMP=$base" >> "$GITHUB_ENV"
170
+ # Yesterday's bases hold dockerd-created root dirs, so a non-root rm
171
+ # EACCESes. They cannot wedge a checkout, but they do fill the disk.
172
+ for old in $(find "$RUNNER_TEMP" -maxdepth 1 -name 'nctt-*' -mtime +0 -printf '%f\n' 2>/dev/null || true); do
173
+ docker run --rm --user 0:0 -v "$RUNNER_TEMP":/t alpine sh -c 'rm -rf -- "/t/$1"' sh "$old" >/dev/null 2>&1 || true
174
+ done
175
+ # LEGACY: a runner that ran the old default still carries a workspace
176
+ # test-temp, and a checkout with clean:true dies on it. Reap the holders
177
+ # first -- rm cannot unlink a live mountpoint even as root (EBUSY), and a
178
+ # RESTARTING leaked container re-creates the path as root seconds after
179
+ # any clean. Match on mount SOURCE, not a label: the holders are untracked
180
+ # by construction, because a killed run labels nothing.
125
181
  tt="$GITHUB_WORKSPACE/test-temp"
126
182
  [ -d "$tt" ] || exit 0
127
- docker run --rm --user 0:0 -v "$tt":/t alpine sh \
128
- -c 'rm -rf /t/* /t/.[!.]* 2>/dev/null || true' || rm -rf "$tt"/* 2>/dev/null || true
183
+ stuck=""
184
+ for c in $(docker ps -aq 2>/dev/null || true); do
185
+ if docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null | grep -q "^$tt/"; then
186
+ stuck="$stuck $c"
187
+ fi
188
+ done
189
+ if [ -n "$stuck" ]; then
190
+ echo "reaping containers holding mounts under $tt:$stuck"
191
+ docker rm -f $stuck || true
192
+ fi
193
+ # The DIRECTORY, not just its contents: an empty but root-owned base
194
+ # still EACCESes a later mkdtemp. Mounting the PARENT is what lets a root
195
+ # container unlink the leaf.
196
+ docker run --rm --user 0:0 -v "$GITHUB_WORKSPACE":/w alpine \
197
+ sh -c 'rm -rf /w/test-temp' || rm -rf "$tt" || true
198
+ # Fail HERE if it survived. The checkout fails on it either way, but
199
+ # reports only an EACCES rmdir with no clue what was holding the path.
200
+ if [ -e "$tt" ]; then
201
+ echo "::error::$tt survived the pre-checkout clean"
202
+ ls -lan "$tt" || true
203
+ exit 1
204
+ fi
129
205
 
130
206
  - uses: actions/checkout@v5
131
207
  with:
@@ -165,23 +241,61 @@ jobs:
165
241
  if: ${{ needs.label.outputs.publish == 'true' && !inputs.dry_run }}
166
242
  runs-on: x64
167
243
  steps:
168
- # Root-owned leftovers under test-temp/ are what `clean: false` used to route
169
- # around: the integration suite bind-mounts host dirs into containers, and a
170
- # non-root git clean then EACCESes on what they wrote -- taking down whatever
171
- # innocent job checked out next. Skipping the clean cured that symptom and
172
- # caused another: every gitignored artifact survived between runs, so a test
173
- # could pass on build output an earlier job left behind and go red the first
174
- # time it landed on a cold runner (probe, 2026-09-01). Clear the leftovers
175
- # from a root container FIRST, and the checkout can clean properly again.
176
- # Best-effort by construction: no docker, no test-temp, or a failed run all
177
- # fall through without failing the job.
178
- - name: Clear stale root-owned test-temp (pre-checkout)
244
+ # Runs BEFORE checkout, and does two things: points the harness's temp base
245
+ # out of the workspace for every later step in this job, and clears whatever
246
+ # the old repo-local default left behind on this runner. The body carries the
247
+ # why. It fails the job only when legacy litter survives -- which would take
248
+ # the checkout down a step later anyway, but report nothing useful.
249
+ - name: Route test-temp out of the workspace (pre-checkout)
179
250
  run: |
180
251
  set -uo pipefail
252
+ # These runners are docker-OUTSIDE-of-docker, so a bind-mount source the
253
+ # HOST daemon has not seen before is created by dockerd, as ROOT, on the
254
+ # host -- even when the launch passes --container-user (reproduced
255
+ # 2026-09-04). The harness's default base is repo-local for an OrbStack
256
+ # inotify constraint that binds macOS dev and nothing here, so every run
257
+ # seeded the CHECKOUT with root-owned dirs a non-root `git clean -ffdx`
258
+ # cannot remove. That took commentary's checks AND integration red on
259
+ # 2026-09-03. Use the sanctioned override, per-run and outside the
260
+ # checkout -- as 8ff17a8d did in June for one debug workflow, and stopped.
261
+ base="$RUNNER_TEMP/nctt-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"
262
+ mkdir -p "$base"
263
+ echo "NORSK_CTL_TEST_TMP=$base" >> "$GITHUB_ENV"
264
+ # Yesterday's bases hold dockerd-created root dirs, so a non-root rm
265
+ # EACCESes. They cannot wedge a checkout, but they do fill the disk.
266
+ for old in $(find "$RUNNER_TEMP" -maxdepth 1 -name 'nctt-*' -mtime +0 -printf '%f\n' 2>/dev/null || true); do
267
+ docker run --rm --user 0:0 -v "$RUNNER_TEMP":/t alpine sh -c 'rm -rf -- "/t/$1"' sh "$old" >/dev/null 2>&1 || true
268
+ done
269
+ # LEGACY: a runner that ran the old default still carries a workspace
270
+ # test-temp, and a checkout with clean:true dies on it. Reap the holders
271
+ # first -- rm cannot unlink a live mountpoint even as root (EBUSY), and a
272
+ # RESTARTING leaked container re-creates the path as root seconds after
273
+ # any clean. Match on mount SOURCE, not a label: the holders are untracked
274
+ # by construction, because a killed run labels nothing.
181
275
  tt="$GITHUB_WORKSPACE/test-temp"
182
276
  [ -d "$tt" ] || exit 0
183
- docker run --rm --user 0:0 -v "$tt":/t alpine sh \
184
- -c 'rm -rf /t/* /t/.[!.]* 2>/dev/null || true' || rm -rf "$tt"/* 2>/dev/null || true
277
+ stuck=""
278
+ for c in $(docker ps -aq 2>/dev/null || true); do
279
+ if docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null | grep -q "^$tt/"; then
280
+ stuck="$stuck $c"
281
+ fi
282
+ done
283
+ if [ -n "$stuck" ]; then
284
+ echo "reaping containers holding mounts under $tt:$stuck"
285
+ docker rm -f $stuck || true
286
+ fi
287
+ # The DIRECTORY, not just its contents: an empty but root-owned base
288
+ # still EACCESes a later mkdtemp. Mounting the PARENT is what lets a root
289
+ # container unlink the leaf.
290
+ docker run --rm --user 0:0 -v "$GITHUB_WORKSPACE":/w alpine \
291
+ sh -c 'rm -rf /w/test-temp' || rm -rf "$tt" || true
292
+ # Fail HERE if it survived. The checkout fails on it either way, but
293
+ # reports only an EACCES rmdir with no clue what was holding the path.
294
+ if [ -e "$tt" ]; then
295
+ echo "::error::$tt survived the pre-checkout clean"
296
+ ls -lan "$tt" || true
297
+ exit 1
298
+ fi
185
299
 
186
300
  - uses: actions/checkout@v5
187
301
  with:
@@ -216,23 +330,61 @@ jobs:
216
330
  if: ${{ !cancelled() }}
217
331
  runs-on: x64
218
332
  steps:
219
- # Root-owned leftovers under test-temp/ are what `clean: false` used to route
220
- # around: the integration suite bind-mounts host dirs into containers, and a
221
- # non-root git clean then EACCESes on what they wrote -- taking down whatever
222
- # innocent job checked out next. Skipping the clean cured that symptom and
223
- # caused another: every gitignored artifact survived between runs, so a test
224
- # could pass on build output an earlier job left behind and go red the first
225
- # time it landed on a cold runner (probe, 2026-09-01). Clear the leftovers
226
- # from a root container FIRST, and the checkout can clean properly again.
227
- # Best-effort by construction: no docker, no test-temp, or a failed run all
228
- # fall through without failing the job.
229
- - name: Clear stale root-owned test-temp (pre-checkout)
333
+ # Runs BEFORE checkout, and does two things: points the harness's temp base
334
+ # out of the workspace for every later step in this job, and clears whatever
335
+ # the old repo-local default left behind on this runner. The body carries the
336
+ # why. It fails the job only when legacy litter survives -- which would take
337
+ # the checkout down a step later anyway, but report nothing useful.
338
+ - name: Route test-temp out of the workspace (pre-checkout)
230
339
  run: |
231
340
  set -uo pipefail
341
+ # These runners are docker-OUTSIDE-of-docker, so a bind-mount source the
342
+ # HOST daemon has not seen before is created by dockerd, as ROOT, on the
343
+ # host -- even when the launch passes --container-user (reproduced
344
+ # 2026-09-04). The harness's default base is repo-local for an OrbStack
345
+ # inotify constraint that binds macOS dev and nothing here, so every run
346
+ # seeded the CHECKOUT with root-owned dirs a non-root `git clean -ffdx`
347
+ # cannot remove. That took commentary's checks AND integration red on
348
+ # 2026-09-03. Use the sanctioned override, per-run and outside the
349
+ # checkout -- as 8ff17a8d did in June for one debug workflow, and stopped.
350
+ base="$RUNNER_TEMP/nctt-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"
351
+ mkdir -p "$base"
352
+ echo "NORSK_CTL_TEST_TMP=$base" >> "$GITHUB_ENV"
353
+ # Yesterday's bases hold dockerd-created root dirs, so a non-root rm
354
+ # EACCESes. They cannot wedge a checkout, but they do fill the disk.
355
+ for old in $(find "$RUNNER_TEMP" -maxdepth 1 -name 'nctt-*' -mtime +0 -printf '%f\n' 2>/dev/null || true); do
356
+ docker run --rm --user 0:0 -v "$RUNNER_TEMP":/t alpine sh -c 'rm -rf -- "/t/$1"' sh "$old" >/dev/null 2>&1 || true
357
+ done
358
+ # LEGACY: a runner that ran the old default still carries a workspace
359
+ # test-temp, and a checkout with clean:true dies on it. Reap the holders
360
+ # first -- rm cannot unlink a live mountpoint even as root (EBUSY), and a
361
+ # RESTARTING leaked container re-creates the path as root seconds after
362
+ # any clean. Match on mount SOURCE, not a label: the holders are untracked
363
+ # by construction, because a killed run labels nothing.
232
364
  tt="$GITHUB_WORKSPACE/test-temp"
233
365
  [ -d "$tt" ] || exit 0
234
- docker run --rm --user 0:0 -v "$tt":/t alpine sh \
235
- -c 'rm -rf /t/* /t/.[!.]* 2>/dev/null || true' || rm -rf "$tt"/* 2>/dev/null || true
366
+ stuck=""
367
+ for c in $(docker ps -aq 2>/dev/null || true); do
368
+ if docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null | grep -q "^$tt/"; then
369
+ stuck="$stuck $c"
370
+ fi
371
+ done
372
+ if [ -n "$stuck" ]; then
373
+ echo "reaping containers holding mounts under $tt:$stuck"
374
+ docker rm -f $stuck || true
375
+ fi
376
+ # The DIRECTORY, not just its contents: an empty but root-owned base
377
+ # still EACCESes a later mkdtemp. Mounting the PARENT is what lets a root
378
+ # container unlink the leaf.
379
+ docker run --rm --user 0:0 -v "$GITHUB_WORKSPACE":/w alpine \
380
+ sh -c 'rm -rf /w/test-temp' || rm -rf "$tt" || true
381
+ # Fail HERE if it survived. The checkout fails on it either way, but
382
+ # reports only an EACCES rmdir with no clue what was holding the path.
383
+ if [ -e "$tt" ]; then
384
+ echo "::error::$tt survived the pre-checkout clean"
385
+ ls -lan "$tt" || true
386
+ exit 1
387
+ fi
236
388
 
237
389
  - uses: actions/checkout@v5
238
390
  with: