← All devlog posts

The Lien

A build in someone else's hands

Getting a build to a tester turned out to be its own engineering project. A stale download link, a version stamp that did not match, three CI runs for one PC build, and a 482 KiB patch where the first upload was 92 MiB.

Handing someone a build sounds like the last step of the work. It turned into a small project of its own, and most of its bugs only showed up when I checked what a tester would get instead of trusting the green tick in CI.

The Android rail

On August 19 a GitHub Actions workflow started building a sideloadable debug APK and publishing it. It runs by hand or from a build-* tag, because each run pulls about 2.5 GB of tooling.

Godot's export presets are gitignored, so the package id and the filters that keep the test runner and a 7.7 MB folder of rejected art drafts out of the build lived on one machine and nowhere else. A committed copy is what lets CI export at all.

A tester also needs to say which build they hit a bug on. The main menu showed a version label, but it was hardcoded, so every build said v0.1. CI now stamps the short commit hash onto it, and the stamp travels inside the build.

Checking the live download link found two defects the same day. The versioned file was named with the full forty-character hash while the title screen showed seven characters, so a tester reporting a build would be naming a file that did not exist. And the stable "latest" link served a build four hours old. The workflow asked for a 60-second cache, and the response carried four hours.

The workflow was innocent. A Cloudflare zone setting for browser cache lifetime raises any shorter cache time to its fixed value and leaves longer ones alone, which is why the 60 seconds on "latest" became four hours while the year-long cache on versioned files came through untouched. The header separated this from a stale edge copy. Before the setting changed, "latest" already returned the fresh file on a cache miss and still carried the four-hour header, which no stale copy can do. The lesson in the project notes is to check the layer above before editing the thing that asks.

That same day, the first player from outside the project was playing the game on Android.

The PC rail

On September 16 the project pivoted from Android to PC. The roadmap records that distribution, market and audience decided it, and that the switch was cheap, because the game had always been authored at a desktop aspect ratio and no in-app purchase had ever been built. The Android rail stays, unshipped.

A Windows export is an executable, a data pack and a folder of 187 files, 86 MB zipped. The distribution plan argues that the sixth build matters more than the first. A tester who has to fetch 86 MB every time quietly stops, and starts reporting bugs fixed a fortnight earlier. So the PC rail pushes to a restricted itch.io page with butler, which uploads a diff against the previous build, and the itch app updates in the background.

It took three runs on September 17. The first failed on an export that had succeeded, because the guard grepped for a success string taken from this repo's own notes, and Godot prints it in a different order, wrapped in terminal color codes. The second was refused at upload because the account's email had never been verified. Even that proved something, since butler had already authenticated and found the project. The third worked, and the export takes about 16 seconds.

The patch claim was the reason for the whole rail, so it was measured. The first push was 91.91 MiB. Build 2 reused 99.55% of the old data and moved a 482.81 KiB patch. I installed the first build and played it. The title screen showed the right commit, the audio played, and the art rendered in a real fight.

Key art for the game. A drifter in a wide hat and long coat, a guitar slung at his side, plays a harmonica where a cracked dirt road forks through dry golden grass at dusk.

What is still open

Links went out to testers today. Nobody has walked the itch app install path yet, and that is the half that delivers the patches, so a silent failure there would look like nobody reporting bugs. A direct browser download also trips SmartScreen, because the executable is unsigned, and a tester has to click More info and then Run anyway.

Steam still needs an app ID, a fee and a review before it replaces any of this.

← All devlog posts