Goal
Ship one version consistently across the GitHub tag, GitHub Release, GameBanana mod page, README install links, andakron.micr.dev.
The GitHub tag is the source of truth. A release is complete only after every required public surface has been confirmed updated. If one surface is stale after publishing starts, treat the release as partially published and repair it from the same tag.
Release contract
Required public surfaces:- GitHub tag:
vX.Y.Z - GitHub Release: readable title, notes,
Akron-vX.Y.Z.zip, its.zip.sha256checksum, the.dependencies.jsondependency manifest, and the.cdx.jsonCycloneDX SBOM - GameBanana: release update and downloadable file for the same tag
- README: Olympus one-click handoff and raw-download links point at Akron’s stable install endpoints
akron.micr.dev: docs are current, install endpoints resolve to the published release, and fallback GameBanana file ids point at the published file
Akron Beta 42. Do not mint a replacement tag for a sync or publishing failure unless rollback or unpublish has been explicitly chosen. Normal recovery repairs the same tag.
Deterministic path
The prepare, publish, and completion phases below are scripted. The prose sections stay authoritative for what each step means and for repairing a partial publish; the scripts are how a normal release runs them.- Write the release notes into the changelog’s
## Unreleasedsection as work merges. This is the one judgment step no script owns. - Cut the release:
main, an existing tag, an unbumped
version, or an empty Unreleased section, then moves the notes under the
release heading, bumps everest.yaml, runs make preflight-release, checks
the package contents contract, commits, pushes main, and pushes the tag.
main and v* tags are admin-bypass only by ruleset, so the cut runs locally
as the release owner; everything after the tag is Actions.
- The tag triggers the
Releaseworkflow as before. When it completes, theVerify Releaseworkflow runsscripts/release/verify-release.shagainst the tag: release assets, checksum, zip integrity, the GameBanana update, bothakron.micr.devinstall endpoints, byte-identity between the GameBanana file and the GitHub release zip, and the README links. A stale surface fails that run instead of waiting for someone to remember a check. The same script runs locally, and the workflow can be dispatched withtag_namewhen repairing an old tag.
Required configuration
See Release Configuration for the non-secret configuration matrix.release-build environment variable:
AKRON_CELESTE_REFS_SHA256: lowercase SHA-256 digest of the exact Celeste reference archive used by CI and release builds.
GAMEBANANA_SUBMISSION_ID: optional GameBanana publisher override. The workflow defaults to Akron’s mod id,681169. Link sync uses its own fixed Akron mod id unlessAKRON_GAMEBANANA_MOD_IDis passed to the sync script.GAMEBANANA_USER_AGENT: optional browser user agent replayed by the GameBanana publisher when the stored auth was captured with a browser profile that GameBanana expects to see again.TAILSCALE_EXIT_NODE: optional Tailscale exit node name or Tailscale IP used to route GameBanana publishing through a trusted network.TAILSCALE_TAGS: optional comma-separated Tailscale tags for the ephemeral Actions node. Defaults totag:ci.
GAMEBANANA_BROWSER can select the browser automation package. The GitHub release workflow pins this value to cloakbrowser; it is not a repository variable.
release-build environment secrets:
AKRON_CELESTE_REFS_URL: archive containing the stripped Celeste references needed by CI.AKRON_CELESTE_REFS_TOKEN: optional token for the Celeste reference archive.
release environment secrets:
AKRON_WEBSITE_TOKEN: token withcontents:writeaccess toMicrock/akron-website.GAMEBANANA_COOKIES: base64-encoded JSON cookie export for GameBanana. This can be a Playwright storage state, an array of cookie objects, or a simple object of cookie names to values.GAMEBANANA_STORAGE_STATE_B64: base64-encoded Playwright storage state for a manually verified GameBanana session.GAMEBANANA_STORAGE_STATE_B64_GZ: gzip-compressed and base64-encoded Playwright storage state. Use this instead ofGAMEBANANA_STORAGE_STATE_B64if the plain base64 value exceeds GitHub’s 48 KB secret limit.GAMEBANANA_USERNAMEandGAMEBANANA_PASSWORD: optional fallback credentials for local/manual runs and automated retries when stored authentication cannot edit the submission. The workflow still requires cookie or storage-state authentication at startup because direct username/password authentication on GitHub-hosted runners can trigger GameBanana’sUNKNOWN_DEVICEcaptcha.TS_OAUTH_CLIENT_IDandTS_OAUTH_SECRET: required only whenTAILSCALE_EXIT_NODEis set. The OAuth client must be allowed to create tagged auth keys for the configuredTAILSCALE_TAGS.
Preflight
- Merge approved work into
main. - Confirm
mainis green. - Pick the release version
X.Y.Z. - Audit every commit since the previous release tag before writing release notes:
CHANGELOG.md section or write down why it is intentionally omitted, such as a
test-only, docs-only, CI-only, or internal refactor commit. Do not leave notes
for commits after previous_tag under the previous version’s changelog section.
If a commit message references issues or feedback, inspect that commit before
summarizing it; do not rely on the latest commit alone.
- Update
CHANGELOG.mdbefore tagging. The workflow extracts release notes from a readable heading such as## Akron Beta 42, the canonical## X.Y.Z, or their bracketed equivalents; the matching section must exist and must cover the audited commit range. - Update docs under
docs/for user-facing changes. - Check for hardcoded version, file id, or release text references that need to change.
- Run the local release preflight:
make preflight-release checks formatting for AkronFeatureRegistry.cs and its registry tests without rewriting files, builds, tests, packages Akron.zip, and verifies the zip can be read. If that focused formatting check fails, run make format, inspect the diff, and rerun the preflight. Format other changed C# files with dotnet format Akron.sln --include <changed-csharp-files>.
Package contents contract
Before publishing, the release package must be a valid zip and contain the expected Everest mod payload. Required contents:everest.yamlbin/Akron.dllbin/ImGui.NET.dllbin/runtimes/linux-x64/native/libcimgui.sobin/runtimes/osx/native/libcimgui.dylib- at least one Windows
cimgui.dllruntime underbin/runtimes/ Dialog/English.txtLICENSEThirdPartyNotices.txt
LICENSE and ThirdPartyNotices.txt must be byte-identical to
the repository copies. Akron-owned material uses CC BY-NC-ND 4.0, while the
components identified in the notices retain their original licenses.
Publish
- Push
main. - Create and push the tag:
- The tag triggers
.github/workflows/release.yml. You can also rerun theReleaseworkflow manually withtag_nameset to the existing tag andprereleaseset as needed.
- checks out the tag
- restores Celeste reference DLLs
- restores, builds, and tests Akron
- verifies
Akron.zip, copies it toAkron-<tag>.zip, and writes a SHA-256 file - writes a dependency manifest and CycloneDX SBOM for the release package
- extracts release notes from
CHANGELOG.md - checks publishing configuration
- publishes the release update and zip to GameBanana
- creates or updates the GitHub Release
- syncs README GameBanana links on
main - syncs
Microck/akron-websitefallback GameBanana file ids
Completion checks
A release is complete only after all checks pass.- GitHub Release:
- Workflow health:
- GameBanana:
- Verify the rendered mod page shows the new release/update.
- Verify the latest file is downloadable and matches the released version.
- Verify
https://akron.micr.dev/olympusresolves to the currenteverest:install URL andhttps://akron.micr.dev/rawresolves to the new raw download file.
- README:
akron.micr.dev:
- Verify the rendered public site reflects the released docs.
- Verify install/download links resolve to the current GameBanana file.
- Verify any changed docs pages render correctly.
Partial publish recovery
Use the same tag for recovery unless rollback/unpublish has been explicitly chosen.Release workflow failed before GameBanana publishing
No public release should exist yet. Fix the configuration, changelog, package, or build failure, then rerun the existing tag:GameBanana failed
Inspect the failed workflow logs and the uploadedgamebanana-debug-vX.Y.Z artifact. Common causes are expired storage state, missing edit permission, captcha/permit pages, or an untrusted runner IP.
If the stored session is invalid, refresh the storage state using the storage-state procedure below, update the secret, and rerun the release workflow for the same tag.
If the runner IP is the issue and TAILSCALE_EXIT_NODE is configured, verify that the workflow reports the configured exit node as active and confirms HTTPS egress before treating the cookie as bad.
GitHub Release failed after GameBanana succeeded
Rerun the release workflow for the same tag. The workflow updates existing GameBanana/GitHub state where possible and uploads release assets with--clobber.
README link sync failed
Run the sync workflow or run the script locally after identifying the intended GameBanana file id:akron.micr.dev link sync failed
Inspect the Sync Akron website GameBanana links step. The usual causes are a missing or under-scoped AKRON_WEBSITE_TOKEN, a changed website source path, or a stale GameBanana file id.
Verify the website repository state:
Rendered public pages are stale
First distinguish deployment delay from wrong source state:- Check the committed README and
Microck/akron-websitesource values. - Check the rendered pages again after the deployment window.
- If source is wrong, repair the source and redeploy.
- If source is right but rendered output is stale, inspect the website deployment provider before changing release assets.
Exceptional rollback or unpublish
Rollback or unpublish is not the normal path for release failures. Use it only after an explicit decision, because deleted or replaced public releases can confuse users, external links, and GameBanana/Olympus install state. Prefer repairing the same tag when the issue is auth, upload, README sync, website sync, or rendered-page staleness.Optional Tailscale exit node
Use this when local verification accepts the stored GameBanana session but GitHub-hosted runner IPs reject the same cookies. On the trusted network that can use the GameBanana session, advertise an approved host as a Tailscale exit node:auth_keys scope and a tag such as tag:ci. Add a matching ACL tag owner rule in Tailscale if needed, then store the credentials:
TAILSCALE_EXIT_NODE as the active exit node, and confirms HTTPS egress. It does not print the Tailscale status or public IP.
GameBanana storage state size
Treat every captured storage-state file as a credential. Restrict the full state before reading or deriving another file from it:681169 will still fail in GitHub Actions because direct username/password login triggers GameBanana UNKNOWN_DEVICE.
Do not upload a full browser profile state if it is too large for GitHub secrets. Keep only GameBanana cookies first:
GAMEBANANA_STORAGE_STATE_B64. Store the verified full state through the compressed secret instead: