Docs

Android on the Platform

Android builds need a 10 GB SDK, a matching JDK, and Gradle. On the Platform they run on a hosted Linux runner instead, signed with a keystore the Platform holds in custody, so you need no Android tooling on your own machine.

The app itself is the same Hotwire Native shell as the DIY track: a thin native wrapper around your deployed site.

Before you start

  • Your app is deployed and in remote mode. Android builds are remote mode only. mode: remote plus an absolute https:// remote.url is required, and the Platform refuses the submission without it.
  • You have connected the CLI with every platform login.
  • A Google Play Console account ($25, one time), only if you are shipping to Play. Direct APK downloads need nothing from Google.
config/everywhere.yml yaml
app:
  name: Notes
  bundle_id: com.example.notes
  version: "1.4.0"
  mode: remote

remote:
  url: https://notes.example.com

build:
  targets:
    - android-arm64:play

platforms:
  android:
    bundle_id: com.example.notes.android   # must be a valid applicationId

Android package names are stricter than bundle ids. An applicationId must be two or more dot-separated segments of letters, digits and _, each starting with a letter, and no hyphens. The default bundle id derived from a multi-word app name is hyphenated (com.example.my-notes), so set platforms.android.bundle_id yourself when yours would be. An invalid one is rejected up front rather than failing minutes into Gradle.

Pick this id carefully: Play ties an app to its package name permanently.

Two ways to ship

Channel Target Artifact Where it goes
Google Play android-arm64:play .aab The Play track set on the release channel
Direct download android-arm64:direct .apk Your update feed and a public download page

You can ship both from one build: --target android-arm64:play,android-arm64:direct.

Upload credentials

Under Credentials โ†’ New:

Android keystore (.jks), always

Create one with the JDK's keytool on any OS:

Terminal bash
keytool -genkeypair -v -keystore upload.jks -alias upload -keyalg RSA -keysize 2048 -validity 10000

Answer the prompts, then upload upload.jks with the store password, key alias, and key password you chose.

Keep your own copy. Google Play ties your app to this key. Lose it and you can never ship updates to that app again.

Google Play service account (.json), Play only

Entirely web-based:

  1. In Google Cloud โ†’ Service Accounts, create a service account, then Keys โ†’ Add key โ†’ JSON and download it.
  2. In Play Console โ†’ Users and permissions, invite that service account's email and grant it Release access to your app.

Upload the JSON. The Platform checks it's a real service-account key with a usable private key before accepting it.

Build it

Terminal bash
every platform build --target android-arm64:play
every platform build --target android-arm64:direct

Either waits for a hosted Linux runner, builds the shell against your deployed URL, signs it with your keystore, and downloads the finished artifact into dist/. The play channel produces an .aab (Play's upload format, which cannot be installed on a phone); direct produces an installable .apk.

The app's versionCode is derived from app.version, so 1.2.3 becomes 10203 and bumping the version bumps the code Play compares against. Set EVERY_ANDROID_VERSION_CODE if your pipeline numbers builds instead of versioning them.

After the build: Google Play

Promote the succeeded build in the dashboard (Promote, pick a channel). The Platform then uploads the .aab to Play itself, over the Play Developer API. The service-account key never leaves the server and never reaches a runner. It opens an edit, uploads the bundle, points the track at the new version code, and commits. Nothing is visible to testers until that commit lands, so a failure part-way through changes nothing.

Which track? Each release channel carries a Google Play track: internal, alpha, beta, or production. New channels start on internal. Set it on the channel's row on the app page before promoting. Release notes from the promote form travel with the release as the track's notes.

Play's own rules still apply, and its refusals come back verbatim on the release:

  • The app's first release must be made in the Play Console by hand. The API cannot create one.
  • The service account needs the Release apps to testing tracks permission.
  • The version code must be higher than every build already uploaded.

After the build: direct downloads

Promote an android-arm64:direct build and the Platform publishes the signed .apk, then serves it from a public install page:

URL text
https://platform.rubyeverywhere.com/d/<org>/<app>/<channel>

The page shows the version, size, and SHA-256, a Download APK button, a QR code so a tester can open the page on the phone itself, and instructions for Android's "install unknown apps" prompt (which every sideloaded app triggers the first time). The channel key is always in the URL, so a link you hand out keeps pointing at that channel.

With a bucket connected, the same release also writes a signed update feed, so installed copies update themselves. Without one, the Platform hosts the .apk and the page works the same way, with no feed to update from.

When a build is refused

  • "Android builds are remote mode only". Set mode: remote in everywhere.yml.
  • "remote mode needs remote.url set to an absolute http(s) URL". The shell has nothing to point at without it.
  • "can't be an Android applicationId". A hyphen or a single-segment id. Set platforms.android.bundle_id.
  • Missing credentials. The keystore for either channel, plus the Play service account for play.

What's the same as DIY

Tabs, icons, colors, permissions, native extensions, and the bridge behave as on the DIY Android page, including platforms.android.target_sdk when you need to get ahead of a Play deadline. The Platform changes where the build runs, not what gets built.

Rails ยท Hanami ยท Sinatra โ€” built with Ruby