Files
omarchy-pkgs/bin/sync-upstream
T
Ryan Hughes 3a70d3279c sync-upstream: ignore ")" in comments when rewriting checksum arrays
set_pkgbuild_array ended its skip over the old array at the first line
holding a ")", comments included. voxtype-bin annotates its arrays with
"# Quickshell OSD launcher + audio-bridge sidecar (new in v0.7.5)", so
the rewrite stopped there and left the rest of the old array behind a
stray ")". Every scheduled sync since voxtype 1.1.0 failed with
"Rewritten PKGBUILD is not valid shell".

Strip comments before looking for the closing paren, add a self-test
fixture that fails on the old awk, and take the update it was blocking:
voxtype-bin 1.0.1 -> 1.1.0.
2026-09-26 19:57:09 -04:00

1070 lines
43 KiB
Bash
Executable File

#!/bin/bash
set -euo pipefail
BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..")
source "$BUILD_ROOT/helpers/message-helpers.sh"
source "$BUILD_ROOT/helpers/paths.sh"
source "$BUILD_ROOT/helpers/package-metadata.sh"
source "$BUILD_ROOT/helpers/upstream-github.sh"
TEMP_DIR=$(mktemp -d)
trap 'rm -rf "$TEMP_DIR"' EXIT
export UPSTREAM_CACHE_DIR="$TEMP_DIR/watch-cache"
SPECIFIC_PACKAGES=()
usage() {
cat <<EOF
Usage: $0 [PACKAGE...]
Update packages that track an upstream vendor release feed instead of the AUR.
Packages opt in declaratively through "upstream" in .omarchy/package.json.
Providers cover GitHub Releases with checksum manifests or API asset digests,
semver-shaped git tags, npm dist-tags, and plain Debian Packages indexes. See
README.md for the schemas. Anything outside those conventions may provide
pkgbuilds/<package>/.omarchy/upstream.sh, a hook that reports JSON on stdout:
{
"pkgver": "1.2.3",
"sha256sums": { "x86_64": ["<sha256>"], "aarch64": ["<sha256>"] }
}
Architecture keys become sha256sums_<arch> in the PKGBUILD; the key "any" means
the unsuffixed sha256sums array. An empty object ({}) reports no update.
When the reported version is newer than the checked-in one, pkgver and the
listed checksum arrays are rewritten and pkgrel is reset to 1.
A package may declare "min_release_age" in .omarchy/package.json ("24h", "2d",
or bare seconds) to quarantine fresh releases until maintainers have had time
to pull a bad or compromised one. The window is exported to the hook as
MIN_RELEASE_AGE_SECONDS so it can select the newest release that has already
cleared it, and enforced here as a backstop: the hook must then report
"published_at" (ISO 8601), and a release younger than the window is treated
as no update. A maintainer shipping an emergency update inside the window
runs: BYPASS_MIN_RELEASE_AGE=1 $0 <package>. Scheduled automation never sets
the bypass, so the resulting change still goes through a reviewed PR.
Arguments:
PACKAGE One or more package names to update (optional)
Commands:
self-test Run the offline fixture tests for release selection, the
quarantine backstop, and metadata parsing
Examples:
$0 # Update every package with an upstream source
$0 openai-codex-desktop # Update specific packages
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help)
usage
exit 0
;;
--*)
print_error "Unknown option: $1"
exit 1
;;
*)
SPECIFIC_PACKAGES+=("$1")
shift
;;
esac
done
if ! command -v vercmp >/dev/null 2>&1; then
print_error "vercmp not found: this needs pacman to decide whether a release is an upgrade"
exit 1
fi
print_header "Upstream Package Sync"
UPDATED=0
SKIPPED=0
FAILED=0
SPECIFIC_MODE=false
get_pkgver() {
local package_dir="$1"
grep -m1 '^pkgver=' "$package_dir/PKGBUILD" | cut -d= -f2- | tr -d "\"'"
}
assert_single_assignment() {
local pkgbuild="$1"
local pattern="$2"
local label="$3"
if [[ $(grep -c "$pattern" "$pkgbuild") -ne 1 ]]; then
print_error "Expected exactly one $label assignment in $pkgbuild"
return 1
fi
}
set_pkgbuild_scalar() {
local pkgbuild="$1"
local field="$2"
local value="$3"
assert_single_assignment "$pkgbuild" "^${field}=" "$field" || return 1
sed -i "s/^${field}=.*/${field}=${value}/" "$pkgbuild"
}
# Replace an array assignment, however many lines the original spans.
set_pkgbuild_array() {
local pkgbuild="$1"
local name="$2"
shift 2
local values=("$@")
assert_single_assignment "$pkgbuild" "^${name}=(" "$name" || return 1
if [[ ${#values[@]} -eq 0 ]]; then
print_error "No values to write for ${name}"
return 1
fi
local block="$TEMP_DIR/array-block"
if [[ ${#values[@]} -eq 1 ]]; then
printf "%s=('%s')\n" "$name" "${values[0]}" > "$block" || return 1
else
{
printf '%s=(\n' "$name"
printf " '%s'\n" "${values[@]}"
printf ')\n'
} > "$block" || return 1
fi
local rewritten="$TEMP_DIR/pkgbuild-rewritten"
if ! awk -v prefix="${name}=(" -v block="$block" '
# The code on a line, minus any comment. Checksum arrays hold quoted hex
# (or SKIP), so a "#" can only ever start a comment here.
function code(s) { sub(/#.*/, "", s); return s }
!replaced && index($0, prefix) == 1 {
while ((getline line < block) > 0) print line
close(block)
replaced = 1
# A ")" anywhere past the opening closes the array; testing for one at end
# of line instead would treat a trailing comment as a continuation and eat
# every line up to the next ")". A ")" inside a comment -- "# sidecar
# (new in v0.7.5)" -- closes nothing, and ending the skip there would
# leave the rest of the old array behind a stray ")".
if (index(code(substr($0, length(prefix) + 1)), ")") == 0) skipping = 1
next
}
skipping { if (code($0) ~ /\)/) skipping = 0; next }
{ print }
' "$pkgbuild" > "$rewritten"; then
print_error "Failed to rewrite ${name} in $pkgbuild"
rm -f "$rewritten"
return 1
fi
mv "$rewritten" "$pkgbuild"
}
# Backstop verdict for a reported release against min_release_age. Returns 0
# when old enough (or no policy is set, or the bypass is deliberate), 1 when
# the release is younger than the window, 2 when the report carries no usable
# published_at and the age cannot be established at all.
release_age_status() {
local release="$1" min_age="$2"
(( min_age > 0 )) || return 0
[[ "${BYPASS_MIN_RELEASE_AGE:-}" != "1" ]] || return 0
local published_at published_epoch
published_at=$(jq -r '.published_at // empty' <<<"$release")
# Strict ISO 8601 before GNU date sees it: date also accepts relative
# expressions like "2 days ago", which would let a buggy hook fabricate an
# age instead of failing closed.
if [[ ! "$published_at" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?(Z|[+-][0-9]{2}:?[0-9]{2})$ ]] \
|| ! published_epoch=$(date --date="$published_at" +%s 2>/dev/null); then
return 2
fi
(( $(date +%s) - published_epoch >= min_age )) || return 1
}
# pacman's own comparator, because nothing else agrees with it at the corners:
# sort -V calls 1.0a newer than 1.0, vercmp calls it older, and pacman is what
# decides whether a published package is an upgrade.
version_is_newer() {
local candidate="$1"
local current="$2"
[[ "$candidate" != "$current" ]] || return 1
[[ "$(vercmp "$candidate" "$current")" -gt 0 ]]
}
validate_release() {
local release="$1"
# pkgver is written into the PKGBUILD, which makepkg sources as shell, so it
# is held to pacman's own character set rather than merely being non-empty.
# The anchors are \A and \z, not ^ and $: jq's $ also matches before a
# trailing newline, which would let "1.0\n" through and break the rewrite.
jq -e '
(.pkgver | type == "string" and test("\\A[A-Za-z0-9._+]+\\z"))
and (.sha256sums | type == "object" and length > 0)
and (.sha256sums | to_entries | all(
.key | test("\\A[a-z0-9_]+\\z")
))
and (.sha256sums | to_entries | all(
.value | type == "array" and length > 0 and all(test("\\A[0-9a-f]{64}\\z"))
))
and (if has("published_at") then (.published_at | type == "string" and length > 0) else true end)
' <<<"$release" >/dev/null
}
# Confirm the rewritten PKGBUILD parses and actually holds what we meant to put
# in it. Editing shell with awk and sed can go wrong in ways no amount of
# pattern-matching anticipates -- an array element carrying a ")" in a comment,
# say -- so the result is checked rather than trusted.
verify_pkgbuild() {
local pkgbuild="$1"
local release="$2"
local pkgver="$3"
shift 3
local arrays=("$@")
if ! bash -n "$pkgbuild" 2>/dev/null; then
print_error "Rewritten PKGBUILD is not valid shell"
return 1
fi
local dump
if ! dump=$(CARCH=x86_64 bash -c '
source "$1" >/dev/null 2>&1 || exit 1
printf "pkgver\t%s\n" "$pkgver"
printf "pkgrel\t%s\n" "$pkgrel"
for name in "${@:2}"; do
declare -n array="$name"
printf "%s\t%s\n" "$name" "${array[*]}"
done
' _ "$pkgbuild" "${arrays[@]}" 2>/dev/null); then
print_error "Rewritten PKGBUILD could not be read back"
return 1
fi
local expected
expected=$(
printf 'pkgver\t%s\n' "$pkgver"
printf 'pkgrel\t1\n'
local array arch
for array in "${arrays[@]}"; do
arch="${array#sha256sums}"
arch="${arch#_}"
[[ -n "$arch" ]] || arch="any"
printf '%s\t%s\n' "$array" \
"$(jq -r --arg arch "$arch" '.sha256sums[$arch] | join(" ")' <<<"$release")"
done
)
if [[ "$dump" != "$expected" ]]; then
print_error "Rewritten PKGBUILD does not hold the reported release"
diff <(echo "$expected") <(echo "$dump") | sed 's/^/ /' >&2 || true
return 1
fi
}
apply_release() {
local package_dir="$1"
local release="$2"
local pkgver="$3"
local pkgbuild="$package_dir/PKGBUILD"
local arch array values
local arrays=()
while IFS= read -r arch; do
if [[ "$arch" == "any" ]]; then
array="sha256sums"
else
array="sha256sums_$arch"
fi
arrays+=("$array")
done < <(jq -r '.sha256sums | keys[]' <<<"$release")
# validate_release guarantees at least one entry, so an empty list here means
# jq died inside the process substitution rather than that there is nothing
# to do.
if [[ ${#arrays[@]} -eq 0 ]]; then
print_error "Could not read the checksum architectures from the reported release"
return 1
fi
# Every edit lands on a scratch copy that replaces the PKGBUILD in one rename
# at the end, so a failure part way through leaves the original untouched
# rather than half updated.
local scratch="$pkgbuild.sync-upstream"
cp "$pkgbuild" "$scratch" || return 1
if ! (
assert_single_assignment "$scratch" '^pkgver=' pkgver || exit 1
assert_single_assignment "$scratch" '^pkgrel=' pkgrel || exit 1
for array in "${arrays[@]}"; do
arch="${array#sha256sums}"
arch="${arch#_}"
[[ -n "$arch" ]] || arch="any"
mapfile -t values < <(jq -r --arg arch "$arch" '.sha256sums[$arch][]' <<<"$release")
set_pkgbuild_array "$scratch" "$array" "${values[@]}" || exit 1
done
set_pkgbuild_scalar "$scratch" pkgver "$pkgver" || exit 1
set_pkgbuild_scalar "$scratch" pkgrel 1 || exit 1
verify_pkgbuild "$scratch" "$release" "$pkgver" "${arrays[@]}" || exit 1
); then
rm -f "$scratch"
return 1
fi
chmod --reference="$pkgbuild" "$scratch"
mv "$scratch" "$pkgbuild"
}
sync_package() {
local package="$1"
local package_dir="$PKGBUILDS_DIR/$package"
local hook="$package_dir/.omarchy/upstream.sh"
if [[ ! -f "$package_dir/PKGBUILD" ]]; then
print_error "Package $package has no PKGBUILD"
((++FAILED))
return 0
fi
if jq -e '.sync == false' "$package_dir/.omarchy/package.json" >/dev/null 2>&1; then
print_info "Skipping $package: upstream updates held by sync=false"
((++SKIPPED))
return 0
fi
local provider has_upstream=false
provider=$(package_upstream_provider "$package_dir")
if package_has_upstream_provider "$package_dir"; then
has_upstream=true
fi
# A present-but-unusable declaration fails loudly; treating it like "no
# upstream source" would silently drop the package from scheduled runs.
if [[ "$has_upstream" == true && -z "$provider" ]]; then
print_error "Package $package has an unusable or ambiguous upstream declaration"
((++FAILED))
return 0
fi
if [[ -n "$provider" && -f "$hook" ]]; then
print_error "Package $package declares both an upstream provider and an upstream.sh hook; keep exactly one"
((++FAILED))
return 0
fi
if [[ -z "$provider" && ! -f "$hook" ]]; then
if [[ "$SPECIFIC_MODE" == true ]]; then
print_error "Package $package has neither an upstream declaration nor .omarchy/upstream.sh"
((++FAILED))
else
print_info "Skipping $package: no upstream source"
((++SKIPPED))
fi
return 0
fi
local min_age
if ! min_age=$(package_min_release_age_seconds "$package_dir"); then
print_error "Invalid min_release_age in $package_dir/.omarchy/package.json"
((++FAILED))
return 0
fi
print_info "Checking $package for upstream releases..."
if [[ "$provider" == watch ]]; then
local result
if ! result=$(python3 "$BUILD_ROOT/helpers/upstream-watch.py" sync "$package_dir" --min-age "$min_age"); then
print_error "Upstream watch failed for $package"
((++FAILED))
elif [[ $(jq -r .status <<<"$result") == updated ]]; then
print_success " $(jq -r '.before + " -> " + .after' <<<"$result")"
((++UPDATED))
else
print_info " $(jq -r '.reason' <<<"$result")"
((++SKIPPED))
fi
return 0
fi
local release release_status=0
if [[ -n "$provider" ]]; then
case "$provider" in
github) release=$(github_upstream_release "$package_dir" "$min_age") || release_status=$? ;;
git_tags) release=$(git_tags_upstream_release "$package_dir") || release_status=$? ;;
npm) release=$(npm_upstream_release "$package_dir") || release_status=$? ;;
debian) release=$(debian_upstream_release "$package_dir") || release_status=$? ;;
esac
if [[ ${release_status:-0} -ne 0 ]]; then
print_error "$provider upstream provider failed for $package"
((++FAILED))
return 0
fi
elif ! release=$(cd "$package_dir" && PACKAGE_NAME="$package" \
MIN_RELEASE_AGE_SECONDS="$min_age" \
BYPASS_MIN_RELEASE_AGE="${BYPASS_MIN_RELEASE_AGE:-}" \
bash .omarchy/upstream.sh); then
print_error "Upstream hook failed for $package"
((++FAILED))
return 0
fi
if ! jq -e . >/dev/null 2>&1 <<<"$release"; then
print_error "Upstream hook for $package did not report valid JSON"
((++FAILED))
return 0
fi
if [[ "$(jq -r 'has("pkgver")' <<<"$release")" == "false" ]]; then
print_info " No upstream update reported"
((++SKIPPED))
return 0
fi
if ! validate_release "$release"; then
print_error "Upstream hook for $package reported a malformed release"
((++FAILED))
return 0
fi
# Backstop for min_release_age: the selection already honors the window,
# but a provider or hook bug must not be able to ship a release younger
# than the policy.
local age_status=0
release_age_status "$release" "$min_age" || age_status=$?
case "$age_status" in
1)
print_warning " Reported release is inside the ${min_age}s minimum release age; leaving it alone"
((++SKIPPED))
return 0
;;
2)
print_error "min_release_age is set for $package but its source reported no usable published_at; refusing an unverifiable release"
((++FAILED))
return 0
;;
esac
local pkgver current_pkgver
pkgver=$(jq -r '.pkgver' <<<"$release")
current_pkgver=$(get_pkgver "$package_dir")
if [[ -z "$current_pkgver" ]]; then
print_error "Could not read pkgver from $package_dir/PKGBUILD"
((++FAILED))
return 0
fi
if [[ "$pkgver" == "$current_pkgver" ]]; then
print_info " Already at $current_pkgver"
((++SKIPPED))
return 0
fi
if ! version_is_newer "$pkgver" "$current_pkgver"; then
print_warning " Upstream reports $pkgver, older than the checked-in $current_pkgver; leaving it alone"
((++SKIPPED))
return 0
fi
if ! apply_release "$package_dir" "$release" "$pkgver"; then
print_error "Failed to update $package"
((++FAILED))
return 0
fi
print_success " $current_pkgver -> $pkgver"
((++UPDATED))
}
# Offline fixture tests: the network fetches in helpers/upstream-github.sh
# are swapped for fixture readers, everything else runs the production code
# paths. Covers release selection (fallback past quarantined releases,
# draft/prerelease filtering, bypass, unchanged version), failure paths
# (unusable tags/timestamps, missing checksums), checksum template mapping
# for both architectures, release API digests, the min_release_age backstop,
# the duration parser, and manifest validation.
cmd_self_test() {
local failures=0
check() {
local desc="$1" expected="$2" got="$3"
if [[ "$expected" == "$got" ]]; then
echo " ok: $desc"
else
echo " FAIL: $desc (expected '$expected', got '$got')"
failures=$((failures + 1))
fi
}
local pkg="$TEMP_DIR/selftest-pkg"
mkdir -p "$pkg/.omarchy"
printf 'pkgver=1.0.0\npkgrel=1\n' > "$pkg/PKGBUILD"
cat > "$pkg/.omarchy/package.json" <<'EOF'
{
"source": "local",
"min_release_age": "24h",
"upstream": {
"github": "example/tool",
"checksums": "SHASUMS256.txt",
"assets": {
"x86_64": "tool-{tag}-x64.tar.xz",
"aarch64": "tool-v{pkgver}-arm64.tar.xz"
}
}
}
EOF
local young old2d old3d
young=$(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ)
old2d=$(date -u -d '2 days ago' +%Y-%m-%dT%H:%M:%SZ)
old3d=$(date -u -d '3 days ago' +%Y-%m-%dT%H:%M:%SZ)
# v2.0.0 is inside the 24h window; v1.9.9/v1.9.8 are a prerelease and a
# draft that would outrank v1.9.0 if the filters failed.
local sum_x19 sum_a19 sum_x20 sum_a20
sum_x19=$(printf 'a%.0s' {1..64})
sum_a19=$(printf 'b%.0s' {1..64})
sum_x20=$(printf 'c%.0s' {1..64})
sum_a20=$(printf 'd%.0s' {1..64})
FIXTURE_RELEASES=$(jq -n --arg young "$young" --arg old2 "$old2d" --arg old3 "$old3d" '[
{tag_name: "v2.0.0", published_at: $young, draft: false, prerelease: false},
{tag_name: "v1.9.9", published_at: $old2, draft: false, prerelease: true},
{tag_name: "v1.9.8", published_at: $old2, draft: true, prerelease: false},
{tag_name: "v1.9.0", published_at: $old2, draft: false, prerelease: false},
{tag_name: "v1.8.0", published_at: $old3, draft: false, prerelease: false}
]')
FIXTURE_CHECKSUMS=$(printf '%s\n' \
"$sum_x19 ./tool-v1.9.0-x64.tar.xz" \
"$sum_a19 tool-v1.9.0-arm64.tar.xz" \
"$sum_x20 *tool-v2.0.0-x64.tar.xz" \
"$sum_a20 tool-v2.0.0-arm64.tar.xz")
github_fetch_releases() { printf '%s' "$FIXTURE_RELEASES"; }
github_fetch_checksums() { printf '%s\n' "$FIXTURE_CHECKSUMS"; }
echo "Release selection:"
local out
out=$(github_upstream_release "$pkg" 86400 2>/dev/null) || out="<error>"
check "quarantine falls back past the young v2.0.0" "1.9.0" "$(jq -r '.pkgver // "<none>"' <<<"$out")"
check "selected release reports its published_at" "$old2d" "$(jq -r '.published_at // "<none>"' <<<"$out")"
check "x86_64 checksum via {tag} template and ./ prefix" "$sum_x19" "$(jq -r '.sha256sums.x86_64[0] // "<none>"' <<<"$out")"
check "aarch64 checksum via {pkgver} template" "$sum_a19" "$(jq -r '.sha256sums.aarch64[0] // "<none>"' <<<"$out")"
out=$(github_upstream_release "$pkg" 0 2>/dev/null) || out="<error>"
check "no policy selects the newest stable release" "2.0.0" "$(jq -r '.pkgver // "<none>"' <<<"$out")"
check "prerelease v1.9.9 and draft v1.9.8 are never selected" "" "$(jq -r 'select(.pkgver == "1.9.9" or .pkgver == "1.9.8") | .pkgver' <<<"$out")"
out=$(BYPASS_MIN_RELEASE_AGE=1 github_upstream_release "$pkg" 86400 2>/dev/null) || out="<error>"
check "bypass lifts the quarantine" "2.0.0" "$(jq -r '.pkgver // "<none>"' <<<"$out")"
check "x86_64 checksum via * binary-mode prefix" "$sum_x20" "$(jq -r '.sha256sums.x86_64[0] // "<none>"' <<<"$out")"
cp "$pkg/.omarchy/package.json" "$pkg/normal.json"
jq '.upstream.latest_only = true' "$pkg/normal.json" > "$pkg/.omarchy/package.json"
FIXTURE_RELEASES=$(jq -n --arg old "$old2d" '[
{tag_name: "v1.9.0", published_at: $old, draft: false, prerelease: false},
{tag_name: "v0.4.1-8", published_at: $old, draft: false, prerelease: false}
]')
FIXTURE_CHECKSUMS=$(printf '%s\n' \
"$sum_x19 ./tool-v1.9.0-x64.tar.xz" \
"$sum_a19 ./tool-v1.9.0-arm64.tar.xz")
out=$(github_upstream_release "$pkg" 0 2>/dev/null) || out="<error>"
check "latest_only ignores incompatible historical tags" "1.9.0" "$(jq -r '.pkgver // "<none>"' <<<"$out")"
cp "$pkg/normal.json" "$pkg/.omarchy/package.json"
FIXTURE_RELEASES=$(jq -n --arg young "$young" --arg old2 "$old2d" --arg old3 "$old3d" '[
{tag_name: "v2.0.0", published_at: $young, draft: false, prerelease: false},
{tag_name: "v1.9.9", published_at: $old2, draft: false, prerelease: true},
{tag_name: "v1.9.8", published_at: $old2, draft: true, prerelease: false},
{tag_name: "v1.9.0", published_at: $old2, draft: false, prerelease: false},
{tag_name: "v1.8.0", published_at: $old3, draft: false, prerelease: false}
]')
FIXTURE_CHECKSUMS=$(printf '%s\n' \
"$sum_x19 ./tool-v1.9.0-x64.tar.xz" \
"$sum_a19 tool-v1.9.0-arm64.tar.xz" \
"$sum_x20 *tool-v2.0.0-x64.tar.xz" \
"$sum_a20 tool-v2.0.0-arm64.tar.xz")
out=$(github_upstream_release "$pkg" 8640000 2>/dev/null) || out="<error>"
check "everything quarantined reports no update" "{}" "$(jq -c . <<<"$out")"
printf 'pkgver=1.9.0\npkgrel=1\n' > "$pkg/PKGBUILD"
out=$(github_upstream_release "$pkg" 86400 2>/dev/null) || out="<error>"
check "already checked in reports no update" "{}" "$(jq -c . <<<"$out")"
printf 'pkgver=1.0.0\npkgrel=1\n' > "$pkg/PKGBUILD"
echo "Failure paths:"
local rc
FIXTURE_RELEASES=$(jq -n '[{tag_name: "v1.9.0", published_at: "not-a-date", draft: false, prerelease: false}]')
rc=0; github_upstream_release "$pkg" 86400 >/dev/null 2>&1 || rc=$?
check "invalid published_at fails the sync" "1" "$rc"
FIXTURE_RELEASES=$(jq -n --arg old "$old2d" '[{tag_name: "release 1.9!", published_at: $old, draft: false, prerelease: false}]')
rc=0; github_upstream_release "$pkg" 86400 >/dev/null 2>&1 || rc=$?
check "unusable tag fails the sync" "1" "$rc"
FIXTURE_RELEASES=$(jq -n --arg old "$old2d" '[{tag_name: "v1.9.0", published_at: $old, draft: false, prerelease: false}]')
FIXTURE_CHECKSUMS="$sum_x19 ./tool-v1.9.0-x64.tar.xz"
rc=0; github_upstream_release "$pkg" 86400 >/dev/null 2>&1 || rc=$?
check "missing aarch64 checksum fails the sync" "1" "$rc"
# A vendor publishing no manifest: checksums come from the digests the
# release API reports per asset, with nothing fetched beyond the feed.
echo "Release API digests:"
local digpkg="$TEMP_DIR/selftest-digests"
mkdir -p "$digpkg/.omarchy"
printf 'pkgver=1.0.0\npkgrel=1\n' > "$digpkg/PKGBUILD"
cat > "$digpkg/.omarchy/package.json" <<'EOF'
{
"source": "local",
"upstream": {
"github": "example/tool",
"digests": true,
"assets": {
"x86_64": "tool-{pkgver}-1-x86_64.pkg.tar.zst",
"aarch64": "tool-{pkgver}-1-aarch64.pkg.tar.zst"
}
}
}
EOF
FIXTURE_RELEASES=$(jq -n --arg old "$old2d" --arg x "$sum_x19" --arg a "$sum_a19" '[
{tag_name: "v1.9.0", published_at: $old, draft: false, prerelease: false, assets: [
{name: "tool-1.9.0-1-x86_64.pkg.tar.zst", digest: ("sha256:" + $x)},
{name: "tool-1.9.0-1-aarch64.pkg.tar.zst", digest: ("sha256:" + $a)},
{name: "tool-1.9.0-1-x86_64.rpm", digest: "sha256:0000000000000000000000000000000000000000000000000000000000000000"}
]}
]')
FIXTURE_CHECKSUMS="manifest must not be consulted"
out=$(github_upstream_release "$digpkg" 0 2>/dev/null) || out="<error>"
check "x86_64 checksum via the asset digest" "$sum_x19" "$(jq -r '.sha256sums.x86_64[0] // "<none>"' <<<"$out")"
check "aarch64 checksum via the asset digest" "$sum_a19" "$(jq -r '.sha256sums.aarch64[0] // "<none>"' <<<"$out")"
FIXTURE_RELEASES=$(jq -n --arg old "$old2d" --arg x "$sum_x19" '[
{tag_name: "v1.9.0", published_at: $old, draft: false, prerelease: false, assets: [
{name: "tool-1.9.0-1-x86_64.pkg.tar.zst", digest: ("sha256:" + $x)},
{name: "tool-1.9.0-1-aarch64.pkg.tar.zst"}
]}
]')
rc=0; github_upstream_release "$digpkg" 0 >/dev/null 2>&1 || rc=$?
check "asset without a digest fails the sync" "1" "$rc"
FIXTURE_RELEASES=$(jq -n --arg old "$old2d" --arg x "$sum_x19" '[
{tag_name: "v1.9.0", published_at: $old, draft: false, prerelease: false, assets: [
{name: "tool-1.9.0-1-x86_64.pkg.tar.zst", digest: ("sha256:" + $x)},
{name: "tool-1.9.0-2-aarch64.pkg.tar.zst", digest: ("sha256:" + $x)}
]}
]')
rc=0; github_upstream_release "$digpkg" 0 >/dev/null 2>&1 || rc=$?
check "asset re-cut under another release number fails the sync" "1" "$rc"
FIXTURE_RELEASES=$(jq -n --arg old "$old2d" --arg x "$sum_x19" --arg a "$sum_a19" '[
{tag_name: "v1.9.0", published_at: $old, draft: false, prerelease: false, assets: [
{name: "tool-1.9.0-1-x86_64.pkg.tar.zst", digest: $x},
{name: "tool-1.9.0-1-aarch64.pkg.tar.zst", digest: ("sha256:" + $a)}
]}
]')
rc=0; github_upstream_release "$digpkg" 0 >/dev/null 2>&1 || rc=$?
check "digest without the sha256: prefix fails the sync" "1" "$rc"
# The provider enforces the declaration shape itself: scheduled runs reach
# it without validate_package_metadata.
jq '.upstream.checksums = "SHASUMS256.txt"' "$digpkg/.omarchy/package.json" > "$digpkg/both.json"
cp "$digpkg/.omarchy/package.json" "$digpkg/good.json"
cp "$digpkg/both.json" "$digpkg/.omarchy/package.json"
rc=0; github_upstream_release "$digpkg" 0 >/dev/null 2>&1 || rc=$?
check "provider rejects checksums and digests together" "1" "$rc"
jq '.upstream.digests = "true"' "$digpkg/good.json" > "$digpkg/.omarchy/package.json"
rc=0; github_upstream_release "$digpkg" 0 >/dev/null 2>&1 || rc=$?
check "provider rejects a non-boolean digests" "1" "$rc"
cp "$digpkg/good.json" "$digpkg/.omarchy/package.json"
echo "Ordered GitHub assets with supplemental sources:"
local multipkg="$TEMP_DIR/selftest-multi-assets" multi_sum support_sum multi_vst
mkdir -p "$multipkg/.omarchy"
printf 'pkgver=1.0.0\npkgrel=1\n' > "$multipkg/PKGBUILD"
cat > "$multipkg/.omarchy/package.json" <<'EOF'
{
"source": "local",
"upstream": {
"github": "example/tool",
"digests": true,
"assets": {
"x86_64": ["tool-{pkgver}-x86_64", "tool-{pkgver}-x86_64.asc"],
"aarch64": ["tool-{pkgver}-aarch64", "tool-{pkgver}-aarch64.asc"]
},
"sources": {
"any": ["https://example.test/tool/{tag}/support.txt"]
}
}
}
EOF
local sum_x19_sig sum_a19_sig
sum_x19_sig=$(printf '1%.0s' {1..64})
sum_a19_sig=$(printf '2%.0s' {1..64})
FIXTURE_RELEASES=$(jq -n --arg old "$old2d" --arg x "$sum_x19" --arg xs "$sum_x19_sig" \
--arg a "$sum_a19" --arg as "$sum_a19_sig" '[
{tag_name: "v1.9.0", published_at: $old, draft: false, prerelease: false, assets: [
{name: "tool-1.9.0-x86_64", digest: ("sha256:" + $x)},
{name: "tool-1.9.0-x86_64.asc", digest: ("sha256:" + $xs)},
{name: "tool-1.9.0-aarch64", digest: ("sha256:" + $a)},
{name: "tool-1.9.0-aarch64.asc", digest: ("sha256:" + $as)}
]}
]')
upstream_fetch_source() { printf 'remote fixture for %s\n' "$1" > "$2"; }
multi_sum=$(github_upstream_release "$multipkg" 0 2>/dev/null) || multi_sum="<error>"
support_sum=$(printf 'remote fixture for %s\n' 'https://example.test/tool/v1.9.0/support.txt' | sha256sum | cut -d' ' -f1)
check "ordered GitHub assets produce an ordered checksum array" "$sum_x19 $sum_x19_sig" \
"$(jq -r '.sha256sums.x86_64 | join(" ")' <<<"$multi_sum")"
check "GitHub supplemental source is downloaded and hashed" "$support_sum" \
"$(jq -r '.sha256sums.any[0]' <<<"$multi_sum")"
multi_vst=0; validate_package_metadata "$multipkg" >/dev/null || multi_vst=$?
check "GitHub asset lists and disjoint sources validate" "0" "$multi_vst"
jq '.upstream.sources.x86_64 = ["https://example.test/duplicate"]' \
"$multipkg/.omarchy/package.json" > "$multipkg/overlap.json"
cp "$multipkg/.omarchy/package.json" "$multipkg/good.json"
cp "$multipkg/overlap.json" "$multipkg/.omarchy/package.json"
multi_vst=0; validate_package_metadata "$multipkg" >/dev/null || multi_vst=$?
check "GitHub assets and sources cannot target the same checksum array" "1" "$multi_vst"
cp "$multipkg/good.json" "$multipkg/.omarchy/package.json"
echo "Debian Packages provider:"
local debpkg="$TEMP_DIR/selftest-debian" deb_sum deb_x_sum deb_a_sum deb_vst
mkdir -p "$debpkg/.omarchy"
printf 'pkgver=1.0.0\npkgrel=1\nsha256sums_x86_64=("old")\nsha256sums_aarch64=("old")\n' > "$debpkg/PKGBUILD"
cat > "$debpkg/.omarchy/package.json" <<'EOF'
{
"source": "local",
"upstream": {
"debian": "https://packages.example.test/dists/stable/main/binary-amd64/Packages",
"package": "example-app",
"sources": {
"x86_64": ["https://downloads.example.test/app-{pkgver}-x64.tar.gz"],
"aarch64": ["https://downloads.example.test/app-{pkgver}-arm64.tar.gz"]
}
}
}
EOF
debian_fetch_packages() {
cat <<'EOF'
Package: unrelated
Version: 99.0.0
Package: example-app
Version: 1.9.0
Package: example-app
Version: 1.10.0
EOF
}
deb_sum=$(debian_upstream_release "$debpkg")
deb_x_sum=$(printf 'remote fixture for %s\n' 'https://downloads.example.test/app-1.10.0-x64.tar.gz' | sha256sum | cut -d' ' -f1)
deb_a_sum=$(printf 'remote fixture for %s\n' 'https://downloads.example.test/app-1.10.0-arm64.tar.gz' | sha256sum | cut -d' ' -f1)
check "Debian provider selects the newest exact package stanza" "1.10.0" "$(jq -r '.pkgver' <<<"$deb_sum")"
check "Debian x86_64 source is hashed" "$deb_x_sum" "$(jq -r '.sha256sums.x86_64[0]' <<<"$deb_sum")"
check "Debian aarch64 source is hashed" "$deb_a_sum" "$(jq -r '.sha256sums.aarch64[0]' <<<"$deb_sum")"
deb_vst=0; validate_package_metadata "$debpkg" >/dev/null || deb_vst=$?
check "Debian declaration validates" "0" "$deb_vst"
printf 'pkgver=1.10.0\npkgrel=1\nsha256sums_x86_64=("old")\nsha256sums_aarch64=("old")\n' > "$debpkg/PKGBUILD"
check "checked-in Debian version avoids source downloads" "{}" "$(debian_upstream_release "$debpkg" | jq -c .)"
echo "End-to-end Debian sync with the checked-in 1password recipe:"
local one_root="$TEMP_DIR/e2e-1password" one_dir one_version=8.12.35
mkdir -p "$one_root"
cp -a "$BUILD_ROOT/pkgbuilds/1password" "$one_root/1password"
one_dir="$one_root/1password"
# Keep the real recipe shape, but make the fixture's starting version stable.
# Otherwise a routine package update can outrun the mocked release below.
sed -i -e 's/^pkgver=.*/pkgver=8.12.34/' -e 's/^pkgrel=.*/pkgrel=2/' "$one_dir/PKGBUILD"
debian_fetch_packages() {
printf 'Package: 1password\nVersion: %s\n' "$one_version"
}
upstream_fetch_source() { printf 'remote fixture for %s\n' "$1" > "$2"; }
local one_prev_updated=$UPDATED one_prev_failed=$FAILED
PKGBUILDS_DIR="$one_root" sync_package 1password >/dev/null 2>&1 || true
check "1password sync updates without failures" "updated=1 failed=0" \
"updated=$((UPDATED - one_prev_updated)) failed=$((FAILED - one_prev_failed))"
check "1password pkgver is the single download version source" "$one_version" \
"$(grep -m1 '^pkgver=' "$one_dir/PKGBUILD" | cut -d= -f2-)"
check "1password pkgrel resets to 1" "1" \
"$(grep -m1 '^pkgrel=' "$one_dir/PKGBUILD" | cut -d= -f2-)"
check "1password x86 URL follows the rewritten pkgver" "1password-${one_version}.x64.tar.gz" \
"$(CARCH=x86_64 bash -c 'source "$1"; basename "${source_x86_64[0]}"' _ "$one_dir/PKGBUILD")"
check "1password ARM URL follows the rewritten pkgver" "1password-${one_version}.arm64.tar.gz" \
"$(CARCH=aarch64 bash -c 'source "$1"; basename "${source_aarch64[0]}"' _ "$one_dir/PKGBUILD")"
echo "Quarantine backstop:"
local rel st
rel=$(jq -n --arg p "$old2d" '{pkgver: "1.9.0", published_at: $p, sha256sums: {}}')
st=0; release_age_status "$rel" 86400 || st=$?
check "old enough passes" "0" "$st"
rel=$(jq -n --arg p "$young" '{pkgver: "2.0.0", published_at: $p, sha256sums: {}}')
st=0; release_age_status "$rel" 86400 || st=$?
check "too young is held" "1" "$st"
st=0; release_age_status "$rel" 0 || st=$?
check "no policy passes anything" "0" "$st"
st=0; BYPASS_MIN_RELEASE_AGE=1 release_age_status "$rel" 86400 || st=$?
check "deliberate bypass passes" "0" "$st"
rel=$(jq -n '{pkgver: "2.0.0", sha256sums: {}}')
st=0; release_age_status "$rel" 86400 || st=$?
check "missing published_at is unprovable" "2" "$st"
rel=$(jq -n '{pkgver: "2.0.0", published_at: "2 days ago", sha256sums: {}}')
st=0; release_age_status "$rel" 86400 || st=$?
check "relative-date expression is unprovable, not an age" "2" "$st"
rel=$(jq -n --arg p "$(date -u -d '2 days ago' +%Y-%m-%dT%H:%M:%S+00:00)" '{pkgver: "1.9.0", published_at: $p, sha256sums: {}}')
st=0; release_age_status "$rel" 86400 || st=$?
check "numeric-offset ISO timestamp passes" "0" "$st"
echo "Duration parser:"
local agepkg="$TEMP_DIR/selftest-age"
mkdir -p "$agepkg/.omarchy"
check_age() {
local json_value="$1" expected="$2" got
jq -n "{source: \"local\", min_release_age: $json_value}" > "$agepkg/.omarchy/package.json"
got=$(package_min_release_age_seconds "$agepkg") || got="<reject>"
check "min_release_age $json_value" "$expected" "$got"
}
check_age '"24h"' 86400
check_age '"90m"' 5400
check_age '"2d"' 172800
check_age '3600' 3600
check_age '"600s"' 600
check_age '"010h"' 36000
check_age '"abc"' "<reject>"
check_age '"24hh"' "<reject>"
check_age 'false' "<reject>"
check_age '""' "<reject>"
check_age '"9999999999"' "<reject>"
echo "Manifest validation:"
printf 'pkgver=1.0.0\n' > "$agepkg/PKGBUILD"
local vst
echo '{"source": "local", "upstream": false}' > "$agepkg/.omarchy/package.json"
vst=0; validate_package_metadata "$agepkg" >/dev/null || vst=$?
check "upstream: false is rejected" "1" "$vst"
echo '{"source": "local", "upstream": {"github": "example/tool"}}' > "$agepkg/.omarchy/package.json"
vst=0; validate_package_metadata "$agepkg" >/dev/null || vst=$?
check "upstream without checksums/assets is rejected" "1" "$vst"
echo '{"source": "local", "upstream": {"github": "example/tool", "checksums": "SHASUMS256.txt", "digests": true, "assets": {"x86_64": "a"}}}' > "$agepkg/.omarchy/package.json"
vst=0; validate_package_metadata "$agepkg" >/dev/null || vst=$?
check "upstream with both checksums and digests is rejected" "1" "$vst"
echo '{"source": "local", "upstream": {"github": "example/tool", "digests": "yes", "assets": {"x86_64": "a"}}}' > "$agepkg/.omarchy/package.json"
vst=0; validate_package_metadata "$agepkg" >/dev/null || vst=$?
check "non-boolean digests is rejected" "1" "$vst"
echo '{"source": "local", "upstream": {"github": "example/tool", "checksums": false, "digests": true, "assets": {"x86_64": "a"}}}' > "$agepkg/.omarchy/package.json"
vst=0; validate_package_metadata "$agepkg" >/dev/null || vst=$?
check "checksums: false alongside digests is rejected" "1" "$vst"
echo '{"source": "local", "upstream": {"github": "example/tool", "checksums": "SUMS", "digests": null, "assets": {"x86_64": "a"}}}' > "$agepkg/.omarchy/package.json"
vst=0; validate_package_metadata "$agepkg" >/dev/null || vst=$?
check "digests: null is rejected" "1" "$vst"
echo '{"source": "local", "upstream": {"github": "example/tool", "checksums": "SUMS", "digests": false, "assets": {"x86_64": "a"}}}' > "$agepkg/.omarchy/package.json"
vst=0; validate_package_metadata "$agepkg" >/dev/null || vst=$?
check "digests: false beside a checksums manifest is accepted" "0" "$vst"
echo '{"source": "local", "upstream": {"github": "example/tool", "digests": false, "assets": {"x86_64": "a"}}}' > "$agepkg/.omarchy/package.json"
vst=0; validate_package_metadata "$agepkg" >/dev/null || vst=$?
check "digests: false alone is rejected" "1" "$vst"
cp "$digpkg/.omarchy/package.json" "$agepkg/.omarchy/package.json"
vst=0; validate_package_metadata "$agepkg" >/dev/null || vst=$?
check "the digests declaration shape is accepted" "0" "$vst"
echo '{"source":"local","upstream":{"github":"example/tool","git_tags":"https://example/tool.git","checksums":"sums","assets":{"any":"tool"}}}' > "$agepkg/.omarchy/package.json"
vst=0; validate_package_metadata "$agepkg" >/dev/null || vst=$?
check "multiple provider types are rejected" "1" "$vst"
cp "$pkg/.omarchy/package.json" "$agepkg/.omarchy/package.json"
vst=0; validate_package_metadata "$agepkg" >/dev/null || vst=$?
check "the real declaration shape is accepted" "0" "$vst"
echo "Git-tag provider:"
local tagpkg="$TEMP_DIR/selftest-tags" tag_sum remote_sum local_sum
mkdir -p "$tagpkg/.omarchy"
printf 'pkgver=1.0.0\npkgrel=4\nsha256sums=("old" "old")\n' > "$tagpkg/PKGBUILD"
printf 'local fixture\n' > "$tagpkg/local.patch"
cat > "$tagpkg/.omarchy/package.json" <<'EOF'
{
"source": "local",
"upstream": {
"git_tags": "https://example.test/tool.git",
"tag_pattern": "release/{pkgver}",
"sources": {
"any": ["https://downloads.example.test/tool-{pkgver}.tar.gz", "file:local.patch"]
}
}
}
EOF
git_tags_fetch_refs() {
printf '%s\n' \
'aaaa refs/tags/release/1.9.0' \
'bbbb refs/tags/release/1.10.0' \
'cccc refs/tags/not-a-release'
}
upstream_fetch_source() { printf 'remote fixture for %s\n' "$1" > "$2"; }
tag_sum=$(git_tags_upstream_release "$tagpkg")
remote_sum=$(printf 'remote fixture for %s\n' 'https://downloads.example.test/tool-1.10.0.tar.gz' | sha256sum | cut -d' ' -f1)
local_sum=$(sha256sum "$tagpkg/local.patch" | cut -d' ' -f1)
check "pacman ordering selects 1.10.0 over 1.9.0" "1.10.0" "$(jq -r '.pkgver' <<<"$tag_sum")"
check "remote source template is downloaded and hashed" "$remote_sum" "$(jq -r '.sha256sums.any[0]' <<<"$tag_sum")"
check "local source entry is hashed" "$local_sum" "$(jq -r '.sha256sums.any[1]' <<<"$tag_sum")"
vst=0; validate_package_metadata "$tagpkg" >/dev/null || vst=$?
check "git-tags declaration validates" "0" "$vst"
echo "npm provider:"
local npmpkg="$TEMP_DIR/selftest-npm" npm_sum npm_tar_sum npm_notes_sum
mkdir -p "$npmpkg/.omarchy"
printf 'pkgver=1.0.0\npkgrel=1\nsha256sums=("old" "old")\n' > "$npmpkg/PKGBUILD"
cat > "$npmpkg/.omarchy/package.json" <<'EOF'
{
"source": "local",
"upstream": {
"npm": "@example/tool",
"dist_tag": "latest",
"sources": {
"any": ["{npm_tarball}", "https://example.test/tool/{pkgver}/notes"]
}
}
}
EOF
npm_fetch_metadata() {
jq -n '{
"dist-tags": {latest: "2.0.0"},
versions: {"2.0.0": {dist: {tarball: "https://registry.npmjs.org/@example/tool/-/tool-2.0.0.tgz"}}},
time: {"2.0.0": "2024-01-02T03:04:05.000Z"}
}'
}
npm_sum=$(npm_upstream_release "$npmpkg")
npm_tar_sum=$(printf 'remote fixture for %s\n' 'https://registry.npmjs.org/@example/tool/-/tool-2.0.0.tgz' | sha256sum | cut -d' ' -f1)
npm_notes_sum=$(printf 'remote fixture for %s\n' 'https://example.test/tool/2.0.0/notes' | sha256sum | cut -d' ' -f1)
check "npm dist-tag selects its version" "2.0.0" "$(jq -r '.pkgver' <<<"$npm_sum")"
check "npm tarball placeholder is hashed" "$npm_tar_sum" "$(jq -r '.sha256sums.any[0]' <<<"$npm_sum")"
check "npm pkgver template is hashed" "$npm_notes_sum" "$(jq -r '.sha256sums.any[1]' <<<"$npm_sum")"
check "npm publication time is preserved" "2024-01-02T03:04:05.000Z" "$(jq -r '.published_at' <<<"$npm_sum")"
vst=0; validate_package_metadata "$npmpkg" >/dev/null || vst=$?
check "npm declaration validates" "0" "$vst"
# A ")" in a comment inside a checksum array must not end the rewrite early;
# voxtype-bin annotates its arrays with "(new in v0.7.5)" and the like.
echo "Checksum array rewrite across commented lines:"
local commented="$TEMP_DIR/commented-pkgbuild" sum_c=$(printf 'c%.0s' {1..64})
cat > "$commented" <<'EOF'
sha256sums_x86_64=(
# Binaries
'aaaa' # tool
# Sidecar (new in v0.7.5)
'bbbb' # sidecar (x86_64)
)
sha256sums=( # support files (arch-independent)
'dddd'
)
package() { :; }
EOF
set_pkgbuild_array "$commented" sha256sums_x86_64 "$sum_c" "$sum_c"
set_pkgbuild_array "$commented" sha256sums "$sum_c"
check "commented arrays rewrite to valid shell" "0" \
"$(bash -n "$commented" 2>/dev/null; echo $?)"
check "commented arrays hold only the new checksums" "$sum_c $sum_c|$sum_c|package" \
"$(bash -c 'source "$1"; printf "%s|%s|%s" "${sha256sums_x86_64[*]}" "${sha256sums[*]}" "$(declare -F package)"' _ "$commented")"
# End to end over the real mise-bin package: its checked-in metadata and
# PKGBUILD, the full sync_package path (selection, validation, backstop,
# rewrite, read-back verification), with only the two network fetches
# replaced by mise-shaped fixtures.
echo "End-to-end sync_package with the checked-in mise-bin metadata:"
local e2e_root="$TEMP_DIR/e2e-pkgbuilds"
mkdir -p "$e2e_root"
cp -a "$BUILD_ROOT/pkgbuilds/mise-bin" "$e2e_root/mise-bin"
# Fixture versions extend the checked-in pkgver so they stay newer no
# matter what version the real package is at when the test runs.
local mise_current mise_aged mise_fresh mise_x64 mise_a64
mise_current=$(grep -m1 '^pkgver=' "$e2e_root/mise-bin/PKGBUILD" | cut -d= -f2- | tr -d "\"'")
mise_aged="${mise_current}.90"
mise_fresh="${mise_current}.91"
mise_x64=$(printf 'e%.0s' {1..64})
mise_a64=$(printf 'f%.0s' {1..64})
FIXTURE_RELEASES=$(jq -n --arg young "$young" --arg old2 "$old2d" \
--arg aged "v$mise_aged" --arg fresh "v$mise_fresh" '[
{tag_name: $fresh, published_at: $young, draft: false, prerelease: false},
{tag_name: $aged, published_at: $old2, draft: false, prerelease: false}
]')
FIXTURE_CHECKSUMS=$(printf '%s\n' \
"$mise_x64 ./mise-v$mise_aged-linux-x64.tar.xz" \
"$mise_a64 ./mise-v$mise_aged-linux-arm64.tar.xz")
local prev_updated=$UPDATED prev_failed=$FAILED
PKGBUILDS_DIR="$e2e_root" sync_package mise-bin >/dev/null 2>&1 || true
check "sync_package updates without failures" "updated=1 failed=0" \
"updated=$((UPDATED - prev_updated)) failed=$((FAILED - prev_failed))"
check "the 24h manifest policy holds v$mise_fresh and ships v$mise_aged" "$mise_aged" \
"$(grep -m1 '^pkgver=' "$e2e_root/mise-bin/PKGBUILD" | cut -d= -f2-)"
check "pkgrel resets to 1" "1" \
"$(grep -m1 '^pkgrel=' "$e2e_root/mise-bin/PKGBUILD" | cut -d= -f2-)"
check "x86_64 checksum lands in the PKGBUILD" "sha256sums_x86_64=('$mise_x64')" \
"$(grep -m1 '^sha256sums_x86_64=' "$e2e_root/mise-bin/PKGBUILD")"
check "aarch64 checksum lands in the PKGBUILD" "sha256sums_aarch64=('$mise_a64')" \
"$(grep -m1 '^sha256sums_aarch64=' "$e2e_root/mise-bin/PKGBUILD")"
# A malformed declaration must fail the run loudly, and still be discovered.
local badpkg="$e2e_root/selftest-broken"
mkdir -p "$badpkg/.omarchy"
printf 'pkgver=1.0.0\npkgrel=1\n' > "$badpkg/PKGBUILD"
echo '{"source": "local", "upstream": false}' > "$badpkg/.omarchy/package.json"
check "malformed upstream stays discoverable for scheduled runs" "yes" \
"$(PKGBUILDS_DIR="$e2e_root" packages_for_upstream_sync | grep -qx selftest-broken && echo yes || echo no)"
prev_failed=$FAILED
PKGBUILDS_DIR="$e2e_root" sync_package selftest-broken >/dev/null 2>&1 || true
check "malformed upstream fails the sync instead of skipping" "1" "$((FAILED - prev_failed))"
FAILED=0
if ! bash "$BUILD_ROOT/pkgbuilds/cua-driver-bin/.omarchy/upstream-test.sh"; then
failures=$((failures + 1))
fi
echo ""
if [[ "$failures" -eq 0 ]]; then
print_success "Self-test passed"
else
print_error "$failures self-test failure(s)"
exit 1
fi
}
if [[ ${#SPECIFIC_PACKAGES[@]} -gt 0 && "${SPECIFIC_PACKAGES[0]}" == "self-test" ]]; then
cmd_self_test
exit 0
fi
if [[ ${#SPECIFIC_PACKAGES[@]} -gt 0 ]]; then
SPECIFIC_MODE=true
for package in "${SPECIFIC_PACKAGES[@]}"; do
sync_package "$package"
done
else
while IFS= read -r package; do
sync_package "$package"
done < <(packages_for_upstream_sync)
fi
echo ""
if [[ $FAILED -gt 0 ]]; then
print_error "Upstream sync completed with failures"
else
print_success "Upstream sync complete!"
fi
echo " Target: $PKGBUILDS_DIR"
echo " Updated: $UPDATED"
echo " Skipped: $SKIPPED"
echo " Failed: $FAILED"
if [[ $FAILED -gt 0 ]]; then
exit 1
fi