CI / check (push) Skipped
CI / e2e (push) Skipped
main is protected (enable_push: false, empty whitelist), so @semantic-release/git's commit-back is rejected by the pre-receive hook -- and it would be rejected *after* the tag was pushed, leaving a tagged release the run then reports as failed. Found by trying to push this branch to main. Whitelisting the CI user was the alternative and is declined: it weakens a protection someone set deliberately and lets a bot push to main without the checks every human PR has to pass. So the release page is the changelog. The changelog plugin now writes a gitignored .release-notes.md, which exists only to carry the notes into gitea-release.sh without interpolating them into a shell command, and CHANGELOG.md is a signpost -- a file claiming to be a changelog while silently never updating is worse than no file. Tags are not protected, so the tag push is unaffected.
97 lines
3.4 KiB
Bash
Executable File
97 lines
3.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# Create the Gitea release for a version semantic-release has just tagged.
|
|
#
|
|
# This is `@semantic-release/exec`'s publishCmd, and it exists because
|
|
# Gitea's API is /api/v1 and @semantic-release/github speaks GitHub's.
|
|
# That is the whole of the Gitea-shaped work: one POST.
|
|
#
|
|
# **The notes come from a file, not from an argument.** Release notes are
|
|
# rendered commit messages — arbitrary text carrying backticks, quotes and
|
|
# `$` — so interpolating ${nextRelease.notes} into a shell command would
|
|
# be an injection whose input is the commit log. @semantic-release/changelog
|
|
# has already written them to .release-notes.md by the time `publish` runs,
|
|
# so the only thing crossing the shell boundary here is a semver string,
|
|
# which is validated below anyway.
|
|
#
|
|
# That file is a gitignored build artifact, not a document: `main` is a
|
|
# protected branch, so nothing commits a changelog back to it and the
|
|
# release page is the changelog. See .releaserc.yml.
|
|
#
|
|
# Usage: scripts/gitea-release.sh <version> # e.g. 0.0.1
|
|
#
|
|
# Environment (all set by .gitea/workflows/release.yml):
|
|
# SERVER_URL https://git.ljones.me
|
|
# OWNER yonlu
|
|
# REPO yonlu/yellowjacket
|
|
# PACKAGE_TOKEN a user PAT with write access
|
|
set -euo pipefail
|
|
|
|
cd "$(dirname "$0")/.."
|
|
|
|
version="${1:?usage: gitea-release.sh <version>}"
|
|
|
|
# Validated rather than trusted: this is the one value that reaches a URL
|
|
# and a JSON document, and semantic-release is not the only thing that
|
|
# could ever call this.
|
|
if ! printf '%s' "$version" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$'; then
|
|
echo "gitea-release: '$version' is not a semver version" >&2
|
|
exit 1
|
|
fi
|
|
|
|
: "${SERVER_URL:?SERVER_URL is not set}"
|
|
: "${REPO:?REPO is not set}"
|
|
: "${PACKAGE_TOKEN:?PACKAGE_TOKEN is not set}"
|
|
|
|
tag="v${version}"
|
|
|
|
# The top section of the changelog is this release's notes: everything
|
|
# from the first `## ` heading to the one after it. awk rather than sed
|
|
# so the "there is no second heading" case (the first release) needs no
|
|
# special handling.
|
|
notes=$(awk '
|
|
/^## / { seen++; if (seen > 1) exit }
|
|
seen { print }
|
|
' .release-notes.md)
|
|
|
|
if [ -z "$notes" ]; then
|
|
echo "gitea-release: found no release section at the top of .release-notes.md" >&2
|
|
echo ' the changelog plugin runs in prepare and this runs in publish, so' >&2
|
|
echo ' an empty section means the plugin order in .releaserc.yml moved.' >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "gitea-release: creating $tag from $(printf '%s' "$notes" | wc -l) lines of notes"
|
|
|
|
# jq builds the body, so a backtick or a quote in a commit subject is data
|
|
# rather than syntax.
|
|
payload=$(jq -n \
|
|
--arg tag "$tag" \
|
|
--arg name "$tag" \
|
|
--arg body "$notes" \
|
|
'{tag_name: $tag, name: $name, body: $body, draft: false, prerelease: false}')
|
|
|
|
code=$(curl -sS -o /tmp/gitea-release.out -w '%{http_code}' \
|
|
-X POST \
|
|
-H "Authorization: token ${PACKAGE_TOKEN}" \
|
|
-H "Content-Type: application/json" \
|
|
-d "$payload" \
|
|
"${SERVER_URL}/api/v1/repos/${REPO}/releases")
|
|
|
|
case "$code" in
|
|
201)
|
|
echo "gitea-release: created ${SERVER_URL}/${REPO}/releases/tag/${tag}"
|
|
;;
|
|
409)
|
|
# Already there. The correct outcome for a re-run of the same tag,
|
|
# and not a failure — the publish workflows are idempotent for the
|
|
# same reason.
|
|
echo "gitea-release: $tag already has a release; leaving it alone"
|
|
;;
|
|
*)
|
|
echo "gitea-release: POST /releases returned $code" >&2
|
|
cat /tmp/gitea-release.out >&2
|
|
exit 1
|
|
;;
|
|
esac
|