#!/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
