This commit is contained in:
Executable
+128
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
umask 077
|
||||
|
||||
SERVER_URL=${GITEA_SERVER_URL:-}
|
||||
REPOSITORY=${GITEA_REPOSITORY:-}
|
||||
TAG=${GITEA_REF_NAME:-}
|
||||
SOURCE_SHA=${GITEA_SHA:-}
|
||||
TOKEN=${GITEA_TOKEN:-}
|
||||
RELEASE_DIR=${KAIDI_RELEASE_DIR:-dist/release}
|
||||
RELEASE_NAME=${KAIDI_RELEASE_NAME:-Kaidi Finance $TAG}
|
||||
RELEASE_BODY=${KAIDI_RELEASE_BODY:-Kaidi Finance $TAG
|
||||
|
||||
Source: $SOURCE_SHA}
|
||||
WORK=$(mktemp -d)
|
||||
AUTH_HEADER=$WORK/gitea-auth-header
|
||||
|
||||
cleanup() {
|
||||
rm -rf "$WORK"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
fail() {
|
||||
printf 'Gitea release failed: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
case "$SERVER_URL" in
|
||||
https://*) ;;
|
||||
*) fail 'GITEA_SERVER_URL must use HTTPS' ;;
|
||||
esac
|
||||
[[ "$REPOSITORY" =~ ^[A-Za-z0-9._-]+/[A-Za-z0-9._-]+$ ]] \
|
||||
|| fail 'GITEA_REPOSITORY must be OWNER/REPO'
|
||||
[[ "$TAG" =~ ^v[0-9A-Za-z][0-9A-Za-z._+-]{0,127}$ ]] || fail 'GITEA_REF_NAME is invalid'
|
||||
[[ "$SOURCE_SHA" =~ ^[0-9A-Fa-f]{40}$ ]] || fail 'GITEA_SHA must be a 40-character commit SHA'
|
||||
[ -n "$TOKEN" ] && [ "${#TOKEN}" -le 512 ] && [[ "$TOKEN" != *$'\n'* ]] && [[ "$TOKEN" != *$'\r'* ]] \
|
||||
|| fail 'GITEA_TOKEN is invalid'
|
||||
[ -d "$RELEASE_DIR" ] || fail 'release directory is missing'
|
||||
|
||||
printf 'Authorization: token %s\nAccept: application/json\n' "$TOKEN" > "$AUTH_HEADER"
|
||||
chmod 0600 "$AUTH_HEADER"
|
||||
|
||||
API="${SERVER_URL%/}/api/v1/repos/$REPOSITORY"
|
||||
ENCODED_TAG=$(jq -rn --arg value "$TAG" '$value | @uri')
|
||||
|
||||
request() {
|
||||
local method=$1 url=$2 output=$3 input=${4:-} status
|
||||
local args=(
|
||||
--silent --show-error --proto '=https' --tlsv1.2
|
||||
--request "$method" --header "@$AUTH_HEADER"
|
||||
--output "$output" --write-out '%{http_code}'
|
||||
)
|
||||
if [ -n "$input" ]; then
|
||||
args+=(--header 'Content-Type: application/json' --data-binary "@$input")
|
||||
fi
|
||||
status=$(curl "${args[@]}" "$url") || fail "$method $url could not be completed"
|
||||
printf '%s\n' "$status"
|
||||
}
|
||||
|
||||
existing_status=$(request GET "$API/releases/tags/$ENCODED_TAG" "$WORK/existing.json")
|
||||
case "$existing_status" in
|
||||
404) ;;
|
||||
200) fail "release $TAG already exists; published releases are immutable" ;;
|
||||
*) fail "release lookup returned HTTP $existing_status" ;;
|
||||
esac
|
||||
|
||||
jq -n \
|
||||
--arg tag "$TAG" \
|
||||
--arg commit "$SOURCE_SHA" \
|
||||
--arg name "$RELEASE_NAME" \
|
||||
--arg body "$RELEASE_BODY" \
|
||||
'{tag_name:$tag,target_commitish:$commit,name:$name,body:$body,draft:true,prerelease:false}' \
|
||||
> "$WORK/create.json"
|
||||
create_status=$(request POST "$API/releases" "$WORK/release.json" "$WORK/create.json")
|
||||
[ "$create_status" = 201 ] || fail "draft release creation returned HTTP $create_status"
|
||||
RELEASE_ID=$(jq -er '.id | numbers' "$WORK/release.json") || fail 'draft release id is missing'
|
||||
|
||||
VERSION=${TAG#v}
|
||||
ARTIFACT="kaidi-finance-$VERSION.tar.gz"
|
||||
ASSETS=(
|
||||
"$ARTIFACT"
|
||||
backend-sbom.cdx.json
|
||||
frontend-sbom.cdx.json
|
||||
release-manifest.json
|
||||
release-manifest.sig
|
||||
release-public.pem
|
||||
install.sh
|
||||
bootstrap-checksums.txt
|
||||
SHA256SUMS
|
||||
)
|
||||
|
||||
: > "$WORK/expected-assets.jsonl"
|
||||
for name in "${ASSETS[@]}"; do
|
||||
path="$RELEASE_DIR/$name"
|
||||
[ -f "$path" ] && [ ! -L "$path" ] || fail "release asset $name is missing or unsafe"
|
||||
size=$(wc -c < "$path" | tr -d '[:space:]')
|
||||
jq -cn --arg name "$name" --argjson size "$size" '{name:$name,size:$size}' \
|
||||
>> "$WORK/expected-assets.jsonl"
|
||||
encoded_name=$(jq -rn --arg value "$name" '$value | @uri')
|
||||
upload_status=$(curl --silent --show-error --proto '=https' --tlsv1.2 \
|
||||
--header "@$AUTH_HEADER" \
|
||||
--form "attachment=@$path;type=application/octet-stream" \
|
||||
--output "$WORK/upload.json" --write-out '%{http_code}' \
|
||||
"$API/releases/$RELEASE_ID/assets?name=$encoded_name") \
|
||||
|| fail "asset $name upload could not be completed"
|
||||
[ "$upload_status" = 201 ] || fail "asset $name upload returned HTTP $upload_status"
|
||||
done
|
||||
jq -s 'sort_by(.name)' "$WORK/expected-assets.jsonl" > "$WORK/expected-assets.json"
|
||||
|
||||
verify_status=$(request GET "$API/releases/$RELEASE_ID" "$WORK/verify.json")
|
||||
[ "$verify_status" = 200 ] || fail "draft release verification returned HTTP $verify_status"
|
||||
jq -e --slurpfile expected "$WORK/expected-assets.json" \
|
||||
'([.assets[] | {name,size}] | sort_by(.name)) == $expected[0]' "$WORK/verify.json" >/dev/null \
|
||||
|| fail 'uploaded release asset names or sizes do not match the signed local set'
|
||||
|
||||
jq -n \
|
||||
--arg commit "$SOURCE_SHA" \
|
||||
--arg name "$RELEASE_NAME" \
|
||||
--arg body "$RELEASE_BODY" \
|
||||
'{target_commitish:$commit,name:$name,body:$body,draft:false,prerelease:false}' \
|
||||
> "$WORK/publish.json"
|
||||
publish_status=$(request PATCH "$API/releases/$RELEASE_ID" "$WORK/published.json" "$WORK/publish.json")
|
||||
[ "$publish_status" = 200 ] || fail "release publication returned HTTP $publish_status"
|
||||
jq -e '.draft == false and .prerelease == false and (.assets | length) == 9' \
|
||||
"$WORK/published.json" >/dev/null || fail 'published release state is invalid'
|
||||
|
||||
printf 'Published Gitea release %s with nine verified assets\n' "$TAG"
|
||||
Reference in New Issue
Block a user