WIP: Paused over-hardening fix wave (boot checkout verification, double-read package manifest)
This commit is contained in:
@@ -7,26 +7,36 @@
|
||||
declare -gA INSTALLER_PROVENANCE=()
|
||||
|
||||
_primary_key_fingerprints() (
|
||||
local home
|
||||
home="$(mktemp -d)" || exit 1
|
||||
chmod 700 "$home"
|
||||
trap 'rm -rf -- "$home"' EXIT
|
||||
local home="" gpg_output
|
||||
trap '[[ -z "$home" ]] || rm -rf -- "$home"' EXIT
|
||||
trap 'exit 130' INT
|
||||
trap 'exit 143' TERM
|
||||
GNUPGHOME="$home" gpg --batch --with-colons --import-options show-only --import "$1" 2>/dev/null \
|
||||
| awk -F: '$1 == "pub" { primary = 1; next } primary && $1 == "fpr" { print $10; primary = 0 }'
|
||||
home="$(mktemp -u -d -t panama-gpg.XXXXXX)" || exit 1
|
||||
if ! mkdir -m 700 -- "$home"; then
|
||||
home=""
|
||||
exit 1
|
||||
fi
|
||||
gpg_output="$(GNUPGHOME="$home" gpg --batch --with-colons \
|
||||
--import-options show-only --import "$1" 2>/dev/null)" || exit 1
|
||||
awk -F: '$1 == "pub" { primary = 1; next } primary && $1 == "fpr" { print $10; primary = 0 }' \
|
||||
<<<"$gpg_output"
|
||||
)
|
||||
|
||||
key_fingerprint_matches() {
|
||||
local file="$1" expected="$2"
|
||||
local file="$1" expected="$2" output
|
||||
local -a primary_fingerprints=()
|
||||
mapfile -t primary_fingerprints < <(_primary_key_fingerprints "$file")
|
||||
output="$(_primary_key_fingerprints "$file")" || return 1
|
||||
[[ -n "$output" ]] || return 1
|
||||
mapfile -t primary_fingerprints <<<"$output"
|
||||
[[ ${#primary_fingerprints[@]} -eq 1 && "${primary_fingerprints[0]}" == "$expected" ]]
|
||||
}
|
||||
|
||||
_key_has_one_primary() {
|
||||
local output
|
||||
local -a primary_fingerprints=()
|
||||
mapfile -t primary_fingerprints < <(_primary_key_fingerprints "$1")
|
||||
output="$(_primary_key_fingerprints "$1")" || return 1
|
||||
[[ -n "$output" ]] || return 1
|
||||
mapfile -t primary_fingerprints <<<"$output"
|
||||
[[ ${#primary_fingerprints[@]} -eq 1 ]]
|
||||
}
|
||||
|
||||
@@ -34,11 +44,15 @@ verify_detached_signature() {
|
||||
local key="$1" signature="$2" content="$3" home
|
||||
_key_has_one_primary "$key" || return 1
|
||||
(
|
||||
home="$(mktemp -d)" || exit 1
|
||||
chmod 700 "$home"
|
||||
trap 'rm -rf -- "$home"' EXIT
|
||||
home=""
|
||||
trap '[[ -z "$home" ]] || rm -rf -- "$home"' EXIT
|
||||
trap 'exit 130' INT
|
||||
trap 'exit 143' TERM
|
||||
home="$(mktemp -u -d -t panama-gpg.XXXXXX)" || exit 1
|
||||
if ! mkdir -m 700 -- "$home"; then
|
||||
home=""
|
||||
exit 1
|
||||
fi
|
||||
GNUPGHOME="$home" gpg --batch --quiet --import "$key" >/dev/null 2>&1 \
|
||||
&& GNUPGHOME="$home" gpg --batch --verify "$signature" "$content" >/dev/null 2>&1
|
||||
)
|
||||
@@ -60,7 +74,11 @@ download_sha256() {
|
||||
[[ "$max_bytes" =~ ^[1-9][0-9]*$ ]] || exit 1
|
||||
[[ -n "$destination" && -d "$directory" ]] || exit 1
|
||||
umask 077
|
||||
part="$(mktemp "$directory/.${filename}.part.XXXXXX")" || exit 1
|
||||
part="$(mktemp -u "$directory/.${filename}.part.XXXXXX")" || exit 1
|
||||
if ! (set -o noclobber; : >"$part") 2>/dev/null; then
|
||||
part=""
|
||||
exit 1
|
||||
fi
|
||||
curl --fail --location --connect-timeout 10 --max-time 600 \
|
||||
--max-filesize "$max_bytes" --output "$part" "$url" \
|
||||
|| exit 1
|
||||
@@ -71,25 +89,25 @@ download_sha256() {
|
||||
)
|
||||
}
|
||||
|
||||
rpm_signature_matches() {
|
||||
local package="$1" key="$2" expected="$3" home db output status
|
||||
rpm_signature_matches() (
|
||||
local package="$1" key="$2" expected="$3" home="" db output
|
||||
trap '[[ -z "$home" ]] || rm -rf -- "$home"' EXIT
|
||||
trap 'exit 130' INT
|
||||
trap 'exit 143' TERM
|
||||
|
||||
key_fingerprint_matches "$key" "$expected" || return 1
|
||||
home="$(mktemp -d)" || return 1
|
||||
chmod 700 "$home"
|
||||
key_fingerprint_matches "$key" "$expected" || exit 1
|
||||
home="$(mktemp -u -d -t panama-rpm-signature.XXXXXX)" || exit 1
|
||||
if ! mkdir -m 700 -- "$home"; then
|
||||
home=""
|
||||
exit 1
|
||||
fi
|
||||
db="$home/rpmdb"
|
||||
mkdir -m 700 "$db" || {
|
||||
rm -rf -- "$home"
|
||||
return 1
|
||||
}
|
||||
|
||||
rpmkeys --dbpath "$db" --import "$key" >/dev/null 2>&1 \
|
||||
&& output="$(rpmkeys --dbpath "$db" --checksig --verbose "$package" 2>&1)"
|
||||
status=$?
|
||||
rm -rf -- "$home"
|
||||
(( status == 0 )) || return 1
|
||||
mkdir -m 700 "$db" || exit 1
|
||||
rpmkeys --dbpath "$db" --import "$key" >/dev/null 2>&1 || exit 1
|
||||
output="$(rpmkeys --dbpath "$db" --checksig --verbose "$package" 2>&1)" \
|
||||
|| exit 1
|
||||
grep -Eqi 'OpenPGP.*signature.*: OK' <<<"$output"
|
||||
}
|
||||
)
|
||||
|
||||
load_installer_provenance() {
|
||||
local file="$1" line name value required
|
||||
|
||||
+52
-31
@@ -11,17 +11,24 @@ content before the applicable verification succeeds.
|
||||
Each command below was run in a private temporary directory on 2026-08-27.
|
||||
The resulting armored public key is vendored under `keys/`; each output was
|
||||
checked with the listed complete primary fingerprint before it was committed.
|
||||
The verification commands use Panama's status-preserving helper: it captures
|
||||
GPG's output only after GPG succeeds, then requires exactly one primary key.
|
||||
|
||||
```bash
|
||||
source setup/lib/artifact-provenance
|
||||
key_fingerprint_matches KEY.asc EXPECTED_COMPLETE_PRIMARY_FINGERPRINT
|
||||
```
|
||||
|
||||
| Key | Source URL | Expected primary fingerprint | Verification command |
|
||||
| --- | --- | --- | --- |
|
||||
| Terra 44 | `https://repos.fyralabs.com/terra44/key.asc` | `AE09157A4DE88B497EA1D5D300CDAB43DE226D6F` | `gpg --batch --with-colons --import-options show-only --import terra44.asc \| awk -F: '$1 == "fpr" { print $10; exit }'` |
|
||||
| Anthropic Claude Code | `https://downloads.claude.ai/keys/claude-code.asc` | `31DDDE24DDFAB679F42D7BD2BAA929FF1A7ECACE` | `gpg --batch --with-colons --import-options show-only --import claude-code.asc \| awk -F: '$1 == "fpr" { print $10; exit }'` |
|
||||
| Bun releases | `https://keys.openpgp.org/vks/v1/by-fingerprint/F3DCC08A8572C0749B3E18888EAB4D40A7B22B59` | `F3DCC08A8572C0749B3E18888EAB4D40A7B22B59` | `gpg --batch --with-colons --import-options show-only --import bun.asc \| awk -F: '$1 == "fpr" { print $10; exit }'` |
|
||||
| RPM Fusion free | `https://download1.rpmfusion.org/free/fedora/RPM-GPG-KEY-rpmfusion-free-fedora-2020` | `E9A491A3DE247814E7E067EAE06F8ECDD651FF2E` | `gpg --batch --with-colons --import-options show-only --import rpmfusion-free.asc \| awk -F: '$1 == "fpr" { print $10; exit }'` |
|
||||
| RPM Fusion nonfree | `https://download1.rpmfusion.org/nonfree/fedora/RPM-GPG-KEY-rpmfusion-nonfree-fedora-2020` | `79BDB88F9BBF73910FD4095B6A2AF96194843C65` | `gpg --batch --with-colons --import-options show-only --import rpmfusion-nonfree.asc \| awk -F: '$1 == "fpr" { print $10; exit }'` |
|
||||
| lionheartp/Hyprland COPR | `https://download.copr.fedorainfracloud.org/results/lionheartp/Hyprland/pubkey.gpg` | `97E23476C89635135407C7D5E9BA41342C4B2995` | `gpg --batch --with-colons --import-options show-only --import hyprland-copr.asc \| awk -F: '$1 == "fpr" { print $10; exit }'` |
|
||||
| Flathub | `https://flathub.org/repo/flathub.flatpakrepo` | `6E5C05D979C76DAF93C081354184DD4D907A7CAE` | `awk -F= '/^GPGKey=/{print $2}' flathub.flatpakrepo \| base64 --decode \| gpg --batch --with-colons --import-options show-only --import \| awk -F: '$1 == "fpr" { print $10; exit }'` |
|
||||
| Claude Desktop Extra | `https://patrickjaja.github.io/claude-desktop-extra/gpg-key.asc` | `825A7D15D78BABE45646D5DF382409F597908867` | `gpg --batch --with-colons --import-options show-only --import claude-desktop.asc \| awk -F: '$1 == "fpr" { print $10; exit }'` |
|
||||
| Terra 44 | `https://repos.fyralabs.com/terra44/key.asc` | `AE09157A4DE88B497EA1D5D300CDAB43DE226D6F` | `key_fingerprint_matches terra44.asc AE09157A4DE88B497EA1D5D300CDAB43DE226D6F` |
|
||||
| Anthropic Claude Code | `https://downloads.claude.ai/keys/claude-code.asc` | `31DDDE24DDFAB679F42D7BD2BAA929FF1A7ECACE` | `key_fingerprint_matches claude-code.asc 31DDDE24DDFAB679F42D7BD2BAA929FF1A7ECACE` |
|
||||
| Bun releases | `https://keys.openpgp.org/vks/v1/by-fingerprint/F3DCC08A8572C0749B3E18888EAB4D40A7B22B59` | `F3DCC08A8572C0749B3E18888EAB4D40A7B22B59` | `key_fingerprint_matches bun.asc F3DCC08A8572C0749B3E18888EAB4D40A7B22B59` |
|
||||
| RPM Fusion free | `https://download1.rpmfusion.org/free/fedora/RPM-GPG-KEY-rpmfusion-free-fedora-2020` | `E9A491A3DE247814E7E067EAE06F8ECDD651FF2E` | `key_fingerprint_matches rpmfusion-free.asc E9A491A3DE247814E7E067EAE06F8ECDD651FF2E` |
|
||||
| RPM Fusion nonfree | `https://download1.rpmfusion.org/nonfree/fedora/RPM-GPG-KEY-rpmfusion-nonfree-fedora-2020` | `79BDB88F9BBF73910FD4095B6A2AF96194843C65` | `key_fingerprint_matches rpmfusion-nonfree.asc 79BDB88F9BBF73910FD4095B6A2AF96194843C65` |
|
||||
| lionheartp/Hyprland COPR | `https://download.copr.fedorainfracloud.org/results/lionheartp/Hyprland/pubkey.gpg` | `97E23476C89635135407C7D5E9BA41342C4B2995` | `key_fingerprint_matches hyprland-copr.asc 97E23476C89635135407C7D5E9BA41342C4B2995` |
|
||||
| Flathub | `https://flathub.org/repo/flathub.flatpakrepo` | `6E5C05D979C76DAF93C081354184DD4D907A7CAE` | `key_fingerprint_matches flathub.asc 6E5C05D979C76DAF93C081354184DD4D907A7CAE` |
|
||||
| Claude Desktop Extra | `https://patrickjaja.github.io/claude-desktop-extra/gpg-key.asc` | `825A7D15D78BABE45646D5DF382409F597908867` | `key_fingerprint_matches claude-desktop.asc 825A7D15D78BABE45646D5DF382409F597908867` |
|
||||
|
||||
The retrieval command for every direct key was:
|
||||
|
||||
@@ -98,16 +105,28 @@ digest, then update the command and this ledger in a second commit.
|
||||
|
||||
Do not replace a key on an automated update. A key rotation is a reviewed
|
||||
repository change: obtain the new key from the publisher record, independently
|
||||
confirm its complete primary fingerprint, update the vendored key and
|
||||
`installers.conf` together, refresh this retrieval record, and add a focused
|
||||
contract case if the verification behavior changes. Until that review lands,
|
||||
verification fails closed and preserves any known-good destination.
|
||||
confirm its complete primary fingerprint, and update every independent pin site
|
||||
in one review:
|
||||
|
||||
## Container-only Terra 44 signed-bootstrap proof
|
||||
- the armored key under `setup/provenance/keys/`;
|
||||
- its fingerprint in `setup/provenance/installers.conf`;
|
||||
- the matching `_require_policy_value` literal in
|
||||
`setup/scripts/install-packages`;
|
||||
- independent fingerprint expectations and command-log fixtures in
|
||||
`tests/setup/package-provenance-contract`;
|
||||
- this retrieval and evidence ledger at `setup/provenance/README.md`.
|
||||
|
||||
On 2026-08-27, a single disposable rootless Podman container proved the Terra
|
||||
bootstrap path without changing the host package database, host keyring, or
|
||||
host repository files. Podman reported `rootless=true`, `runtime=crun`, and a
|
||||
Until all sites agree, verification fails closed and preserves any known-good
|
||||
destination. Add or update a focused contract whenever verification behavior
|
||||
changes.
|
||||
|
||||
## Historical container-only Terra 44 signed-bootstrap proof
|
||||
|
||||
On 2026-08-27, a single disposable rootless Podman container validated Terra's
|
||||
then-reviewed signed bootstrap without changing the host package database,
|
||||
host keyring, or host repository files. This is retained historical publisher
|
||||
evidence; Panama's runtime installer no longer installs `terra-release`.
|
||||
Podman reported `rootless=true`, `runtime=crun`, and a
|
||||
user graph root. The fresh image was
|
||||
`registry.fedoraproject.org/fedora@sha256:62f199d1eb34170a7bb2277485676d89c0e91aae4086151c4043062cce51c77c`
|
||||
(`sha256:87d8a4a90c0457689db68624cac1026fb2201cbdc1e99cc5455a8f8876118498`).
|
||||
@@ -115,12 +134,13 @@ The container (`5fc8fa42bb85afb3b57b336ca29b58a32fad50d460583329a4e910cc29fb4d2d
|
||||
had no mounts and was removed automatically after `podman stop`.
|
||||
|
||||
Before copying the only host file admitted to the container,
|
||||
`keys/terra44.asc`, this exact host check reported the complete primary
|
||||
fingerprint `AE09157A4DE88B497EA1D5D300CDAB43DE226D6F`:
|
||||
`keys/terra44.asc`, this status-preserving host check accepted the complete
|
||||
primary fingerprint `AE09157A4DE88B497EA1D5D300CDAB43DE226D6F`:
|
||||
|
||||
```bash
|
||||
gpg --batch --with-colons --import-options show-only --import setup/provenance/keys/terra44.asc \
|
||||
| awk -F: '$1 == "fpr" { print $10; exit }'
|
||||
source setup/lib/artifact-provenance
|
||||
key_fingerprint_matches setup/provenance/keys/terra44.asc \
|
||||
AE09157A4DE88B497EA1D5D300CDAB43DE226D6F
|
||||
```
|
||||
|
||||
Its SHA-256 was
|
||||
@@ -144,14 +164,16 @@ podman exec panama-terra-proof-20260827 /bin/bash -lc '
|
||||
'
|
||||
```
|
||||
|
||||
Inside the container the copied and installed key both had the recorded
|
||||
SHA-256 before and after installation. `terra-release-44-9.noarch` was
|
||||
installed. Its effective `terra` configuration reported `gpgcheck = 1`,
|
||||
`pkg_gpgcheck = 1`, and `repo_gpgcheck = 1`; no GPG-bypass option was used.
|
||||
The package's own `/etc/yum.repos.d/terra.repo` uses its Terra metalink and
|
||||
`RPM-GPG-KEY-terra44`. That differs from Panama's deliberately staged local
|
||||
key/base-URL file in `install-packages`, which replaces the release-generated
|
||||
file only after this verified bootstrap step.
|
||||
The retained command output records the copied key's SHA-256 and DNF's
|
||||
successful `terra-release-44-9.noarch` transaction. The command itself pins the
|
||||
temporary Terra base URL and local staged key and enables package and repository
|
||||
signature checks. It does not include a separate post-install fingerprint or
|
||||
effective-repository query, so this ledger makes no independent post-check
|
||||
claim. Production publishes the reviewed root-staged key/repository pair
|
||||
directly and commits it only after the effective-repository post-check
|
||||
succeeds; failure restores the prior pair. Publisher-only package transactions
|
||||
use a fresh command-line repository identity, the reviewed base URL, and a
|
||||
newly fingerprint-verified private root key snapshot.
|
||||
|
||||
Although the command runner returned after 30 seconds while DNF was still
|
||||
loading metadata, Podman's retained event log records the exact command's
|
||||
@@ -165,6 +187,5 @@ podman events --since '2026-08-27T10:55:00-04:00' --until '2026-08-27T11:02:00-0
|
||||
|
||||
The first `exec` event, at `timeNano=1787842633591543881`, is the documented
|
||||
key-install and DNF command. Its matching first `exec_died` event, at
|
||||
`timeNano=1787842671276003275`, records `ContainerExitCode:0`. The
|
||||
same-container post-check independently confirmed the installed package and
|
||||
effective signature settings above; no retry or second container was used.
|
||||
`timeNano=1787842671276003275`, records `ContainerExitCode:0`. No retry or
|
||||
second container was used, and no stronger post-check evidence is retained.
|
||||
|
||||
@@ -53,7 +53,12 @@ if [[ "${PANAMA_NVIDIA:-no}" == yes ]]; then
|
||||
warn "Secure Boot question, or disable Secure Boot first."
|
||||
else
|
||||
log "Installing the NVIDIA driver"
|
||||
if sudo dnf install -y akmod-nvidia xorg-x11-drv-nvidia-cuda; then
|
||||
if sudo dnf install -y \
|
||||
--repo=fedora --repo=updates \
|
||||
--repo=rpmfusion-free --repo=rpmfusion-free-updates \
|
||||
--repo=rpmfusion-nonfree --repo=rpmfusion-nonfree-updates \
|
||||
--from-repo=rpmfusion-nonfree,rpmfusion-nonfree-updates \
|
||||
akmod-nvidia xorg-x11-drv-nvidia-cuda; then
|
||||
# nouveau has to be out of the way before the kernel would otherwise
|
||||
# bind it, which is why these are kernel arguments and not a modprobe
|
||||
# drop-in. modeset=1 is what makes the Wayland session work at all.
|
||||
|
||||
+699
-174
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,75 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
_collect_vicinae_inputs() {
|
||||
local extension="$1" output="$2"
|
||||
[[ -d "$extension" && ! -L "$extension" \
|
||||
&& -f "$extension/package.json" && ! -L "$extension/package.json" \
|
||||
&& -f "$extension/package-lock.json" && ! -L "$extension/package-lock.json" ]] \
|
||||
|| return 1
|
||||
|
||||
# Everything authored below the extension affects its build. npm's
|
||||
# dependency tree is the sole exception and is reproduced from the lock.
|
||||
find "$extension" -mindepth 1 \
|
||||
\( -path "$extension/node_modules" -prune \) -o \
|
||||
! -type d -print0 >"$output" || return 1
|
||||
LC_ALL=C sort -z -o "$output" "$output" || return 1
|
||||
}
|
||||
|
||||
_write_vicinae_manifest() {
|
||||
local extension="$1" inputs="$2" output="$3"
|
||||
local input relative digest
|
||||
: >"$output" || return 1
|
||||
while IFS= read -r -d '' input; do
|
||||
[[ -f "$input" && ! -L "$input" && -r "$input" ]] || return 1
|
||||
relative="${input#"$extension"/}"
|
||||
[[ "$relative" != "$input" && -n "$relative" ]] || return 1
|
||||
digest="$(sha256sum -- "$input" | awk '{ print $1 }')" || return 1
|
||||
[[ "$digest" =~ ^[0-9a-f]{64}$ ]] || return 1
|
||||
printf '%s\0%s\0' "$relative" "$digest" >>"$output" || return 1
|
||||
done <"$inputs"
|
||||
}
|
||||
|
||||
_vicinae_extension_digest() (
|
||||
local extension="${1%/}" work=""
|
||||
trap '[[ -z "$work" ]] || rm -rf -- "$work"' EXIT
|
||||
trap 'exit 130' INT
|
||||
trap 'exit 143' TERM
|
||||
work="$(mktemp -u -d -t panama-vicinae-digest.XXXXXX)" || exit 1
|
||||
if ! mkdir -m 700 -- "$work"; then
|
||||
work=""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
_collect_vicinae_inputs "$extension" "$work/inputs.before" || exit 1
|
||||
_write_vicinae_manifest \
|
||||
"$extension" "$work/inputs.before" "$work/manifest.before" || exit 1
|
||||
_collect_vicinae_inputs "$extension" "$work/inputs.after" || exit 1
|
||||
_write_vicinae_manifest \
|
||||
"$extension" "$work/inputs.after" "$work/manifest.after" || exit 1
|
||||
cmp -s -- "$work/inputs.before" "$work/inputs.after" || exit 1
|
||||
cmp -s -- "$work/manifest.before" "$work/manifest.after" || exit 1
|
||||
sha256sum -- "$work/manifest.before" | awk '{ print $1 }'
|
||||
)
|
||||
|
||||
_record_vicinae_digest() (
|
||||
local built="$1" digest="$2" receipt temporary=""
|
||||
trap '[[ -z "$temporary" ]] || rm -f -- "$temporary"' EXIT
|
||||
trap 'exit 130' INT
|
||||
trap 'exit 143' TERM
|
||||
[[ -d "$built" && ! -L "$built" ]] || exit 1
|
||||
receipt="$built/.panama-source-sha256"
|
||||
temporary="$(mktemp -u "$built/.panama-source-sha256.XXXXXX")" || exit 1
|
||||
umask 077
|
||||
if ! (set -o noclobber; : >"$temporary") 2>/dev/null; then
|
||||
temporary=""
|
||||
exit 1
|
||||
fi
|
||||
printf '%s\n' "$digest" >"$temporary" || exit 1
|
||||
mv -f -- "$temporary" "$receipt" || exit 1
|
||||
temporary=""
|
||||
)
|
||||
|
||||
panama_path="${PANAMA_PATH:-$HOME/.local/share/Panama}"
|
||||
vicinae_data_dir="${VICINAE_DATA_DIR:-$HOME/.local/share/vicinae}"
|
||||
source_dir="$panama_path/config/local/share/vicinae/scripts"
|
||||
@@ -81,18 +150,36 @@ if [[ -d "$extensions_source" ]] && command -v npm >/dev/null 2>&1; then
|
||||
[[ -f "$extension/package.json" ]] || continue
|
||||
name="$(basename "$extension")"
|
||||
|
||||
# Skip a build that would produce what is already there. `npm ci`
|
||||
# alone takes long enough to be worth not repeating on every re-run of
|
||||
# a stage that is otherwise nearly instant.
|
||||
# Skip only when a prior successful build records the digest of both
|
||||
# manifests and every source byte. Directory mtimes do not change when
|
||||
# an existing source file is edited.
|
||||
built="$vicinae_data_dir/extensions/$name"
|
||||
if [[ -d "$built" && "$extension/src" -ot "$built" ]]; then
|
||||
receipt="$built/.panama-source-sha256"
|
||||
if ! source_digest="$(_vicinae_extension_digest "$extension")"; then
|
||||
printf 'Vicinae extension %s inputs could not be verified; skipping\n' \
|
||||
"$name" >&2
|
||||
continue
|
||||
fi
|
||||
if [[ -f "$receipt" && ! -L "$receipt" ]] \
|
||||
&& cmp -s <(printf '%s\n' "$source_digest") "$receipt"; then
|
||||
printf 'Vicinae extension %s is already built\n' "$name"
|
||||
continue
|
||||
fi
|
||||
|
||||
printf 'Building Vicinae extension %s\n' "$name"
|
||||
if ! (cd "$extension" && npm ci --silent >/dev/null 2>&1 && npm run build >/dev/null 2>&1); then
|
||||
if ! (cd "$extension" && npm ci --silent >/dev/null 2>&1 \
|
||||
&& npm run build >/dev/null 2>&1); then
|
||||
printf 'Vicinae extension %s did not build; skipping\n' "$name" >&2
|
||||
continue
|
||||
fi
|
||||
if ! final_digest="$(_vicinae_extension_digest "$extension")" \
|
||||
|| [[ "$final_digest" != "$source_digest" ]]; then
|
||||
printf 'Vicinae extension %s changed while building; receipt withheld\n' \
|
||||
"$name" >&2
|
||||
continue
|
||||
fi
|
||||
if ! _record_vicinae_digest "$built" "$source_digest"; then
|
||||
printf 'Vicinae extension %s receipt could not be recorded\n' "$name" >&2
|
||||
fi
|
||||
done
|
||||
elif [[ -d "$extensions_source" ]]; then
|
||||
|
||||
Reference in New Issue
Block a user