6047d1a9f7
Helm writes a refreshed repository index to the default cache directory and then looks for it in the overridden one, so setting both is how the chart tooling breaks on this machine already.
212 lines
8.6 KiB
YAML
212 lines
8.6 KiB
YAML
name: Release
|
|
|
|
# Checkout, interpolation and caching conventions match ci.yaml -- see the header there
|
|
# for why there are no JS actions and why every `${{ }}` goes through `env:`.
|
|
#
|
|
# There is no upload-artifact/download-artifact equivalent here (both are JS actions, and
|
|
# this Gitea has no artifact store wired up), so the job that builds the binaries is also
|
|
# the job that publishes them. Nothing is handed between jobs at all.
|
|
on:
|
|
push:
|
|
tags:
|
|
- 'v*'
|
|
workflow_dispatch:
|
|
|
|
# A tag is not normally re-pushed, so this mostly matters when one is force-moved during
|
|
# a botched release -- the superseded run stops holding runner slots.
|
|
concurrency:
|
|
group: release-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
REPO_URL: https://git.ryuvia.com/niklas/terdut-server.git
|
|
API: https://git.ryuvia.com/api/v1/repos/niklas/terdut-server
|
|
REGISTRY: git.ryuvia.com
|
|
IMAGE: git.ryuvia.com/niklas/terdut-server
|
|
|
|
jobs:
|
|
# Gates every publishing job below. A tag that fails here publishes nothing: the
|
|
# binaries, the image and the chart are all downstream of it.
|
|
test:
|
|
runs-on: ubuntu-latest
|
|
container:
|
|
image: golang:1.26.6-bookworm
|
|
volumes:
|
|
- go-mod-cache:/go/pkg/mod
|
|
- go-build-cache:/root/.cache/go-build
|
|
- gobin-cache:/go/bin
|
|
steps:
|
|
- name: Checkout
|
|
env:
|
|
REF_NAME: ${{ github.ref_name }}
|
|
run: git clone --depth=1 --branch "$REF_NAME" "$REPO_URL" .
|
|
|
|
- name: Vet
|
|
run: go vet ./...
|
|
|
|
- name: Test
|
|
run: go test ./...
|
|
|
|
binaries:
|
|
needs: test
|
|
runs-on: ubuntu-latest
|
|
container:
|
|
image: golang:1.26.6-bookworm
|
|
volumes:
|
|
- go-mod-cache:/go/pkg/mod
|
|
- go-build-cache:/root/.cache/go-build
|
|
- gobin-cache:/go/bin
|
|
steps:
|
|
- name: Checkout
|
|
env:
|
|
REF_NAME: ${{ github.ref_name }}
|
|
run: git clone --depth=1 --branch "$REF_NAME" "$REPO_URL" .
|
|
|
|
- name: Build every target
|
|
env:
|
|
REF_NAME: ${{ github.ref_name }}
|
|
run: |
|
|
set -eu
|
|
mkdir -p dist
|
|
for target in linux/amd64 linux/arm64 darwin/amd64 darwin/arm64; do
|
|
GOOS="${target%/*}"
|
|
GOARCH="${target#*/}"
|
|
out="dist/terdut-${REF_NAME}-${GOOS}-${GOARCH}"
|
|
echo "building $out"
|
|
GOOS="$GOOS" GOARCH="$GOARCH" go build \
|
|
-ldflags "-w -s -X main.version=${REF_NAME}" \
|
|
-o "$out" ./cmd/terdut
|
|
done
|
|
|
|
# Creating the release is made idempotent rather than assumed-new: a re-run of a
|
|
# failed release must not die on the release that already exists. Assets are
|
|
# replaced the same way, so a re-run repairs a partial upload.
|
|
- name: Publish the release
|
|
env:
|
|
REF_NAME: ${{ github.ref_name }}
|
|
TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
|
run: |
|
|
set -eu
|
|
auth="Authorization: token $TOKEN"
|
|
|
|
body=$(curl -sf -H "$auth" "$API/releases/tags/$REF_NAME" || true)
|
|
if [ -z "$body" ]; then
|
|
body=$(curl -sf -X POST -H "$auth" -H 'Content-Type: application/json' \
|
|
-d "{\"tag_name\":\"$REF_NAME\",\"name\":\"$REF_NAME\"}" \
|
|
"$API/releases")
|
|
fi
|
|
|
|
# The release object serialises `id` first, so the first match is the release's
|
|
# own id and not one of the nested author/asset ids.
|
|
release_id=$(printf '%s' "$body" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2)
|
|
[ -n "$release_id" ] || { echo "::error::could not determine release id"; exit 1; }
|
|
echo "release id $release_id"
|
|
|
|
for f in dist/*; do
|
|
name=$(basename "$f")
|
|
# Drop an existing asset of the same name first: Gitea happily stores two
|
|
# attachments with one name, and the updater matches by name.
|
|
old=$(curl -sf -H "$auth" "$API/releases/$release_id/assets" \
|
|
| tr '}' '\n' | grep "\"name\":\"$name\"" \
|
|
| grep -o '"id":[0-9]*' | head -1 | cut -d: -f2 || true)
|
|
if [ -n "$old" ]; then
|
|
curl -sf -X DELETE -H "$auth" "$API/releases/$release_id/assets/$old" || true
|
|
fi
|
|
echo "uploading $name"
|
|
curl -sf -X POST -H "$auth" -F "attachment=@$f" \
|
|
"$API/releases/$release_id/assets?name=$name" > /dev/null
|
|
done
|
|
|
|
# Host mode on purpose (no `container:`): this is the only context with a Docker CLI
|
|
# pointed at the dind daemon. A `container:` job would sit on the dind bridge with no
|
|
# docker socket at all.
|
|
image:
|
|
needs: test
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
env:
|
|
REF_NAME: ${{ github.ref_name }}
|
|
run: git clone --depth=1 --branch "$REF_NAME" "$REPO_URL" .
|
|
|
|
- name: Log in to the registry
|
|
env:
|
|
TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
|
run: echo "$TOKEN" | docker login "$REGISTRY" -u niklas --password-stdin
|
|
|
|
# The default "docker" driver cannot build more than one platform at a time; the
|
|
# docker-container driver can. Reused across runs if it survived the last one.
|
|
- name: Prepare buildx
|
|
run: docker buildx create --name terdut --use 2>/dev/null || docker buildx use terdut
|
|
|
|
# No QEMU: the Dockerfile's builder stage runs on $BUILDPLATFORM and cross-compiles
|
|
# from TARGETARCH, so both platforms build natively. See the comment in Dockerfile.
|
|
- name: Build and push
|
|
env:
|
|
REF_NAME: ${{ github.ref_name }}
|
|
run: |
|
|
docker buildx build \
|
|
--platform linux/amd64,linux/arm64 \
|
|
--build-arg "VERSION=${REF_NAME}" \
|
|
--tag "${IMAGE}:latest" \
|
|
--tag "${IMAGE}:${REF_NAME}" \
|
|
--push .
|
|
|
|
# Also host mode: helm is baked into the runner image, and a `container:` job could not
|
|
# install it -- get.helm.sh is unreachable from the dind bridge.
|
|
chart:
|
|
needs: test
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
env:
|
|
REF_NAME: ${{ github.ref_name }}
|
|
run: git clone --depth=1 --branch "$REF_NAME" "$REPO_URL" .
|
|
|
|
# This job is the only thing that publishes the chart, which is what keeps the
|
|
# published metadata honest. There used to be a second publisher on every charts/**
|
|
# push to main, and the two raced for the same chart version with different answers:
|
|
# this one stamps version and appVersion from the tag, that one took Chart.yaml
|
|
# verbatim, where appVersion is the hardcoded "latest". Whichever landed first won,
|
|
# so the metadata of a release depended on which runner was quicker -- chart 0.9.0
|
|
# went out on 2026-08-08 reading appVersion "latest" that way.
|
|
#
|
|
# It could not be fixed by making both agree: the tag is pushed after the branch, so
|
|
# a workflow triggered by the main push cannot know the version it is about to be
|
|
# tagged with. One publisher, triggered by the tag.
|
|
#
|
|
# The cost is that the chart only ships with an app release. That is no real loss --
|
|
# the sed below ties the chart version to the app version, so a chart-only change
|
|
# has no version of its own to be released under anyway. Chart fixes ride the next
|
|
# tag.
|
|
- name: Stamp the chart version from the tag
|
|
env:
|
|
REF_NAME: ${{ github.ref_name }}
|
|
run: |
|
|
set -eu
|
|
if ! echo "$REF_NAME" | grep -qE '^v[0-9]'; then
|
|
echo "::error::refusing to publish a chart for non-version tag ${REF_NAME}"
|
|
exit 1
|
|
fi
|
|
CHART_VERSION="${REF_NAME#v}"
|
|
sed -i "s/^version:.*/version: ${CHART_VERSION}/" charts/terdut-server/Chart.yaml
|
|
sed -i "s/^appVersion:.*/appVersion: \"${REF_NAME}\"/" charts/terdut-server/Chart.yaml
|
|
cat charts/terdut-server/Chart.yaml
|
|
|
|
- name: Package and push
|
|
env:
|
|
REF_NAME: ${{ github.ref_name }}
|
|
TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
|
run: |
|
|
set -eu
|
|
echo "$TOKEN" | helm registry login "$REGISTRY" -u niklas --password-stdin
|
|
# Isolated repo config: the machine-wide helm repo list is not this job's
|
|
# business, and one unreachable entry in it aborts otherwise-fine commands.
|
|
# HELM_REPOSITORY_CACHE is deliberately NOT overridden alongside it -- helm
|
|
# writes a refreshed index to the default cache and then looks for it in the
|
|
# overridden one.
|
|
export HELM_REPOSITORY_CONFIG="$PWD/.helm-repos.yaml"
|
|
: > "$HELM_REPOSITORY_CONFIG"
|
|
helm package charts/terdut-server -d dist
|
|
helm push "dist/terdut-server-${REF_NAME#v}.tgz" "oci://${REGISTRY}/niklas"
|