Release / build-and-push (push) Successful in 2m34s
Add a top-level run-name so the Gitea Actions runs list shows 'Release v0.3.0' rather than the tagged commit's subject. Uses the same expression (inputs.tag || ref_name) as the TAG env, so it resolves for both tag pushes and manual workflow_dispatch.
113 lines
4.5 KiB
YAML
113 lines
4.5 KiB
YAML
name: Release
|
|
|
|
# Overrides the default run name (the tagged commit's message) so the Actions
|
|
# runs list shows "Release v0.3.0" instead.
|
|
run-name: "Release ${{ gitea.event.inputs.tag || gitea.ref_name }}"
|
|
|
|
# Publishes the Bookhoard container image to the Gitea container registry AND
|
|
# creates a Gitea Release whose body is the annotated tag's message (generated
|
|
# locally by `make release VERSION=...` via git-cliff). Triggered by a version
|
|
# tag push, or manually via workflow_dispatch with a tag. Pushing to main does
|
|
# nothing, so work-in-progress commits never ship. Each release publishes two
|
|
# image tags: the version (e.g. v0.3.0) and "latest".
|
|
on:
|
|
push:
|
|
tags:
|
|
- 'v*'
|
|
workflow_dispatch:
|
|
inputs:
|
|
tag:
|
|
description: 'Tag to release (e.g. v0.3.0)'
|
|
required: true
|
|
type: string
|
|
|
|
jobs:
|
|
build-and-push:
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
env:
|
|
# Resolve the target tag for both triggers: explicit input on manual
|
|
# dispatch, otherwise the pushed tag ref.
|
|
TAG: ${{ gitea.event.inputs.tag || gitea.ref_name }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
with:
|
|
# Full history ensures the tag annotation (the release notes) is present.
|
|
fetch-depth: 0
|
|
ref: ${{ gitea.event.inputs.tag || gitea.ref }}
|
|
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@v3
|
|
|
|
- name: Login to Gitea Container Registry
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: git.linuxhg.com
|
|
username: ${{ gitea.actor }}
|
|
# PAT stored as a repo Actions secret (auto GITHUB_TOKEN lacks package scope in Gitea)
|
|
password: ${{ secrets.REGISTRY_TOKEN }}
|
|
|
|
- name: Build and push image
|
|
uses: docker/build-push-action@v5
|
|
with:
|
|
context: .
|
|
file: ./Dockerfile
|
|
push: true
|
|
# Publishes both the exact version (e.g. v0.2.0) and the movable "latest" tag.
|
|
# Deployments default to "latest" via ${IMAGE_TAG:-latest} in docker-compose.yml;
|
|
# pin or roll back by setting IMAGE_TAG in .env.
|
|
tags: |
|
|
git.linuxhg.com/bookhoard/bookhoard:${{ env.TAG }}
|
|
git.linuxhg.com/bookhoard/bookhoard:latest
|
|
|
|
- name: Create Gitea Release
|
|
env:
|
|
# REGISTRY_TOKEN is reused for release creation because Gitea's auto
|
|
# GITHUB_TOKEN cannot create releases on this instance. The PAT must
|
|
# carry write:repository scope. Idempotent: re-runs update an existing
|
|
# release for this tag instead of failing with 409. On any HTTP error
|
|
# the API response body is printed so a 403 names the missing scope.
|
|
TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
|
REPO: ${{ gitea.repository }}
|
|
run: |
|
|
set -euo pipefail
|
|
: "${TAG:?TAG is required}"
|
|
API="https://git.linuxhg.com/api/v1/repos/${REPO}/releases"
|
|
AUTH="Authorization: token ${TOKEN}"
|
|
# Release body = the annotated tag's message (the git-cliff notes).
|
|
BODY="$(git tag -l --format='%(contents)' "${TAG}")"
|
|
|
|
# Tags containing a '-' (e.g. v0.3.0-rc1) are published as pre-releases.
|
|
PRE="false"; case "${TAG}" in *-*) PRE="true";; esac
|
|
|
|
PAYLOAD=$(jq -n \
|
|
--arg t "${TAG}" --arg n "${TAG}" --arg b "${BODY}" --argjson p "${PRE}" \
|
|
'{tag_name:$t, name:$n, body:$b, draft:false, prerelease:$p}')
|
|
|
|
# POST/PATCH the release, surfacing Gitea's error message on failure
|
|
# (e.g. "token does not have write scope") instead of failing silently.
|
|
api_call() {
|
|
local method="$1" url="$2" resp code rbody
|
|
resp="$(curl -sS -w '\n%{http_code}' -X "${method}" \
|
|
-H "${AUTH}" -H "Content-Type: application/json" \
|
|
-d "${PAYLOAD}" "${url}")"
|
|
code="$(printf '%s' "${resp}" | tail -n1)"
|
|
rbody="$(printf '%s' "${resp}" | sed '$d')"
|
|
if [ "${code}" -ge 400 ]; then
|
|
echo "::error::Release API ${code} (${method} ${url}): ${rbody}" >&2
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
EXISTING_ID="$(curl -sS -H "${AUTH}" "${API}/tags/${TAG}" | jq -r '.id // empty' 2>/dev/null || true)"
|
|
if [ -n "${EXISTING_ID}" ]; then
|
|
api_call PATCH "${API}/${EXISTING_ID}"
|
|
echo "Updated existing release id=${EXISTING_ID} for ${TAG}"
|
|
else
|
|
api_call POST "${API}"
|
|
echo "Created new release for ${TAG}"
|
|
fi
|