Releases: vertti/preflight
Release list
v0.20.0
A correctness release. Five checks could previously report success without verifying anything, or verify the wrong thing.
Upgrade notes
Each of these can turn a check that passed on v0.19.0 red on v0.20.0. That is the point — the old result was not trustworthy — but it may need action in existing pipelines.
| Change | What used to happen |
|---|---|
| Version constraints read the right number | s3cmd version 2.3.0 parsed as 3.0.0, so --min/--max/--exact/--range answered about a digit in the program name. Also affected bzip2, log4j, x264, p7zip. |
file size/content flags reject directories |
preflight file /etc --min-size 999999999 reported [OK]. --not-empty, --min-size, --max-size, --contains and --match all skipped the constraint on a directory. |
cmd --match no longer suppresses version constraints |
--match 'git version' --min 99 reported [OK] while --min 99 alone correctly failed. |
preflight run executes every check |
It stopped at the first failure, hiding every check after it. Exit code is now 1 if any check failed, rather than the first child's code. |
install.sh will not install an unverified binary |
With neither sha256sum nor shasum present it warned and installed anyway. Set PREFLIGHT_SKIP_CHECKSUM=1 to opt out deliberately. |
Also in this release
.preflightfiles support quoted arguments, so a value may contain a space:env GREETING --exact "hello world". Single quotes are literal, which keeps regexes and JSON intact.- Multi-line check output is readable again. A program's own line breaks are preserved and indented instead of escaped to
\n, so a failingpreflight cmdshows real help text. A checked program still cannot forge a result line — those start at column 0 and nothing it emits ever does. --exact ""asserts an empty value inenvandjson, instead of being indistinguishable from omitting the flag.
What's Changed
- Fix version extraction picking digits out of program names by @vertti in #239
- Fix file size and content checks silently passing on directories by @vertti in #240
- Run every check in a .preflight file, not just up to the first failure by @vertti in #243
- Fix --match silently disabling version constraints in cmd check by @vertti in #241
- Add quoting to .preflight files so arguments can contain spaces by @vertti in #244
- Update ghcr.io/vertti/preflight:latest Docker digest to e108e90 by @renovate[bot] in #242
- Fail the installer when it cannot verify the downloaded binary by @vertti in #246
- Remove a redundant null branch and a stale gjson comment in jsoncheck by @vertti in #247
- Print multi-line check details as indented lines, not escaped newlines by @vertti in #245
- Make --exact "" assert an empty value in env and json checks by @vertti in #248
Full Changelog: v0.19.0...v0.20.0
v0.19.0
What's Changed
- Test the .preflight command loop by @vertti in #223
- Give the run-named files and functions distinct names by @vertti in #224
- Fix --hide-value and --mask-value leaking on failure paths by @vertti in #225
- Fix memory limit detection under host cgroup namespaces and cgroup v1 by @vertti in #226
- Escape control characters in printed check results by @vertti in #228
- Fix --min-cpus ignoring container CPU quotas by @vertti in #229
- Bound HTTP response body reads by @vertti in #230
- Add a CA bundle to the released image by @vertti in #231
- Stop recommending that secrets be echoed into build logs by @vertti in #232
- Collapse the duplicated retry-failure handling by @vertti in #235
- Update jdx/mise-action digest to 3c2e0cf by @renovate[bot] in #236
- Update dependency go to v1.26.6 by @renovate[bot] in #237
- Update alpine Docker tag to v3.24 by @renovate[bot] in #234
- Update module go:golang.org/x/vuln/cmd/govulncheck to v1.7.0 by @renovate[bot] in #238
- Update dependency hk to v1.55.0 by @renovate[bot] in #222
- Update ghcr.io/vertti/preflight:latest Docker digest to 3921e02 by @renovate[bot] in #216
Full Changelog: v0.18.1...v0.19.0
v0.18.1
Security and reliability fixes
Unlike v0.18.0, nothing here turns a passing check into a failing one. Upgrading is safe for existing pipelines.
Security
preflight prometheus no longer follows redirects. It shared its HTTP client's design with preflight http but not its redirect guard, so a 3xx response sent the request — including custom headers — to whatever host the redirect named. Go strips Authorization and Cookie across hosts but not custom headers, and Mimir, Cortex, Thanos and Grafana Cloud carry tenancy in exactly those. The destination could both harvest credentials and decide the check's verdict. Both commands now share one client with redirects off by default; preflight http --follow-redirects still opts in. (#219)
.preflight files can no longer run other binaries. The format's contract is that every line runs preflight. The parser enforced it with a prefix test, so a line like preflight/../evil.sh counted as already-prefixed while the substitution that rewrites the line to the real binary — which tests for the exact token — did not fire. The path then ran relative to the working directory. Since .preflight is discovered by walking up from the current directory, git clone && cd repo && preflight run was remote code execution from repo content. The parser now compares the first token, and the substitution is unconditional. (#220)
Release binaries are compressed by a verified UPX. Shipped in v0.18.0; noted here because it wasn't called out at the time. (#212)
Reliability
preflight cmd --timeout is now enforced. A version command that leaves a background child holding stdout kept Wait blocked past the deadline, because the context kills only the direct child. A command that daemonizes — a service wrapper, an agent, a docker shim — hung the check indefinitely rather than failing it. Measured at 31s for a 2s timeout; now 1s. In a container entrypoint this was a permanent stall, which is worse than a reported failure. (#218)
Failing checks no longer print the flag reference. Cobra treats any error from a command as a usage mistake, so every failed check emitted Error: check failed plus the full flag list — up to 30 lines to deliver one. Real usage errors (unknown flag, missing argument, bad flag combination) still show usage. (#214)
$ preflight env NOPE
[FAIL] env: NOPE
not set
Documentation
Every flag in the README and usage guide is now checked against the binary. Four documented invocations did not work at all, including the Quick Start's tcp --retry and the version-constraint example's --min ^1.0. Sixteen working flags were missing from the tables, among them --json-path, which the README advertises as a reason to use preflight. (#215)
The binary size claims were also wrong in both directions: the published binary is UPX-compressed, so it is ~2.5MB rather than the ~6MB the usage guide claimed for copying it into an image.
Internal
The cross-platform CI matrix could not fail — every build step ended in || echo, so the six required Build checks passed through compile errors. Behind them, the Windows test build had been broken by a mock stranded behind a //go:build unix tag; go build skips _test.go files, so the documented verification step could not see it. Both fixed, and the matrix now runs go vet. (#213)
Tests no longer depend on the machine they run on. One created and deleted a directory inside the real $HOME; others inherited the developer's ~/.gitconfig (commit.gpgsign = true broke them) or assumed port 1 was free. (#217)
What's Changed
- Make the cross-platform CI gate able to fail by @vertti in #213
- Stop dumping usage on every failed check by @vertti in #214
- Fix documented flags that don't exist by @vertti in #215
- Make tests independent of the machine they run on by @vertti in #217
- Enforce --timeout when a command leaves a child running by @vertti in #218
- Share one HTTP client and stop promcheck following redirects by @vertti in #219
- Stop .preflight lines escaping to other binaries by @vertti in #220
- Derive the subcommand list from cobra by @vertti in #221
Full Changelog: v0.18.0...v0.18.1
v0.18.0
Checks that were silently passing now fail
This release fixes four cases where preflight reported [OK] while validating nothing. Upgrading may turn a green pipeline red. That is the intended outcome — the environment was already broken, preflight just wasn't saying so — but it means this is not a drop-in upgrade if you use any of the flags below.
| If you use | You previously got | You now get |
|---|---|---|
file <path> --writable / --executable |
[OK] whenever anyone had the permission bit |
[OK] only if the current user can actually write/execute |
file <path> --mode 0o600 (or any malformed octal) |
[OK] — the constraint parsed as 0 and matched everything |
An error naming the bad value |
file <path> --symlink-target X without --symlink |
[OK] — the target was never compared |
The path must be a symlink pointing at X |
preflight -- ./app or preflight --help -- ./app |
The app started, exit 0 | Refuses to exec, exit 1 |
What to check before upgrading
--writableon paths you don't own. The common case is a root-owned directory checked from an unprivileged container user. It passed before and fails now — which is what you wanted the check to tell you.- Malformed
--modevalues.0o600is the Go and Python spelling and was silently accepted as "no constraint". Use0600or600. - Templated entrypoints. If check arguments can resolve empty,
preflight -- /appnow exits 1 instead of starting the app unchecked. An empty or comments-only.preflightfile behaves the same way.
Fixes
--writable and --executable now ask the OS. They tested mode & 0o222, i.e. whether any of owner/group/other had the bit — not whether this process can act. A root-owned 0644 file returned [OK] for --writable to a non-root user. Now uses faccessat(AT_EACCESS) on unix; Windows keeps the permission-bit approximation, since there is no access(2) and Go reports a synthesized mode. (#211)
--mode rejects malformed octal. Parsing used Sscanf("%o"), which stops at the first non-octal character and reports no error once one digit is consumed. 0o600 became mode 0, and the minimum-permission test actual & 0 != 0 is false for every file — so a 0000 file passed a check the docs recommend for TLS private keys. (#209)
--symlink-target is honored on its own. It was only read inside the --symlink branch, so alone it parsed a target and never compared it. It now implies --symlink. (#209)
Exec mode requires a check to have run. The root command has no handler, so preflight -- ./app printed help, returned success, and started the app. So did preflight --help -- ./app. A gate that verified nothing reported success. (#210)
Supply chain
The UPX download is verified before it runs. release.yml fetched UPX over curl | tar with no integrity check and then compressed every published artifact with it. Because checksums are generated from the compressed output, a substituted UPX would have produced binaries whose checksums were internally consistent. The tarball's SHA-256 is now pinned and verified. (#212)
Note that checksums.txt is still generated in the same workflow that builds the binaries, so it detects corruption, not a compromised release. Signing/provenance attestation is tracked separately.
Internal
Local mise run test-coverage now uses the same -coverpkg as CI, so local coverage matches what Codecov gates on (94.6%, previously reported as 92.9% locally). (#208)
What's Changed
- Update ghcr.io/vertti/preflight:latest Docker digest to 8519b98 by @renovate[bot] in #92
- Update module golang.org/x/term to v0.39.0 by @renovate[bot] in #93
- Update Node.js to 732887b by @renovate[bot] in #94
- Update Node.js to 7326fb2 by @renovate[bot] in #95
- Update Node.js to bf22df2 by @renovate[bot] in #96
- Update dependency hk to v1.31.0 by @renovate[bot] in #98
- Update jdx/mise-action digest to 6d1e696 by @renovate[bot] in #97
- Update dependency hk to v1.32.0 by @renovate[bot] in #99
- Update dependency hk to v1.34.0 by @renovate[bot] in #100
- Update docker/login-action digest to c94ce9f by @renovate[bot] in #101
- Update dependency hk to v1.35.0 by @renovate[bot] in #102
- Update Node.js to 4660b1c by @renovate[bot] in #103
- Update actions/checkout digest to de0fac2 by @renovate[bot] in #104
- Update dependency hk to v1.36.0 by @renovate[bot] in #105
- Update dependency golangci-lint to v2.9.0 by @renovate[bot] in #108
- Update dependency go to v1.26.0 by @renovate[bot] in #107
- Update module golang.org/x/term to v0.40.0 by @renovate[bot] in #106
- Update docker/build-push-action digest to 10e90e3 by @renovate[bot] in #109
- Update Node.js to a81a03d by @renovate[bot] in #110
- Update dependency pkl to v0.31.0 by @renovate[bot] in #114
- Update dependency dprint to v0.52.0 by @renovate[bot] in #113
- Update jdx/mise-action digest to e79ddf6 by @renovate[bot] in #115
- Update dependency hk to v1.37.0 by @renovate[bot] in #116
- Update Node.js to e8e2e91 by @renovate[bot] in #112
- Update dependency go to v1.26.1 by @renovate[bot] in #120
- Update docker/build-push-action action to v7 by @renovate[bot] in #119
- Update docker/setup-buildx-action action to v4 by @renovate[bot] in #118
- Update docker/login-action action to v4 by @renovate[bot] in #117
- Update jdx/mise-action digest to 5228313 by @renovate[bot] in #121
- Update dependency hk to v1.38.0 by @renovate[bot] in #122
- Update dependency just to v1.47.0 by @renovate[bot] in #126
- Update dependency just to v1.47.1 by @renovate[bot] in #128
- Update dependency dprint to v0.53.0 by @renovate[bot] in #127
- Update module golang.org/x/term to v0.41.0 by @renovate[bot] in #123
- Update jdx/mise-action action to v4 by @renovate[bot] in #124
- Update softprops/action-gh-release digest to 153bb8e by @renovate[bot] in #125
- Update dependency hk to v1.39.0 by @renovate[bot] in #132
- Add automerge for CI-covered dependencies by @vertti in #134
- Update codecov/codecov-action digest to 1af5884 by @renovate[bot] in #130
- Update jdx/mise-action digest to 1648a78 by @renovate[bot] in #131
- Update codecov/codecov-action digest to 75cd116 by @renovate[bot] in #135
- Update dependency dprint to v0.53.1 by @renovate[bot] in #136
- Update dependency pkl to v0.31.1 by @renovate[bot] in #137
- Update dependency just to v1.48.1 by @renovate[bot] in #133
- Update codecov/codecov-action action to v6 by @renovate[bot] in #138
- Update docker/login-action digest to 4907a6d by @renovate[bot] in #141
- Update dependency just to v1.49.0 by @renovate[bot] in #142
- Update dependency dprint to v0.53.2 by @renovate[bot] in #139
- Update Node.js to 06e5c9f by @renovate[bot] in #129
- Update dependency hk to v1.41.0 by @renovate[bot] in #140
- Update Node.js to b506e73 by @renovate[bot] in #143
- Update dependency go to v1.26.2 by @renovate[bot] in #144
- Update dependency dprint to v0.54.0 by @renovate[bot] in #145
- Update module golang.org/x/term to v0.42.0 by @renovate[bot] in #146
- Update dependency hk to v1.41.1 by @renovate[bot] in #147
- Update docker/build-push-action digest to bcafcac by @renovate[bot] in #148
- Update softprops/action-gh-release digest to 3bb1273 by @renovate[bot] in #149
- Update softprops/action-gh-release action to v3 by @renovate[bot] in #150
- Update dependency just to v1.50.0 by @renovate[bot] in #153
- Update Node.js to dad1a61 by @renovate[bot] in #152
- Update dependency hk to v1.43.0 by @renovate[bot] in #151
- Update...
v0.17.1
What's Changed
- Pin ghcr.io/vertti/preflight Docker tag to 7113440 by @renovate[bot] in #87
- Replace Makefile with justfile by @vertti in #88
- Update dependency just to v1.46.0 by @renovate[bot] in #89
- Update dependency hk to v1.29.0 by @renovate[bot] in #90
- Update dependency golangci-lint to v2.8.0 by @renovate[bot] in #91
Full Changelog: v0.17.0...v0.17.1
v0.17.0
What's Changed
- Fix GitHub Actions security issues by @vertti in #75
- Improve test coverage across packages by @vertti in #76
- Add Cobra command execution tests by @vertti in #77
- Update README to lead with value, add FAQ by @vertti in #78
- Format What It Does section as bullet list by @vertti in #79
- Add exec mode for shell-less entrypoints by @vertti in #80
- Add CI tests for distroless and scratch images by @vertti in #81
- Add tests to improve coverage to 93.5% by @vertti in #82
- Refactor tests to reduce repetition and boilerplate by @vertti in #84
- Add FUNDING.yml with thanks.dev and Buy Me a Coffee by @vertti in #85
- Add testifylint for testify best practices by @vertti in #86
Full Changelog: v0.16.0...v0.17.0
v0.16.0
What's Changed
- Replace Dependabot with Renovate by @vertti in #59
- Add security scanning to CI by @vertti in #60
- Add security documentation and nilnesserr linter by @vertti in #61
- Update dependency golangci-lint to v2.7.2 by @renovate[bot] in #63
- Update Node.js to v24 by @renovate[bot] in #69
- Update dependency hk to v1.28.0 by @renovate[bot] in #68
- Update dependency pkl to v0.30.2 by @renovate[bot] in #66
- Update dependency dprint to v0.51.1 by @renovate[bot] in #67
- Replace abandoned dependencies gjson and go-supportscolor by @vertti in #72
- Update Node.js to b83af04 by @renovate[bot] in #71
- Update module golang.org/x/term to v0.38.0 by @renovate[bot] in #73
New Contributors
Full Changelog: v0.15.0...v0.16.0
v0.15.0
v0.14.0
What's Changed
Full Changelog: v0.13.0...v0.14.0
v0.13.0
What's Changed
- Improve error messages to show actual values by @vertti in #52
- Color error detail lines red by @vertti in #53
Full Changelog: v0.12.2...v0.13.0