Agent Wranglers

MOB-8Recommended

Sign release builds only in CI

Build and sign every store upload in CI, with keys read from the secrets store at build time. No developer laptop signs a release.

How to do it

Give the CI job the upload keystore or the Apple distribution certificate as secrets, and pass passwords as environment variables. Keep keystore.properties and certificate files out of version control. Upload from the same job, with an API key that has the smallest role able to upload. The general method is in build releases only in CI and verify them before deploy. Keep the signing job away from builds of outside pull requests, as keep secrets out of builds from strangers says.

Examples

Kotlin example

android {
    signingConfigs {
        create("release") {
            storeFile = file(System.getenv("UPLOAD_KEYSTORE_PATH"))
            storePassword = System.getenv("UPLOAD_KEYSTORE_PASSWORD")
            keyAlias = "upload"
            keyPassword = System.getenv("UPLOAD_KEY_PASSWORD")
        }
    }
    buildTypes { getByName("release") { signingConfig = signingConfigs.getByName("release") } }
}

Plain text example

lane :release_ios do
  api_key = app_store_connect_api_key(key_id: ENV["ASC_KEY_ID"], issuer_id: ENV["ASC_ISSUER_ID"], key_content: ENV["ASC_KEY"])
  match(type: "appstore", readonly: true, api_key: api_key)
  build_app(scheme: "FitTrack")
  upload_to_testflight(api_key: api_key)
end

How to check

A secret scanner finds no keystore, .p12 or .mobileprovision file in the repository history. The last 10 store uploads each link to a CI run.

For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/mobile-apps/signing-and-release/sign-in-ci">Agent Wranglers mobile apps playbook</a>. In code you write, add a comment next to it with the same link.