Automating a Google Play launch: from a 60-minute checklist to zero manual steps
By the third game, publishing had become the slowest part of making one. The game itself took an afternoon. Then came about an hour of clicking: create the app in Play Console, fill in a dozen App content forms, create the AdMob app and ad units, tick it into the consent messages, set up the purchases, upload the listing in fifteen languages, choose countries, send for review. And every click was a chance to get one of them wrong.
So I wrote a launch tool. Its job is to take a game from "the repo builds" to "submitted for review" with no human steps. From Spider Solitaire onwards, that's how every game has shipped.
Steps that check before they act
The design is simple and it's the part I'd recommend to anyone. A launch is a list of steps, and every step has two halves:
- a check: a read-only question asked of the real service. Does this app exist in Play Console? Is this ad unit in AdMob? Does the privacy page return 200 and describe this game? Does Play have both store descriptions?
- a run: the action that makes the check true.
launch run goes through the list, skips every step whose check already passes, and stops at the first one that fails, saving a screenshot and the page text. Fix the problem, run it again, and it carries on from there. Nothing is marked "done" because a script exited cleanly. It's done when the service says so.
That property paid for itself again and again. Every time a step was broken, its own check caught it. In a couple of hundred runs, nothing false has been marked done.
What the pipeline covers
About forty steps, in phases:
- Prepare: a unique upload key (Play refuses a key that already signs another app), privacy and delete-data pages on this site, a Firebase app, analytics wiring.
- Play record: create the app, contact details.
- Ads and billing: the AdMob app and its units, consent messages, RevenueCat products and entitlement.
- Build and upload: a release build, an ad smoke test on an emulator (the build must show
canRequestAds: trueand load a real ad; see the consent bug), the internal track, testers, one-time products, store listings. - App content: data safety, privacy, ads, content rating, target audience, advertising ID and the rest, plus category and tags.
- Release: production draft, countries, confirm, submit.
- After approval: link AdMob to the live listing.
Where there's no API, drive the browser
A surprising amount of Play Console has no API: creating the app, most App content declarations, the store category, production countries, tester lists and "Send for review". AdMob won't create apps or ad units through its API for most accounts either.
Those steps drive a signed-in Chrome over the DevTools protocol. It works, but Google's consoles are built from Material components that ignore naive clicks, and the first live run broke in five places, every one of them "clicked, but nothing happened":
- a policy checkbox click landed on a help link inside its label,
- a table row search failed because the text of all the cells ran together,
- a tick missed because the row was below the fold,
- a "Publish" confirmation was a
<div>that ignored DOM clicks and only responded to focus plus Enter, - a dropdown in a dialog was 0 pixels tall while it animated open.
The lesson I wrote on the wall: a browser flow that clicked isn't a flow that worked. Every browser step re-reads the page afterwards to confirm the change actually stuck.
The results
- Bible Word Search: forked and submitted in about two hours, with no manual console work.
- Spider Solitaire: the first launch with zero human minutes.
- Minesweeper: built and taken to one step short of submit in about three hours.
- Word Push: submitted about ninety minutes after I decided to ship it.
What isn't automated: building the game, playing it, and deciding to press go. Those are the parts I want to spend my time on.
Failures that became checks
- A fork inherited its parent's launch state. Copying a repo copied its "every step done" file, so Word Push would have skipped creating its own Firebase, Play and AdMob apps. State files are now stamped with the package name, and the tool refuses a mismatch.
- An app went into review by itself. With managed publishing off, any API edit can send every pending change for review. Steps now run in an order where that's harmless.
- Changing an app mid-review restarts the review. Two tester rollouts cancelled one game's first review twice. The tool now checks the publishing state before touching anything.
- The after-approval job never ran. An hourly cron was meant to spot approvals and link AdMob. Cron's
PATHdidn't include Node, so for ten days it logged "exited null" 69 times and linked nothing. I'd been doing those links by hand without noticing. Now new cron scripts get tested under an empty environment first, and "exited null" means the process never started. - The guessed content-rating answer. A rating question whose text didn't match the tool's pattern got a default "No". On a video poker game, that was wrong. More on that in its own post.
If you publish more than one or two apps, I'd strongly recommend this shape even if you automate only half the steps: read-only checks against the real services, plus a resumable run. It turns publishing from a checklist you hope you followed into one you can prove.