The installer for a sp00nznet recomp: here's my game, Build, here's my recomp.
Each game repo builds its own installer from this one, with a
launcher.json describing the game. The player opens it, and it:
- Finds the game: in the Steam libraries, or in the registry where a store (the EA App, say) records it. Not found, Choose folder… picks it.
- Builds: downloads the game repo (at the commit the installer was built
from) and its toolkit as zips, so no git is needed, then runs the repo's own
setup script with no questions asked. Its
[3/11] What it's doinglines drive the progress bar; the full output is under Show details and in<build folder>/logs/. - Plays: a Play button for each launcher the setup left, and desktop shortcuts on request. Opened again later, it starts here.
| Pick a game (the all-games launcher) | Your copy | What to build |
|---|---|---|
![]() |
![]() |
![]() |
| Building | Play | |
![]() |
![]() |
If the setup stops, it shows the script's own one-sentence reason and its last lines, with Try again.
Windows, Linux and macOS, one Go program (Ebitengine).
- Nothing from any game is bundled or downloaded. The build is made from the player's copy and stays on their machine.
- The setup scripts stay the source of truth. The installer knows what
launcher.jsonsays and nothing else: no build steps, no flags of its own. The scripts run the same from a terminal.
In the game's repo:
-
launcher/launcher.json(redalert2-recomp's is the sample):Key titleThe heading repoowner/nameof the game's repoaccent#rrggbbfor the buttons and the barnoteWhat the build needs and how long it takes, by OS ( windows,darwin,linux) ordefaultgame.name,game.filesThe game, and the files that prove a folder is it (a glob such as *.isomatches a file the player named; `agame.steamIts folder under steamapps/commongame.registryWindows: HKLM\key\valueentries holding its foldersourcesMore repos the setup expects beside the game's ( {"repo", "ref", "dir"}), its toolkitsetupThe setup command by OS or default;{game}is the game's folder. It should install what's missing without asking.playA glob for the launchers the setup leaves in the repo's root choicesOptional, pick-your-own: [{"id", "label", "note"}]. Build first shows a box per choice; the ticked ids reachsetupas{choices}, comma-separated (skifree's consoles installer lists its consoles here, so only the toolchains picked are downloaded) -
The setup prints each step as
[n/N] What it's doing(for the bar), ends a failure withSetup stopped: <why>, exits non-zero on failure, and doesn't wait for Enter at the end whenRECOMP_LAUNCHERis set. -
.github/workflows/installer.yml:on: push: { branches: [main], tags: ["v*"] } permissions: { contents: write } jobs: installer: uses: sp00nznet/recomp-launcher/.github/workflows/installer.yml@main with: { name: My-Game-Installer }
Every push leaves the three installers as artifacts; a
v*tag puts them on its release. A game whose setup runs on fewer systems addsplatforms: windows(any ofwindows linux macos) towith:.
The same program built with -X main.all=true (or run with --all) opens on
a list of every game in catalog.json. Picking one fetches that
game's launcher/launcher.json at its newest release tag, and from there it
is that game's installer: your copy, then its outputs (choices: PC, Android,
a console...), then Build. Nothing is downloaded until a game is picked, and
only the toolchains for the outputs ticked. A v* tag here puts it on the
release (all-games.yml).
A game joins the list (and its platform's tab) with a line in catalog.json, {repo, title, platform}, once it has a release
carrying launcher/launcher.json. A game with more than one output lists
them as choices so the player picks.
go run . # the sample game in game/
go test ./...
go build -ldflags=-H=windowsgui -o installer.exe . # Windows, no console window
go run . --shots screens # every screen as a PNG, then quit
go run . --build # start building at once
Linux needs libgl1-mesa-dev xorg-dev libasound2-dev (Ebitengine, with cgo).
A game's own installer is this with its launcher.json in game/ and
-ldflags "-X main.commit=<the game repo's commit>".
The installers aren't signed by a developer: Windows SmartScreen asks (More
info, Run anyway). On a Mac the app is signed ad hoc only: open it once,
then Open Anyway in System Settings, Privacy & Security. If macOS still
says it is damaged (an installer built before 0.2.1), run
xattr -dr com.apple.quarantine on the app.
MIT: see LICENSE.




