mirror of
https://code.forgejo.org/actions/forgejo-release.git
synced 2025-10-13 16:11:56 +03:00
Currently if you run this action and aren't using a Debian based runner you need to install all tools ahead of time or the Action fails. This should improve the behaviour so that it will attempt to install all dependencies on a best effort basis.
259 lines
7.6 KiB
Bash
Executable file
259 lines
7.6 KiB
Bash
Executable file
#!/bin/bash
|
|
# 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() {
|
|
local 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 ! test -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 ! test -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
|
|
test -s "$TAG_FILE"
|
|
}
|
|
|
|
matched_tag() {
|
|
if get_tag; then
|
|
local sha=$(jq --raw-output .commit.sha <"$TAG_FILE")
|
|
test "$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
|
|
local assets=()
|
|
for file in "$RELEASE_DIR"/*; do
|
|
assets=("${assets[@]}" -a "$file")
|
|
done
|
|
if $PRERELEASE || 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 "${assets[@]}" --repo $REPO --note "$RELEASENOTES" --tag "$TAG" --title "$TITLE" --draft ${releaseType} >&"$TMP_DIR"/tea.log; 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 "${assets[@]}" --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() {
|
|
local state="$1"
|
|
|
|
local id=$(api GET repos/$REPO/releases/tags/"$TAG_URL" | jq --raw-output .id)
|
|
|
|
api PATCH repos/$REPO/releases/"$id" --data-raw '{"draft": '"$state"', "hide_archive_links": '$HIDE_ARCHIVE_LINK'}'
|
|
}
|
|
|
|
maybe_use_release_note_assistant() {
|
|
if "$RELEASE_NOTES_ASSISTANT"; 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() {
|
|
local passphrase
|
|
if test -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
|
|
if [[ $asset =~ .sha256$ ]]; then
|
|
continue
|
|
fi
|
|
gpg --armor --detach-sign --no-tty --pinentry-mode loopback $passphrase <"$asset" >"$asset".asc
|
|
done
|
|
}
|
|
|
|
maybe_sign_release() {
|
|
if test -s "$GPG_PRIVATE_KEY"; then
|
|
sign_release
|
|
fi
|
|
}
|
|
|
|
maybe_override() {
|
|
if test "$OVERRIDE" = "false"; then
|
|
return
|
|
fi
|
|
api DELETE repos/$REPO/releases/tags/"$TAG_URL" >&/dev/null || true
|
|
if get_tag && ! matched_tag; then
|
|
delete_tag
|
|
fi
|
|
}
|
|
|
|
upload() {
|
|
setup_api
|
|
setup_tea
|
|
rm -f ~/.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() {
|
|
local ready=false
|
|
for i in $(seq $RETRY); do
|
|
if api GET repos/$REPO/releases/tags/"$TAG_URL" | jq --raw-output .draft >"$TMP_DIR"/draft; then
|
|
if test "$(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
|
|
done
|
|
if ! $ready; then
|
|
echo "no release for $TAG"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
download() {
|
|
setup_api
|
|
(
|
|
mkdir -p $RELEASE_DIR
|
|
cd $RELEASE_DIR
|
|
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 # `name` may contain whitespace, therefore, it must be last
|
|
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
|
|
}
|
|
|
|
${@:-missing}
|