#!/usr/bin/env sh # SPDX-License-Identifier: MIT set -e if ${VERBOSE:-false}; then set -x; fi : ${FORGEJO:=https://codeberg.org} : ${REPO:=forgejo-integration/forgejo} : ${TITLE:=$TAG} : ${RELEASE_DIR:=dist/release} : ${DOWNLOAD_LATEST:=false} : ${TMP_DIR:=$(mktemp -d)} : ${GNUPGHOME:=$TMP_DIR} : ${TEA_BIN:=$TMP_DIR/tea} : ${TEA_VERSION:=0.10.1} : ${OVERRIDE:=false} : ${HIDE_ARCHIVE_LINK:=false} : ${RETRY:=1} : ${DELAY:=10} RELEASE_NOTES_ASSISTANT_VERSION=v1.4.1 # renovate: datasource=forgejo-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org TAG_FILE="$TMP_DIR/tag$$.json" TAG_URL=$(echo "$TAG" | sed 's/\//%2F/g') export GNUPGHOME get_arch() { arch=$(uname -m) case "$arch" in x86_64) arch="amd64" ;; i386|i686) arch="i386" ;; aarch64) arch="arm64" ;; armv7l) arch="armhf" ;; # Debian uses armhf for hard-float 32-bit ARM armv6l) arch="armel" ;; # older 32-bit ARM riscv64) arch="riscv64" ;; ppc64le) arch="ppc64el" ;; s390x) arch="s390x" ;; *) echo "Unknown architecture: $arch" >&2; return 1 ;; esac echo "$arch" } setup_tea() { if command -v tea >/dev/null 2>&1; then TEA_BIN=$(command -v tea) elif ! [ -f "$TEA_BIN" ]; then ARCH=$(get_arch) curl -sL "https://dl.gitea.io/tea/$TEA_VERSION/tea-$TEA_VERSION-linux-$ARCH" >"$TEA_BIN" chmod +x "$TEA_BIN" fi } get_tag() { if ! [ -f "$TAG_FILE" ]; then if api GET repos/$REPO/tags/"$TAG_URL" >"$TAG_FILE"; then echo "tag $TAG exists" else echo "tag $TAG does not exists" fi fi [ -s "$TAG_FILE" ] } matched_tag() { if get_tag; then sha=$(jq --raw-output .commit.sha <"$TAG_FILE") [ "$sha" = "$SHA" ] else return 1 fi } ensure_tag() { if get_tag; then if ! matched_tag; then cat "$TAG_FILE" echo "the tag SHA in the $REPO repository does not match the tag SHA that triggered the build: $SHA" return 1 fi else create_tag fi } create_tag() { api POST repos/$REPO/tags --data-raw '{"tag_name": "'"$TAG"'", "target": "'"$SHA"'"}' >"$TAG_FILE" } delete_tag() { if get_tag; then api DELETE repos/$REPO/tags/"$TAG_URL" rm -f "$TAG_FILE" fi } upload_release() { # assets is defined as a list of arguments, where values may contain whitespace and need to be quoted like this -a "my file.txt" -a "file.txt". # It is expanded using "${assets[@]}" which preserves the separation of arguments and not split whitespace containing values. # For reference, see https://github.com/koalaman/shellcheck/wiki/SC2086#exceptions set -- for file in "$RELEASE_DIR"/*; do set -- "$@" -a "$file" done releaseType="" if ${PRERELEASE:-false} || echo "$TAG" | grep -qi -- '-rc'; then releaseType="--prerelease" echo "Uploading as Pre-Release" else echo "Uploading as Stable" fi ensure_tag if ! "$TEA_BIN" release create "$@" --repo "$REPO" --note "$RELEASENOTES" --tag "$TAG" --title "$TITLE" --draft ${releaseType} >"$TMP_DIR"/tea.log 2>&1; then if grep --quiet 'Unknown API Error: 500' "$TMP_DIR"/tea.log && grep --quiet 'services/release/release.go:194' "$TMP_DIR"/tea.log; then echo "workaround v1.20 race condition https://codeberg.org/forgejo/forgejo/issues/1370" sleep 10 "$TEA_BIN" release create "$@" --repo "$REPO" --note "$RELEASENOTES" --tag "$TAG" --title "$TITLE" --draft ${releaseType} else cat "$TMP_DIR"/tea.log return 1 fi fi maybe_use_release_note_assistant release_draft false } release_draft() { state="$1" rid=$(api GET repos/$REPO/releases/tags/"$TAG_URL" | jq --raw-output .id) api PATCH repos/$REPO/releases/"$rid" --data-raw '{"draft": '"$state"', "hide_archive_links": '"$HIDE_ARCHIVE_LINK"'}' } maybe_use_release_note_assistant() { if ${RELEASE_NOTES_ASSISTANT:-false}; then curl --fail -s -S -o rna "https://code.forgejo.org/forgejo/release-notes-assistant/releases/download/$RELEASE_NOTES_ASSISTANT_VERSION/release-notes-assistant" chmod +x ./rna mkdir -p "$RELEASE_NOTES_ASSISTANT_WORKDIR" ./rna --workdir="$RELEASE_NOTES_ASSISTANT_WORKDIR" --storage release --storage-location "$TAG" --token "$TOKEN" --forgejo-url "$SCHEME://$HOST" --repository "$REPO" --token "$TOKEN" release "$TAG" fi } sign_release() { passphrase="" if [ -s "$GPG_PASSPHRASE" ]; then passphrase="--passphrase-file $GPG_PASSPHRASE" fi gpg --import --no-tty --pinentry-mode loopback $passphrase "$GPG_PRIVATE_KEY" for asset in "$RELEASE_DIR"/*; do case "$asset" in *.sha256) continue ;; esac gpg --armor --detach-sign --no-tty --pinentry-mode loopback $passphrase <"$asset" >"$asset".asc done } maybe_sign_release() { if [ -s "$GPG_PRIVATE_KEY" ]; then sign_release fi } maybe_override() { if [ "$OVERRIDE" = "false" ]; then return fi api DELETE repos/$REPO/releases/tags/"$TAG_URL" >/dev/null 2>&1 || true if get_tag && ! matched_tag; then delete_tag fi } upload() { setup_api setup_tea rm -f "$HOME/.config/tea/config.yml" GITEA_SERVER_TOKEN=$TOKEN "$TEA_BIN" login add --url "$FORGEJO" maybe_sign_release maybe_override upload_release } setup_api() { # Check if jq and curl are available if command -v jq >/dev/null 2>&1 && command -v curl >/dev/null 2>&1; then return 0 fi echo "jq and/or curl missing, attempting to install..." >&2 if command -v apt-get >/dev/null 2>&1; then apt-get -qq update && apt-get install -y -qq jq curl elif command -v apk >/dev/null 2>&1; then apk add --no-cache jq curl else echo "No supported package manager found. Please install jq and curl manually." >&2 return 1 fi } api() { method=$1 shift path=$1 shift curl --fail -X "$method" -sS -H "Content-Type: application/json" -H "Authorization: token $TOKEN" "$@" "$FORGEJO/api/v1/$path" } wait_release() { ready=false i=1 while [ "$i" -le "$RETRY" ]; do if api GET repos/$REPO/releases/tags/"$TAG_URL" | jq --raw-output .draft >"$TMP_DIR"/draft; then if [ "$(cat "$TMP_DIR"/draft)" = "false" ]; then ready=true break fi echo "release $TAG is still a draft" else echo "release $TAG does not exist yet" fi echo "waiting $DELAY seconds" sleep "$DELAY" i=$((i+1)) done if [ "$ready" != "true" ]; then echo "no release for $TAG" return 1 fi } download() { setup_api ( mkdir -p "$RELEASE_DIR" cd "$RELEASE_DIR" || exit 1 if [ "${DOWNLOAD_LATEST}" = "true" ]; then echo "Downloading the latest release" api GET repos/$REPO/releases/latest >"$TMP_DIR"/assets.json elif [ "${DOWNLOAD_LATEST}" = "false" ]; then wait_release echo "Downloading tagged release ${TAG}" api GET repos/$REPO/releases/tags/"$TAG_URL" >"$TMP_DIR"/assets.json fi jq --raw-output '.assets[] | "\(.browser_download_url) \(.name)"' <"$TMP_DIR"/assets.json | while read url name; do url=$(echo "$url" | sed "s#/download/${TAG}/#/download/${TAG_URL}/#") curl --fail -H "Authorization: token $TOKEN" -o "$name" -L "$url" done ) } missing() { echo "need upload or download argument got nothing" exit 1 } if [ "$#" -gt 0 ]; then "$@" else missing fi