CLI

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

  1. Trust your CI, so its jobs can release the game: ase studio trust --preset github.
  2. Set up the stores you release to with ase integrate add.
  3. 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:

  1. 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.
  2. Build: the commit, stamped with the version, the commit and the game's ids from ASE Brain. Each build in release.toml is exported on its own OS, then played headless as a smoke test.
  3. Preflight: every target's checks finish before anything publishes.
  4. Brain: the game's brain/game.toml and brain/events.toml go to ASE Brain first, and the release waits until every service has them. If ASE Brain refuses them, nothing ships.
  5. Publish: every other target, in parallel, from the same builds.
  6. 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.