Skip to content

Make local signed release builds possible without leaking passwords - #9

Merged
daxpryce merged 2 commits into
mainfrom
local-release-builds
Sep 7, 2026
Merged

daxpryce merged 2 commits into
mainfrom
local-release-builds

Conversation

@daxpryce

@daxpryce daxpryce commented Sep 7, 2026

Copy link
Copy Markdown
Owner

Building a personal-profile release locally is the only way to get that artifact, since the workflow only ever publishes public builds. Two things made it worse than it needed to be.

A successful build could hand you an uninstallable APK

Without the four signing variables, releaseSigningAvailable is false, no signingConfig is created, and AGP emits app-personal-play-release-unsigned.apk. scripts/build-apk then copied that to dist/autospeed-personalPlay-release.apk -- a name with no hint that it cannot be installed -- and exited 0.

Confirmed before fixing:

$ apksigner verify dist/autospeed-personalPlay-release.apk
DOES NOT VERIFY
ERROR: Missing META-INF/MANIFEST.MF

Installing it fails with INSTALL_PARSE_FAILED_NO_CERTIFICATES, far from the cause. scripts/build-apk now verifies release output with apksigner and fails with an actionable message instead of copying it.

Signing passwords had to go through argv

The only documented way to get the variables into the container was --remote-env, which puts passwords in the host process list -- the same exposure docs/releases.md already warns about for keytool. devcontainer.json now forwards all four from localEnv, exactly as it already did for AUTOSPEED_PERSONAL_USE_ACK, so they can be exported in the shell instead. Unset variables forward as empty, which is the existing "no signing config" path, so a clean clone is unaffected.

Verification

  • Guard fires: unsigned build now fails and copies nothing into dist/.
  • Positive path exercised with a throwaway keystore (since the real passwords are not mine to hold): forwarding worked with no container rebuild, and the resulting APK verified as signed. Keystore and test APK deleted afterwards.
  • Release output is what was wanted: 821,595 B vs the debug build's 11,114,906 B, debuggable absent rather than true, application ID io.pryce.android.autospeed.personal rather than .personal.debug.
  • ./scripts/container-run ./scripts/check passes.

docs/releases.md gains the local-build recipe the new error message points at, including that AUTOSPEED_SIGNING_STORE_FILE is a container-side path.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

Building a personal-profile release locally is the only way to obtain
that artifact, and two things made it needlessly hazardous.

Without the four signing variables no signingConfig is created and AGP
emits `*-release-unsigned.apk`. scripts/build-apk copied that into dist/
under a name giving no hint it could not be installed, and exited 0; the
failure surfaced much later as INSTALL_PARSE_FAILED_NO_CERTIFICATES.
Verify release output with apksigner and fail with an actionable message.

Getting the variables into the container also required --remote-env,
placing passwords in the host process list, which docs/releases.md
already warns against for keytool. Forward all four from localEnv as
devcontainer.json already did for AUTOSPEED_PERSONAL_USE_ACK. Unset
variables forward as empty, which is the existing no-signing path, so a
clean clone is unaffected.

Document the local recipe, including that AUTOSPEED_SIGNING_STORE_FILE
is a path inside the container.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 379a93aa-5a91-48d4-af89-3c240ef6d47f
Copilot AI lite review requested due to automatic review settings September 7, 2026 05:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The current apksigner selection logic is lexicographic and can pick the wrong build-tools version when multiple versions are installed.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Enables safer and more reliable local signed release APK builds (including personal-profile variants) by preventing unsigned “successful” release artifacts from being treated as installable and by avoiding passing signing secrets via host argv.

Changes:

  • scripts/build-apk now verifies release APKs with apksigner and fails with guidance if the output is unsigned.
  • docs/releases.md documents a local signed release build workflow that relies on host-exported env vars forwarded into the devcontainer.
  • .devcontainer/devcontainer.json forwards signing env vars from localEnv into the container to avoid --remote-env password exposure.
File summaries
File Description
scripts/build-apk Adds release APK signature verification via apksigner to prevent copying unsigned artifacts into dist/.
docs/releases.md Adds local-release build instructions aligned with the new scripts/build-apk failure mode and devcontainer env forwarding.
.devcontainer/devcontainer.json Forwards signing-related environment variables from the host into the devcontainer.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread scripts/build-apk Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 7, 2026 05:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

scripts/build-apk may non-deterministically verify/copy the wrong APK when both signed and unsigned release artifacts coexist in the output directory.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

scripts/build-apk:59

  • apk is selected earlier via find ... -print -quit, which is non-deterministic if multiple APKs exist in the outputs directory (e.g. after switching from an unsigned build to a signed build, both *-release-unsigned.apk and *-release.apk can coexist). In that case this verification (and the subsequent copy) may target the wrong/stale artifact and incorrectly fail (or copy an unsigned APK despite a signed one being present). Consider re-selecting the release APK here to prefer the signed *-release.apk when present before running apksigner.
  • Files reviewed: 3/3 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@daxpryce
daxpryce merged commit d18c289 into main Sep 7, 2026
2 checks passed
@daxpryce
daxpryce deleted the local-release-builds branch September 7, 2026 05:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants