diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..0eb40d9 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,31 @@ +name: Tests + +on: + pull_request: + push: + branches: [master] + workflow_dispatch: + +jobs: + self-tests: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + persist-credentials: false + + # An Arch container for vercmp: version ordering has to be decided by + # the same comparator pacman uses on users' machines. + - name: Run self-tests + run: | + docker run --rm \ + -v "$PWD:/workspace:ro" \ + -w /workspace \ + archlinux:base-devel bash -lc ' + set -euo pipefail + pacman -Syu --noconfirm jq + ./bin/sync-upstream self-test + ./bin/omarchy-pkgs self-test + ' diff --git a/README.md b/README.md index f1dadf6..6ba724c 100644 --- a/README.md +++ b/README.md @@ -499,7 +499,9 @@ Minimal examples: Fields: -- `source`: `aur` or `local`. A `local` package can still follow an upstream release with an `.omarchy/upstream.sh` hook. +- `source`: `aur` or `local`. A `local` package can still follow an upstream release, either declaratively via `upstream` or with an `.omarchy/upstream.sh` hook. +- `upstream`: optional for `local` packages whose vendor ships tagged GitHub releases with a checksum manifest asset. `{ "github": "owner/repo", "checksums": "SHASUMS256.txt", "assets": { "": "name-{tag}.tar.xz" } }` — see [Sync Upstream Releases](#sync-upstream-releases). Mutually exclusive with `.omarchy/upstream.sh`. +- `min_release_age`: optional quarantine for upstream releases (`"24h"`, `"2d"`, or bare seconds). The newest release older than the window ships; anything younger waits, and a release whose age cannot be proven fails the sync. Bypass deliberately with `BYPASS_MIN_RELEASE_AGE=1 bin/sync-upstream `. - `sync`: optional for AUR packages; defaults to `true`. Set `false` for AUR-origin packages that Omarchy maintains manually. - `aur`: optional AUR package name when it differs from the local package directory, usually for split packages. - `release_ring`: optional. `fast` means the package is built directly for stable as well as edge. Packages without a ring build in edge and reach stable through tested artifact promotion (`bin/repo migrate`). diff --git a/bin/sync-upstream b/bin/sync-upstream index 99bd2e1..a454f67 100755 --- a/bin/sync-upstream +++ b/bin/sync-upstream @@ -592,6 +592,7 @@ EOF check_age '"abc"' "" check_age '"24hh"' "" check_age 'false' "" + check_age '""' "" check_age '"9999999999"' "" echo "Manifest validation:" @@ -616,22 +617,28 @@ EOF mkdir -p "$e2e_root" cp -a "$BUILD_ROOT/pkgbuilds/mise-bin" "$e2e_root/mise-bin" - local mise_x64 mise_a64 + # 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" '[ - {tag_name: "v2026.9.1", published_at: $young, draft: false, prerelease: false}, - {tag_name: "v2026.9.0", published_at: $old2, draft: false, prerelease: false} + 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-v2026.9.0-linux-x64.tar.xz" \ - "$mise_a64 ./mise-v2026.9.0-linux-arm64.tar.xz") + "$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 v2026.9.1 and ships v2026.9.0" "2026.9.0" \ + 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-)" diff --git a/helpers/package-metadata.sh b/helpers/package-metadata.sh index 1a13745..f73cc19 100644 --- a/helpers/package-metadata.sh +++ b/helpers/package-metadata.sh @@ -93,10 +93,14 @@ package_min_release_age_seconds() { echo 0 return 0 fi + # A present-but-empty value maps to "unparseable", not to "absent": only a + # missing key means no hold, so '"min_release_age": ""' cannot silently + # disable the quarantine. raw=$(jq -r ' - if has("min_release_age") then - .min_release_age | if type == "string" or type == "number" then tostring else "unparseable" end - else "" end + if has("min_release_age") | not then "" + elif (.min_release_age | type) == "string" or (.min_release_age | type) == "number" then + .min_release_age | tostring | if . == "" then "unparseable" else . end + else "unparseable" end ' "$metadata") if [[ -z "$raw" ]]; then echo 0