To isolate some work I created a separate git worktree and cut iOS and Android releases from it. One script takes it from archive to upload.
A binary containing none of my changes went out. No warning, no error.
Does your release script build "the directory you're in," or a path written down somewhere?
The manifest points at the main checkout
The release manifest pinned the project path to the main checkout with an absolute path.
Run the script from a worktree and it builds that absolute path. Your worktree's changes never come near it. Archive succeeded. Upload succeeded. One such build actually reached the store (harmless, because I hadn't attached the build to a version — but it stays in the build list).
This is the second time a release tool touched something other than what I named — the first was the dry run that shipped to production. The fix is to pass a copy of the manifest with the path changed. Which is where the second trap is.
Argument position is silently ignored
python3 release.py release <app> --config <copy> --version X --build-number Y # works
python3 release.py --config <copy> release <app> ... # silently ignored--config is defined on the shared parser (the subcommand's parent), so the same destination is parsed twice and the subparser's default, parsed later, wins.
No error, no warning. And that exact combination is how the main-checkout build got uploaded — meaning I reproduced the accident after diagnosing it.
My first suspicion was that the config file format was wrong. The real trap is the structure: the same dest defined on two parsers.
Second: a worktree has no keys
Git does not copy untracked or ignored files into a worktree.
So the keystore config, the signing key file, and the backend and map keys in local properties are all missing. The Android release bundle can't be produced.
That one fails loudly, which is the better outcome. The quiet failure is far more expensive.
What would you do?
You need to release from a worktree. Which way?
- Build from the main checkout — matches the manifest. But then another session's uncommitted changes ride out in your release. That's abandoning the isolation.
- Pass a manifest copy — get the argument position wrong and it's silently ignored. What I do now.
- Make the script verify where it's running — stop if the manifest path differs from the current worktree.
The third is the real answer, and it means fixing the release tool. For now I'm on the second.
Three checks
- Does your release manifest contain absolute paths? Then worktree, container, or CI — it builds that one location regardless.
- Is the same option defined on both the parent parser and the subparser? Then position decides, silently.
--helpwon't show you. - Do you verify the uploaded artifact contains your commit? "Upload succeeded" says nothing about what was uploaded.
The honest part
I solved the secrets problem by copying them from the main checkout. That's a stopgap. Deleting the worktree deletes the copies, so exposure risk is low — but having "hand-copy the key files" as a step on the release path is not good.
And the first defect is still quiet. Build succeeds, upload succeeds. To see what went out you have to dig through the artifact. Until that check lives inside the release script, this accident can happen again.