Skip to content

release: 5.9.0 - #1691

Merged
Jaro-c merged 9 commits into
mainfrom
develop
Sep 4, 2026
Merged

Jaro-c merged 9 commits into
mainfrom
develop

Conversation

@Jaro-c

@Jaro-c Jaro-c commented Sep 4, 2026

Copy link
Copy Markdown
Member

Release developmain for 5.9.0. Merge commit, not squash: main is the released state.

What ships

The live board on every command that acts (#1671 to #1675, PRs #1676, #1677, #1678). up, down, start, stop, restart, kill, pause, unpause, create, rm, scale, pull and push draw the same board up had: a working verb while a row is in flight, a final verb with the elapsed time, one row per resource. The board keeps animating under NO_COLOR, cuts rows to the terminal width, and in a pipe prints plain lines that never contradict each other. pull counts the layers as they copy.

build on the board (#1681, PR #1684). One row per image, Building n/m from the builder's STEP lines, the builder stream folded under the row on a terminal and replayed above the error when a build fails; in a pipe every line is prefixed with the image tag and the image id goes to stdout.

rm, scale-down and ps read right (#1686, #1687, #1688, PR #1689). rm and a scale-down open their rows before stopping and removing, so Removed carries the time; rm --stop resumes only what is paused and draws nothing otherwise; ps and events fail on a compose file named with -f that does not exist instead of answering by label.

The release itself changes

Every binary is read before it is signed (#1679, PR #1682). Windows: DllCharacteristics must carry DYNAMIC_BASE, HIGH_ENTROPY_VA, NX_COMPAT and GUARD_CF; Control Flow Guard is newly enabled for both MSVC targets, and the 5.8.0 executables measured without it. macOS: MH_PIE and a stripped symbol table. Linux keeps the gate from 5.7.1. Each script has a fixture with one control per property, and the Windows gate ran on a podup.exe built in a pull request before this release.

The windows-arm64 asset is built and run on an arm64 runner (#1680, PR #1683). Until now it cross-compiled on x86_64 and its smoke test was skipped.

This run is the first release that exercises the Windows and macOS gates on the assets that ship. If one fails, the release stops before signing, which is the point.

Verified before opening this

Signed-off-by: Jaro-c 75870284+Jaro-c@users.noreply.github.com

Fixes #1671.

## What

`pull`, `restart`, `stop`, `start`, `kill`, `pause`, `unpause`, `rm` and
`run` open the live board over the resources they will touch and close
it on every exit, the way `up` does; in a pipe the plain lines stay.
Each container action gets a working verb before the call (`Stopping`,
`Restarting`, `Killing`, `Pausing`, `Unpausing`, `Removing`), so the row
carries the spinner and the clock while Podman works, and a skipped
container ends its row with `Skipped` instead of silence.

## Measured (pseudo-terminal, `script`, local Podman 5.7.0)

| command | board before | board after | last row |
|---|---|---|---|
| pull | 0 | 1 | `✔ Image alpine:3.20 Pulled 0.0s` |
| restart, stop, start, pause, unpause, kill, rm | 0 | 1 | `✔ Container
ux-web-1 Restarted` and so on |
| run --rm | 0 | 1 | network rows, then the command's output on stdout |
| stop with `stop_grace_period: 3s` on a container that ignores SIGTERM
| frozen `Pending` | `⠋ Stopping 0.0s` for 35 frames, then `✔ Stopped
3.2s` |

Piped `pull` prints `Pulling` and `Pulled` as before (test
`a_piped_pull_keeps_the_plain_lines`).

## How it was built

MiniMax wrote the wiring and nine tests under a brief; I added the
working verbs, the `Skipped` final and its test after the captures
showed rows jumping from `Pending` to the final verb with no activity in
between. Unit: 1824 pass. Clippy and fmt clean.

Note for the merge order: #1672/#1673/#1675 (in review next) make the
plain sink print only final verbs; whichever lands second adapts the
piped-pull expectation.

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
…lain sink never contradicts itself (#1677)

Fixes #1672, #1673 and #1675.

## What

- The live sink is chosen by the terminal alone; `NO_COLOR` and `--ansi
never` remove the styling and keep the spinner, the repaint and the
markers. Measured: `NO_COLOR=1 up -d` now hides the cursor and repaints
with zero SGR sequences.
- Rows and the summary line are cut to the measured width with `…` on
the name. Measured at `stty rows 24 cols 30`: longest row 29 columns,
cursor moves unchanged.
- The plain sink never contradicts itself: a transitional verb is held
until the final one; both lines are printed when work happened
(`Pulling` then `Pulled`, so a log still shows when it started), only
the final when nothing was done (`Exists`, `Running`, `Absent`,
`Skipped`); the held line is flushed at `end` and at the error exit.
Piped `up -d` on an existing network prints `Network ux_default Exists`
and nothing else for it.
- A failed `up` printed its `Failed` row twice; once now. Interactive
`exec` wrote a stray byte before the output (`^@hi` under `script`); the
first bytes are `hi`. Empty `volumes`, `images`, `ps` and `ls` print `no
volumes` / `no images` / `no containers` / `no projects` on stderr and
nothing on stdout.

## How it was built

MiniMax wrote the fixes and the eight named tests under a brief. Two
things I changed on review: its plain sink dropped every transitional
line, which broke four contract tests in `tests/reporting_contract.rs`
that hold `Pulling` then `Pulled` in a pipe (the rule above keeps them
and drops only the contradiction); and it had rewritten every
pre-existing em dash in `docs/commands.md` into broken punctuation, so
the docs diff was rebuilt from `HEAD` plus its five new paragraphs.
Unit: 1824 pass; the 14 contract tests pass.

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>

---------

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
Fixes #1674.

Podman 5.7.0 sends no byte counts on the pull API (measured: `Copying
blob` lines in libpod mode, `Pulling fs layer`/`Download complete` with
an empty `progressDetail` in compat mode), so the row counts layers:
`Pulling 1 layer`, `Pulling 4 layers` as digests appear, `Pulling 4/4`
once every copy is reported, then `Pulled`. In a pipe, `Pulling` and
`Pulled` as before.

Measured on a real pull of `python:3.12-alpine` after `podman rmi`: the
row moved through `Pulling 1 layer`, `2 layers`, `3 layers`, 20 frames
at `4 layers`, `4/4`, `Pulled`.

MiniMax wrote it under a brief; I fixed the plural and one comment.
Unit: 1827 pass, the 15 contract tests pass.

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
… it (#1683)

Fixes #1680.

A pull-request job (`windows-arm64.yml`, same shape as
`linux-hardening.yml`) builds `aarch64-pc-windows-msvc` on
`windows-11-arm` and runs `--version`. Its run on this pull request is
the measurement that the label exists for this organization and that the
binary runs. The release matrix moves the `podup-windows-arm64.exe` leg
to that runner and the smoke skip from #1649 goes away; the threat-model
row no longer names an exception.

What this pull request must show: the `aarch64-pc-windows-msvc on
windows-11-arm` job green. If the label is not available, that job is
the only red and this pull request waits.

MiniMax wrote it under a brief.

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
…fore signing (#1682)

Fixes #1679.

## Measured first

An independent parser over the 5.8.0 assets: both Windows binaries have
`DllCharacteristics 0x8160` (`DYNAMIC_BASE`, `HIGH_ENTROPY_VA`,
`NX_COMPAT`, no `GUARD_CF`); `podup-darwin-arm64` is `PIE`. Control Flow
Guard is opt-in on rustc for MSVC.

## What

- `.cargo/config.toml`: `-C control-flow-guard` for both MSVC targets.
- `.github/scripts/check-hardening.ps1` parses the PE header itself (no
Visual Studio tooling) and requires the four `DllCharacteristics` bits;
its test builds a minimal PE32+ and one control per bit.
- `.github/scripts/check-hardening-macos.sh` requires `MH_PIE` in `otool
-h` and no local symbols in `nm -p`; its test builds controls with
`clang` where the tools exist and says `SKIP` elsewhere; it runs on the
macOS legs.
- `release.yml`: both scripts run in the legs that can read the file,
before signing.
- `lint-powershell.yml`: a `windows-latest` job runs the PE fixture,
then builds the release binary with the pinned toolchain and reads it,
so the rustflag is proven on a pull request and not at tag time. Paths
include `.cargo/**` and `Cargo.toml`.
- Threat model rows and the supply-chain sentence.

## What this pull request must show

The `check-hardening.ps1 tests` job green with the built binary passing
all four bits: that is the measurement that `control-flow-guard` reaches
the linker. Nothing macOS could be executed on this Linux machine; the
macOS legs run at release time and the shell test skips here with a
printed reason.

MiniMax wrote it under a brief; I added the build-and-read step and the
exec bits on the two shell files.

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>

---------

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
…olded under it (#1684)

Closes #1681.

## What changes

`podup build`, and the build `up` runs when a service's image is
missing, draw one row per image on the board every other verb command
already uses. The row reads `Building`, then `Building n/m` as each
`STEP n/m` line arrives, then `Built` or `Failed` with the elapsed time.

On a terminal the buildah stream is folded under the row: the last four
lines sit dimmed below it while it builds and vanish when the row
finishes. A failed build replays its whole stream once, above the error,
so the reason is on screen. In a pipe every stream line goes to stderr
prefixed with the image tag, the way `logs` prefixes container output,
and the verb lines are written in plain form with no escape sequence.

The image id goes to stdout only when stdout is not a terminal, so
`podup build | awk '{print $1}'` still reads it; on a terminal the row
is the record. Buildah emits the id as a bare 64-hex line after
`Successfully tagged` (measured on Podman 5.7 today); the reader matches
that line and falls back to asking libpod for the tag's id.

`up --build` runs the build board first and the `up` board after it,
because `up` resolves images concurrently and a forced build inside that
stage would build services in parallel, which `build` does not do.
`docs/commands.md` says so.

## Measured under a pseudo-terminal (`script -qfc`)

Fresh build, deduplicated frames:

```
[+] Running 0/1
 ⠙ Image     localhost/ux-talker:1 Building 1/3   0.0s
 STEP 1/3: FROM docker.io/library/alpine:3.20
 ⠸ Image     localhost/ux-talker:1 Building 2/3   0.2s
 --> 8e66f4345325
 ⠼ Image     localhost/ux-talker:1 Building 3/3   0.3s
 COMMIT localhost/ux-talker:1
 ✔ Image     localhost/ux-talker:1 Built   0.3s
```

Failed build:

```
 ✘ Image     localhost/ux-bad:1 Failed   0.4s
localhost/ux-bad:1 | STEP 1/3: FROM docker.io/library/alpine:3.20
localhost/ux-bad:1 | STEP 2/3: RUN echo one
localhost/ux-bad:1 | one
localhost/ux-bad:1 | --> a8f9c8f333e8
localhost/ux-bad:1 | STEP 3/3: RUN false
podup: error: build error: building at STEP "RUN false": while running runtime: exit status 1
```

Piped build: stdout is the single line `3ec8da61bf44…882b`; stderr
carries ` Image localhost/ux-talker:1 Building`, the prefixed stream,
and ` Image localhost/ux-talker:1 Built`, with zero escape sequences.
`up -d --build` shows the build board, then the network and container
board. A cached rebuild reads the same rows in 0.1s.

## Tests

- `build_draws_a_row_per_image_and_counts_steps`,
`a_failed_build_prints_its_stream_once_before_the_error`,
`build_stream_progress_parses_a_three_step_dockerfile`,
`parse_image_id_line_only_matches_the_bare_full_id` (unit, against the
fake Podman).
- `up_with_a_missing_image_builds_on_the_same_board` (unit).
- `a_piped_build_prefixes_every_stream_line_with_the_service` and
`build_writes_the_image_id_to_stdout` (contract, against real Podman).
- `notes_keep_the_last_four_lines_per_row` (unit).

The three plain-sink tests in the progress module share one process-wide
buffer and failed 3 runs in 20 under the parallel harness; they now hold
a lock, 0 in 20 after. Suite, clippy and fmt clean locally.

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>

---------

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
Bump for the minor release carrying the live board on every verb
command,
the build and pull rows, and the hardening gates for the Windows and
macOS
binaries with Control Flow Guard newly enabled. Nothing existing changes
what it does; the output is what changed.

## Verified

The three version comparisons of release.yml run locally: crate=5.9.0
lock=5.9.0 deb=5.9.0, GATE OK; cargo metadata --locked accepts the
lockfile.

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
… and a missing named compose file fails ps and events (#1689)

Closes #1686, #1687 and #1688, the three findings of the pseudo-terminal
pass over develop at 91d7260 (every command, in a terminal, in a pipe,
with NO_COLOR and at 30 columns).

## rm and scale-down (#1686)

Both called the final line without opening the row, so `Removed` had no
elapsed time and the scale-down stop said nothing for its whole grace.
Measured after:

```
 ⠙ Container tfix-talker-2 Stopping  10.0s
 ⠙ Container tfix-talker-2 Removing  10.1s
 ✔ Container tfix-talker-2 Removed  10.1s
```

Every arm now closes the row (`Absent` on 404, `Skipped` on a running
container without `-f`, `Failed` otherwise), so the plain sink never
flushes a row left at `Removing`. Tests:
`stop_and_remove_opens_the_row_before_the_stop_and_closes_it_removed`
pins the verb sequence against the fake Podman; the rm board test now
asserts `Removing` precedes `Removed` for each container.

## rm --stop (#1688)

It ran the whole `unpause` command first, which drew a board over every
container, closed each unpaused one as `Skipped` and noted `no
containers to unpause`. `unpause_paused` takes one project listing,
keeps the paused containers, and draws nothing when there are none.
Measured after, nothing paused: the board opens at `Stopped`; one paused
container: `Unpausing`, `Unpaused`, then the stop and the removal, for
that container only. Test:
`unpause_paused_touches_only_the_paused_containers` covers both cases,
including that no `/unpause` request is sent when nothing is paused.

## ps and events with a missing named file (#1687)

`podup -f /nonexistent.yml ps` printed `no containers` and exited 0. A
file named with `-f` or `COMPOSE_FILE` that does not exist now fails
with `compose file not found: /nonexistent.yml`, exit 1, for both
label-only commands; a directory with no compose file and no `-f` still
answers by label (measured: `no containers`, exit 0, from `/tmp`). Test:
`a_named_compose_file_that_is_missing_fails_ps_and_events`.

Suite, clippy and fmt clean locally.

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>

---------

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
…xes (#1690)

#1689 landed after the bump (#1685), so the 5.9.0 changelog entry the
.deb ships did not name its three fixes. One bullet added; nothing else
changes.

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>

Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
@Jaro-c Jaro-c added the type:chore Maintenance with no product impact label Sep 4, 2026
@Jaro-c
Jaro-c merged commit cee5d8c into main Sep 4, 2026
73 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type:chore Maintenance with no product impact

Development

Successfully merging this pull request may close these issues.

1 participant