Fix: Lock installer input state
This commit is contained in:
+3
-3
@@ -39,10 +39,10 @@ __pycache__/
|
|||||||
/config/firefox/chrome/panama-theme.css
|
/config/firefox/chrome/panama-theme.css
|
||||||
/config/firefox/chrome/panama-theme.css.tmp
|
/config/firefox/chrome/panama-theme.css.tmp
|
||||||
|
|
||||||
# Build products of the Vicinae extension. The source is the repository's; the
|
# Build products of the Vicinae extension. The source and reviewed lockfile are
|
||||||
# dependency tree and the bundle it produces are machine state, rebuilt by
|
# in the repository; the dependency tree and bundle are machine state.
|
||||||
# `panama apps`.
|
|
||||||
/config/local/share/vicinae/extensions/*/node_modules/
|
/config/local/share/vicinae/extensions/*/node_modules/
|
||||||
/config/local/share/vicinae/extensions/*/dist/
|
/config/local/share/vicinae/extensions/*/dist/
|
||||||
/config/local/share/vicinae/extensions/*/build/
|
/config/local/share/vicinae/extensions/*/build/
|
||||||
/config/local/share/vicinae/extensions/*/package-lock.json
|
/config/local/share/vicinae/extensions/*/package-lock.json
|
||||||
|
!/config/local/share/vicinae/extensions/panama-search/package-lock.json
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -89,36 +89,56 @@ STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/panama"
|
|||||||
PACKAGES_HASH="$STATE_DIR/packages-hash"
|
PACKAGES_HASH="$STATE_DIR/packages-hash"
|
||||||
|
|
||||||
hash_packages() {
|
hash_packages() {
|
||||||
local file relative size
|
local file relative size fixed_input digest
|
||||||
|
|
||||||
{
|
for fixed_input in \
|
||||||
find "$PANAMA_PATH/setup/packages" -maxdepth 1 -type f -print0
|
"$PANAMA_PATH/setup/scripts/install-packages" \
|
||||||
printf '%s\0' \
|
"$PANAMA_PATH/setup/lib/artifact-provenance"; do
|
||||||
"$PANAMA_PATH/setup/scripts/install-packages" \
|
[[ -f "$fixed_input" && ! -L "$fixed_input" && -r "$fixed_input" ]] || return 1
|
||||||
"$PANAMA_PATH/setup/lib/artifact-provenance"
|
done
|
||||||
find "$PANAMA_PATH/setup/provenance" -type f -print0
|
|
||||||
} | LC_ALL=C sort -z | while IFS= read -r -d '' file; do
|
digest="$(
|
||||||
relative="${file#"$PANAMA_PATH"/}"
|
{
|
||||||
size="$(wc -c <"$file")"
|
find "$PANAMA_PATH/setup/packages" -maxdepth 1 -type f -print0 || exit 1
|
||||||
printf '%s\0%s\0' "$relative" "$size"
|
printf '%s\0' \
|
||||||
cat -- "$file"
|
"$PANAMA_PATH/setup/scripts/install-packages" \
|
||||||
printf '\0'
|
"$PANAMA_PATH/setup/lib/artifact-provenance" || exit 1
|
||||||
done | sha256sum | cut -d' ' -f1
|
find "$PANAMA_PATH/setup/provenance" -type f -print0 || exit 1
|
||||||
|
} | LC_ALL=C sort -z | while IFS= read -r -d '' file; do
|
||||||
|
relative="${file#"$PANAMA_PATH"/}"
|
||||||
|
size="$(wc -c <"$file")" || exit 1
|
||||||
|
printf '%s\0%s\0' "$relative" "$size" || exit 1
|
||||||
|
cat -- "$file" || exit 1
|
||||||
|
printf '\0' || exit 1
|
||||||
|
done | sha256sum | cut -d' ' -f1
|
||||||
|
)" || return 1
|
||||||
|
printf '%s\n' "$digest"
|
||||||
}
|
}
|
||||||
|
|
||||||
packages_needed() {
|
packages_needed() {
|
||||||
|
local current_hash recorded_hash
|
||||||
|
|
||||||
(( FORCE_PACKAGES )) && return 0
|
(( FORCE_PACKAGES )) && return 0
|
||||||
(( UPGRADE )) || return 0
|
(( UPGRADE )) || return 0
|
||||||
[[ -r "$PACKAGES_HASH" ]] || return 0
|
[[ -r "$PACKAGES_HASH" ]] || return 0
|
||||||
[[ "$(hash_packages)" != "$(cat "$PACKAGES_HASH")" ]]
|
current_hash="$(hash_packages)" || return 2
|
||||||
|
recorded_hash="$(cat "$PACKAGES_HASH")" || return 2
|
||||||
|
[[ "$current_hash" != "$recorded_hash" ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
# Written only after the stage succeeds, mirroring the rule panama-migrate
|
# Written only after the stage succeeds, mirroring the rule panama-migrate
|
||||||
# documents for its markers: a step that did not complete has not happened, and
|
# documents for its markers: a step that did not complete has not happened, and
|
||||||
# recording it as done hides it forever.
|
# recording it as done hides it forever.
|
||||||
record_packages_hash() {
|
record_packages_hash() {
|
||||||
|
local temporary_hash
|
||||||
mkdir -p "$STATE_DIR"
|
mkdir -p "$STATE_DIR"
|
||||||
hash_packages >"$PACKAGES_HASH"
|
temporary_hash="$(mktemp "$STATE_DIR/.packages-hash.XXXXXX")" || return 1
|
||||||
|
if hash_packages >"$temporary_hash"; then
|
||||||
|
mv -f -- "$temporary_hash" "$PACKAGES_HASH"
|
||||||
|
else
|
||||||
|
rm -f -- "$temporary_hash"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Repository trust is checked before the installer can reach its bootstrap DNF.
|
# Repository trust is checked before the installer can reach its bootstrap DNF.
|
||||||
@@ -322,14 +342,25 @@ for stage in "${STAGES[@]}"; do
|
|||||||
script="$PANAMA_PATH/setup/scripts/$stage"
|
script="$PANAMA_PATH/setup/scripts/$stage"
|
||||||
[[ -x "$script" ]] || continue
|
[[ -x "$script" ]] || continue
|
||||||
printf '\n=== %s ===\n' "$stage"
|
printf '\n=== %s ===\n' "$stage"
|
||||||
if [[ "$stage" == install-packages ]] && ! packages_needed; then
|
if [[ "$stage" == install-packages ]]; then
|
||||||
echo "The package lists have not changed since the last run; skipping."
|
package_state_status=0
|
||||||
echo "Run with --packages to install them anyway."
|
packages_needed || package_state_status=$?
|
||||||
continue
|
if (( package_state_status == 1 )); then
|
||||||
|
echo "The package lists have not changed since the last run; skipping."
|
||||||
|
echo "Run with --packages to install them anyway."
|
||||||
|
continue
|
||||||
|
elif (( package_state_status != 0 )); then
|
||||||
|
failed+=("$stage")
|
||||||
|
printf '!!! %s could not read its tracked installation inputs\n' "$stage" >&2
|
||||||
|
continue
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
if "$script"; then
|
if "$script"; then
|
||||||
if [[ "$stage" == install-packages ]]; then
|
if [[ "$stage" == install-packages ]]; then
|
||||||
record_packages_hash
|
if ! record_packages_hash; then
|
||||||
|
failed+=("$stage")
|
||||||
|
printf '!!! %s could not record its tracked installation inputs\n' "$stage" >&2
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
stage_status=$?
|
stage_status=$?
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ fi
|
|||||||
# ── The extension ────────────────────────────────────────────────────────────
|
# ── The extension ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
manifest="$extension/package.json"
|
manifest="$extension/package.json"
|
||||||
|
lockfile="$extension/package-lock.json"
|
||||||
if [[ ! -f "$manifest" ]]; then
|
if [[ ! -f "$manifest" ]]; then
|
||||||
note 'the panama-search extension has no manifest'
|
note 'the panama-search extension has no manifest'
|
||||||
else
|
else
|
||||||
@@ -76,6 +77,15 @@ else
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f "$lockfile" ]]; then
|
||||||
|
note 'the panama-search extension has no package-lock.json, so npm ci cannot install a fresh clone'
|
||||||
|
elif git -C "$repo_dir" check-ignore -q "$lockfile" 2>/dev/null; then
|
||||||
|
note 'the panama-search package-lock.json is ignored, so a fresh clone cannot use npm ci'
|
||||||
|
elif ! git -C "$repo_dir" ls-files --error-unmatch -- \
|
||||||
|
'config/local/share/vicinae/extensions/panama-search/package-lock.json' >/dev/null 2>&1; then
|
||||||
|
note 'the panama-search package-lock.json is not tracked, so a fresh clone cannot use npm ci'
|
||||||
|
fi
|
||||||
|
|
||||||
# ── One engine, written twice ────────────────────────────────────────────────
|
# ── One engine, written twice ────────────────────────────────────────────────
|
||||||
#
|
#
|
||||||
# The script command and the extension both have to know where a search goes.
|
# The script command and the extension both have to know where a search goes.
|
||||||
@@ -124,23 +134,29 @@ trap 'rm -rf -- "$fixture_root"' EXIT
|
|||||||
mkdir -p "$fixture_root/config/local/share/vicinae/scripts" \
|
mkdir -p "$fixture_root/config/local/share/vicinae/scripts" \
|
||||||
"$fixture_root/config/local/share/vicinae/extensions/panama-search/src" \
|
"$fixture_root/config/local/share/vicinae/extensions/panama-search/src" \
|
||||||
"$fixture_root/bin"
|
"$fixture_root/bin"
|
||||||
printf '{"name":"panama-search","dependencies":{"left-pad":"1.3.0"}}\n' \
|
fixture_extension="$fixture_root/config/local/share/vicinae/extensions/panama-search"
|
||||||
>"$fixture_root/config/local/share/vicinae/extensions/panama-search/package.json"
|
cp -- "$manifest" "$fixture_extension/package.json"
|
||||||
printf '{"lockfileVersion":3,"packages":{}}\n' \
|
cp -- "$lockfile" "$fixture_extension/package-lock.json"
|
||||||
>"$fixture_root/config/local/share/vicinae/extensions/panama-search/package-lock.json"
|
cmp -s -- "$lockfile" "$fixture_extension/package-lock.json" \
|
||||||
lockfile="$fixture_root/config/local/share/vicinae/extensions/panama-search/package-lock.json"
|
|| note 'the fresh-clone fixture did not consume the repository package-lock.json'
|
||||||
|
sed -i 's/"dependencies": {/"dependencies": {"fixture-mismatch": "1.0.0",/' \
|
||||||
|
"$fixture_extension/package.json"
|
||||||
|
lockfile="$fixture_extension/package-lock.json"
|
||||||
lock_before="$fixture_root/package-lock.before"
|
lock_before="$fixture_root/package-lock.before"
|
||||||
cp -- "$lockfile" "$lock_before"
|
cp -- "$lockfile" "$lock_before"
|
||||||
|
repository_lock_sha256="$(sha256sum -- "$lock_before" | awk '{ print $1 }')"
|
||||||
cat >"$fixture_root/bin/npm" <<'EOF'
|
cat >"$fixture_root/bin/npm" <<'EOF'
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
printf '%s\n' "$*" >>"${NPM_LOG:?}"
|
printf '%s\n' "$*" >>"${NPM_LOG:?}"
|
||||||
[[ "${1:-}" == ci ]] || exit 64
|
[[ "${1:-}" == ci ]] || exit 64
|
||||||
|
[[ "$(sha256sum -- package-lock.json | awk '{ print $1 }')" == "${NPM_EXPECTED_LOCK_SHA256:?}" ]] || exit 65
|
||||||
exit 1
|
exit 1
|
||||||
EOF
|
EOF
|
||||||
chmod +x "$fixture_root/bin/npm"
|
chmod +x "$fixture_root/bin/npm"
|
||||||
stage_status=0
|
stage_status=0
|
||||||
stage_output="$(PATH="$fixture_root/bin:$PATH" PANAMA_PATH="$fixture_root" \
|
stage_output="$(PATH="$fixture_root/bin:$PATH" PANAMA_PATH="$fixture_root" \
|
||||||
VICINAE_DATA_DIR="$fixture_root/vicinae-data" NPM_LOG="$fixture_root/npm.log" \
|
VICINAE_DATA_DIR="$fixture_root/vicinae-data" NPM_LOG="$fixture_root/npm.log" \
|
||||||
|
NPM_EXPECTED_LOCK_SHA256="$repository_lock_sha256" \
|
||||||
bash "$stage" 2>&1)" || stage_status=$?
|
bash "$stage" 2>&1)" || stage_status=$?
|
||||||
[[ "$stage_status" -eq 0 ]] \
|
[[ "$stage_status" -eq 0 ]] \
|
||||||
|| note "the Vicinae stage returned $stage_status for a lockfile mismatch instead of remaining nonfatal"
|
|| note "the Vicinae stage returned $stage_status for a lockfile mismatch instead of remaining nonfatal"
|
||||||
|
|||||||
@@ -40,6 +40,20 @@ STAGE_NAMES=(install-packages link-dotfiles link-skills link-user change-setting
|
|||||||
link-vicinae-scripts setup-server link-server setup-identity
|
link-vicinae-scripts setup-server link-server setup-identity
|
||||||
install-hardware)
|
install-hardware)
|
||||||
|
|
||||||
|
copy_hash_inputs() {
|
||||||
|
local root="$1" source relative
|
||||||
|
while IFS= read -r -d '' source; do
|
||||||
|
relative="${source#"$repo_dir"/}"
|
||||||
|
mkdir -p "$(dirname "$root/$relative")"
|
||||||
|
cp -- "$source" "$root/$relative"
|
||||||
|
done < <(
|
||||||
|
find "$repo_dir/setup/packages" -maxdepth 1 -type f -print0
|
||||||
|
find "$repo_dir/setup/provenance" -type f -print0
|
||||||
|
)
|
||||||
|
mkdir -p "$root/setup/lib"
|
||||||
|
cp -- "$repo_dir/setup/lib/artifact-provenance" "$root/setup/lib/artifact-provenance"
|
||||||
|
}
|
||||||
|
|
||||||
# A PANAMA_PATH that looks enough like the real one for install to run, and
|
# A PANAMA_PATH that looks enough like the real one for install to run, and
|
||||||
# records what it was asked to do instead of doing it.
|
# records what it was asked to do instead of doing it.
|
||||||
build_fixture() {
|
build_fixture() {
|
||||||
@@ -51,10 +65,7 @@ build_fixture() {
|
|||||||
|
|
||||||
cp "$installer" "$root/install"
|
cp "$installer" "$root/install"
|
||||||
: >"$root/bin/ascii"
|
: >"$root/bin/ascii"
|
||||||
printf 'base-package\n' >"$root/setup/packages/core-packages"
|
copy_hash_inputs "$root"
|
||||||
printf 'artifact provenance\n' >"$root/setup/lib/artifact-provenance"
|
|
||||||
printf 'reviewed installer inputs\n' >"$root/setup/provenance/installers.conf"
|
|
||||||
printf 'reviewed key\n' >"$root/setup/provenance/keys/fixture.asc"
|
|
||||||
|
|
||||||
local stage
|
local stage
|
||||||
for stage in "${STAGE_NAMES[@]}"; do
|
for stage in "${STAGE_NAMES[@]}"; do
|
||||||
@@ -134,6 +145,20 @@ run_install() {
|
|||||||
return "$status"
|
return "$status"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
run_hash() {
|
||||||
|
local root="$1"
|
||||||
|
sed -n '/^hash_packages() {/,/^}$/p' "$root/install" >"$root/hash-only"
|
||||||
|
printf 'set -uo pipefail\nhash_packages\n' >>"$root/hash-only"
|
||||||
|
PANAMA_PATH="$root" bash "$root/hash-only" 2>"$root/hash-only.err"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_hash_failure() {
|
||||||
|
local root="$1" description="$2" status=0 digest
|
||||||
|
digest="$(run_hash "$root")" || status=$?
|
||||||
|
[[ "$status" -ne 0 && -z "$digest" ]] \
|
||||||
|
|| note "$description produced a digest instead of failing closed"
|
||||||
|
}
|
||||||
|
|
||||||
# ── 1. The interview never runs on an upgrade ────────────────────────────────
|
# ── 1. The interview never runs on an upgrade ────────────────────────────────
|
||||||
|
|
||||||
build_fixture "$tmp/a"
|
build_fixture "$tmp/a"
|
||||||
@@ -197,6 +222,19 @@ done
|
|||||||
|
|
||||||
# ── 3. The packages hash gates the stage, and a failure does not record it ───
|
# ── 3. The packages hash gates the stage, and a failure does not record it ───
|
||||||
|
|
||||||
|
package_inputs=()
|
||||||
|
while IFS= read -r -d '' input; do
|
||||||
|
package_inputs+=("${input#"$repo_dir"/}")
|
||||||
|
done < <(find "$repo_dir/setup/packages" -maxdepth 1 -type f -print0)
|
||||||
|
provenance_inputs=()
|
||||||
|
while IFS= read -r -d '' input; do
|
||||||
|
provenance_inputs+=("${input#"$repo_dir"/}")
|
||||||
|
done < <(find "$repo_dir/setup/provenance" -type f -print0)
|
||||||
|
(( ${#package_inputs[@]} > 0 )) \
|
||||||
|
|| note 'the current repository has no top-level package input to exercise'
|
||||||
|
(( ${#provenance_inputs[@]} > 0 )) \
|
||||||
|
|| note 'the current repository has no provenance input to exercise'
|
||||||
|
|
||||||
# Second run, nothing changed: the stage must be skipped.
|
# Second run, nothing changed: the stage must be skipped.
|
||||||
install_status=0
|
install_status=0
|
||||||
ran_again="$(run_install "$tmp/a" --upgrade)" || install_status=$?
|
ran_again="$(run_install "$tmp/a" --upgrade)" || install_status=$?
|
||||||
@@ -213,33 +251,75 @@ ran_forced="$(run_install "$tmp/a" --upgrade --packages)" || install_status=$?
|
|||||||
grep -qx 'install-packages' <<<"$ran_forced" \
|
grep -qx 'install-packages' <<<"$ran_forced" \
|
||||||
|| note '--packages did not force install-packages to run'
|
|| note '--packages did not force install-packages to run'
|
||||||
|
|
||||||
# A changed list brings the stage back.
|
# Every current package and provenance member is part of the state definition.
|
||||||
printf 'another-package\n' >>"$tmp/a/setup/packages/core-packages"
|
# Dynamically discovering them makes this fail when a new reviewed input is
|
||||||
install_status=0
|
# added but omitted from hash_packages.
|
||||||
ran_changed="$(run_install "$tmp/a" --upgrade)" || install_status=$?
|
for relative in "${package_inputs[@]}" "${provenance_inputs[@]}" \
|
||||||
[[ "$install_status" -eq 0 ]] \
|
'setup/scripts/install-packages' 'setup/lib/artifact-provenance'; do
|
||||||
|| note "install --upgrade failed after a package-list change with status $install_status"
|
printf 'changed %s\n' "$relative" >>"$tmp/a/$relative"
|
||||||
grep -qx 'install-packages' <<<"$ran_changed" \
|
|
||||||
|| note 'a changed package list did not bring install-packages back'
|
|
||||||
|
|
||||||
# The package stage consumes more than package lists. Each reviewed input must
|
|
||||||
# independently invalidate a recorded package state so an upgrade cannot keep
|
|
||||||
# using a stale trust or installer policy.
|
|
||||||
for state_input in \
|
|
||||||
'setup/scripts/install-packages installer adapter' \
|
|
||||||
'setup/lib/artifact-provenance provenance helper' \
|
|
||||||
'setup/provenance/installers.conf installer provenance' \
|
|
||||||
'setup/provenance/keys/fixture.asc provenance key'; do
|
|
||||||
read -r relative label <<<"$state_input"
|
|
||||||
printf 'changed %s\n' "$label" >>"$tmp/a/$relative"
|
|
||||||
install_status=0
|
install_status=0
|
||||||
ran_input_changed="$(run_install "$tmp/a" --upgrade)" || install_status=$?
|
ran_input_changed="$(run_install "$tmp/a" --upgrade)" || install_status=$?
|
||||||
[[ "$install_status" -eq 0 ]] \
|
[[ "$install_status" -eq 0 ]] \
|
||||||
|| note "install --upgrade failed after a $label change with status $install_status"
|
|| note "install --upgrade failed after changing $relative with status $install_status"
|
||||||
grep -qx 'install-packages' <<<"$ran_input_changed" \
|
grep -qx 'install-packages' <<<"$ran_input_changed" \
|
||||||
|| note "a changed $label did not bring install-packages back"
|
|| note "a changed $relative did not bring install-packages back"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# A path-only change must invalidate state even when the file bytes are exact.
|
||||||
|
for relative in "${package_inputs[0]:-}" "${provenance_inputs[0]:-}"; do
|
||||||
|
[[ -n "$relative" ]] || continue
|
||||||
|
build_fixture "$tmp/path-rename"
|
||||||
|
run_install "$tmp/path-rename" --upgrade >/dev/null
|
||||||
|
mv -- "$tmp/path-rename/$relative" "$tmp/path-rename/$relative.renamed"
|
||||||
|
install_status=0
|
||||||
|
ran_renamed="$(run_install "$tmp/path-rename" --upgrade)" || install_status=$?
|
||||||
|
[[ "$install_status" -eq 0 ]] \
|
||||||
|
|| note "install --upgrade failed after renaming $relative with status $install_status"
|
||||||
|
grep -qx 'install-packages' <<<"$ran_renamed" \
|
||||||
|
|| note "renaming $relative without changing bytes did not bring install-packages back"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Fixed hash inputs must not silently disappear or degrade into a directory or
|
||||||
|
# link. An unreadable package input also proves a failed content read cannot be
|
||||||
|
# hidden by the final digest command.
|
||||||
|
for fixed_input in setup/scripts/install-packages setup/lib/artifact-provenance; do
|
||||||
|
for case_name in missing directory symlink unreadable; do
|
||||||
|
case_root="$tmp/hash-${fixed_input//\//-}-$case_name"
|
||||||
|
build_fixture "$case_root"
|
||||||
|
fixed_path="$case_root/$fixed_input"
|
||||||
|
case "$case_name" in
|
||||||
|
missing) rm -- "$fixed_path" ;;
|
||||||
|
directory) rm -- "$fixed_path"; mkdir -- "$fixed_path" ;;
|
||||||
|
symlink)
|
||||||
|
printf 'untrusted target\n' >"$case_root/untrusted-target"
|
||||||
|
rm -- "$fixed_path"
|
||||||
|
ln -s "$case_root/untrusted-target" "$fixed_path"
|
||||||
|
;;
|
||||||
|
unreadable) chmod 000 "$fixed_path" ;;
|
||||||
|
esac
|
||||||
|
assert_hash_failure "$case_root" "$fixed_input $case_name"
|
||||||
|
done
|
||||||
|
done
|
||||||
|
read_failure_root="$tmp/hash-package-read-failure"
|
||||||
|
build_fixture "$read_failure_root"
|
||||||
|
chmod 000 "$read_failure_root/${package_inputs[0]}"
|
||||||
|
assert_hash_failure "$read_failure_root" "${package_inputs[0]} unreadable"
|
||||||
|
|
||||||
|
# A hash failure is an installer failure, not a reason to skip the package
|
||||||
|
# stage and retain a stale stamp.
|
||||||
|
build_fixture "$tmp/hash-failure"
|
||||||
|
run_install "$tmp/hash-failure" --upgrade >/dev/null
|
||||||
|
cp -- "$tmp/hash-failure/state/panama/packages-hash" "$tmp/hash-failure/stamp-before"
|
||||||
|
rm -- "$tmp/hash-failure/setup/lib/artifact-provenance"
|
||||||
|
install_status=0
|
||||||
|
ran_hash_failure="$(run_install "$tmp/hash-failure" --upgrade)" || install_status=$?
|
||||||
|
[[ "$install_status" -ne 0 ]] \
|
||||||
|
|| note 'a failed package-state hash returned success'
|
||||||
|
grep -qx 'install-packages' <<<"$ran_hash_failure" \
|
||||||
|
&& note 'a failed package-state hash still ran install-packages'
|
||||||
|
cmp -s -- "$tmp/hash-failure/stamp-before" "$tmp/hash-failure/state/panama/packages-hash" \
|
||||||
|
|| note 'a failed package-state hash wrote a new packages-hash stamp'
|
||||||
|
|
||||||
# A failing stage must not record the hash, or the failure is hidden forever.
|
# A failing stage must not record the hash, or the failure is hidden forever.
|
||||||
build_fixture "$tmp/c" 1
|
build_fixture "$tmp/c" 1
|
||||||
install_status=0
|
install_status=0
|
||||||
|
|||||||
Reference in New Issue
Block a user