@norskvideo/ctl-dev-kit 0.1.85 → 0.1.86

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.
@@ -30,23 +30,61 @@ jobs:
30
30
  drift:
31
31
  runs-on: x64
32
32
  steps:
33
- # Root-owned leftovers under test-temp/ are what `clean: false` used to route
34
- # around: the integration suite bind-mounts host dirs into containers, and a
35
- # non-root git clean then EACCESes on what they wrote -- taking down whatever
36
- # innocent job checked out next. Skipping the clean cured that symptom and
37
- # caused another: every gitignored artifact survived between runs, so a test
38
- # could pass on build output an earlier job left behind and go red the first
39
- # time it landed on a cold runner (probe, 2026-09-01). Clear the leftovers
40
- # from a root container FIRST, and the checkout can clean properly again.
41
- # Best-effort by construction: no docker, no test-temp, or a failed run all
42
- # fall through without failing the job.
43
- - name: Clear stale root-owned test-temp (pre-checkout)
33
+ # Runs BEFORE checkout, and does two things: points the harness's temp base
34
+ # out of the workspace for every later step in this job, and clears whatever
35
+ # the old repo-local default left behind on this runner. The body carries the
36
+ # why. It fails the job only when legacy litter survives -- which would take
37
+ # the checkout down a step later anyway, but report nothing useful.
38
+ - name: Route test-temp out of the workspace (pre-checkout)
44
39
  run: |
45
40
  set -uo pipefail
41
+ # These runners are docker-OUTSIDE-of-docker, so a bind-mount source the
42
+ # HOST daemon has not seen before is created by dockerd, as ROOT, on the
43
+ # host -- even when the launch passes --container-user (reproduced
44
+ # 2026-09-04). The harness's default base is repo-local for an OrbStack
45
+ # inotify constraint that binds macOS dev and nothing here, so every run
46
+ # seeded the CHECKOUT with root-owned dirs a non-root `git clean -ffdx`
47
+ # cannot remove. That took commentary's checks AND integration red on
48
+ # 2026-09-03. Use the sanctioned override, per-run and outside the
49
+ # checkout -- as 8ff17a8d did in June for one debug workflow, and stopped.
50
+ base="$RUNNER_TEMP/nctt-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"
51
+ mkdir -p "$base"
52
+ echo "NORSK_CTL_TEST_TMP=$base" >> "$GITHUB_ENV"
53
+ # Yesterday's bases hold dockerd-created root dirs, so a non-root rm
54
+ # EACCESes. They cannot wedge a checkout, but they do fill the disk.
55
+ for old in $(find "$RUNNER_TEMP" -maxdepth 1 -name 'nctt-*' -mtime +0 -printf '%f\n' 2>/dev/null || true); do
56
+ docker run --rm --user 0:0 -v "$RUNNER_TEMP":/t alpine sh -c 'rm -rf -- "/t/$1"' sh "$old" >/dev/null 2>&1 || true
57
+ done
58
+ # LEGACY: a runner that ran the old default still carries a workspace
59
+ # test-temp, and a checkout with clean:true dies on it. Reap the holders
60
+ # first -- rm cannot unlink a live mountpoint even as root (EBUSY), and a
61
+ # RESTARTING leaked container re-creates the path as root seconds after
62
+ # any clean. Match on mount SOURCE, not a label: the holders are untracked
63
+ # by construction, because a killed run labels nothing.
46
64
  tt="$GITHUB_WORKSPACE/test-temp"
47
65
  [ -d "$tt" ] || exit 0
48
- docker run --rm --user 0:0 -v "$tt":/t alpine sh \
49
- -c 'rm -rf /t/* /t/.[!.]* 2>/dev/null || true' || rm -rf "$tt"/* 2>/dev/null || true
66
+ stuck=""
67
+ for c in $(docker ps -aq 2>/dev/null || true); do
68
+ if docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null | grep -q "^$tt/"; then
69
+ stuck="$stuck $c"
70
+ fi
71
+ done
72
+ if [ -n "$stuck" ]; then
73
+ echo "reaping containers holding mounts under $tt:$stuck"
74
+ docker rm -f $stuck || true
75
+ fi
76
+ # The DIRECTORY, not just its contents: an empty but root-owned base
77
+ # still EACCESes a later mkdtemp. Mounting the PARENT is what lets a root
78
+ # container unlink the leaf.
79
+ docker run --rm --user 0:0 -v "$GITHUB_WORKSPACE":/w alpine \
80
+ sh -c 'rm -rf /w/test-temp' || rm -rf "$tt" || true
81
+ # Fail HERE if it survived. The checkout fails on it either way, but
82
+ # reports only an EACCES rmdir with no clue what was holding the path.
83
+ if [ -e "$tt" ]; then
84
+ echo "::error::$tt survived the pre-checkout clean"
85
+ ls -lan "$tt" || true
86
+ exit 1
87
+ fi
50
88
 
51
89
  - uses: actions/checkout@v5
52
90
  with:
@@ -68,23 +106,61 @@ jobs:
68
106
  quality:
69
107
  runs-on: x64
70
108
  steps:
71
- # Root-owned leftovers under test-temp/ are what `clean: false` used to route
72
- # around: the integration suite bind-mounts host dirs into containers, and a
73
- # non-root git clean then EACCESes on what they wrote -- taking down whatever
74
- # innocent job checked out next. Skipping the clean cured that symptom and
75
- # caused another: every gitignored artifact survived between runs, so a test
76
- # could pass on build output an earlier job left behind and go red the first
77
- # time it landed on a cold runner (probe, 2026-09-01). Clear the leftovers
78
- # from a root container FIRST, and the checkout can clean properly again.
79
- # Best-effort by construction: no docker, no test-temp, or a failed run all
80
- # fall through without failing the job.
81
- - name: Clear stale root-owned test-temp (pre-checkout)
109
+ # Runs BEFORE checkout, and does two things: points the harness's temp base
110
+ # out of the workspace for every later step in this job, and clears whatever
111
+ # the old repo-local default left behind on this runner. The body carries the
112
+ # why. It fails the job only when legacy litter survives -- which would take
113
+ # the checkout down a step later anyway, but report nothing useful.
114
+ - name: Route test-temp out of the workspace (pre-checkout)
82
115
  run: |
83
116
  set -uo pipefail
117
+ # These runners are docker-OUTSIDE-of-docker, so a bind-mount source the
118
+ # HOST daemon has not seen before is created by dockerd, as ROOT, on the
119
+ # host -- even when the launch passes --container-user (reproduced
120
+ # 2026-09-04). The harness's default base is repo-local for an OrbStack
121
+ # inotify constraint that binds macOS dev and nothing here, so every run
122
+ # seeded the CHECKOUT with root-owned dirs a non-root `git clean -ffdx`
123
+ # cannot remove. That took commentary's checks AND integration red on
124
+ # 2026-09-03. Use the sanctioned override, per-run and outside the
125
+ # checkout -- as 8ff17a8d did in June for one debug workflow, and stopped.
126
+ base="$RUNNER_TEMP/nctt-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"
127
+ mkdir -p "$base"
128
+ echo "NORSK_CTL_TEST_TMP=$base" >> "$GITHUB_ENV"
129
+ # Yesterday's bases hold dockerd-created root dirs, so a non-root rm
130
+ # EACCESes. They cannot wedge a checkout, but they do fill the disk.
131
+ for old in $(find "$RUNNER_TEMP" -maxdepth 1 -name 'nctt-*' -mtime +0 -printf '%f\n' 2>/dev/null || true); do
132
+ docker run --rm --user 0:0 -v "$RUNNER_TEMP":/t alpine sh -c 'rm -rf -- "/t/$1"' sh "$old" >/dev/null 2>&1 || true
133
+ done
134
+ # LEGACY: a runner that ran the old default still carries a workspace
135
+ # test-temp, and a checkout with clean:true dies on it. Reap the holders
136
+ # first -- rm cannot unlink a live mountpoint even as root (EBUSY), and a
137
+ # RESTARTING leaked container re-creates the path as root seconds after
138
+ # any clean. Match on mount SOURCE, not a label: the holders are untracked
139
+ # by construction, because a killed run labels nothing.
84
140
  tt="$GITHUB_WORKSPACE/test-temp"
85
141
  [ -d "$tt" ] || exit 0
86
- docker run --rm --user 0:0 -v "$tt":/t alpine sh \
87
- -c 'rm -rf /t/* /t/.[!.]* 2>/dev/null || true' || rm -rf "$tt"/* 2>/dev/null || true
142
+ stuck=""
143
+ for c in $(docker ps -aq 2>/dev/null || true); do
144
+ if docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null | grep -q "^$tt/"; then
145
+ stuck="$stuck $c"
146
+ fi
147
+ done
148
+ if [ -n "$stuck" ]; then
149
+ echo "reaping containers holding mounts under $tt:$stuck"
150
+ docker rm -f $stuck || true
151
+ fi
152
+ # The DIRECTORY, not just its contents: an empty but root-owned base
153
+ # still EACCESes a later mkdtemp. Mounting the PARENT is what lets a root
154
+ # container unlink the leaf.
155
+ docker run --rm --user 0:0 -v "$GITHUB_WORKSPACE":/w alpine \
156
+ sh -c 'rm -rf /w/test-temp' || rm -rf "$tt" || true
157
+ # Fail HERE if it survived. The checkout fails on it either way, but
158
+ # reports only an EACCES rmdir with no clue what was holding the path.
159
+ if [ -e "$tt" ]; then
160
+ echo "::error::$tt survived the pre-checkout clean"
161
+ ls -lan "$tt" || true
162
+ exit 1
163
+ fi
88
164
 
89
165
  - uses: actions/checkout@v5
90
166
  with:
@@ -160,23 +236,61 @@ jobs:
160
236
  if: ${{ !cancelled() && github.event_name == 'push' }}
161
237
  runs-on: x64
162
238
  steps:
163
- # Root-owned leftovers under test-temp/ are what `clean: false` used to route
164
- # around: the integration suite bind-mounts host dirs into containers, and a
165
- # non-root git clean then EACCESes on what they wrote -- taking down whatever
166
- # innocent job checked out next. Skipping the clean cured that symptom and
167
- # caused another: every gitignored artifact survived between runs, so a test
168
- # could pass on build output an earlier job left behind and go red the first
169
- # time it landed on a cold runner (probe, 2026-09-01). Clear the leftovers
170
- # from a root container FIRST, and the checkout can clean properly again.
171
- # Best-effort by construction: no docker, no test-temp, or a failed run all
172
- # fall through without failing the job.
173
- - name: Clear stale root-owned test-temp (pre-checkout)
239
+ # Runs BEFORE checkout, and does two things: points the harness's temp base
240
+ # out of the workspace for every later step in this job, and clears whatever
241
+ # the old repo-local default left behind on this runner. The body carries the
242
+ # why. It fails the job only when legacy litter survives -- which would take
243
+ # the checkout down a step later anyway, but report nothing useful.
244
+ - name: Route test-temp out of the workspace (pre-checkout)
174
245
  run: |
175
246
  set -uo pipefail
247
+ # These runners are docker-OUTSIDE-of-docker, so a bind-mount source the
248
+ # HOST daemon has not seen before is created by dockerd, as ROOT, on the
249
+ # host -- even when the launch passes --container-user (reproduced
250
+ # 2026-09-04). The harness's default base is repo-local for an OrbStack
251
+ # inotify constraint that binds macOS dev and nothing here, so every run
252
+ # seeded the CHECKOUT with root-owned dirs a non-root `git clean -ffdx`
253
+ # cannot remove. That took commentary's checks AND integration red on
254
+ # 2026-09-03. Use the sanctioned override, per-run and outside the
255
+ # checkout -- as 8ff17a8d did in June for one debug workflow, and stopped.
256
+ base="$RUNNER_TEMP/nctt-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"
257
+ mkdir -p "$base"
258
+ echo "NORSK_CTL_TEST_TMP=$base" >> "$GITHUB_ENV"
259
+ # Yesterday's bases hold dockerd-created root dirs, so a non-root rm
260
+ # EACCESes. They cannot wedge a checkout, but they do fill the disk.
261
+ for old in $(find "$RUNNER_TEMP" -maxdepth 1 -name 'nctt-*' -mtime +0 -printf '%f\n' 2>/dev/null || true); do
262
+ docker run --rm --user 0:0 -v "$RUNNER_TEMP":/t alpine sh -c 'rm -rf -- "/t/$1"' sh "$old" >/dev/null 2>&1 || true
263
+ done
264
+ # LEGACY: a runner that ran the old default still carries a workspace
265
+ # test-temp, and a checkout with clean:true dies on it. Reap the holders
266
+ # first -- rm cannot unlink a live mountpoint even as root (EBUSY), and a
267
+ # RESTARTING leaked container re-creates the path as root seconds after
268
+ # any clean. Match on mount SOURCE, not a label: the holders are untracked
269
+ # by construction, because a killed run labels nothing.
176
270
  tt="$GITHUB_WORKSPACE/test-temp"
177
271
  [ -d "$tt" ] || exit 0
178
- docker run --rm --user 0:0 -v "$tt":/t alpine sh \
179
- -c 'rm -rf /t/* /t/.[!.]* 2>/dev/null || true' || rm -rf "$tt"/* 2>/dev/null || true
272
+ stuck=""
273
+ for c in $(docker ps -aq 2>/dev/null || true); do
274
+ if docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null | grep -q "^$tt/"; then
275
+ stuck="$stuck $c"
276
+ fi
277
+ done
278
+ if [ -n "$stuck" ]; then
279
+ echo "reaping containers holding mounts under $tt:$stuck"
280
+ docker rm -f $stuck || true
281
+ fi
282
+ # The DIRECTORY, not just its contents: an empty but root-owned base
283
+ # still EACCESes a later mkdtemp. Mounting the PARENT is what lets a root
284
+ # container unlink the leaf.
285
+ docker run --rm --user 0:0 -v "$GITHUB_WORKSPACE":/w alpine \
286
+ sh -c 'rm -rf /w/test-temp' || rm -rf "$tt" || true
287
+ # Fail HERE if it survived. The checkout fails on it either way, but
288
+ # reports only an EACCES rmdir with no clue what was holding the path.
289
+ if [ -e "$tt" ]; then
290
+ echo "::error::$tt survived the pre-checkout clean"
291
+ ls -lan "$tt" || true
292
+ exit 1
293
+ fi
180
294
 
181
295
  - uses: actions/checkout@v5
182
296
  with:
@@ -70,17 +70,61 @@ jobs:
70
70
  - runner: '["x64"]'
71
71
  runs-on: ${{ fromJSON(matrix.runner) }}
72
72
  steps:
73
- # BEFORE checkout: launched instances leave root-owned bind-mount target
74
- # dirs under test-temp/ that the non-root runner user cannot remove, so
75
- # actions/checkout's own cleanup would fail the job before anything runs.
76
- # Nuke them from a throwaway root container first. Best-effort.
77
- - name: Clear stale root-owned test-temp (pre-checkout)
73
+ # Runs BEFORE checkout, and does two things: points the harness's temp base
74
+ # out of the workspace for every later step in this job, and clears whatever
75
+ # the old repo-local default left behind on this runner. The body carries the
76
+ # why. It fails the job only when legacy litter survives -- which would take
77
+ # the checkout down a step later anyway, but report nothing useful.
78
+ - name: Route test-temp out of the workspace (pre-checkout)
78
79
  run: |
79
80
  set -uo pipefail
81
+ # These runners are docker-OUTSIDE-of-docker, so a bind-mount source the
82
+ # HOST daemon has not seen before is created by dockerd, as ROOT, on the
83
+ # host -- even when the launch passes --container-user (reproduced
84
+ # 2026-09-04). The harness's default base is repo-local for an OrbStack
85
+ # inotify constraint that binds macOS dev and nothing here, so every run
86
+ # seeded the CHECKOUT with root-owned dirs a non-root `git clean -ffdx`
87
+ # cannot remove. That took commentary's checks AND integration red on
88
+ # 2026-09-03. Use the sanctioned override, per-run and outside the
89
+ # checkout -- as 8ff17a8d did in June for one debug workflow, and stopped.
90
+ base="$RUNNER_TEMP/nctt-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"
91
+ mkdir -p "$base"
92
+ echo "NORSK_CTL_TEST_TMP=$base" >> "$GITHUB_ENV"
93
+ # Yesterday's bases hold dockerd-created root dirs, so a non-root rm
94
+ # EACCESes. They cannot wedge a checkout, but they do fill the disk.
95
+ for old in $(find "$RUNNER_TEMP" -maxdepth 1 -name 'nctt-*' -mtime +0 -printf '%f\n' 2>/dev/null || true); do
96
+ docker run --rm --user 0:0 -v "$RUNNER_TEMP":/t alpine sh -c 'rm -rf -- "/t/$1"' sh "$old" >/dev/null 2>&1 || true
97
+ done
98
+ # LEGACY: a runner that ran the old default still carries a workspace
99
+ # test-temp, and a checkout with clean:true dies on it. Reap the holders
100
+ # first -- rm cannot unlink a live mountpoint even as root (EBUSY), and a
101
+ # RESTARTING leaked container re-creates the path as root seconds after
102
+ # any clean. Match on mount SOURCE, not a label: the holders are untracked
103
+ # by construction, because a killed run labels nothing.
80
104
  tt="$GITHUB_WORKSPACE/test-temp"
81
105
  [ -d "$tt" ] || exit 0
82
- docker run --rm --user 0:0 -v "$tt":/t alpine sh \
83
- -c 'rm -rf /t/* /t/.[!.]* 2>/dev/null || true' || rm -rf "$tt"/* 2>/dev/null || true
106
+ stuck=""
107
+ for c in $(docker ps -aq 2>/dev/null || true); do
108
+ if docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null | grep -q "^$tt/"; then
109
+ stuck="$stuck $c"
110
+ fi
111
+ done
112
+ if [ -n "$stuck" ]; then
113
+ echo "reaping containers holding mounts under $tt:$stuck"
114
+ docker rm -f $stuck || true
115
+ fi
116
+ # The DIRECTORY, not just its contents: an empty but root-owned base
117
+ # still EACCESes a later mkdtemp. Mounting the PARENT is what lets a root
118
+ # container unlink the leaf.
119
+ docker run --rm --user 0:0 -v "$GITHUB_WORKSPACE":/w alpine \
120
+ sh -c 'rm -rf /w/test-temp' || rm -rf "$tt" || true
121
+ # Fail HERE if it survived. The checkout fails on it either way, but
122
+ # reports only an EACCES rmdir with no clue what was holding the path.
123
+ if [ -e "$tt" ]; then
124
+ echo "::error::$tt survived the pre-checkout clean"
125
+ ls -lan "$tt" || true
126
+ exit 1
127
+ fi
84
128
 
85
129
  - uses: actions/checkout@v5
86
130
  with:
@@ -288,16 +332,61 @@ jobs:
288
332
  if: ${{ !cancelled() && github.event_name != 'repository_dispatch' }}
289
333
  runs-on: x64
290
334
  steps:
291
- # Same reason as the first checkout in this file: clear root-owned leftovers
292
- # from a root container so the checkout below can clean properly. Best-effort
293
- # by construction -- no docker, no test-temp, or a failed run all fall through.
294
- - name: Clear stale root-owned test-temp (pre-checkout)
335
+ # Runs BEFORE checkout, and does two things: points the harness's temp base
336
+ # out of the workspace for every later step in this job, and clears whatever
337
+ # the old repo-local default left behind on this runner. The body carries the
338
+ # why. It fails the job only when legacy litter survives -- which would take
339
+ # the checkout down a step later anyway, but report nothing useful.
340
+ - name: Route test-temp out of the workspace (pre-checkout)
295
341
  run: |
296
342
  set -uo pipefail
343
+ # These runners are docker-OUTSIDE-of-docker, so a bind-mount source the
344
+ # HOST daemon has not seen before is created by dockerd, as ROOT, on the
345
+ # host -- even when the launch passes --container-user (reproduced
346
+ # 2026-09-04). The harness's default base is repo-local for an OrbStack
347
+ # inotify constraint that binds macOS dev and nothing here, so every run
348
+ # seeded the CHECKOUT with root-owned dirs a non-root `git clean -ffdx`
349
+ # cannot remove. That took commentary's checks AND integration red on
350
+ # 2026-09-03. Use the sanctioned override, per-run and outside the
351
+ # checkout -- as 8ff17a8d did in June for one debug workflow, and stopped.
352
+ base="$RUNNER_TEMP/nctt-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"
353
+ mkdir -p "$base"
354
+ echo "NORSK_CTL_TEST_TMP=$base" >> "$GITHUB_ENV"
355
+ # Yesterday's bases hold dockerd-created root dirs, so a non-root rm
356
+ # EACCESes. They cannot wedge a checkout, but they do fill the disk.
357
+ for old in $(find "$RUNNER_TEMP" -maxdepth 1 -name 'nctt-*' -mtime +0 -printf '%f\n' 2>/dev/null || true); do
358
+ docker run --rm --user 0:0 -v "$RUNNER_TEMP":/t alpine sh -c 'rm -rf -- "/t/$1"' sh "$old" >/dev/null 2>&1 || true
359
+ done
360
+ # LEGACY: a runner that ran the old default still carries a workspace
361
+ # test-temp, and a checkout with clean:true dies on it. Reap the holders
362
+ # first -- rm cannot unlink a live mountpoint even as root (EBUSY), and a
363
+ # RESTARTING leaked container re-creates the path as root seconds after
364
+ # any clean. Match on mount SOURCE, not a label: the holders are untracked
365
+ # by construction, because a killed run labels nothing.
297
366
  tt="$GITHUB_WORKSPACE/test-temp"
298
367
  [ -d "$tt" ] || exit 0
299
- docker run --rm --user 0:0 -v "$tt":/t alpine sh \
300
- -c 'rm -rf /t/* /t/.[!.]* 2>/dev/null || true' || rm -rf "$tt"/* 2>/dev/null || true
368
+ stuck=""
369
+ for c in $(docker ps -aq 2>/dev/null || true); do
370
+ if docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null | grep -q "^$tt/"; then
371
+ stuck="$stuck $c"
372
+ fi
373
+ done
374
+ if [ -n "$stuck" ]; then
375
+ echo "reaping containers holding mounts under $tt:$stuck"
376
+ docker rm -f $stuck || true
377
+ fi
378
+ # The DIRECTORY, not just its contents: an empty but root-owned base
379
+ # still EACCESes a later mkdtemp. Mounting the PARENT is what lets a root
380
+ # container unlink the leaf.
381
+ docker run --rm --user 0:0 -v "$GITHUB_WORKSPACE":/w alpine \
382
+ sh -c 'rm -rf /w/test-temp' || rm -rf "$tt" || true
383
+ # Fail HERE if it survived. The checkout fails on it either way, but
384
+ # reports only an EACCES rmdir with no clue what was holding the path.
385
+ if [ -e "$tt" ]; then
386
+ echo "::error::$tt survived the pre-checkout clean"
387
+ ls -lan "$tt" || true
388
+ exit 1
389
+ fi
301
390
 
302
391
  - uses: actions/checkout@v5
303
392
  with:
@@ -42,36 +42,117 @@ jobs:
42
42
  publish:
43
43
  runs-on: x64
44
44
  steps:
45
- # A guide's engine tier launches the same docker instances the integration
46
- # suite does, leaving root-owned bind-mount target dirs under test-temp/
47
- # that the non-root runner can't remove which fails actions/checkout's
48
- # own cleanup before anything runs. Nuke them from a throwaway root
49
- # container first. Best-effort. (Mirrors integration.yml.)
50
- - name: Clear stale root-owned test-temp (pre-checkout)
45
+ # Runs BEFORE checkout, and does two things: points the harness's temp base
46
+ # out of the workspace for every later step in this job, and clears whatever
47
+ # the old repo-local default left behind on this runner. The body carries the
48
+ # why. It fails the job only when legacy litter survives -- which would take
49
+ # the checkout down a step later anyway, but report nothing useful.
50
+ - name: Route test-temp out of the workspace (pre-checkout)
51
51
  run: |
52
52
  set -uo pipefail
53
+ # These runners are docker-OUTSIDE-of-docker, so a bind-mount source the
54
+ # HOST daemon has not seen before is created by dockerd, as ROOT, on the
55
+ # host -- even when the launch passes --container-user (reproduced
56
+ # 2026-09-04). The harness's default base is repo-local for an OrbStack
57
+ # inotify constraint that binds macOS dev and nothing here, so every run
58
+ # seeded the CHECKOUT with root-owned dirs a non-root `git clean -ffdx`
59
+ # cannot remove. That took commentary's checks AND integration red on
60
+ # 2026-09-03. Use the sanctioned override, per-run and outside the
61
+ # checkout -- as 8ff17a8d did in June for one debug workflow, and stopped.
62
+ base="$RUNNER_TEMP/nctt-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"
63
+ mkdir -p "$base"
64
+ echo "NORSK_CTL_TEST_TMP=$base" >> "$GITHUB_ENV"
65
+ # Yesterday's bases hold dockerd-created root dirs, so a non-root rm
66
+ # EACCESes. They cannot wedge a checkout, but they do fill the disk.
67
+ for old in $(find "$RUNNER_TEMP" -maxdepth 1 -name 'nctt-*' -mtime +0 -printf '%f\n' 2>/dev/null || true); do
68
+ docker run --rm --user 0:0 -v "$RUNNER_TEMP":/t alpine sh -c 'rm -rf -- "/t/$1"' sh "$old" >/dev/null 2>&1 || true
69
+ done
70
+ # LEGACY: a runner that ran the old default still carries a workspace
71
+ # test-temp, and a checkout with clean:true dies on it. Reap the holders
72
+ # first -- rm cannot unlink a live mountpoint even as root (EBUSY), and a
73
+ # RESTARTING leaked container re-creates the path as root seconds after
74
+ # any clean. Match on mount SOURCE, not a label: the holders are untracked
75
+ # by construction, because a killed run labels nothing.
53
76
  tt="$GITHUB_WORKSPACE/test-temp"
54
77
  [ -d "$tt" ] || exit 0
55
- docker run --rm --user 0:0 -v "$tt":/t alpine sh \
56
- -c 'rm -rf /t/* /t/.[!.]* 2>/dev/null || true' || rm -rf "$tt"/* 2>/dev/null || true
78
+ stuck=""
79
+ for c in $(docker ps -aq 2>/dev/null || true); do
80
+ if docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null | grep -q "^$tt/"; then
81
+ stuck="$stuck $c"
82
+ fi
83
+ done
84
+ if [ -n "$stuck" ]; then
85
+ echo "reaping containers holding mounts under $tt:$stuck"
86
+ docker rm -f $stuck || true
87
+ fi
88
+ # The DIRECTORY, not just its contents: an empty but root-owned base
89
+ # still EACCESes a later mkdtemp. Mounting the PARENT is what lets a root
90
+ # container unlink the leaf.
91
+ docker run --rm --user 0:0 -v "$GITHUB_WORKSPACE":/w alpine \
92
+ sh -c 'rm -rf /w/test-temp' || rm -rf "$tt" || true
93
+ # Fail HERE if it survived. The checkout fails on it either way, but
94
+ # reports only an EACCES rmdir with no clue what was holding the path.
95
+ if [ -e "$tt" ]; then
96
+ echo "::error::$tt survived the pre-checkout clean"
97
+ ls -lan "$tt" || true
98
+ exit 1
99
+ fi
57
100
 
58
- # Root-owned leftovers under test-temp/ are what `clean: false` used to route
59
- # around: the integration suite bind-mounts host dirs into containers, and a
60
- # non-root git clean then EACCESes on what they wrote -- taking down whatever
61
- # innocent job checked out next. Skipping the clean cured that symptom and
62
- # caused another: every gitignored artifact survived between runs, so a test
63
- # could pass on build output an earlier job left behind and go red the first
64
- # time it landed on a cold runner (probe, 2026-09-01). Clear the leftovers
65
- # from a root container FIRST, and the checkout can clean properly again.
66
- # Best-effort by construction: no docker, no test-temp, or a failed run all
67
- # fall through without failing the job.
68
- - name: Clear stale root-owned test-temp (pre-checkout)
101
+ # Runs BEFORE checkout, and does two things: points the harness's temp base
102
+ # out of the workspace for every later step in this job, and clears whatever
103
+ # the old repo-local default left behind on this runner. The body carries the
104
+ # why. It fails the job only when legacy litter survives -- which would take
105
+ # the checkout down a step later anyway, but report nothing useful.
106
+ - name: Route test-temp out of the workspace (pre-checkout)
69
107
  run: |
70
108
  set -uo pipefail
109
+ # These runners are docker-OUTSIDE-of-docker, so a bind-mount source the
110
+ # HOST daemon has not seen before is created by dockerd, as ROOT, on the
111
+ # host -- even when the launch passes --container-user (reproduced
112
+ # 2026-09-04). The harness's default base is repo-local for an OrbStack
113
+ # inotify constraint that binds macOS dev and nothing here, so every run
114
+ # seeded the CHECKOUT with root-owned dirs a non-root `git clean -ffdx`
115
+ # cannot remove. That took commentary's checks AND integration red on
116
+ # 2026-09-03. Use the sanctioned override, per-run and outside the
117
+ # checkout -- as 8ff17a8d did in June for one debug workflow, and stopped.
118
+ base="$RUNNER_TEMP/nctt-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"
119
+ mkdir -p "$base"
120
+ echo "NORSK_CTL_TEST_TMP=$base" >> "$GITHUB_ENV"
121
+ # Yesterday's bases hold dockerd-created root dirs, so a non-root rm
122
+ # EACCESes. They cannot wedge a checkout, but they do fill the disk.
123
+ for old in $(find "$RUNNER_TEMP" -maxdepth 1 -name 'nctt-*' -mtime +0 -printf '%f\n' 2>/dev/null || true); do
124
+ docker run --rm --user 0:0 -v "$RUNNER_TEMP":/t alpine sh -c 'rm -rf -- "/t/$1"' sh "$old" >/dev/null 2>&1 || true
125
+ done
126
+ # LEGACY: a runner that ran the old default still carries a workspace
127
+ # test-temp, and a checkout with clean:true dies on it. Reap the holders
128
+ # first -- rm cannot unlink a live mountpoint even as root (EBUSY), and a
129
+ # RESTARTING leaked container re-creates the path as root seconds after
130
+ # any clean. Match on mount SOURCE, not a label: the holders are untracked
131
+ # by construction, because a killed run labels nothing.
71
132
  tt="$GITHUB_WORKSPACE/test-temp"
72
133
  [ -d "$tt" ] || exit 0
73
- docker run --rm --user 0:0 -v "$tt":/t alpine sh \
74
- -c 'rm -rf /t/* /t/.[!.]* 2>/dev/null || true' || rm -rf "$tt"/* 2>/dev/null || true
134
+ stuck=""
135
+ for c in $(docker ps -aq 2>/dev/null || true); do
136
+ if docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null | grep -q "^$tt/"; then
137
+ stuck="$stuck $c"
138
+ fi
139
+ done
140
+ if [ -n "$stuck" ]; then
141
+ echo "reaping containers holding mounts under $tt:$stuck"
142
+ docker rm -f $stuck || true
143
+ fi
144
+ # The DIRECTORY, not just its contents: an empty but root-owned base
145
+ # still EACCESes a later mkdtemp. Mounting the PARENT is what lets a root
146
+ # container unlink the leaf.
147
+ docker run --rm --user 0:0 -v "$GITHUB_WORKSPACE":/w alpine \
148
+ sh -c 'rm -rf /w/test-temp' || rm -rf "$tt" || true
149
+ # Fail HERE if it survived. The checkout fails on it either way, but
150
+ # reports only an EACCES rmdir with no clue what was holding the path.
151
+ if [ -e "$tt" ]; then
152
+ echo "::error::$tt survived the pre-checkout clean"
153
+ ls -lan "$tt" || true
154
+ exit 1
155
+ fi
75
156
 
76
157
  - uses: actions/checkout@v5
77
158
  with: