Files
portal/.gitea/workflows/publish.yml
T

108 lines
5.5 KiB
YAML

name: Publish release
# Portal does not deploy itself. Cutting a version is deliberate:
# `cargo release <level>` bumps Cargo.toml, commits, tags v<semver>,
# and pushes; this workflow reacts to the tag and publishes the
# artifact as a Gitea release. Each content repo (uhhm/questions,
# redoal/questions) pins PORTAL_RELEASE to one of these tags in its
# own deploy workflow - bumping the pin there is what rolls a version
# out to a site. Plain main pushes only run test.yml.
on:
push:
tags: ["v*"]
jobs:
publish:
runs-on: bare
env:
# The bare runner's own systemd service intentionally has a minimal
# PATH/HOME (no rustup default toolchain in reach) - point it at the
# shared toolchain install directly rather than assuming an ambient
# dev shell environment.
CARGO_HOME: /var/local/cargo
RUSTUP_HOME: /var/local/rustup
PATH: /var/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/bin
# Shared with interactive dev builds (see ~/.config/fish/config.fish)
# so a crate compiled once, by either a manual build or CI, is
# cached for the other - real cache hits, not just a warm toolchain.
# SCCACHE_SERVER_PORT deliberately differs from the interactive
# dev shell's (4227): sccache's server is discovered by a fixed
# TCP port shared by every local user, so if both contexts used
# the same port, whichever one's server happened to be running
# would silently "win" and serve build requests it doesn't have
# filesystem permission for. Separate ports keep each context's
# own server answering its own requests; the cache directory
# (not the server process) is what's actually shared.
SCCACHE_DIR: /var/local/sccache
SCCACHE_SERVER_PORT: "4228"
CARGO_TARGET_DIR: /var/local/cargo-target
# cargo-leptos's own downloaded tools (wasm-bindgen, wasm-opt) cache
# under $XDG_CACHE_HOME - left at its default that resolves inside
# this unit's systemd StateDirectory, files it creates there end up
# owned by the kernel's overflow "nobody" uid instead of the
# runner's own dynamic uid (a StateDirectory quirk, not something
# in our control), so a later run can't execute what an earlier run
# downloaded. Redirecting it to our own known-good shared dir avoids
# that entirely.
XDG_CACHE_HOME: /var/local/leptos-cache
steps:
- uses: actions/checkout@v4
# SITE_NAME is read via option_env! (src/app.rs) - compile-time,
# not a runtime env var. It is only the last-resort fallback name:
# each instance's site.yaml (content-driven branding) overrides it,
# so one artifact serves every site.
- name: Build
run: SITE_NAME=${{ vars.PORTAL_SITE_NAME }} cargo leptos build --release
- name: Package
run: |
set -euo pipefail
tag="${{ github.ref_name }}"
stage=$(mktemp -d)
cp "$CARGO_TARGET_DIR/release/portal" "$stage/portal"
# hash-files = true: leptos resolves hash.txt next to the running
# binary, so it ships beside portal in the release dir.
cp "$CARGO_TARGET_DIR/release/hash.txt" "$stage/hash.txt"
# site-root ("target/site" in Cargo.toml) is project-relative,
# not affected by CARGO_TARGET_DIR - only the plain `cargo build`
# outputs (release/, front/) move with that override.
cp -r target/site "$stage/site"
tar -C "$stage" -czf "portal-$tag.tar.gz" portal hash.txt site
# The bare binary as well: a content repo's CI runs `portal lint`
# and needs nothing else from the release. Named to sort after
# the tarball: Gitea lists assets by name, and deploy scripts
# written before 0.6 take the first one.
cp "$stage/portal" portal-bin
rm -rf "$stage"
# The run's own ephemeral token has write access to this repo -
# no long-lived PAT to manage. The tag already exists (cargo
# release pushed it), so the release attaches to it; a re-run
# finds the existing release instead of failing.
- name: Publish release
run: |
set -euo pipefail
tag="${{ github.ref_name }}"
api="${{ github.server_url }}/api/v1/repos/${{ github.repository }}"
auth="Authorization: token ${{ secrets.GITHUB_TOKEN }}"
# The release notes are this version's section of CHANGELOG.md.
notes=$(awk -v v="${tag#v}" '/^## /{p = ($2 == v)} p' CHANGELOG.md | tail -n +2)
[ -n "$(printf "%s" "$notes" | tr -d "[:space:]")" ] || { echo "CHANGELOG.md has no section for $tag - write it and re-tag"; exit 1; }
body=$(jq -n --arg tag "$tag" --arg notes "$notes" '{tag_name: $tag, name: $tag, body: $notes}')
id=$(curl -sf -X POST -H "$auth" -H 'Content-Type: application/json' \
-d "$body" "$api/releases" | jq .id) \
|| id=$(curl -sf -H "$auth" "$api/releases/tags/$tag" | jq .id)
# Replace the asset if a re-run already uploaded one.
for aid in $(curl -sf -H "$auth" "$api/releases/$id/assets" | jq '.[].id'); do
curl -sf -X DELETE -H "$auth" "$api/releases/$id/assets/$aid"
done
curl -sf -X POST -H "$auth" \
-F "attachment=@portal-$tag.tar.gz" \
"$api/releases/$id/assets?name=portal-$tag.tar.gz" > /dev/null
curl -sf -X POST -H "$auth" \
-F "attachment=@portal-bin" \
"$api/releases/$id/assets?name=portal-x86_64-linux" > /dev/null
echo "published $tag"