A build of a candidate (0.1.0-rc.1+git4-8fa62f9) put a raw + in the release lookup and the upload's ?name=, where it reads as a space. Housecarl's script has the same fix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
152 lines
5.8 KiB
Bash
Executable File
152 lines
5.8 KiB
Bash
Executable File
#!/bin/sh
|
|
# Publishes a built release (make 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, .debs)
|
|
# plus a SHA256SUMS of them attached;
|
|
# * the .debs in the owner's Debian package registry, so servers can
|
|
# 'apt install guard' 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 -: guard_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/$(printf '%s' "$tag" | sed 's/+/%2B/g')")
|
|
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: ("Guard " + $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
|
|
# A + (build metadata, 0.1.0-rc.1+git4-8fa62f9) would be a space in a query.
|
|
call "" -X POST -F "attachment=@$f" "$api/releases/$id/assets?name=$(printf '%s' "$name" | sed 's/+/%2B/g')" >/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
|