Pre-releases (versions with a hyphen) now upload their .debs to the "testing" distribution instead of "stable", so servers following stable never get an RC from apt upgrade. An RC can take its release notes from its final version's section or Unreleased; the workflow checks the notes with 'gitea-release.sh notes'. Also fix publishing RCs, whose .deb names carry the Debian version (0.2.0~rc.1) and were rejected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
151 lines
5.6 KiB
Bash
Executable File
151 lines
5.6 KiB
Bash
Executable File
#!/bin/sh
|
|
# Publishes a built release (make dist, make cli-dist, make deb for each
|
|
# architecture) to Gitea:
|
|
#
|
|
# * a release for tag v<VERSION> with the CHANGELOG section for VERSION as
|
|
# its notes, and every file in dist/ (tarballs, CLI binaries, .debs)
|
|
# plus a SHA256SUMS of them attached;
|
|
# * the .debs in the owner's Debian package registry, so servers can
|
|
# 'apt install jarl' and get updates with apt (docs/install.md).
|
|
#
|
|
# Run by .gitea/workflows/release.yml when a v* tag is pushed. Running it
|
|
# again for the same version replaces the attachments and packages.
|
|
#
|
|
# A version with a hyphen (0.2.0-rc.1) is a pre-release: Gitea marks the
|
|
# release so and doesn't count it as the latest, and its packages go to
|
|
# the "testing" distribution, so servers that follow "stable" never get
|
|
# a pre-release from apt.
|
|
#
|
|
# Release notes: the CHANGELOG section "## [VERSION]". A pre-release
|
|
# without one of its own uses the section of the version it leads up to
|
|
# ("## [0.2.0]"), or else "## [Unreleased]".
|
|
#
|
|
# 'gitea-release.sh notes' only prints the notes, failing if there are
|
|
# none (the workflow checks this before building).
|
|
#
|
|
# Variables:
|
|
# GITEA_URL e.g. https://gitea.example.com (in Actions:
|
|
# GITHUB_SERVER_URL)
|
|
# GITEA_REPO owner/name (in Actions: GITHUB_REPOSITORY)
|
|
# GITEA_TOKEN a token with write:repository and write:package
|
|
# VERSION default: the VERSION file
|
|
# DEB_DISTRIBUTION the apt distribution (default: stable, testing for
|
|
# a pre-release)
|
|
# DEB_COMPONENT the apt component (default: main)
|
|
#
|
|
# Needs curl and jq.
|
|
set -eu
|
|
|
|
VERSION=${VERSION:-$(cat VERSION)}
|
|
prerelease=false
|
|
case $VERSION in *-*) prerelease=true ;; esac
|
|
|
|
# section prints the CHANGELOG section headed "## [$1]" (without the
|
|
# heading); nothing if there is none.
|
|
section() {
|
|
awk -v v="$1" '
|
|
/^## \[/ { if (on) exit; on = index($0, "## [" v "]") == 1; next }
|
|
on' CHANGELOG.md
|
|
}
|
|
|
|
# The release notes (see above). A section with only blank lines counts
|
|
# as missing.
|
|
notes=$(section "$VERSION")
|
|
from="[$VERSION]"
|
|
if [ "$prerelease" = true ]; then
|
|
for s in "${VERSION%%-*}" Unreleased; do
|
|
[ -z "$(printf '%s' "$notes" | tr -d '[:space:]')" ] || break
|
|
notes=$(section "$s")
|
|
from="[$s]"
|
|
done
|
|
fi
|
|
if [ -z "$(printf '%s' "$notes" | tr -d '[:space:]')" ]; then
|
|
if [ "$prerelease" = true ]; then
|
|
echo "gitea-release: CHANGELOG.md has no '## [$VERSION]', '## [${VERSION%%-*}]' or '## [Unreleased]' section with notes" >&2
|
|
else
|
|
echo "gitea-release: CHANGELOG.md has no '## [$VERSION]' section with notes" >&2
|
|
fi
|
|
exit 1
|
|
fi
|
|
if [ "${1:-}" = notes ]; then
|
|
echo "gitea-release: notes for $VERSION from CHANGELOG.md $from" >&2
|
|
printf '%s\n' "$notes"
|
|
exit 0
|
|
fi
|
|
|
|
: "${GITEA_URL:=${GITHUB_SERVER_URL:-}}" "${GITEA_REPO:=${GITHUB_REPOSITORY:-}}"
|
|
: "${GITEA_URL:?set GITEA_URL}" "${GITEA_REPO:?set GITEA_REPO}" "${GITEA_TOKEN:?set GITEA_TOKEN}"
|
|
if [ "$prerelease" = true ]; then
|
|
DEB_DISTRIBUTION=${DEB_DISTRIBUTION:-testing}
|
|
else
|
|
DEB_DISTRIBUTION=${DEB_DISTRIBUTION:-stable}
|
|
fi
|
|
DEB_COMPONENT=${DEB_COMPONENT:-main}
|
|
GITEA_URL=${GITEA_URL%/}
|
|
owner=${GITEA_REPO%%/*}
|
|
tag=v$VERSION
|
|
api=$GITEA_URL/api/v1/repos/$GITEA_REPO
|
|
|
|
# curl with the token; fails on HTTP errors except the codes in $ok.
|
|
call() {
|
|
ok=$1
|
|
shift
|
|
out=$(mktemp)
|
|
code=$(curl -sS -o "$out" -w '%{http_code}' -H "Authorization: token $GITEA_TOKEN" "$@")
|
|
case " 200 201 204 $ok " in
|
|
*" $code "*) cat "$out"; rm -f "$out"; return 0 ;;
|
|
esac
|
|
echo "gitea-release: HTTP $code from $*" >&2
|
|
cat "$out" >&2
|
|
echo >&2
|
|
rm -f "$out"
|
|
return 1
|
|
}
|
|
|
|
files=$(find dist -maxdepth 1 -type f ! -name SHA256SUMS | sort)
|
|
[ -n "$files" ] || { echo "gitea-release: nothing in dist/" >&2; exit 1; }
|
|
# (Debian package versions have ~ for -: jarl_0.2.0~rc.1_amd64.deb.)
|
|
debversion=$(printf '%s' "$VERSION" | tr - '~')
|
|
for f in $files; do
|
|
case $f in *"$VERSION"* | *"$debversion"*) ;; *) echo "gitea-release: $f is not version $VERSION" >&2; exit 1 ;; esac
|
|
done
|
|
# shellcheck disable=SC2046 # file names have no spaces
|
|
(cd dist && sha256sum $(printf '%s\n' "$files" | sed 's|^dist/||')) > dist/SHA256SUMS
|
|
files="$files dist/SHA256SUMS"
|
|
|
|
# The release (created, or found when publishing again).
|
|
release=$(call 404 "$api/releases/tags/$tag")
|
|
id=$(printf '%s\n' "$release" | jq -r '.id // empty')
|
|
if [ -z "$id" ]; then
|
|
body=$(jq -n --arg tag "$tag" --arg notes "$notes" --argjson pre "$prerelease" \
|
|
'{tag_name: $tag, name: ("Jarl " + $tag), body: $notes, prerelease: $pre}')
|
|
release=$(call "" -X POST -H 'Content-Type: application/json' -d "$body" "$api/releases")
|
|
id=$(printf '%s\n' "$release" | jq -r .id)
|
|
echo "gitea-release: created release $tag"
|
|
fi
|
|
for f in $files; do
|
|
name=${f#dist/}
|
|
old=$(printf '%s\n' "$release" | jq -r --arg n "$name" '.assets[]? | select(.name == $n) | .id')
|
|
[ -z "$old" ] || call "" -X DELETE "$api/releases/$id/assets/$old" >/dev/null
|
|
call "" -X POST -F "attachment=@$f" "$api/releases/$id/assets?name=$name" >/dev/null
|
|
echo "gitea-release: attached $name"
|
|
done
|
|
|
|
# The Debian packages. Gitea refuses a package version it already has
|
|
# (409), so publishing again deletes it first.
|
|
pool=$GITEA_URL/api/packages/$owner/debian/pool/$DEB_DISTRIBUTION/$DEB_COMPONENT
|
|
for f in dist/*.deb; do
|
|
[ -e "$f" ] || continue
|
|
# shellcheck disable=SC2016,SC2046 # dpkg-deb's fields; split into "$@"
|
|
set -- $(dpkg-deb --showformat '${Package} ${Version} ${Architecture}' --show "$f")
|
|
if ! call 409 --upload-file "$f" "$pool/upload" >/dev/null; then
|
|
echo "gitea-release: upload of $f failed" >&2
|
|
exit 1
|
|
fi
|
|
if [ "$code" = 409 ]; then
|
|
call "" -X DELETE "$pool/$1/$2/$3" >/dev/null
|
|
call "" --upload-file "$f" "$pool/upload" >/dev/null
|
|
fi
|
|
echo "gitea-release: uploaded $1 $2 $3 to $DEB_DISTRIBUTION/$DEB_COMPONENT"
|
|
done
|