feat: add the nightly deb reconciler
This commit is contained in:
1 parent
4f730b914e
commit
f340028aae
5 files changed
+786
-1
No files matched your search
@@ -0,0 +1,181 @@
|
||||
#!/usr/bin/env bash
|
||||
# Shared helpers for the packaging autoupdaters (rpm/.autoupdate, deb/.autoupdate).
|
||||
# Sourced by autoupdate.sh and latest-version.sh; never executed directly.
|
||||
#
|
||||
# latest-version.sh is a verbatim copy of aur/.autoupdate's, so the names it
|
||||
# relies on (die, validate_version) keep the exact behaviour they have there.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
log() { printf '==> %s\n' "$*"; }
|
||||
info() { printf ' -> %s\n' "$*"; }
|
||||
warn() { printf 'WARNING: %s\n' "$*" >&2; }
|
||||
die() { printf 'ERROR: %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
# Fail up front on a missing tool rather than midway through, where an absent
|
||||
# binary tends to be read as "condition false" and silently takes the wrong
|
||||
# branch (aur/.autoupdate learned this from diff going missing).
|
||||
#
|
||||
# usage: require_tools [extra-tool ...]
|
||||
require_tools() {
|
||||
local t missing=()
|
||||
for t in git curl jq sha256sum awk sed grep tar gzip "$@"; do
|
||||
command -v "$t" >/dev/null 2>&1 || missing+=("$t")
|
||||
done
|
||||
[ "${#missing[@]}" -eq 0 ] || die "missing required tool(s): ${missing[*]}"
|
||||
return 0
|
||||
}
|
||||
|
||||
require_noninteractive() {
|
||||
[ -t 0 ] && die "refusing to prompt: this script must never depend on interactive input"
|
||||
return 0
|
||||
}
|
||||
|
||||
# A version we are willing to write into a spec or a changelog.
|
||||
VERSION_RE='^[0-9][0-9a-zA-Z._+]*$'
|
||||
|
||||
validate_version() {
|
||||
local ver="${1-}" what="${2-version}"
|
||||
[ -n "$ver" ] || die "${what} is empty"
|
||||
[[ "$ver" =~ $VERSION_RE ]] || die "${what} '${ver}' does not look like a version"
|
||||
return 0
|
||||
}
|
||||
|
||||
# True if $1 is strictly newer than $2. Same ordering as latest-version.sh
|
||||
# (sort -V), so "newest tag" and "newer than packaged" can never disagree.
|
||||
version_gt() {
|
||||
[ "$1" != "$2" ] && [ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | tail -n1)" = "$1" ]
|
||||
}
|
||||
|
||||
# Clone the package repo, or bring an existing clone back to origin's state.
|
||||
# usage: clone_or_reset <url> <dir>
|
||||
clone_or_reset() {
|
||||
local url="$1" dir="$2"
|
||||
if [ -d "$dir/.git" ]; then
|
||||
git -C "$dir" fetch --quiet origin
|
||||
git -C "$dir" reset --quiet --hard origin/HEAD
|
||||
else
|
||||
rm -rf "$dir"
|
||||
git clone --quiet "$url" "$dir" || die "could not clone ${url}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Identity for the changelog entry and the bump commit: GIT_NAME/GIT_EMAIL
|
||||
# (the same variables the workflow uses), else the git config the workflow
|
||||
# wrote. Never guessed. Sets MAINT_NAME and MAINT_EMAIL in the calling shell
|
||||
# rather than printing them, because a failing $(...) inside a larger string
|
||||
# does not trip `set -e`.
|
||||
resolve_identity() {
|
||||
MAINT_NAME="${GIT_NAME:-}"
|
||||
MAINT_EMAIL="${GIT_EMAIL:-}"
|
||||
[ -n "$MAINT_NAME" ] || MAINT_NAME="$(git config user.name || true)"
|
||||
[ -n "$MAINT_EMAIL" ] || MAINT_EMAIL="$(git config user.email || true)"
|
||||
[ -n "$MAINT_NAME" ] || die "no maintainer name: set GIT_NAME or git config user.name"
|
||||
[ -n "$MAINT_EMAIL" ] || die "no maintainer email: set GIT_EMAIL or git config user.email"
|
||||
return 0
|
||||
}
|
||||
|
||||
git_commit() {
|
||||
resolve_identity
|
||||
git -c user.name="$MAINT_NAME" -c user.email="$MAINT_EMAIL" commit --quiet -m "$1"
|
||||
}
|
||||
|
||||
# Download the upstream release tarball (or copy the local test tarball) to
|
||||
# <out>, and check that every entry sits under <topdir>/ when one is given.
|
||||
# usage: fetch_tarball <url> <out> [topdir]
|
||||
fetch_tarball() {
|
||||
local url="$1" out="$2" topdir="${3:-}"
|
||||
mkdir -p "$(dirname "$out")"
|
||||
if [ -n "${AUTOUPDATE_TEST_TARBALL:-}" ]; then
|
||||
[ -f "$AUTOUPDATE_TEST_TARBALL" ] || die "AUTOUPDATE_TEST_TARBALL '${AUTOUPDATE_TEST_TARBALL}' not found"
|
||||
info "test mode: using ${AUTOUPDATE_TEST_TARBALL} instead of ${url}"
|
||||
cp "$AUTOUPDATE_TEST_TARBALL" "$out"
|
||||
else
|
||||
info "fetching ${url}"
|
||||
curl --silent --show-error --location --fail --retry 3 --retry-delay 2 \
|
||||
--max-time 900 -o "${out}.part" "$url" \
|
||||
|| die "download failed: ${url}"
|
||||
mv "${out}.part" "$out"
|
||||
fi
|
||||
gzip -t "$out" 2>/dev/null || die "${out} is not a gzip tarball"
|
||||
if [ -n "$topdir" ]; then
|
||||
# awk reads the whole listing, so tar never dies of SIGPIPE under pipefail.
|
||||
tar -tzf "$out" | awk -F/ -v d="$topdir" '$1 != d { bad = 1 } END { exit bad }' \
|
||||
|| die "tarball entries are not all under ${topdir}/"
|
||||
fi
|
||||
info "sha256 $(sha256sum "$out" | awk '{print $1}')"
|
||||
}
|
||||
|
||||
# --- Gitea package registry ---------------------------------------------------
|
||||
#
|
||||
# Basic auth with the same credential the workflow stores for git
|
||||
# (https://<GIT_EMAIL>:<GIT_TOKEN>@<GIT_HOST>): GIT_TOKEN from the environment
|
||||
# if set, otherwise `git credential fill`. Gitea ignores the username when the
|
||||
# password is a token. The credential is handed to curl as a config file on
|
||||
# stdin, so it never appears on a command line, in `ps`, or in a log.
|
||||
|
||||
registry_curl_config() {
|
||||
local user="${GIT_EMAIL:-}" pass="${GIT_TOKEN:-}" cred
|
||||
if [ -z "$pass" ]; then
|
||||
cred="$(printf 'protocol=https\nhost=%s\n\n' "$GIT_HOST" \
|
||||
| GIT_TERMINAL_PROMPT=0 GIT_ASKPASS=true git credential fill 2>/dev/null)" \
|
||||
|| die "no registry credentials: set GIT_TOKEN, or store a token for https://${GIT_HOST} in a git credential helper"
|
||||
user="$(printf '%s\n' "$cred" | sed -n 's/^username=//p')"
|
||||
pass="$(printf '%s\n' "$cred" | sed -n 's/^password=//p')"
|
||||
fi
|
||||
[ -n "$pass" ] || die "no registry token for https://${GIT_HOST}"
|
||||
printf 'user = "%s:%s"\n' "${user:-autoupdate}" "$pass"
|
||||
}
|
||||
|
||||
# registry_request <method> <url> [curl args...]
|
||||
# Prints the HTTP status; the response body lands in $REGISTRY_BODY.
|
||||
registry_request() {
|
||||
local method="$1" url="$2"; shift 2
|
||||
local cfg code
|
||||
cfg="$(registry_curl_config)" || exit 1
|
||||
code="$(printf '%s' "$cfg" | curl --silent --show-error --max-time 600 \
|
||||
--config - --request "$method" --output "$REGISTRY_BODY" \
|
||||
--write-out '%{http_code}' "$@" "$url")" \
|
||||
|| die "${method} ${url} failed"
|
||||
printf '%s' "$code"
|
||||
}
|
||||
|
||||
# True if the registry already holds <name> at <version>; false on 404; dies on
|
||||
# anything else, so a broken query is never mistaken for "not published yet".
|
||||
# usage: registry_has <owner> <type> <name> <version>
|
||||
registry_has() {
|
||||
local url="https://${GIT_HOST}/api/v1/packages/$1/$2/$3/$4" code
|
||||
code="$(registry_request GET "$url" --retry 3 --retry-delay 2)"
|
||||
case "$code" in
|
||||
200) return 0 ;;
|
||||
404) return 1 ;;
|
||||
*) cat "$REGISTRY_BODY" >&2 2>/dev/null || true
|
||||
die "registry lookup ${url} failed${code:+ (HTTP ${code})}" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Upload <file> with PUT; Gitea answers 201 Created.
|
||||
# usage: registry_upload <url> <file>
|
||||
registry_upload() {
|
||||
local url="$1" file="$2" code
|
||||
code="$(registry_request PUT "$url" --upload-file "$file")"
|
||||
case "$code" in
|
||||
201) log "uploaded $(basename "$file")" ;;
|
||||
409) die "the registry already has $(basename "$file") (HTTP 409); refusing to overwrite without FORCE_REBUILD" ;;
|
||||
*) cat "$REGISTRY_BODY" >&2 2>/dev/null || true
|
||||
die "upload of $(basename "$file") to ${url} answered HTTP ${code}" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Delete one published file (FORCE_REBUILD replacing an existing build).
|
||||
# usage: registry_delete <url>
|
||||
registry_delete() {
|
||||
local url="$1" code
|
||||
code="$(registry_request DELETE "$url")"
|
||||
case "$code" in
|
||||
204|200) info "deleted ${url}" ;;
|
||||
404) info "nothing to delete at ${url}" ;;
|
||||
*) cat "$REGISTRY_BODY" >&2 2>/dev/null || true
|
||||
die "DELETE ${url} answered HTTP ${code}" ;;
|
||||
esac
|
||||
}
|
||||
Reference in new issue
Block a user