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 matching uploaded file metadata for the same tag; private submissions may reject anonymous downloads
- README: Olympus one-click handoff and raw-download links point at Akron’s stable install endpoints
akron.micr.dev: docs are current, and both install endpoints resolve to the published GitHub Release archive
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.
Normal releases are published as the latest GitHub Release, including versions
whose tag contains -beta. Mark a release as a GitHub prerelease only when
explicitly requested, using the manual workflow’s prerelease input. The tag’s
spelling alone does not select that flag.
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, GameBanana file metadata, bothakron.micr.devinstall endpoints, byte-identity between the public download 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:
- Confirm the publisher created the versioned update and the Core Files API lists a matching, unarchived release archive.
- A private GameBanana submission may reject anonymous downloads and updates. Do not use its file links for public installation.
- Verify
https://akron.micr.dev/olympusresolves to the GitHub release archive with theeverest:protocol, andhttps://akron.micr.dev/rawdownloads that archive.
- README:
akron.micr.dev:
- Verify the rendered public site reflects the released docs.
- Verify install/download links resolve to the current GitHub release archive and the downloaded bytes match its checksum.
- 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: