Generalize the release-age quarantine into a manifest policy

Move the hold from a mise-only hardcode to min_release_age in
.omarchy/package.json ("24h", "2d", or bare seconds), alongside source and
release_ring where package policy already lives. bin/sync-upstream exports
the window to every hook as MIN_RELEASE_AGE_SECONDS so a hook that can walk
its release feed selects the newest release that has cleared it, and
enforces it as a backstop: with a policy set, the hook must report
published_at, and a release younger than the window is treated as no
update. A hook that cannot prove the age fails the sync rather than
shipping unverified. BYPASS_MIN_RELEASE_AGE=1 replaces the package-specific
bypass for deliberate emergency updates; scheduled automation never sets it.

The mise hook keeps its release-list walk but reads the window from the
environment and reports published_at; the other upstream hooks are
untouched and unaffected until they opt in.
This commit is contained in:
Ryan Hughes
2026-08-24 19:17:22 -04:00
parent eca3ce7815
commit 48ad6b9d7b
4 changed files with 83 additions and 10 deletions
+2 -1
View File
@@ -1,4 +1,5 @@
{
"source": "local",
"release_ring": "fast"
"release_ring": "fast",
"min_release_age": "24h"
}
+14 -8
View File
@@ -4,12 +4,15 @@ set -euo pipefail
repo="jdx/mise"
# Keep a compromised mise release from reaching Omarchy before there has been
# a full day for maintainers and the community to notice and pull it. Walking
# the release list instead of gating on /releases/latest alone means mise's
# near-daily cadence cannot starve updates: the newest release that has
# finished its quarantine ships even while an even newer one is still inside
# it. Nothing younger than the window ever ships without the explicit bypass.
minimum_release_age_seconds=$((24 * 60 * 60))
# time for maintainers and the community to notice and pull it. The window
# comes from min_release_age in .omarchy/package.json, exported by
# bin/sync-upstream as MIN_RELEASE_AGE_SECONDS. Walking the release list
# instead of gating on /releases/latest alone means mise's near-daily cadence
# cannot starve updates: the newest release that has finished its quarantine
# ships even while an even newer one is still inside it. Nothing younger than
# the window ever ships without the explicit BYPASS_MIN_RELEASE_AGE=1 bypass,
# which bin/sync-upstream honors too.
minimum_release_age_seconds=${MIN_RELEASE_AGE_SECONDS:-0}
now=$(date +%s)
releases=$(curl -fsSL "https://api.github.com/repos/$repo/releases?per_page=20")
@@ -17,6 +20,7 @@ releases=$(curl -fsSL "https://api.github.com/repos/$repo/releases?per_page=20")
candidates=0
best_tag=""
best_pkgver=""
best_published_at=""
while IFS=$'\t' read -r tag published_at; do
if [[ ! "$tag" =~ ^v([A-Za-z0-9._+]+)$ ]]; then
echo "mise release has an invalid tag: ${tag:-<empty>}" >&2
@@ -31,7 +35,7 @@ while IFS=$'\t' read -r tag published_at; do
candidates=$((candidates + 1))
if (( now - published_epoch < minimum_release_age_seconds )); then
if [[ "${MISE_BIN_BYPASS_RELEASE_AGE:-}" == "1" ]]; then
if [[ "${BYPASS_MIN_RELEASE_AGE:-}" == "1" ]]; then
echo "Bypassing mise release-age gate for $tag" >&2
else
continue
@@ -41,6 +45,7 @@ while IFS=$'\t' read -r tag published_at; do
if [[ -z "$best_pkgver" ]] || [[ "$(vercmp "$pkgver" "$best_pkgver")" -gt 0 ]]; then
best_tag=$tag
best_pkgver=$pkgver
best_published_at=$published_at
fi
done < <(jq -r '.[] | select((.draft or .prerelease) | not) | [.tag_name // empty, .published_at // empty] | @tsv' <<<"$releases")
@@ -78,6 +83,7 @@ aarch64=$(checksum_for "mise-v${pkgver}-linux-arm64.tar.xz")
jq -n \
--arg pkgver "$pkgver" \
--arg published_at "$best_published_at" \
--arg x86_64 "$x86_64" \
--arg aarch64 "$aarch64" \
'{pkgver: $pkgver, sha256sums: {x86_64: [$x86_64], aarch64: [$aarch64]}}'
'{pkgver: $pkgver, published_at: $published_at, sha256sums: {x86_64: [$x86_64], aarch64: [$aarch64]}}'