Framework

Native Android CI/CD you can self-host

Gradle wrapper validation, signed app bundles, and Play track uploads — with your upload keystore imported rather than regenerated.

The pipeline, step by step

  • android/bootstrapwrapper validation and dependency resolution
  • android/builda signed AAB or APK
  • android/testJUnit, and Espresso on an emulator
  • android/bundletooluniversal APK extraction from a bundle
  • distribute/playa track upload, with staged rollout

Every one of these is in the step library, with the credentials it can ask for and whether it may run on a fork’s pull request — generated from the file the server itself reads.

The keystore is imported, never generated

Under Play App Signing, replacing an upload key requires Google’s reset process. A tool that generates a new keystore when it cannot find one has locked you out of your own listing — so Conductor imports the one you have and refuses to invent a replacement.

Emulator images match their host

An x86_64 system image does not boot on Apple silicon and an arm64-v8a image does not boot on an Intel Mac. The architecture is matched to the runner rather than hardcoded, which sounds obvious until you meet a fleet with one of each.

Staged rollout, and the ability to stop it

Play supports a real staged rollout and a real halt, and both are exposed — including the rollout percentage on the dashboard, so “what is actually live” is a thing you can see rather than a thing you go and check.

What a build produces

AAB, APK, mapping.txt, and native debug symbols.

Symbol files are collected on every build rather than on the ones somebody remembers to configure. A crash report that arrives three weeks later resolving to a memory address is a crash report nobody can act on, and the difference is a build-time decision that cannot be made retroactively.