Splux Linux

source-based Linux using SPS

SPS · log · files · refs · tags · releases · people · languages · GitHub · commit

d2c1429


sget: rebuild when a cached package's definition digest changes.

Date
Commitd2c142988abf0474719e8ef1662d2580bac72ae6
GitHubmirror

Parents

Stat

 DESIGN.md                  |  5 +++++
 bin/mkpkg                  |  9 ++++++---
 bin/sget                   | 43 ++++++++++++++++++++++++++++++++++++-------
 lib/package.awk            |  8 ++++++--
 tests/test_build.sh        |  5 ++++-
 tests/test_dependencies.sh |  7 +++++--
 tests/test_highlevel.sh    |  9 +++++++++
 7 files changed, 71 insertions(+), 15 deletions(-)

Diff

diff --git a/DESIGN.md b/DESIGN.md
index f9e92b5..4ad8cbb 100644
--- a/DESIGN.md
+++ b/DESIGN.md
@@ -143,12 +143,17 @@ etc/...
 
 ```text
 format 1
+definition_sha256 VALUE
 name VALUE
 version VALUE
 release VALUE
 arch VALUE
 ```
 
+`definition_sha256` binds a built artifact to the indexed recipe and its
+support files. `sget` rebuilds a cached artifact when this digest is absent or
+does not match, even when name, version, and release are unchanged.
+
 `.SPS/files` is a sorted list of normalized payload paths. Directory entries
 end in `/`.
 
diff --git a/bin/mkpkg b/bin/mkpkg
index 4a80419..477b5cd 100755
--- a/bin/mkpkg
+++ b/bin/mkpkg
@@ -246,7 +246,7 @@ command -v mktemp >/dev/null 2>&1 || die "mktemp is required"
 recipe_dir=$(CDPATH= cd "$(dirname "$recipe")" 2>/dev/null && pwd) ||
 	die "cannot enter recipe directory"
 recipe_file=$recipe_dir/$(basename "$recipe")
-sps_definition_sha256 "$recipe_file" >/dev/null ||
+definition_sha256=$(sps_definition_sha256 "$recipe_file") ||
 	die_status 4 "invalid package definition support inputs"
 default_arch=${SPS_ARCH:-}
 if [ -z "$default_arch" ]; then
@@ -957,8 +957,11 @@ meta=$stage_dir/.SPS/meta
 files=$stage_dir/.SPS/files
 file_hashes=$stage_dir/.SPS/hashes
 
-awk -F '\t' '
-BEGIN { print "format\t1" }
+awk -F '\t' -v definition_sha256="$definition_sha256" '
+BEGIN {
+	print "format\t1"
+	print "definition_sha256\t" definition_sha256
+}
 $1 == "name" || $1 == "version" || $1 == "release" || $1 == "arch" ||
 $1 == "description" {
 	scalar[$1] = $0
diff --git a/bin/sget b/bin/sget
index ba372f9..a45b904 100755
--- a/bin/sget
+++ b/bin/sget
@@ -222,6 +222,7 @@ sget_verify_artifact_identity()
     sget_verify_version=$3
     sget_verify_release=$4
     sget_verify_arch=$5
+    sget_verify_definition=${6-}
 
     case $sget_verify_archive in
         *.tar.gz|*.tgz)
@@ -254,7 +255,15 @@ sget_verify_artifact_identity()
     [ "$1" = "$sget_verify_name" ] &&
     [ "$2" = "$sget_verify_version" ] &&
     [ "$3" = "$sget_verify_release" ] &&
-    [ "$4" = "$sget_verify_arch" ]
+    [ "$4" = "$sget_verify_arch" ] || return 1
+
+    [ -n "$sget_verify_definition" ] || return 0
+    sget_verify_cached_definition=$(printf '%s\n' "$sget_verify_meta" |
+        awk -v sps_action=meta-get -v sps_key_name=definition_sha256 \
+            -v sps_program=sget -f "$SPS_LIBDIR/package.awk" 2>/dev/null) ||
+        return 2
+    [ "$sget_verify_cached_definition" = "$sget_verify_definition" ] ||
+        return 2
 }
 
 # Set by the upgrade path.
@@ -462,12 +471,31 @@ sget_install()
                 fi
             done
             if [ -n "$sget_artifact" ]; then
+                sget_cache_status=0
                 sget_verify_artifact_identity "$sget_artifact" "$sget_package" \
-                    "$sget_version" "$sget_release" "$sget_arch" ||
-                    sps_die "$SPS_EX_PACKAGE" "cached artifact identity is invalid for '$sget_package': $sget_artifact"
-                sget_note "binary cache hit"
-                printf '%s: using cached %s\n' "$sps_prog" "${sget_artifact##*/}"
-            else
+                    "$sget_version" "$sget_release" "$sget_arch" \
+                    "$sget_recipe_hash" || sget_cache_status=$?
+                case $sget_cache_status in
+                    0)
+                        sget_note "binary cache hit"
+                        printf '%s: using cached %s\n' \
+                            "$sps_prog" "${sget_artifact##*/}"
+                        ;;
+                    2)
+                        printf '%s: discarding stale cached %s\n' \
+                            "$sps_prog" "${sget_artifact##*/}"
+                        rm -f "$sget_artifact" ||
+                            sps_die "$SPS_EX_PERMISSION" \
+                                "cannot replace stale cache artifact: $sget_artifact"
+                        sget_artifact=
+                        ;;
+                    *)
+                        sps_die "$SPS_EX_PACKAGE" \
+                            "cached artifact identity is invalid for '$sget_package': $sget_artifact"
+                        ;;
+                esac
+            fi
+            if [ -z "$sget_artifact" ]; then
                 sget_note "binary cache miss"
                 sget_note "building $sget_recipe_dir"
                 sget_artifact_file=$sget_work/artifact-$sget_package
@@ -497,7 +525,8 @@ sget_install()
                 [ -f "$sget_artifact" ] ||
                     sps_die "$SPS_EX_BUILD" "mkpkg artifact for '$sget_package' is missing: $sget_artifact"
                 sget_verify_artifact_identity "$sget_artifact" "$sget_package" \
-                    "$sget_version" "$sget_release" "$sget_arch" ||
+                    "$sget_version" "$sget_release" "$sget_arch" \
+                    "$sget_recipe_hash" ||
                     sps_die "$SPS_EX_PACKAGE" "mkpkg produced the wrong package identity for '$sget_package'"
             fi
 
diff --git a/lib/package.awk b/lib/package.awk
index f2fa1bd..f66b84c 100644
--- a/lib/package.awk
+++ b/lib/package.awk
@@ -57,13 +57,17 @@ function sps_meta_line(line,    pos, key, value, controls) {
     sps_meta_count[key]++
     if (!(key in sps_meta_first))
         sps_meta_first[key] = value
-    if ((key == "format" || key == "name" || key == "version" ||
-         key == "release" || key == "arch") && sps_meta_count[key] > 1) {
+    if ((key == "format" || key == "definition_sha256" || key == "name" ||
+         key == "version" || key == "release" || key == "arch") &&
+        sps_meta_count[key] > 1) {
         sps_diag("duplicate metadata key '" key "'")
         return 1
     }
     if (key == "format" && value != "1")
         sps_diag("unsupported package format '" value "'")
+    if (key == "definition_sha256" &&
+        (value !~ /^[0123456789abcdef]+$/ || length(value) != 64))
+        sps_diag("invalid package definition digest")
     if (key == "name" && !sps_valid_name(value))
         sps_diag("invalid package name '" value "'")
     if ((key == "version" || key == "release") &&
diff --git a/tests/test_build.sh b/tests/test_build.sh
index bba6721..6ae05cb 100755
--- a/tests/test_build.sh
+++ b/tests/test_build.sh
@@ -84,7 +84,10 @@ $1 == "name" && $2 == "hello-sps" { name = 1 }
 $1 == "version" && $2 == "1.0" { version = 1 }
 $1 == "release" && $2 == "1" { release = 1 }
 $1 == "arch" && $2 == "any" { arch = 1 }
-END { exit !(format && name && version && release && arch) }
+$1 == "definition_sha256" && $2 ~ /^[0-9a-f]+$/ && length($2) == 64 {
+	definition = 1
+}
+END { exit !(format && name && version && release && arch && definition) }
 ' "$tmp/extract/.SPS/meta" || fail "package metadata is incomplete"
 
 cat >"$tmp/expected-files" <<'EOF'
diff --git a/tests/test_dependencies.sh b/tests/test_dependencies.sh
index 1cbe8f3..b4a4cfc 100755
--- a/tests/test_dependencies.sh
+++ b/tests/test_dependencies.sh
@@ -167,6 +167,7 @@ contains "$(cat "$tmp/explicit.out")" 'explicit'
 
 cat > "$tmp/fakebin/mkpkg" <<'EOF_FAKE_MKPKG'
 #!/bin/sh
+. "$SPS_LIBDIR/common.sh"
 printf "mkpkg\t%s\n" "$PWD" >> "$TEST_LOG"
 artifact_file=
 recipe=recipe
@@ -200,10 +201,12 @@ version=$(awk '$1=="version" {print $2; exit}' "$recipe")
 release=$(awk '$1=="release" {print $2; exit}' "$recipe")
 arch=$(awk '$1=="arch" {print $2; exit}' "$recipe")
 [ -n "$arch" ] || arch=$(uname -m)
+definition_sha256=$(sps_definition_sha256 "$recipe") || exit 4
 work=$PWD/.fakepkg.$$
 mkdir -p "$work/.SPS"
-printf "format\t1\nname\t%s\nversion\t%s\nrelease\t%s\narch\t%s\n" \
-    "$name" "$version" "$release" "$arch" > "$work/.SPS/meta"
+printf "format\t1\ndefinition_sha256\t%s\nname\t%s\nversion\t%s\nrelease\t%s\narch\t%s\n" \
+    "$definition_sha256" "$name" "$version" "$release" "$arch" \
+    > "$work/.SPS/meta"
 : > "$work/.SPS/files"
 : > "$work/.SPS/hashes"
 artifact=$PWD/$name-$version-$release-$arch.pkg.tar
diff --git a/tests/test_highlevel.sh b/tests/test_highlevel.sh
index c501e4a..2dc8d5f 100755
--- a/tests/test_highlevel.sh
+++ b/tests/test_highlevel.sh
@@ -73,6 +73,15 @@ sget upgrade >"$tmp/upgrade.out"
 reinstall=$(sget install --reinstall app)
 contains "$reinstall" 'using cached app-1.0-1-any.pkg.tar'
 
+# A cache filename is not enough to prove that an artifact matches the
+# current recipe/support tree. Rebuild an unchanged version when its package
+# definition changes, rather than reusing a stale library from an earlier
+# live-setup attempt.
+printf '%s\n' '# definition changed without a release bump' >>"$repo/app/recipe"
+src update >/dev/null
+reinstall=$(sget install --reinstall app)
+contains "$reinstall" 'discarding stale cached app-1.0-1-any.pkg.tar'
+
 # Do not trust the cache filename. A different valid package under this name
 # must be rejected before pkin sees it.
 app_cache=$(find "$cache/packages" -type f -name 'app-1.0-1-any.pkg.tar*' | head -1)