chore(signing): bootstrap android keystore and play credentials from 1password - #567
Open
RonenMars wants to merge 2 commits into
Open
chore(signing): bootstrap android keystore and play credentials from 1password#567RonenMars wants to merge 2 commits into
RonenMars wants to merge 2 commits into
Conversation
…1password scripts/bootstrap-local-signing-op.sh only covered iOS, so the Android upload keystore and the Play publisher service account had to be reconstructed by hand on every machine. PLAY_SA_JSON_B64 in particular has no local fallback — ship-android.sh aborts at its first step without it. Adds a --platform ios|android|all flag and an Android section that pulls the four ANDROID_* keystore fields plus PLAY_SA_JSON_B64 from two more 1Password items, then hands them to the existing bootstrap-android-signing.sh and fetch-play-credentials.sh, which already validate them with keytool and jq. ship-android.sh self-bootstraps the same way ship-ios.sh does, and stays inert in CI because scripts/.env.signing-op is gitignored and deploy.yml materializes both artifacts in earlier steps.
RonenMars
force-pushed
the
chore/local-signing-op-android
branch
from
August 8, 2026 20:05
b370d57 to
cb6f2b9
Compare
The 1Password bootstrap was undocumented — deployment.md still told you to export ANDROID_* and PLAY_SA_JSON_B64 by hand, and nothing explained why a fresh worktree needs a bootstrap run when the keystore and .p8 are already on the machine. Adds a Local credentials section covering the machine-global versus per-checkout split, the per-checkout command, and the everyday ship commands. Records the asymmetry that ship-android.sh calls the 1Password wrapper itself while ship-ios.sh only skips its bootstrap when .env.signing already exists, so iOS fails with ASC_KEY_ID must be set in a fresh worktree. Also records why none of this reaches CI, including the easy-to-miss detail that deploy.yml does not export PLAY_SA_JSON_B64 into ship-android.sh because Actions env is scoped per step.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
scripts/bootstrap-local-signing-op.shpulled every iOS credential from 1Password, but had no Android half.The upload keystore and the Play publisher service account had to be rebuilt by hand on every machine, and
PLAY_SA_JSON_B64is the one credential with no local fallback —fetch-play-credentials.shhard-requires it, sonpm run ship:androidaborted at its first step on any machine where~/.config/threadbase/play-console-sa.jsonwas absent.What changed
bootstrap-local-signing-op.shgains a--platform ios|android|allflag (defaultall) and an Android section that reads the fourANDROID_*keystore fields plusPLAY_SA_JSON_B64from two more 1Password items.It hands them to the existing
bootstrap-android-signing.shandfetch-play-credentials.shrather than duplicating their work — those already validate the keystore withkeytool -listand the service-account JSON with ajqshape check.require_fieldnow takes an optional vault/item so the Android item can be read without disturbing any existing call site, andOP_VAULT/OP_ITEMare required only when iOS is in scope.The whole Android half degrades to a skip-with-warning when
OP_ANDROID_*is unset, matching how the existing Sentry block behaves.ship-android.shgains one self-bootstrap hook, placed before the promote fast path so it covers both that and the build path.Why the hook is inert in CI
Two independent conditions, either sufficient on its own.
scripts/.env.signing-opis gitignored, so it never exists on a runner and the-ftest fails.deploy.ymlmaterializes both artifacts beforeship-android.shruns — the Play credential cache in the Fetch Play credentials step and.env.signing.androidin the Bootstrap Android signing step — so the missing-artifact test fails too.Worth stating explicitly because it is easy to assume otherwise:
deploy.ymldoes not exportPLAY_SA_JSON_B64intoship-android.sh.That variable is scoped to the Fetch Play credentials step, and Actions
env:is per-step, so it is unset by the time Ship Android runs.The
PLAY_SA_JSON_B64check in the guard is therefore for a local shell that exports it directly, not for CI.On a machine with neither 1Password nor the artifacts, the fall-through is today's behavior:
fetch-play-credentials.sh's own hard error.Verification
bash -nclean on both scripts;--help,--platform bogus(exit 2) and the missing-config path all behave.--platform ios --dry-runis unchanged against a real vault: all ASC fields OK, both provisioning profiles resolved with app-groups present, Sentry OK, Distribution cert found.--platform android --dry-runresolves all five Android/Play fields.A live
--platform androidrun wrote a keystore byte-identical to the pre-existing one (same file SHA-256, same certificate fingerprint, aliasupload), a validplay-console-sa.jsonat mode 600, and an.env.signing.androidcarrying all fourTB_MOBILE_UPLOAD_*.No secrets in the diff: only the gitignored
scripts/.env.signing-opholds real vault and item names, and the committed.examplecarries placeholders only.Note that CI cannot exercise the new
oppath by design, so the evidence above is from local runs.Follow-up, not in this PR
The Android 1Password item can carry a
sha256_fingerprintfield thatbootstrap-android-signing.shdoes not check.Wiring it in would catch a wrong-keystore rotation at bootstrap time rather than at Play upload time.