Publishing with gplay

Google Play Console is a lot of clicking. Uploading a build, filling in your store listing in every language, creating subscriptions and remembering to activate both halves of each one — all of it is web forms.

gplay is a free, open-source (MIT) command-line tool for Google Play Console. NowKit uses it, when you let it, to do those parts for you.

It's completely optional. Say no and every /kit-* command works exactly as it always has, walking you through Play Console by hand. Nothing gets installed without asking.

Setting it up

You're offered this once, inside /kit-sign-release, when you're actually ready to upload a build. Not during setup — it's useless until you have a Play Console account and an app created there.

Three things happen:

  1. Install — one small binary (Homebrew on macOS/Linux, PowerShell on Windows, no WSL).
  2. Sign in — a browser opens; sign in with your Google account. The agent runs gplay setup --auto, which creates a service account: a robot account that Google Play accepts uploads from.
  3. Grant it access — one visit to Play Console → Users and permissionsInvite new users, pasting the robot account's email and ticking four boxes.

That's it, once, for the life of the app. Every later command just works.

Shipping a second app?

The robot account is created once per Google Cloud project, so your next app gets the same robot email — and Play refuses to invite it twice with "User already exists."

You're not inviting it again, you're editing it: Users and permissions → click the existing …gserviceaccount.com row → App permissionsAdd app → pick the new app → tick the same four boxes → ApplySave changes.

If you ship several apps, the Account permissions tab grants across every app on the developer account including ones you create later — one grant instead of repeating this each time. It's your own robot account on your own developer account, so the caution that applies to RevenueCat's key doesn't apply here.

The four permissions

In the App permissions tab of the invite dialog, tick:

  • Releases → Release apps to testing tracks
  • Releases → Release to production, exclude devices, and use Play App Signing
  • Releases → Manage testing tracks and edit tester lists
  • Store presence → Manage store presence

Leave everything else unticked. In particular, don't grant Admin and don't grant the financial permissions — those belong to the separate service account RevenueCat uses. Keeping them apart means no third-party dashboard ever holds a key that can publish to your production track.

Your Play Console account and your Google Cloud account don't have to be the same email. The two get connected by the invite above, not by sharing a login.

What it does for you

Uploading builds

/kit-sign-release sends the signed AAB to your testing track directly. /kit-publish-to-play's update path — the flow you'll run for every release after the first — becomes essentially one command instead of a walk through the console.

Creating your in-app products

The most error-prone step in the kit. Google nests a subscription inside a base plan and both must be activated; miss one and your paywall silently shows nothing. /kit-setup-paywall now creates the products from the plan /kit-plan-monetization wrote, activated correctly.

Product IDs are permanent. Once premium_monthly exists, that name is reserved forever — deleting the product doesn't free it. The kit shows you every ID and price and waits for your explicit yes before creating anything, and it never makes throwaway "test" products.

Store listing, screenshots and translations

/kit-publish-to-play pushes your title, short and full descriptions, screenshots, feature graphic and release notes. If you ran /kit-translate-listing, every language uploads at once — instead of pasting three fields per locale into a browser.

Staged rollouts

Something the kit couldn't do at all before. Release to a fraction of your users, watch, then ramp:

# 10% → watch → 50% → everyone
gplay rollout update   --package <your.app.id> --track production --rollout 0.5
gplay rollout complete --package <your.app.id> --track production

# something's wrong
gplay rollout halt     --package <your.app.id> --track production

For a first production release, 0.1 → 0.5 → complete over about a week is a sensible default. The agent will offer it.

Per-country pricing

A flat US price prices your app out of India, Brazil and Indonesia. /kit-plan-monetization can convert one base price into every market's local equivalent.

What it can't do

Google's API simply doesn't cover these, so they stay manual — no tool can automate them:

  • Creating your Play developer account, paying the $25, identity verification
  • Creating the app in Play Console (every command below needs the app to already exist)
  • The whole "Set up your app" checklist — privacy policy, sign-in details, ads, content rating, target audience, data safety, government/financial/health declarations
  • The Advertising ID declaration
  • Submitting for review. On an app Google has never reviewed, the API refuses to send changes for review at all — it commits them as changes not sent for review — so a first release lands on the track unconfirmed and you finish it in the browser, through Dashboard → Create and publish a release (Preview and confirmSend the release to Google for reviewPublish your app). Publishing overview is empty on that path. For an app that's already live, it's the usual Publishing overview → Send app for review.
  • Pre-registration (/kit-pre-register-setup is unchanged)
  • Tester email lists — the API only handles Google Groups, not the console's email lists
  • The AI asset declaration on the store listing's Review step — pushing a listing through the API doesn't set it, so you still answer that one in the browser

The 12-tester / 14-day closed-testing requirement is Google policy, not an API limit, so nothing changes there either.

Previewing before a write

gplay supports --dry-run on the metadata commands (sync import-listings, sync import-images, listings update), and the kit uses it before overwriting your live store listing.

Releases are different — there is no dry run for an upload. Before a release the kit runs:

gplay validate --package <your.app.id> --bundle app-release.aab

which is the canonical readiness report — local artifact, metadata, screenshots, release notes, plus the remote track and listing state — and then shows you the exact upload command and waits for your go. That gate matters more than a preview would: Play permanently burns the versionCode in an uploaded bundle. Deleting the release afterwards does not give it back.

If something goes wrong

invalid_grant right after setup. Normal. A freshly created key takes up to a minute to propagate through Google's auth. Wait and retry.

Your app doesn't appear. Same cause — the Play Console invite takes a moment to take effect. If it still doesn't show after a minute, check the invite went to the robot account's exact email address.

The very first upload of a brand-new app fails. Play may require the first build of an app that has never had one to go through the web console so it can enrol Play App Signing. If that happens the agent falls back to the browser for that one upload — every upload after it works through gplay.

Starting over. Your credentials live in ~/.gplay/, outside your project, so they're never at risk of being committed. Delete that folder to reset.