This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
# Publish one immutable, signed release to a Gitea-compatible API. The script
|
||||
# is intentionally separate from the workflow so operators can dry-run the
|
||||
# exact same asset selection locally without ever exposing a signing key.
|
||||
# Publish one immutable release to a Gitea-compatible API. SHA256SUMS is
|
||||
# always generated; an Ed25519 detached signature is added when a signing key
|
||||
# is supplied. The script remains separate from the workflow so operators can
|
||||
# dry-run the exact same asset selection locally without exposing a key.
|
||||
PATH=/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/bin
|
||||
export PATH
|
||||
umask 077
|
||||
@@ -23,6 +24,7 @@ DRY_RUN=0
|
||||
AUTH_CONFIG=''
|
||||
SUMS_TMP=''
|
||||
SIG_TMP=''
|
||||
SIGNATURE_GENERATED=0
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
@@ -30,8 +32,12 @@ Usage: publish-gitea-release.sh TAG [ASSET_DIR] [--dry-run]
|
||||
|
||||
Required in publish mode:
|
||||
GITEA_TOKEN (or GITHUB_TOKEN) API token with release write access
|
||||
|
||||
Optional:
|
||||
TALLYNOTE_RELEASE_SIGNING_KEY_FILE Ed25519 private-key file
|
||||
or TALLYNOTE_RELEASE_SIGNING_KEY PEM value supplied by CI secret
|
||||
TALLYNOTE_RELEASE_SIGNING_KEY PEM value supplied by CI secret
|
||||
|
||||
Without a signing key, the release is published with SHA256SUMS only.
|
||||
EOF
|
||||
}
|
||||
die() { printf 'release publisher: %s\n' "$*" >&2; exit 1; }
|
||||
@@ -116,7 +122,9 @@ API_ROOT=${API_ROOT%/}
|
||||
validate_api_root "$API_ROOT"
|
||||
[[ -d "$ASSET_DIR" && ! -L "$ASSET_DIR" ]] || die "asset directory is invalid: $ASSET_DIR"
|
||||
command -v sha256sum >/dev/null 2>&1 || die 'sha256sum is required'
|
||||
command -v "$OPENSSL_BIN" >/dev/null 2>&1 || die 'openssl is required'
|
||||
if [[ -n "$SIGNING_KEY_FILE" || -n "$SIGNING_KEY_VALUE" ]]; then
|
||||
command -v "$OPENSSL_BIN" >/dev/null 2>&1 || die 'openssl is required when signing a release'
|
||||
fi
|
||||
[[ "$CURL_BIN" != *[[:space:]]* && "$CURL_BIN" != *[[:cntrl:]]* ]] || die 'curl executable path is invalid'
|
||||
command -v "$CURL_BIN" >/dev/null 2>&1 || die 'curl is required'
|
||||
|
||||
@@ -164,8 +172,6 @@ elif [[ -n "$SIGNING_KEY_VALUE" ]]; then
|
||||
chmod 600 "$temporary_key"
|
||||
printf '%s\n' "$SIGNING_KEY_VALUE" > "$temporary_key"
|
||||
unset SIGNING_KEY_VALUE
|
||||
else
|
||||
[[ "$DRY_RUN" -eq 1 ]] || die 'TALLYNOTE_RELEASE_SIGNING_KEY_FILE or TALLYNOTE_RELEASE_SIGNING_KEY is required'
|
||||
fi
|
||||
if [[ -n "$temporary_key" ]]; then
|
||||
"$OPENSSL_BIN" pkey -in "$temporary_key" -noout >/dev/null 2>&1 || die 'signing key is not a valid private key'
|
||||
@@ -174,16 +180,18 @@ if [[ -n "$temporary_key" ]]; then
|
||||
chmod 600 "$SIG_TMP"
|
||||
mv -f -- "$SIG_TMP" "$SIG_FILE"
|
||||
SIG_TMP=''
|
||||
SIGNATURE_GENERATED=1
|
||||
fi
|
||||
|
||||
log "tag: $TAG"
|
||||
log "assets: ${#assets[@]} archive(s), SHA256SUMS${temporary_key:+, SHA256SUMS.sig}"
|
||||
asset_summary="assets: ${#assets[@]} archive(s), SHA256SUMS"
|
||||
if (( SIGNATURE_GENERATED )); then asset_summary+=", SHA256SUMS.sig"; fi
|
||||
log "$asset_summary"
|
||||
if (( DRY_RUN )); then
|
||||
log 'dry-run: no API request was sent'
|
||||
exit 0
|
||||
fi
|
||||
[[ -n "$TOKEN" ]] || die 'GITEA_TOKEN (or GITHUB_TOKEN) is required'
|
||||
[[ -s "$SIG_FILE" ]] || die 'signature was not generated'
|
||||
command -v jq >/dev/null 2>&1 || die 'jq is required for Gitea API publishing'
|
||||
write_auth_config
|
||||
unset TOKEN
|
||||
@@ -230,7 +238,8 @@ assets_endpoint="$API_ROOT/repos/$repo_path/releases/$release_id/assets"
|
||||
existing=$(api_curl "$assets_endpoint") || die '无法读取现有 Release 资产'
|
||||
while IFS=$'\t' read -r existing_id existing_name; do
|
||||
[[ -n "$existing_id" && -n "$existing_name" ]] || continue
|
||||
for candidate in "${assets[@]}" "$SUMS_FILE" "$SIG_FILE"; do
|
||||
candidates=("${assets[@]}" "$SUMS_FILE" "$SIG_FILE")
|
||||
for candidate in "${candidates[@]}"; do
|
||||
[[ "$existing_name" == "$(basename -- "$candidate")" ]] || continue
|
||||
api_curl -X DELETE "$assets_endpoint/$existing_id" >/dev/null || die "无法删除旧资产:$existing_name"
|
||||
done
|
||||
@@ -245,5 +254,7 @@ upload_asset() {
|
||||
}
|
||||
for file in "${assets[@]}"; do upload_asset "$file"; done
|
||||
upload_asset "$SUMS_FILE"
|
||||
upload_asset "$SIG_FILE"
|
||||
if (( SIGNATURE_GENERATED )); then
|
||||
upload_asset "$SIG_FILE"
|
||||
fi
|
||||
log "published $TAG to $REPOSITORY"
|
||||
|
||||
Reference in New Issue
Block a user