Skip to content

v0.24.3

Choose a tag to compare

@github-actions github-actions released this 02 Nov 21:52
· 600 commits to main since this release
Immutable release. Only release title and notes can be modified.

v0.24.3 - 2026-11-02

Added

  • A nightly "Stress" job in the analysis workflow: the runner's concurrency-sensitive tests (cancels, deadlines and timeouts, the memory gate, the native store's locks, pg's connect and the log writer, the broker's long-poll, log uploads) run 100 times over under stress-ng's CPU and memory load, with the database, failing on the first round that fails, its output an artifact. -Dtest-filter=upload now finds the log upload tests, and pg's test of a server that never answers no longer fails under Valgrind (its connect to the answering server had 200 ms). rr and Valgrind's Helgrind and DRD were tried and left out: GitHub's runner has no performance counters for rr, and Helgrind and DRD cannot see Zig's futex-based locks.
  • More race detection. The nightly analysis workflow runs the native backend's tests under ThreadSanitizer on x86_64 and arm64, rootless and as root with the service images ("ThreadSanitizer, native"), with the runner binary they start for each step (zig-runner native-step) built with it too, as ZIG_TEST_SANITIZE_THREAD=1 now builds it; and the end-to-end suite on both backends with a runner built under ThreadSanitizer ("ThreadSanitizer, end to end", e2e.sh build BIN tsan), failing on any report in the runner's log or the jobs' (e2e.sh races). TSan's runtime needs glibc's dynamic loader, so these builds are dynamically linked PIEs: the release's static PIE crashes at startup with it. "Go tools" runs go test -race and a race build of each module under runner/tools, and runs rsadiff, the one tool with goroutines of its own, with that build.

Fixed

  • The native layer store's locking is model-checked with TLC (proofs/tla/StoreLocking.tla, run by the new store-model workflow), and the model found two races, both fixed. A Dockerfile build's layer was published without its key's lock, so another runner's collect could take the half-published entry for a dead runner's and remove it while it was renamed into place, leaving a published layer with a half-deleted tree or no lock file; layers.zig now publishes under the key's lock, as a step does. And an eviction that opened an entry's lock file just before another runner evicted that entry would take the lock on the unlinked file and rename away the entry published under the key since, from under the job holding it; evict now checks its lock is on the file the path names, as open and lockKey do.
  • A native runner whose binary a deployment replaced while it kept running (an instance not in zig-runner-deploy's UNITS, as razorbill's zig-runner@bench.service was) now goes on running native jobs: it took its own path from /proc/self/exe for each job, which then reads "/usr/local/bin/zig-runner (deleted)", and every job fell back to Dagger with "could not make the job's sandbox: FileNotFound" when its network helper could not be started. Its helpers now run /proc//exe, the binary it started from, until it restarts, and "Set up job" says so; a rootless runner that starts its helpers under unshare, which cannot run that, sends its jobs to Dagger saying why. Every native set-up failure now says what failed and on which path ("starting the job's network helper /usr/local/bin/zig-runner (deleted): FileNotFound"), not the error's name alone. A test replaces a running runner's binary as the deployment does and runs a native job on it, in both native passes.
  • A problem matcher's pattern with a lookbehind no longer crashes the runner ("integer overflow") on a log line that is not valid UTF-8: a backreference compared bytes, so a lone 0xC3 matched the first byte of "À" and ended inside it, and a step back from there ((?=.(.))\1(?<=.) on C3 80 C3) went past the start of the line. Backreferences now compare whole characters, as .NET does, and a step back from inside a character takes the byte before as a character of its own, never more than the text before it. Found by the nightly fuzzer; its input is a seed now, the case is in the .NET comparison's fixed cases, and tests cover lookbehinds and backreferences (folded or not) that do not fit before or after where they are tried.

Security

  • A new security workflow scans for secrets and checks dependencies. "Secrets" runs gitleaks (pinned by version and checksum, .github/scripts/install-gitleaks.sh) over each pull request's own commits and weekly over main's whole history, failing on any finding and never printing the value; the one finding in the history so far, a test's made-up layer key in runner/src/native/layers.zig, is allowed in .gitleaks.toml, narrowly and with why, and gitleaks:allow comments are ignored. "Dependency review" fails a pull request that brings a dependency with a known vulnerability of moderate severity or worse, or a license outside the permissive ones (MIT, Apache-2.0, BSD, ISC, Zlib, Unicode), as GitHub's dependency graph sees them: the Go tools' modules and the workflows' actions, not runner/build.zig.zon or pinned images. Renovate follows gitleaks' version.
  • Fuzz targets and property checks for the text paths that guard secrets and a job's environment, run nightly by the analysis workflow, and the bugs they and their review found, each fixed: a GITHUB_OUTPUT or step summary value holding ::zig-file-command::env lines was read as the step's GITHUB_ENV (setting BASH_ENV ran code in later steps), now each file command's part carries its length; a heredoc value's EOF\r line ended a value delimited by EOF, so the lines after it set more variables, now file commands are read exactly as GitHub's runner reads them on Linux (a CRLF file's values keep their \r); overlapping secrets left most of one shown (***cdef), now they are masked as one span as GitHub masks them; secrets were not masked in base64 at an offset (base64(user:password)) or the other forms GitHub's runner masks (command line, expression and XML escapes, inside double quotes, PowerShell's halves); a secret across the cut of a line longer than 1 MiB was shown half on each line, and a long ::add-mask:: line was cut and its tail shown; and the engine's HTTP/2 connection kept CONTINUATION frames without end (a memory flood) and read frames past the 16 KiB a peer may send.