Group the top-level docs into docs/project, docs/design, docs/assets and docs/process, each with its own README index. Delete the superseded docs/screens/head-review page and sheets. Rewrite every markdown link, absolute docs path and tool/test reference to the new locations, fix the pre-existing broken anchors and remove the dead Joke Shop photo-board link. check_docs OK, Python tooling 167/167.
9.5 KiB
Releasing Gig Line
This guide describes how to produce downloadable executables and publish them so the README "Downloads" links always point at the latest release.
1. Install export templates
Godot needs platform export templates to build standalone binaries. The first time you export, either:
- Open the editor and use Editor → Manage Export Templates (downloads the matching templates), or
- Install them manually from the
Godot downloads page by placing the
Godot_v4.7.2-stable_export_templates.tpzcontents into~/.local/share/godot/export_templates/4.7.2.stable/.
The templates must match the editor binary you use locally. CI and local development both use Godot 4.7.2 with its matching templates. Verify with:
godot --headless --export-release "Linux" build/gig-line-smoke.x86_64
2. Bump the version
project.godot sets config/version. Keep it in sync with the release tag
(e.g. v0.3.0 → config/version="0.3.0", plus a git tag v0.3.0).
Every major, minor and patch increment is a version in its own right: add the
CHANGELOG.md entry, run the local build (tools/build_releases.sh), verify the
artifact names/sizes/checksums, and only then commit and tag. A changelog entry
without a matching build is not a completed version. Unversioned development tracks
do not get a placeholder version; assign one when the scope enters a release.
3. CI (Gitea Actions)
The repository ships a Gitea Actions workflow (.gitea/workflows/build.yml):
- Every push/PR runs the checks job: Godot 4.7.2 install, project import, and the full headless test suite. This is the fast safety net.
- Tags (
v*) and manualworkflow_dispatchruns additionally execute the build job: export templates install, import,tools/build_releases.shfor Linux/Windows/macOS, artifact upload, and — on tags — release publication viatools/publish_release.sh.
tools/publish_release.sh picks the first available credential, in order:
GITEA_TOKEN(repo secret, write:repository scope),GITEA_AUTH_HEADER(a ready-madeAuthorization: ...header),~/.git-credentialsfor the host,- the local git extraheader that
actions/checkoutconfigures on the CI job.
Option 4 is what makes tag builds publish without any repo secret: the runner
already injects basic-auth credentials for the checkout, and the script reuses that
header for the API (same server). github.token is not available in this Gitea
setup, so do not rely on it.
Disk space: Gitea stores release assets under
/var/lib/gitea/data/attachmentson the host root filesystem (7.8 GB here). When it is full, asset uploads fail withHTTP 500 … no space left on deviceand evengit pushcan fail with "unpacker error".publish_release.shnow verifies the asset count and exits non-zero, so this is visible.Incident (2026-10-31):
tools/publish_release.shnever setAUTHin its~/.git-credentialsbranch, so every API call failed silently (AUTH[@]was unbound underset -u). Combined with noGITEA_TOKENsecret on the repo, tag builds from v0.2.2 to v0.2.8 produced installers but published nothing. The script is fixed (AUTH=(-u "$CRED")) and v0.2.9 was published locally with all five artifacts. If CI publishing fails again, run./tools/publish_release.sh <version>from a checkout with~/.git-credentials.
Godot cache evaluation: Gitea Actions cache was tested for the Godot binary and trimmed 4.7.2 templates. Restores worked intermittently, but cache saves timed out in
CreateCacheEntry/ were rejected as reserved by another job. The cache steps were removed because they added restore/save overhead without a reliable hit. Revisit when the host's Gitea Actions cache service is healthy; otherwise keep downloading the binary/templates directly.
The workflow lives in
.gitea/workflows/(Gitea's canonical path). The old.github/workflows/ci.ymlwas removed to avoid double runs on hosts that scan both directories.
4. Local build (alternative to CI)
godot --headless --path . --script res://src/test/test_runner.gd # all tests
godot --headless --path . --quit-after 120 # boot smoke test
./tools/build_releases.sh
Artifacts are written to build/ (all exports embed the PCK — single files):
build/gig-line-linux-<version>.x86_64build/gig-line-windows-<version>.exebuild/gig-line-macos-<version>.zipbuild/gig-line-web-<version>.zip— browser build (non-threaded Godot web template; unzip and serve the folder, no COOP/COEP headers required)build/gig-line-desktop-hq-<version>.zip— optional Forward+ Windows/Linux build withoverride.cfg; requires a Vulkan-capable GPU/driver
When run in CI (tags/dispatch), the build job also packages:
build/gig-line-setup-<version>.exe— Windows installer (NSIS, incl. shortcuts + uninstaller; aptnsison the runner)build/gig-line-<version>-x86_64.AppImage— Linux AppImage (packaging/linux/build_appimage.sh, appimagetool viaAPPIMAGE_EXTRACT_AND_RUN)
The optional Desktop HQ zip is assembled by tools/build_releases.sh from
packaging/desktop_hq/; keep override.cfg next to the extracted executable.
It is not part of the default Compatibility/Web build.
tools/publish_release.sh preflights every artifact for existence and non-zero
size, checks free space in build/, writes build/SHA256SUMS-<version>.txt, and
uploads only after those checks pass. If an upload or the final asset-count check
fails, it removes the assets it just uploaded (and removes a newly created
release) so the host never keeps a partial release. Use
PUBLISH_DRY_RUN=1 ./tools/publish_release.sh to run the preflight and checksum
steps without touching the API. Set RETAIN_RELEASES=7 to prune older releases
after a successful publish; the default 0 disables pruning and never deletes
history implicitly. A retention of seven keeps the newest seven releases,
including the latest 0.8/0.9 line.
0.10.0 macOS signing decision
The 0.10.0 release candidate will continue to ship an unsigned macOS zip with
the documented right-click/xattr quarantine instructions. Signing and
notarization remain a public-release blocker until an Apple Developer account and
a reproducible signing/notarization job are available; they are not silently
claimed as complete. The same limitation is recorded in
known-limitations.md.
Always run the release build from a clean checkout or detached worktree. Godot
can pack files from ignored tmp/ and old build/ folders into the PCK; the
0.9.6 dirty-tree build produced 1.6–3.1 GB artifacts, while the clean worktree
produced the expected ~160–240 MB packages.
Locally you can build the AppImage with bash packaging/linux/build_appimage.sh <version>;
the NSIS installer requires makensis (covered by CI).
macOS builds are unsigned and will need
xattr -dr com.apple.quarantineguidance or notarization for production releases.
5. Publish on the repository host (Gitea/Forgejo)
The repository lives at https://git.allucanget.biz/zwitschi/security-game.
Preferred path — use the helper script. It needs either a token or the same
credentials used by git push (it falls back to ~/.git-credentials):
GITEA_TOKEN=your_api_token ./tools/publish_release.sh 0.2.1
# or simply: ./tools/publish_release.sh 0.2.1 (uses ~/.git-credentials)
The script creates (or reuses) the release from project.godot's
config/version, uploads the three portable artifacts plus the NSIS
installer, AppImage, Web zip and Desktop HQ zip when present, and prints the
release URL. If CI already
published the release, re-running the script only adds missing assets.
Host note: the release is fully built by CI, but anonymous users can only browse and download when the instance sets
[service] REQUIRE_SIGNIN_VIEW=falseinapp.ini(the repo itself is public). This is a host-admin setting, not a repository file.
Manual alternative:
Via the web UI
- Create a New Release with tag
v<version>. - Attach all artifacts from
build/(portables, installer/AppImage, Web and optional Desktop HQ). - Write release notes (see checklist below).
Via the API
TOKEN="your_api_token"
REPO="zwitschi/security-game"
VERSION="0.3.0"
API="https://git.allucanget.biz/api/v1/repos/$REPO/releases"
# Create the release
RELEASE_JSON=$(curl -s -X POST "$API" \
-H "Authorization: token $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"tag_name\":\"v$VERSION\",\"name\":\"v$VERSION\",\"body\":\"Playable prototype release.\"}")
RELEASE_ID=$(echo "$RELEASE_JSON" | python3 -c "import sys,json;print(json.load(sys.stdin)['id'])")
for f in build/gig-line-*"$VERSION"*; do
curl -s -X POST "$API/$RELEASE_ID/assets" \
-H "Authorization: token $TOKEN" \
-F "attachment=@$f"
done
6. Update the README
The Downloads section links to the host releases page
(https://git.allucanget.biz/zwitschi/security-game/releases), so no README
edit is needed when the page follows the naming pattern. If artifact names
change, update tools/build_releases.sh first.
Release notes checklist
- All headless tests pass (exit code 0).
- Game boots cleanly in a smoke test.
- Controls documented in
docs/process/getting-started.mdmatch the build. - Version in
project.godotmatches the tag. - Export templates used are the official ones for the pinned engine.
- Artifacts uploaded; README downloads page shows them.