Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 38 additions & 2 deletions tempoup/tempoup
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ set -e

# NOTE: if you make modifications to this script, please increment the version number.
# WARNING: the SemVer pattern: major.minor.patch must be followed as we use it to determine if the script is up to date.
TEMPOUP_INSTALLER_VERSION="0.0.14"
TEMPOUP_INSTALLER_VERSION="0.0.15"

REPO="tempoxyz/tempo"
# GPG key fingerprint for release signing verification
Expand Down Expand Up @@ -433,7 +433,14 @@ get_latest_version() {
# Prefer authenticated gh to avoid the 60 req/hr anonymous rate-limit.
local version
if gh_authenticated; then
version=$(gh release list --repo "$REPO" --limit 100 --json tagName --jq '.[].tagName' 2>/dev/null | \
# Authenticated users can see draft releases, but their assets are not
# available through the public download URLs used below. Exclude drafts
# explicitly, and retain the JSON filter as a defense against changes in
# gh's flag behavior.
version=$(gh release list --repo "$REPO" --limit 100 \
--exclude-drafts --exclude-pre-releases \
--json tagName,isDraft,isPrerelease \
--jq '.[] | select(.isDraft == false and .isPrerelease == false) | .tagName' 2>/dev/null | \
grep '^v[0-9]*\.[0-9]*\.[0-9]*$' | head -n 1)
else
local api_url="https://api.github.com/repos/$REPO/releases?per_page=100"
Expand All @@ -449,6 +456,33 @@ get_latest_version() {
echo "$version"
}

# Fail with an actionable error before downloading when an authenticated GitHub
# lookup can prove that a release is private or missing required assets.
validate_release_assets() {
local tag="$1"
shift

gh_authenticated || return 0

local release_info
if ! release_info=$(gh release view "$tag" --repo "$REPO" \
--json isDraft,assets \
--jq '["draft=" + (.isDraft | tostring), (.assets[].name)] | .[]' 2>/dev/null); then
error "Release $tag was not found on GitHub. Choose a published release with 'tempoup -i <VERSION>'."
fi

if [[ "$(printf '%s\n' "$release_info" | head -n 1)" == "draft=true" ]]; then
error "Release $tag is still a draft and cannot be installed. Choose the latest published release with 'tempoup -i <VERSION>'."
fi

local asset
for asset in "$@"; do
if ! printf '%s\n' "$release_info" | tail -n +2 | grep -Fxq "$asset"; then
error "Release $tag does not contain required asset $asset. Choose a compatible published release with 'tempoup -i <VERSION>'."
fi
done
}

# Main installation logic
main() {
# Check if tempoup installer is up to date
Expand Down Expand Up @@ -488,6 +522,8 @@ main() {

ARCHIVE_NAME="tempo-${VERSION_TAG}-${TARGET}.${ARCHIVE_EXT}"

validate_release_assets "$VERSION_TAG" "$ARCHIVE_NAME" "${ARCHIVE_NAME}.sha256"

# Create temporary directory
TMP_DIR=$(mktemp -d)
trap 'rm -rf "$TMP_DIR"' EXIT
Expand Down
62 changes: 60 additions & 2 deletions tempoup/test-tempoup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ contains() {
}

log_contains() {
if [[ ! -f "$1" ]] || ! grep -qF "$2" "$1"; then
if [[ ! -f "$1" ]] || ! grep -qF -- "$2" "$1"; then
fail "expected $1 to contain $2"
fi
}

log_lacks() {
[[ ! -f "$1" ]] || ! grep -qF "$2" "$1" || fail "expected $1 not to contain $2"
[[ ! -f "$1" ]] || ! grep -qF -- "$2" "$1" || fail "expected $1 not to contain $2"
}

run_source() {
Expand Down Expand Up @@ -91,6 +91,32 @@ FAKE_BREW
chmod +x "$1/brew"
}

fake_gh() {
mkdir -p "$1"
cat > "$1/gh" <<'FAKE_GH'
#!/usr/bin/env bash
printf '%s\n' "$*" >> "$GH_LOG"
case "$1 $2" in
"auth status") exit 0 ;;
"release list")
[[ " $* " == *" --exclude-drafts "* ]] || exit 20
[[ " $* " == *" --exclude-pre-releases "* ]] || exit 21
printf 'v1.13.1\n'
;;
"release view")
case "$GH_RELEASE_STATE" in
draft) printf 'draft=true\ntempo-v1.13.2-aarch64-apple-darwin.tar.gz\ntempo-v1.13.2-aarch64-apple-darwin.tar.gz.sha256\n' ;;
missing) printf 'draft=false\ntempo-v1.13.1-aarch64-apple-darwin.tar.gz\n' ;;
published) printf 'draft=false\ntempo-v1.13.1-aarch64-apple-darwin.tar.gz\ntempo-v1.13.1-aarch64-apple-darwin.tar.gz.sha256\n' ;;
*) exit 22 ;;
esac
;;
*) exit 23 ;;
esac
FAKE_GH
chmod +x "$1/gh"
}

setup_case() {
CASE_DIR="$TMP_ROOT/$1"
FAKE_BIN="$CASE_DIR/bin"
Expand Down Expand Up @@ -174,6 +200,38 @@ fi
contains "$output" "could not launch because libusb is missing"
ok "tempo launch verification catches libusb failure"

GH_CASE_DIR="$TMP_ROOT/gh-release-selection"
GH_BIN="$GH_CASE_DIR/bin"
GH_LOG="$GH_CASE_DIR/gh.log"
mkdir -p "$GH_CASE_DIR"
fake_gh "$GH_BIN"

latest="$(GH_LOG="$GH_LOG" PATH="$GH_BIN:$PATH" run_tempoup 'get_latest_version')"
[[ "$latest" == "v1.13.1" ]] || fail "latest release selection returned $latest"
log_contains "$GH_LOG" "--exclude-drafts"
log_contains "$GH_LOG" "--exclude-pre-releases"
ok "latest release selection excludes drafts and prereleases"

if output="$(GH_LOG="$GH_LOG" GH_RELEASE_STATE=draft PATH="$GH_BIN:$PATH" run_tempoup \
'validate_release_assets v1.13.2 tempo-v1.13.2-aarch64-apple-darwin.tar.gz tempo-v1.13.2-aarch64-apple-darwin.tar.gz.sha256' 2>&1)"; then
printf '%s\n' "$output" >&2
fail "draft release validation succeeded"
fi
contains "$output" "still a draft"
ok "draft release validation fails clearly"

if output="$(GH_LOG="$GH_LOG" GH_RELEASE_STATE=missing PATH="$GH_BIN:$PATH" run_tempoup \
'validate_release_assets v1.13.1 tempo-v1.13.1-aarch64-apple-darwin.tar.gz tempo-v1.13.1-aarch64-apple-darwin.tar.gz.sha256' 2>&1)"; then
printf '%s\n' "$output" >&2
fail "release with missing checksum validation succeeded"
fi
contains "$output" "does not contain required asset"
ok "release asset validation catches missing files"

GH_LOG="$GH_LOG" GH_RELEASE_STATE=published PATH="$GH_BIN:$PATH" run_tempoup \
'validate_release_assets v1.13.1 tempo-v1.13.1-aarch64-apple-darwin.tar.gz tempo-v1.13.1-aarch64-apple-darwin.tar.gz.sha256'
ok "published release asset validation succeeds"

run_install '
should_verify_tempo_after_tempoup
should_verify_tempo_after_tempoup --unsafe-skip-verify
Expand Down
Loading