ci: run the build pipeline on Gitea Actions

Point `uses:` at full GitHub URLs so step resolution does not depend on
the instance's DEFAULT_ACTIONS_URL, publish the image to Gitea's own
container registry, and drop the Pages deploy.

Pages has no Gitea equivalent and the container image is the deploy
artefact now, so the job is removed rather than replaced. `build` stays:
it is what produces the site bundle, and test/refresh.test.ts pins the
refresh-bookkeeping restore to the slice between `build:` and `image:`.
Its BASE_PATH goes, though — that existed only for the /<repo>/ subpath
Pages served from, and serve.ts serves the image at the root.

The registry host is derived from github.server_url rather than
hardcoded, so a fork on another instance pushes to its own registry.
Layer cache moves from type=gha to type=registry: Gitea does expose the
Actions cache API, but buildx's gha backend is the least reliable part
of that compatibility surface and we are already authenticated to a
registry that can hold the layers.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
Lucas Winther
2026-09-16 04:28:53 +02:00
co-authored by Claude Opus 5
parent b2ec0951f4
commit ec80e76cdc
+38 -59
View File
@@ -20,9 +20,9 @@ jobs:
name: Typecheck, test, feed
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: https://github.com/actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- uses: https://github.com/oven-sh/setup-bun@v2
with:
bun-version: "1.3"
@@ -36,7 +36,7 @@ jobs:
# Restore-only: refresh.yml owns writing this cache, and a miss just
# returns the pre-existing fallback behaviour.
- name: Restore refresh bookkeeping
uses: actions/cache/restore@v4
uses: https://github.com/actions/cache/restore@v4
with:
path: snapshots/*.state.json
key: refresh-state-
@@ -105,8 +105,8 @@ jobs:
runs-on: ubuntu-latest
needs: check
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- uses: https://github.com/actions/checkout@v4
- uses: https://github.com/oven-sh/setup-bun@v2
with:
bun-version: "1.3"
- run: bun install --frozen-lockfile
@@ -114,19 +114,19 @@ jobs:
# This is the job whose output is deployed, so this is the one that must
# see the refresh bookkeeping; see the same step in `check`.
- name: Restore refresh bookkeeping
uses: actions/cache/restore@v4
uses: https://github.com/actions/cache/restore@v4
with:
path: snapshots/*.state.json
key: refresh-state-
restore-keys: refresh-state-
- name: Build
# Pages serves from /<repo>/, so the app is built with a matching base
# href. Built at the domain root it would 404 on every asset.
env:
BASE_PATH: /${{ github.event.repository.name }}/
# Built at the domain root, because the container image is what gets
# deployed and `serve.ts` serves from `/`. A BASE_PATH belongs here only
# if this artefact is ever hosted under a subpath again — it was set to
# /<repo>/ for GitHub Pages, which this pipeline no longer deploys to.
run: bun run build
- uses: actions/upload-artifact@v4
- uses: https://github.com/actions/upload-artifact@v4
with:
name: site
path: public/
@@ -143,66 +143,45 @@ jobs:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: https://github.com/actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: https://github.com/docker/setup-buildx-action@v3
- name: Lowercase image name
id: img
# GHCR rejects any uppercase in a repository name, and
# github.repository preserves the owner's casing verbatim.
run: echo "name=ghcr.io/${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT"
# Gitea's container registry, like GHCR before it, rejects any uppercase
# in a repository name, and github.repository preserves the owner's
# casing verbatim. The host is read from github.server_url rather than
# hardcoded, so a fork on another Gitea instance pushes to its own
# registry instead of this one.
env:
SERVER_URL: ${{ github.server_url }}
run: |
host="${SERVER_URL#https://}"
host="${host#http://}"
host="${host%%/*}"
# Two outputs on purpose: login wants the bare host, the tags want the
# full path. Passing the full path as the registry authenticates
# against a host that does not exist.
echo "host=${host}" >> "$GITHUB_OUTPUT"
echo "name=${host}/${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT"
- uses: docker/login-action@v3
- uses: https://github.com/docker/login-action@v3
with:
registry: ghcr.io
registry: ${{ steps.img.outputs.host }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v6
- uses: https://github.com/docker/build-push-action@v6
with:
context: .
push: true
tags: |
${{ steps.img.outputs.name }}:latest
${{ steps.img.outputs.name }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
# Deploying to Pages needs two one-time steps that CI cannot do for itself:
#
# 1. Settings → Pages → Source: "GitHub Actions".
# The default GITHUB_TOKEN cannot do this. Creating a Pages site needs
# `administration: write`, which is not a permission a workflow can grant
# GITHUB_TOKEN, so `configure-pages` with enablement: true fails with
# "Resource not accessible by integration".
# 2. Settings → Secrets and variables → Actions → Variables:
# set DEPLOY_PAGES to "true".
#
# Gated on that variable so the pipeline stays green for anyone who does not
# want Pages, rather than failing on every push forever.
pages:
name: Deploy to Pages
runs-on: ubuntu-latest
needs: build
if: >-
github.ref == 'refs/heads/main' &&
github.event_name != 'pull_request' &&
vars.DEPLOY_PAGES == 'true'
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deploy.outputs.page_url }}
steps:
- uses: actions/download-artifact@v4
with:
name: site
path: public
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v3
with:
path: public
- id: deploy
uses: actions/deploy-pages@v4
# Registry cache rather than `type=gha`. Gitea does expose the Actions
# cache API, but buildx's gha backend is the least reliable corner of
# that compatibility surface, and we are already authenticated to a
# registry that can hold the layers.
cache-from: type=registry,ref=${{ steps.img.outputs.name }}:buildcache
cache-to: type=registry,ref=${{ steps.img.outputs.name }}:buildcache,mode=max