Release a game
A release builds one commit of your game's main, publishes it with its notes to every target in release.toml, and only then tags it. The game's own release.yml workflow does it, one ase release command per step, so a release behaves the same for every game and every studio.
Before your first release
- Trust your CI, so its jobs can release the game:
ase studio trust --preset github. - Set up the stores you release to with
ase integrate add. - For Steam, Google Play or the App Store, promote the game to live:
ase games promote neon-drift.
Run a release
In GitHub, open Actions > Release > Run workflow, or use the GitHub CLI:
gh workflow run release.yml # the next version
gh workflow run release.yml -f version=1.2.0 # a version you choose
gh workflow run release.yml -f dry_run=true # build and check every target, publishing nothing
The version defaults to the latest release's minor version plus one, or 0.1.0 for the first.
A release runs these steps:
- Prepare: the version, the release notes and the plan of builds and targets. It reads the game's store keys from ASE Brain to see which mobile targets are set up.
- Build: the commit, stamped with the version, the commit and the game's ids from ASE Brain. Each build in
release.tomlis exported on its own OS, then played headless as a smoke test. - Preflight: every target's checks finish before anything publishes.
- Brain: the game's
brain/game.tomlandbrain/events.tomlgo to ASE Brain first, and the release waits until every service has them. If ASE Brain refuses them, nothing ships. - Publish: every other target, in parallel, from the same builds.
- Tag: once any target has published, the commit is tagged
v1.2.0.
The release's commit must still be the tip of main when it starts publishing. If main moved, the release fails rather than shipping something else.
When something fails
Nothing reaches your repo until a target has published. A run that fails before that leaves no tag behind, and the next run takes the same version.
Once any target has published, the version is out: it's tagged even if other targets failed. Re-run the failed jobs within a day to publish that version to the targets that missed it. Otherwise those targets get the next version.
A dry run skips a target whose keys are missing and says why, and a real release fails on it. Android and iOS are the exception: until their signing and upload keys are set, their builds and store targets are skipped with a warning.
Targets
release.toml lists the targets a release publishes to:
| Target | Publishes | Needs |
|---|---|---|
brain |
The game's config and events to ASE Brain, then the version's notes to the store service | Nothing more |
github |
A GitHub release with the player builds, notes and clips | Nothing more |
downloads |
A private download page for playtesters, with the release builds and notes | Nothing more |
servers |
The Linux server build to Fleet, for dedicated-server lobbies | Nothing more |
steam |
The depots, onto Steam's staging branch |
Steam keys, a live game |
play |
The AAB, to Google Play's internal track | Google Play keys, a live game |
appstore |
The IPA, to App Store Connect | App Store keys, a live game |
Store targets upload the build without making it live. You release it to players from the studio console.
A release can hold only the brain target (targets = ["brain"], builds = []). It builds nothing and changes only the game's config, for example to block a version.
Config only
To publish brain/game.toml and brain/events.toml without a build or a version:
gh workflow run release.yml -f config_only=true
Add -f dry_run=true to have ASE Brain check the change without publishing it. Locally, ase release config --game neon-drift --dry-run does the same check, as you, signed in with ase login.
Release notes
Release notes come from your commits. End a commit message with one or more trailers, each on its own line:
Add rocket jumps
Changelog-New: Rocket jumps.
Changelog-Fixed: Touch controls no longer stick. [mobile]
| Key | For |
|---|---|
Changelog-New |
Something players can do now |
Changelog-Changed |
Something that works differently |
Changelog-Fixed |
Something that works again |
Write each note in the present tense, about what players see: stores show it as written. Keep the text on the same line as its key. A [mobile] or [desktop] suffix limits a note to those stores' builds.
A release's notes are the trailers of every commit since the last release tag, grouped as New, Changed, Fixed. Commits without a trailer are listed in the run's summary, so you can catch a missed one. See them before releasing:
ase release notes
Store keys in CI
Signing and upload keys live in ASE Brain, never in your repo. A job reads them with its release credential:
ase release secrets --game neon-drift
On GitHub Actions this adds them to $GITHUB_ENV, masked. Elsewhere it prints export lines:
eval "$(ase release secrets --game neon-drift --format shell)"
Set keys with ase integrate add, or one at a time with ase studio secret:
ase studio secret ANDROID_KEYSTORE_BASE64 --game neon-drift < keystore.b64
Without --game, a key is the studio's and every game uses it. A game's own key wins.
Other CI
Every step is an ase command, so any CI can release a game. The CLI reference lists the steps release.yml runs. Give the job a release credential and call the same commands.