feat: auto-track newest Godot and Blender in publish workflow

Replace the fixed 4.7.1 Dockerfile/workflow pin with:
- a nightly schedule that resolves the newest stable Godot (4.0+) and
  newest stable Blender, and builds+pushes <version> plus a floating
  latest tag only when the Godot version actually changed
- a workflow_dispatch free-text version input to (re)build any specific
  Godot version on demand, always with newest-at-build-time Blender,
  overwriting that tag without touching latest
- scripts/resolve-godot-version.sh and scripts/resolve-blender-url.sh,
  with shared parsing logic in scripts/lib.sh and an offline smoke test
  in scripts/test-lib.sh
- Blender is now fetched directly from download.blender.org instead of
  apt (Ubuntu noble's apt package is stuck on 4.0.2)
- drop the push-to-main trigger; schedule + workflow_dispatch only

Scope is Godot 4.0+ only — .blend import is a Godot 4 feature, so 3.x
builds have no use for the Blender toolchain this image adds.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-06 20:55:21 +02:00
co-authored by Claude-Bot
parent 1d15d6950f
commit e1f3cba486
8 changed files with 369 additions and 19 deletions
+173
View File
@@ -0,0 +1,173 @@
# Handoff spec: custom godot-ci image
Status: **done, and superseded** — the `godot-ci` repo this spec asked for exists and is built.
Its versioning has since moved past what's written below: instead of one Dockerfile pinned to
`4.7.1`, it now auto-tracks the newest stable Godot (4.0+) and newest stable Blender on a nightly
schedule, with a manual `workflow_dispatch` to (re)build any specific version. See that repo's
`README.md` for the current behavior; this file is kept for the original problem statement and
scope rationale, not as a source of truth on tags/versions. Decided via wayfinder map
[#158](https://git.arthurerlich.de/haylan/Project4x/issues/158), ticket
[#162](https://git.arthurerlich.de/haylan/Project4x/issues/162).
## Problem
`export-template.yml`'s `export` job runs in `barichello/godot-ci:4.7.1` and, on every single run,
`apt-get install`s a from-source Godot export-template toolchain before it can build anything. That
install is pure dead time repeated on every release. A custom image extending
`barichello/godot-ci:4.7.1` with the toolchain pre-installed removes it.
## What this spec is for
A fresh Gitea agent session, given only this file, must be able to:
1. Create a new repo.
2. Add a `Dockerfile` and a publish workflow to it.
3. Get the image published to that repo's Gitea package (container) registry.
4. Report back the final image reference so `export-template.yml` here can be pointed at it.
No other context from this repo should be required.
## Scope: what goes in the image
Everything `export-template.yml`'s "Update and install dependencies" step currently
`apt-get install`s, unconditionally (all of it — see [Decisions](#decisions) below for why the
narrower alternative was rejected):
```
libfontconfig1
build-essential scons pkg-config xz-utils curl
# Linux leg:
libx11-dev libxcursor-dev libxinerama-dev libgl1-mesa-dev \
libasound2-dev libpulse-dev libudev-dev libxi-dev libxrandr-dev libwayland-dev
# Windows leg:
mingw-w64
# + these two update-alternatives calls, baked in at image build time:
update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix
update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix
# conditionally, when ENABLE_BLENDER is on:
blender
```
Current source of truth for this list: `.gitea/workflows/export-template.yml` in
`haylan/Project4x`, "Update and install dependencies" step. **Re-check that step before building**
— it may have drifted since this spec was written.
### What stays out of the image
- **The SCons object cache** (`.scons_cache`, restored via `actions/cache@v3` keyed on
`scons-${GODOT_VERSION}-${artifact_name}`). This is build _state_, not a package — baking it into
the image would pin it to image-build time instead of the actual release run, going stale
immediately.
- **`nsis` / `osslsigncode`.** These do NOT belong in this image. They run in `release.yml`'s
`create-release` job, which is a plain `ubuntu-latest` runner — it does not use
`barichello/godot-ci` or any container at all. (Earlier drafts of this ticket assumed they were
part of the export container's toolchain; that was wrong. The NSIS installer has since been
removed entirely — see ADR 0001's superseded note — so only `osslsigncode`/`zip` remain in
`create-release`, and neither is expensive enough to justify its own image.)
## Registry path and versioning
- **Registry:** this Gitea instance's container/package registry (`git.arthurerlich.de`), scoped
to the new repo you create — Gitea packages are per-repo/owner, so the path is
`git.arthurerlich.de/haylan/<new-repo-name>`.
- **Tag:** pin to the Godot version only, e.g. `git.arthurerlich.de/haylan/<new-repo-name>:4.7.1`.
This mirrors `barichello/godot-ci`'s own tag scheme and `Project4x`'s `GODOT_VERSION` env var. A
Godot version bump in `Project4x` means building and pushing a new tag here too — that coupling
is intentional, not an oversight.
- Pick `<new-repo-name>` yourself (e.g. `godot-ci-toolchain` or similar); it isn't fixed by this
spec.
## Auth: PAT required
Confirmed for this Gitea instance: `GITEA_TOKEN` (the automatic per-run Actions token) **cannot**
push to the package/container registry. The new repo's publish workflow needs a **Personal Access
Token** stored as a repo secret (e.g. `REGISTRY_PUSH_PAT`).
- **Scope needed:** `write:package` (add `read:package` too if the publish workflow ever needs to
pull the image back, e.g. to test it before tagging `latest`).
- Generate it from the account that should own the published packages, under
Settings → Applications → Generate New Token, with only that scope checked.
- Document the token's owner/scope in the new repo's README once created — this repo
(`Project4x`) has no record of it and shouldn't need one; the coupling is one-way
(`Project4x` only ever pulls the finished image, never pushes to it).
## Dockerfile skeleton
```dockerfile
FROM barichello/godot-ci:4.7.1
RUN apt-get update && apt-get install -y --no-install-recommends \
libfontconfig1 \
build-essential scons pkg-config xz-utils curl \
libx11-dev libxcursor-dev libxinerama-dev libgl1-mesa-dev \
libasound2-dev libpulse-dev libudev-dev libxi-dev libxrandr-dev libwayland-dev \
mingw-w64 \
blender \
&& update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix \
&& update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix \
&& rm -rf /var/lib/apt/lists/*
```
Notes for whoever builds this:
- `blender` is baked in unconditionally here even though `Project4x`'s `ENABLE_BLENDER` flag is
currently `"false"` — the image can't cheaply branch on a flag it doesn't know about at build
time, and the package is small next to the rest of this list. If that turns out to add
meaningful image size/pull time for a project that never flips the flag on, split into a second
`-blender` tagged variant instead.
- Combine into one `RUN` (as above) so it's a single image layer — no benefit to splitting these
across layers here.
## Publish workflow skeleton
New repo, `.gitea/workflows/publish.yml`:
```yaml
name: Publish godot-ci image
on:
push:
branches: [main]
paths: [Dockerfile]
workflow_dispatch:
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Log in to Gitea registry
run: echo "${{ secrets.REGISTRY_PUSH_PAT }}" | docker login git.arthurerlich.de -u <owner-username> --password-stdin
- name: Build and push
run: |
docker build -t git.arthurerlich.de/haylan/<new-repo-name>:4.7.1 .
docker push git.arthurerlich.de/haylan/<new-repo-name>:4.7.1
```
Fill in `<owner-username>` and `<new-repo-name>` once the repo exists.
## How `Project4x` would then reference the new image
One-line change in `.gitea/workflows/export-template.yml`:
```diff
- container: barichello/godot-ci:4.7.1
+ container: git.arthurerlich.de/haylan/<new-repo-name>:4.7.1
```
The "Update and install dependencies" step's `apt-get install` calls for the baked-in packages
become redundant at that point (harmless no-ops — `apt-get install` on an already-installed
package is a fast no-op, so removing them is a cleanup, not a correctness requirement) and can be
trimmed in a follow-up ticket once the image is live and proven.
## Decisions
- **Bake-in scope:** everything in the current apt-get list, not a narrower "slow packages only"
subset — keeps the image and `export-template.yml`'s toolchain description in exact sync, so
there's only one place (this spec + the Dockerfile) to update when the list changes, instead of
maintaining a second smaller list that drifts from the first.
- **Tag scheme:** pinned to `GODOT_VERSION` rather than tracking image/toolchain revisions
independently — simpler to reason about, and this repo's own SCons cache is already keyed the
same way, so a Godot bump already forces everything else to rebuild in lockstep.