diff --git a/bootc-build/setup-runner/action.yml b/bootc-build/setup-runner/action.yml index ada92cf5..e7c19988 100644 --- a/bootc-build/setup-runner/action.yml +++ b/bootc-build/setup-runner/action.yml @@ -7,7 +7,7 @@ inputs: description: "Storage backend: 'btrfs' (remove-software + BTRFS loopback for /var/lib/containers) or 'remove-software' (remove-software only)" default: "btrfs" update-podman: - description: "Install podman/buildah/crun/skopeo from Ubuntu resolute (25.04)" + description: "Upgrade podman/buildah/crun/skopeo from Ubuntu resolute (26.04) on runner images that ship an older stack; no-op on ubuntu-26.04 runners" default: "true" native-overlay: description: "Use native rootful overlay storage instead of fuse-overlayfs; resets rootful Podman storage" @@ -52,31 +52,51 @@ runs: mount-opts: compress-force=zstd:2 loopback-free: "1" - - name: Add Ubuntu resolute apt source + - name: Select podman source if: inputs.update-podman == 'true' + id: podman-source shell: bash run: | - set -eux - # TODO: remove when Ubuntu 26.04 runners ship a new-enough podman - # Old podman (Ubuntu 24.04) does not push layer annotations (ostree.components) - # needed by the rpm-ostree rechunker and does not support zstd:chunked push. - IDV=$(. /usr/lib/os-release && echo ${ID}-${VERSION_ID}) - test "${IDV}" = "ubuntu-24.04" - if [ "$(dpkg --print-architecture)" = "amd64" ]; then - mirror="http://azure.archive.ubuntu.com/ubuntu" - else - mirror="http://ports.ubuntu.com/ubuntu-ports" - fi - echo "deb ${mirror} resolute universe main" | sudo tee /etc/apt/sources.list.d/resolute.list + set -euo pipefail + IDV=$(. /usr/lib/os-release && echo "${ID}-${VERSION_ID}") + case "${IDV}" in + ubuntu-24.04) + # Ubuntu 24.04's podman (4.9.x) does not push layer annotations + # (ostree.components) needed by the rpm-ostree rechunker and does not + # support zstd:chunked push. The resolute (26.04) packages install on + # noble, so pull the whole stack from there. + if [ "$(dpkg --print-architecture)" = "amd64" ]; then + mirror="http://azure.archive.ubuntu.com/ubuntu" + else + mirror="http://ports.ubuntu.com/ubuntu-ports" + fi + echo "deb ${mirror} resolute universe main" | sudo tee /etc/apt/sources.list.d/resolute.list + echo "install-from-resolute=true" >> "$GITHUB_OUTPUT" + ;; + ubuntu-26.04) + # resolute *is* 26.04: verify the runner's podman is 5.x or newer + PODMAN_VERSION=$(podman --version 2>/dev/null | awk '{print $3}' || echo "unknown") + if [[ ! "${PODMAN_VERSION}" =~ ^([0-9]+)\. ]] || (( BASH_REMATCH[1] < 5 )); then + echo "::error::ubuntu-26.04 runner image has podman '${PODMAN_VERSION}', but version 5.x or newer is required for layer annotations and zstd:chunked push." + exit 1 + fi + echo "::notice::${IDV} ships podman ${PODMAN_VERSION}; skipping the resolute apt source." + echo "install-from-resolute=false" >> "$GITHUB_OUTPUT" + ;; + *) + echo "::error::setup-runner supports ubuntu-24.04 and ubuntu-26.04 runner images; found '${IDV}'. Set update-podman: 'false' to skip the podman upgrade." + exit 1 + ;; + esac - name: Compute apt cache key (weekly rotation) - if: inputs.update-podman == 'true' + if: steps.podman-source.outputs.install-from-resolute == 'true' id: apt-cache-key shell: bash run: echo "week=$(date +%Y-W%V)" >> "$GITHUB_OUTPUT" - name: Restore apt package cache - if: inputs.update-podman == 'true' + if: steps.podman-source.outputs.install-from-resolute == 'true' id: apt-cache-restore uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: @@ -86,7 +106,7 @@ runs: apt-resolute-podman-${{ runner.os }}-${{ runner.arch }}- - name: Install podman stack from Ubuntu resolute - if: inputs.update-podman == 'true' + if: steps.podman-source.outputs.install-from-resolute == 'true' shell: bash run: | set -eux @@ -103,7 +123,7 @@ runs: sudo rm -f /etc/needrestart/conf.d/99-no-restarts.conf - name: Save apt package cache - if: inputs.update-podman == 'true' && steps.apt-cache-restore.outputs.cache-hit != 'true' + if: steps.podman-source.outputs.install-from-resolute == 'true' && steps.apt-cache-restore.outputs.cache-hit != 'true' uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: /var/cache/apt/archives diff --git a/docs/skills/composite-actions.md b/docs/skills/composite-actions.md index c0f8f6b1..5df5033d 100644 --- a/docs/skills/composite-actions.md +++ b/docs/skills/composite-actions.md @@ -457,7 +457,7 @@ jobs: Always add `workflow_dispatch` alongside `workflow_run` so the workflow can be triggered manually without waiting for the upstream workflow to run. | `chmod 777` before cache save | `dnf-cache` | [actions/cache#1533](https://github.com/actions/cache/issues/1533) - root-owned files break cache agent | | `chown ~/.sigstore` before cosign | `sign-and-publish` | Runner sigstore cache created with wrong ownership | -| podman upgraded from Ubuntu resolute | `setup-runner` | Ubuntu 24.04 podman too old for `ostree.components` annotations + `zstd:chunked` push | +| podman upgraded from Ubuntu resolute | `setup-runner` | Ubuntu 24.04 podman too old for `ostree.components` annotations + `zstd:chunked` push (skipped on 26.04 runners after verifying Podman >= 5) | | `-v $(pwd):/run/src` + `--security-opt=label=disable` | `chunka` | buildah < v1.44 drops bind-mounts without these; needed for the OCI output dir (`out/`) to survive to the final stage | | `sudo rm -rf out` | `chunka` | Containerfile.splitter leaves `out/` dir in CWD (v0.6.0+; was `out.ociarchive` in v0.5.0); stale dir breaks re-runs | | `sudo podman save \| podman load` | `chunka` | buildah (root) and podman (user) use separate container stores | diff --git a/docs/skills/composite-actions/action-reference.md b/docs/skills/composite-actions/action-reference.md index 569f1a08..2dbc8633 100644 --- a/docs/skills/composite-actions/action-reference.md +++ b/docs/skills/composite-actions/action-reference.md @@ -36,7 +36,7 @@ Sets up a GitHub Actions runner for bootc image building. Two storage backends: - `btrfs` (default): mounts a BTRFS volume at `/var/lib/containers` via `ublue-os/container-storage-action` - `remove-software`: frees disk by nuking Android/Haskell/dotnet toolchains -Upgrades podman from Ubuntu **resolute** (25.04) because older Ubuntu 24.04 runner images ship a version too old to support layer annotations (`ostree.components`) and `zstd:chunked` push. +Upgrades podman from Ubuntu **resolute** (26.04) on `ubuntu-24.04` runner images because stock noble ships Podman 4.9.x, which is too old to support layer annotations (`ostree.components`) and `zstd:chunked` push. On `ubuntu-26.04` runner images, setup-runner verifies the runner already ships Podman 5.x or newer and skips the resolute apt source. Any other runner image fails fast with an actionable error. Installs optional tools (`just`, `cosign`, `oras`, `syft`) via `install-tools` JSON array input. @@ -65,11 +65,12 @@ Native-overlay mode is opt-in and destructive to existing **rootful** Podman sta Combine it with `update-podman: "true"` when the consumer requires Podman 5 on every hosted runner. Runner images that predate the static Podman bundle ship apt Podman 4.9.3, which fails -the version gate unless Resolute packages are installed. Images that include the bundle keep -`/usr/local/bin/podman` ahead of the Resolute packages on `PATH` (including sudo's -`secure_path`); normalizing that mixed stack is a separate concern from this mode. Use +the version gate unless Resolute packages are installed. On `ubuntu-26.04` runners, `update-podman: "true"` +verifies the runner's Podman is already version 5 or newer and skips the Resolute apt source as a no-op. +Images that include the bundle keep `/usr/local/bin/podman` ahead of the Resolute packages on `PATH` +(including sudo's `secure_path`); normalizing that mixed stack is a separate concern from this mode. Use `update-podman: "false"` only when the caller can rely on the runner-provided Podman already -being version 5 or newer. +being version 5 or newer on any runner image. Do not merely delete `mount_program` from the runner configuration: the FUSE-only `fsync=0` mount option and containers/storage's persistent mount-program marker must be removed too. diff --git a/docs/skills/testing.md b/docs/skills/testing.md index 43a70c43..2cb7dc16 100644 --- a/docs/skills/testing.md +++ b/docs/skills/testing.md @@ -47,7 +47,7 @@ Coverage gate: `--cov-fail-under=75` | `detect-changes` image_flavors shell logic | `tests/bats/test_detect_changes.bats` (8 tests) | | `push-image` push/retry/alias shell logic | `tests/bats/test_push_image.bats` (16 tests) | | `sign-and-publish` keyless/key validation + SBOM attach/cache/path guards | `tests/bats/test_sign_and_publish.bats` (19 tests) | -| `setup-runner` native-overlay setup | `tests/bats/test_setup_runner.bats` (9 tests) | +| `setup-runner` native-overlay setup & podman source selection | `tests/bats/test_setup_runner.bats` (16 tests) | | `chunka` config temp-file creation + `fs.protected_regular` drift guard | `tests/bats/test_chunka.bats` (4 tests) | | `reusable-renovate-automerge.yml` check-rollup classification | `tests/bats/test_renovate_automerge_checks.bats` (8 tests) | | `reusable-renovate-automerge.yml` PR-lookup / qualification matcher | `tests/bats/test_renovate_automerge_find_pr.bats` (10 tests) | diff --git a/tests/bats/test_setup_runner.bats b/tests/bats/test_setup_runner.bats index 71b1eb45..44f1966b 100644 --- a/tests/bats/test_setup_runner.bats +++ b/tests/bats/test_setup_runner.bats @@ -1,5 +1,6 @@ #!/usr/bin/env bats -# Tests for bootc-build/setup-runner native-overlay validation and storage setup. +# Tests for bootc-build/setup-runner native-overlay validation, storage setup, +# and podman source selection. # # The shell logic lives inline in bootc-build/setup-runner/action.yml. Keep these # snippets verbatim so action changes must update their regression tests. @@ -15,6 +16,44 @@ case "${NATIVE_OVERLAY}" in esac EOF ) +# Verbatim from bootc-build/setup-runner/action.yml ("Select podman source" step) +# Note: only the os-release source file is substituted via MOCK_OS_RELEASE. +PODMAN_SOURCE_LOGIC=$(cat <<'EOF' +set -euo pipefail +IDV=$(. "${MOCK_OS_RELEASE:-/usr/lib/os-release}" && echo "${ID}-${VERSION_ID}") +case "${IDV}" in + ubuntu-24.04) + # Ubuntu 24.04's podman (4.9.x) does not push layer annotations + # (ostree.components) needed by the rpm-ostree rechunker and does not + # support zstd:chunked push. The resolute (26.04) packages install on + # noble, so pull the whole stack from there. + if [ "$(dpkg --print-architecture)" = "amd64" ]; then + mirror="http://azure.archive.ubuntu.com/ubuntu" + else + mirror="http://ports.ubuntu.com/ubuntu-ports" + fi + echo "deb ${mirror} resolute universe main" | sudo tee /etc/apt/sources.list.d/resolute.list + echo "install-from-resolute=true" >> "$GITHUB_OUTPUT" + ;; + ubuntu-26.04) + # resolute *is* 26.04: verify the runner's podman is 5.x or newer + # before skipping the apt source. + PODMAN_VERSION=$(podman --version 2>/dev/null | awk '{print $3}' || echo "unknown") + if [[ ! "${PODMAN_VERSION}" =~ ^([0-9]+)\. ]] || (( BASH_REMATCH[1] < 5 )); then + echo "::error::ubuntu-26.04 runner image has podman '${PODMAN_VERSION}', but version 5.x or newer is required for layer annotations and zstd:chunked push." + exit 1 + fi + echo "::notice::${IDV} ships podman ${PODMAN_VERSION}; skipping the resolute apt source." + echo "install-from-resolute=false" >> "$GITHUB_OUTPUT" + ;; + *) + echo "::error::setup-runner supports ubuntu-24.04 and ubuntu-26.04 runner images; found '${IDV}'. Set update-podman: 'false' to skip the podman upgrade." + exit 1 + ;; +esac +EOF +) + NATIVE_OVERLAY_LOGIC=$(cat <<'NATIVE_LOGIC_EOF' set -euo pipefail @@ -95,6 +134,8 @@ setup() { mkdir -p "${MOCK_DIR}" touch "${CALL_LOG}" export PATH="${MOCK_DIR}:${PATH}" + export GITHUB_OUTPUT="${TEST_TMP}/github_output" + touch "${GITHUB_OUTPUT}" cat > "${MOCK_DIR}/sudo" <<'EOF' #!/usr/bin/env bash @@ -114,7 +155,12 @@ case "${1:-}" in exit 0 ;; tee) - cat > "${MOCK_STORAGE_CONF}" + shift + if [[ "${1:-}" == "/etc/apt/sources.list.d/resolute.list" ]]; then + cat > "${MOCK_RESOLUTE_LIST}" + else + cat > "${MOCK_STORAGE_CONF}" + fi ;; *) exec "$@" @@ -122,6 +168,17 @@ case "${1:-}" in esac EOF chmod +x "${MOCK_DIR}/sudo" + cat > "${MOCK_DIR}/dpkg" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail +if [[ "$*" == "--print-architecture" ]]; then + echo "${MOCK_DPKG_ARCH:-amd64}" + exit 0 +fi +exit 1 +EOF + chmod +x "${MOCK_DIR}/dpkg" + cat > "${MOCK_DIR}/podman" <<'EOF' #!/usr/bin/env bash @@ -148,6 +205,7 @@ EOF # kernels enable the overlay module's redirect_dir); make the default # mock match that observation. export MOCK_PODMAN_INFO_JSON='{"store":{"graphDriverName":"overlay","graphStatus":{"Native Overlay Diff":"false"},"graphOptions":{}}}' + export MOCK_RESOLUTE_LIST="${TEST_TMP}/resolute.list" } teardown() { @@ -239,3 +297,102 @@ teardown() { [ "${status}" -ne 0 ] [[ "${output}" == *"still reports a mount_program"* ]] } + +@test "podman source selection configures resolute apt repo on ubuntu-24.04 amd64" { + export MOCK_OS_RELEASE="${TEST_TMP}/os-release" + cat <<'EOF' > "${MOCK_OS_RELEASE}" +ID=ubuntu +VERSION_ID=24.04 +EOF + export MOCK_DPKG_ARCH="amd64" + + run bash -c "${PODMAN_SOURCE_LOGIC}" + + [ "${status}" -eq 0 ] + grep -q "^install-from-resolute=true$" "${GITHUB_OUTPUT}" + grep -q "deb http://azure.archive.ubuntu.com/ubuntu resolute universe main" "${MOCK_RESOLUTE_LIST}" +} + +@test "podman source selection configures resolute ports repo on ubuntu-24.04 arm64" { + export MOCK_OS_RELEASE="${TEST_TMP}/os-release" + cat <<'EOF' > "${MOCK_OS_RELEASE}" +ID=ubuntu +VERSION_ID=24.04 +EOF + export MOCK_DPKG_ARCH="arm64" + + run bash -c "${PODMAN_SOURCE_LOGIC}" + + [ "${status}" -eq 0 ] + grep -q "^install-from-resolute=true$" "${GITHUB_OUTPUT}" + grep -q "deb http://ports.ubuntu.com/ubuntu-ports resolute universe main" "${MOCK_RESOLUTE_LIST}" +} + +@test "podman source selection skips apt repo on ubuntu-26.04 when podman >= 5" { + export MOCK_OS_RELEASE="${TEST_TMP}/os-release" + cat <<'EOF' > "${MOCK_OS_RELEASE}" +ID=ubuntu +VERSION_ID=26.04 +EOF + export MOCK_PODMAN_VERSION="5.7.0" + + run bash -c "${PODMAN_SOURCE_LOGIC}" + + [ "${status}" -eq 0 ] + grep -q "^install-from-resolute=false$" "${GITHUB_OUTPUT}" + [[ "${output}" == *"ships podman 5.7.0; skipping the resolute apt source"* ]] + [ ! -f "${MOCK_RESOLUTE_LIST}" ] +} + +@test "podman source selection rejects ubuntu-26.04 if podman is older than 5" { + export MOCK_OS_RELEASE="${TEST_TMP}/os-release" + cat <<'EOF' > "${MOCK_OS_RELEASE}" +ID=ubuntu +VERSION_ID=26.04 +EOF + export MOCK_PODMAN_VERSION="4.9.3" + + run bash -c "${PODMAN_SOURCE_LOGIC}" + + [ "${status}" -ne 0 ] + [[ "${output}" == *"ubuntu-26.04 runner image has podman '4.9.3', but version 5.x or newer is required"* ]] + [ ! -f "${MOCK_RESOLUTE_LIST}" ] +} + +@test "podman source selection fails fast on unsupported runner image" { + export MOCK_OS_RELEASE="${TEST_TMP}/os-release" + cat <<'EOF' > "${MOCK_OS_RELEASE}" +ID=ubuntu +VERSION_ID=22.04 +EOF + + run bash -c "${PODMAN_SOURCE_LOGIC}" + + [ "${status}" -ne 0 ] + [[ "${output}" == *"supports ubuntu-24.04 and ubuntu-26.04 runner images; found 'ubuntu-22.04'"* ]] +} + +@test "podman source selection fails fast on non-ubuntu runner image" { + export MOCK_OS_RELEASE="${TEST_TMP}/os-release" + cat <<'EOF' > "${MOCK_OS_RELEASE}" +ID=fedora +VERSION_ID=42 +EOF + + run bash -c "${PODMAN_SOURCE_LOGIC}" + + [ "${status}" -ne 0 ] + [[ "${output}" == *"supports ubuntu-24.04 and ubuntu-26.04 runner images; found 'fedora-42'"* ]] +} + +@test "setup-runner action.yml contains the verbatim podman source selection logic" { + ACTION_FILE="${BATS_TEST_DIRNAME}/../../bootc-build/setup-runner/action.yml" + # Ensure the action.yml contains the expected branch structures and outputs + grep -q 'case "${IDV}" in' "${ACTION_FILE}" + grep -q 'ubuntu-24.04)' "${ACTION_FILE}" + grep -q 'ubuntu-26.04)' "${ACTION_FILE}" + grep -q 'echo "deb \${mirror} resolute universe main" | sudo tee /etc/apt/sources.list.d/resolute.list' "${ACTION_FILE}" + grep -q 'install-from-resolute=true' "${ACTION_FILE}" + grep -q 'install-from-resolute=false' "${ACTION_FILE}" + grep -q "supports ubuntu-24.04 and ubuntu-26.04 runner images" "${ACTION_FILE}" +}