mirror of
https://github.com/openglow-org/forgefirm.git
synced 2026-10-19 02:51:42 -07:00
pack-licenses.sh packs the image's license tree into one tar.gz and removes the tree. It piped tar into gzip under /bin/sh (dash, no pipefail): the pipeline's status is gzip's, and tar's is lost. The check that follows proves only that the bundle holds license.manifest. So a member tar cannot read leaves a bundle without that text, the build green, and the tree removed, the only copy of the text with it. tar now runs the compressor itself (-I 'gzip -9 -n'), so its exit status covers the read and the write. A failed pack removes the partial bundle and the script refuses, with the tree left for the build log; pack_licenses in forgefirm-image.bb fails do_rootfs on the refusal. The bundle's bytes do not change: tar feeds the same stream to the same gzip, and a tree packed by both forms gives one sha256. Proof: forgetest-ci "Unit tests" (`python -B -m unittest discover -s tests -v` in forgetest), on Ubuntu 24.04 with python 3.12 as an unprivileged user: test_image.py's PackLicensesTests gain test_a_license_text_tar_cannot_read_is_refused_and_the_tree_left (a text under zlib/ with mode 0: a nonzero exit, a message on stderr, no bundle, the manifest left); the class's 5 tests OK. Negative control on a scratch copy: the new case against the script without this change fails at assertRefused with an exit status of 0. At the shell, on a fixture tree with the unreadable member, the script without this change exits 0, leaves the bundle without the text and removes the tree; with it, the script exits 1, leaves no bundle and leaves the tree. On a readable tree both forms write byte-identical bundles (one sha256 for both). pyflakes reports no undefined name. Acceptance: every catalog test. pack-licenses.sh is under meta-forgefirm, whose content hash is part of the platform in every fingerprint, so the image that carries this change needs the whole catalog on the bench.
43 lines
1.8 KiB
Bash
Executable File
43 lines
1.8 KiB
Bash
Executable File
#!/bin/sh
|
|
# Copyright 2026 514 LLC d/b/a OpenGlow
|
|
# Written by Scott Wiederhold
|
|
# https://community.openglow.org
|
|
# SPDX-License-Identifier: MIT
|
|
#
|
|
# pack-licenses.sh <common-licenses directory> <tar.gz>
|
|
#
|
|
# Packs the image's license tree into one reproducible tar.gz (sorted
|
|
# names, no timestamps, no owners), its members under the tree's own
|
|
# name, and removes the tree. It refuses, before it writes the tar or
|
|
# removes anything, a tree that is missing, a tree with no
|
|
# license.manifest, and a manifest that does not name the project's own
|
|
# recipes: forgectrl, grblhal-glowforge and kernel-module-glowforge.
|
|
# forgefirm-image.bb's pack_licenses runs it; forgetest's test_image.py
|
|
# runs it on a fixture.
|
|
|
|
dir=${1:?common-licenses directory}
|
|
out=${2:?output tar.gz}
|
|
fail() { echo "pack-licenses: $*" >&2; exit 1; }
|
|
|
|
[ -d "$dir" ] || fail "$dir is missing (COPY_LIC_DIRS off?)"
|
|
manifest=$dir/license.manifest
|
|
[ -f "$manifest" ] || fail "no license.manifest in $dir"
|
|
# A line counts with its newline: an unterminated last line is not read.
|
|
lines=$(wc -l < "$manifest")
|
|
for name in forgectrl grblhal-glowforge kernel-module-glowforge; do
|
|
head -n "$lines" "$manifest" | grep -Fxq "RECIPE NAME: $name" \
|
|
|| fail "$manifest does not name the recipe $name"
|
|
done
|
|
|
|
base=$(basename "$dir")
|
|
mkdir -p "$(dirname "$out")" || fail "cannot make the directory of $out"
|
|
# tar runs the compressor itself, so its exit status covers both: a
|
|
# member it cannot read fails the pack, and a failed pack leaves no bundle.
|
|
tar -C "$(dirname "$dir")" --sort=name --mtime=@0 --owner=0 --group=0 \
|
|
--numeric-owner -I 'gzip -9 -n' -cf "$out" "$base" \
|
|
|| { rm -f "$out"; fail "cannot pack $dir into $out"; }
|
|
# the tree goes only once the bundle holds its manifest
|
|
tar -tzf "$out" "$base/license.manifest" > /dev/null 2>&1 \
|
|
|| fail "$out holds no $base/license.manifest"
|
|
rm -rf "$dir"
|