Have the package build name its output instead of searching for it

build-deb-container.sh chose the package to return by listing the output
directory for fips_*_*.deb files modified in the last ten minutes and taking
the one that sorted last by name. Any package already in that directory that
sorted higher, such as one with a larger version left by an earlier run, was
returned instead of the package just built, and every caller installed,
tested or published that stale file.

build-deb.sh now takes --name-file and writes the basename of the package it
produced there. The container build passes a fresh per-run directory for that
file, reads the name back, and fails if the build did not name a package, if
the name is not a package file name, or if the named file does not exist. The
last line of stdout is still the package path, so callers are unchanged.

The deb-install harness had the same defect in its own fallback build: it
listed its cache directory after building and took the first file. It now
clears the cache before building, takes the path from the build's last line of
stdout, and every scenario installs that path rather than re-listing the
directory.
This commit is contained in:
Johnathan Corgan
2026-09-19 02:52:06 +00:00
parent af65b72f5b
commit 9c7c6a49f7
3 changed files with 64 additions and 14 deletions
+28 -4
View File
@@ -101,12 +101,22 @@ if [ -n "$FEATURES" ]; then
# from the default build of the same commit. It refuses --features with
# --no-build for that reason, so the two cases cannot share one command.
# The version still comes from the host, because the image has no git.
BUILD_CMD="packaging/debian/build-deb.sh --features '$FEATURES' --version '$VERSION' --output-dir /out"
BUILD_CMD="packaging/debian/build-deb.sh --features '$FEATURES' --version '$VERSION' --output-dir /out --name-file /name/deb"
else
BUILD_CMD="cargo build --release --locked
packaging/debian/build-deb.sh --no-build --version '$VERSION' --output-dir /out"
packaging/debian/build-deb.sh --no-build --version '$VERSION' --output-dir /out --name-file /name/deb"
fi
# The build names the package it produced rather than this script picking one
# out of the output directory. The output directory is the caller's and may
# already hold packages from earlier runs; a search there by name or by age
# could return one of those, and a package that sorts higher by name was
# returned in preference to the one just built. The name travels through a
# directory of its own, created fresh for this run, so a name left by an
# earlier run cannot be read and nothing extra is left in the output directory.
NAME_DIR=$(mktemp -d)
trap 'rm -rf "$NAME_DIR"' EXIT
# The source is mounted read-only so a build cannot leave artifacts in the tree.
# CARGO_TARGET_DIR and the registry live in named volumes, which is what makes a
# second run fast; they are per-base-image so a floor change does not reuse
@@ -115,6 +125,7 @@ VOL_SUFFIX="${FIPS_BUILD_IMAGE//[:\/]/-}"
docker run --rm \
-v "$REPO_ROOT":/src:ro \
-v "$DEST_ABS":/out \
-v "$NAME_DIR":/name \
-v "fips-deb-target-${VOL_SUFFIX}":/target \
-v "fips-deb-registry-${VOL_SUFFIX}":/usr/local/cargo/registry \
-e CARGO_TARGET_DIR=/target \
@@ -123,8 +134,21 @@ docker run --rm \
"$IMAGE_TAG" \
bash -euo pipefail -c "$BUILD_CMD" >&2
DEB=$(find "$DEST_ABS" -maxdepth 1 -name "fips_*_*.deb" -newermt '-10 minutes' -print | sort | tail -1)
[ -n "$DEB" ] || { echo "build-deb-container: no .deb was produced." >&2; exit 1; }
DEB_NAME=""
[ -f "$NAME_DIR/deb" ] && DEB_NAME=$(head -n 1 "$NAME_DIR/deb")
[ -n "$DEB_NAME" ] || {
echo "build-deb-container: the build did not name its package" >&2
exit 1
}
if [[ "$DEB_NAME" == */* || "$DEB_NAME" != fips_*_*.deb ]]; then
echo "build-deb-container: the build named '$DEB_NAME', which is not a package file name" >&2
exit 1
fi
DEB="$DEST_ABS/$DEB_NAME"
[ -f "$DEB" ] || {
echo "build-deb-container: the build named $DEB_NAME but $DEB does not exist" >&2
exit 1
}
# Check the artifact here rather than in one workflow, so every producer is
# gated: the release, the CI job, a local run and packaging/Makefile all reach
+14 -1
View File
@@ -2,7 +2,8 @@
# Build a .deb package for FIPS using cargo-deb.
#
# Usage: ./build-deb.sh [--target <triple>] [--version <version>] [--no-build]
# [--features <list>]
# [--features <list>] [--output-dir <dir>]
# [--name-file <path>]
#
# Prerequisites: cargo-deb (install with: cargo install cargo-deb)
# Output: deploy/fips_<version>_<arch>.deb
@@ -26,6 +27,10 @@ Options:
--output-dir <dir> Where to put the finished .deb. Defaults to deploy/ under
the project root. Exists so the container build can write
to a mount and leave the source tree read-only.
--name-file <path> Also write the finished package's file name (basename
only) to <path>. The container build reads it so it
never has to guess which .deb in the output directory
this run produced.
-h, --help Show this help
EOF
}
@@ -35,6 +40,7 @@ VERSION_OVERRIDE=""
NO_BUILD=0
FEATURES=""
DEST_DIR=""
NAME_FILE=""
while [[ $# -gt 0 ]]; do
case "$1" in
@@ -58,6 +64,10 @@ while [[ $# -gt 0 ]]; do
DEST_DIR="${2:?missing value for --output-dir}"
shift 2
;;
--name-file)
NAME_FILE="${2:?missing value for --name-file}"
shift 2
;;
-h|--help)
usage
exit 0
@@ -182,6 +192,9 @@ fi
cp "${DEB_FILE}" "${DEST_DIR}/"
BASENAME=$(basename "${DEB_FILE}")
if [[ -n "${NAME_FILE}" ]]; then
printf '%s\n' "${BASENAME}" > "${NAME_FILE}"
fi
echo "Package built: ${DEST_DIR}/${BASENAME}"
echo ""
echo "Install with: sudo dpkg -i ${DEST_DIR}/${BASENAME}"
+22 -9
View File
@@ -58,6 +58,10 @@ SKIP=0
# however many scenarios run in one process.
SUPPLIED_DEB=""
DEB_PREPARED=0
# The package the scenarios install, set by build_deb() on every path that
# succeeds. Scenarios use it rather than listing the cache directory, so which
# file they install never depends on what else happens to be in there.
DEB_PATH=""
# ─────────────────────────────────────────────────────────────────────
# Helpers
@@ -202,6 +206,7 @@ build_deb() {
fi
rm -f "$DEB_CACHE_DIR"/*.deb
cp "$SUPPLIED_DEB" "$DEB_CACHE_DIR/"
DEB_PATH="$DEB_CACHE_DIR/$(basename "$SUPPLIED_DEB")"
DEB_PREPARED=1
log "Installing the supplied package $(basename "$SUPPLIED_DEB")"
return 0
@@ -218,6 +223,7 @@ build_deb() {
local cached_age
cached_age=$(stat -c '%Y' "$cached_deb" 2>/dev/null || echo 0)
if awk "BEGIN { exit !($cached_age >= $newest_src) }"; then
DEB_PATH="$cached_deb"
log "Using cached .deb at $cached_deb"
return 0
fi
@@ -233,20 +239,28 @@ build_deb() {
# not exhibit a defect that only the release environment produced. It stayed
# green through five releases that could not start on two of the five
# distributions in its own matrix.
#
# The cache holds one package at a time. Clearing it first is what keeps the
# reuse check above honest, since that check looks at whichever package it
# finds; and the package installed is the one the build names on the last
# line of its stdout, never one found by listing the directory.
log "Building the .deb in the pinned build container (slow on first run)"
if ! bash "$REPO_ROOT/packaging/debian/build-deb-container.sh" \
--output-dir "$DEB_CACHE_DIR" >&2; then
rm -f "$DEB_CACHE_DIR"/*.deb
local build_out
if ! build_out=$(bash "$REPO_ROOT/packaging/debian/build-deb-container.sh" \
--output-dir "$DEB_CACHE_DIR"); then
echo " ERROR: container build failed" >&2
return 1
fi
cached_deb=$(ls "$DEB_CACHE_DIR"/fips_*_amd64.deb 2>/dev/null | head -1)
if [ -n "$cached_deb" ]; then
log "Cached at $cached_deb ($(stat -c %s "$cached_deb") bytes)"
else
echo " ERROR: no .deb produced by the container build" >&2
cached_deb=$(printf '%s\n' "$build_out" | tail -n 1)
if [ -z "$cached_deb" ] || [ ! -f "$cached_deb" ]; then
echo " ERROR: the container build did not report a package path: '$cached_deb'" >&2
return 1
fi
DEB_PATH="$cached_deb"
log "Cached at $cached_deb ($(stat -c %s "$cached_deb") bytes)"
return 0
}
# ─────────────────────────────────────────────────────────────────────
@@ -267,8 +281,7 @@ _run_deb_install_scenario() {
build_deb || { fail ".deb build failed"; return; }
local cached_deb
cached_deb=$(ls "$DEB_CACHE_DIR"/fips_*_amd64.deb 2>/dev/null | head -1)
local cached_deb="$DEB_PATH"
if [ -z "$cached_deb" ] || [ ! -f "$cached_deb" ]; then
fail "no .deb available at $DEB_CACHE_DIR"
return