Skip to content

Latest commit

 

History

History
140 lines (106 loc) · 6.35 KB

File metadata and controls

140 lines (106 loc) · 6.35 KB

Releasing Playola Radio iOS

This document describes how releases ship today and the target flow once TestFlight upload is automated on CircleCI.

App Store promotion (TestFlight to public App Store) is intentionally manual in both flows.

Current release flow

  1. Create the release PR. On a clean develop, run:

    ./scripts/release.sh

    The script creates release/X.Y.Z, bumps MARKETING_VERSION in PlayolaRadio.xcodeproj/project.pbxproj, runs agvtool to bump the build number, lists the PRs merged since the last release, and opens a release PR targeting main.

  2. Merge the release PR using "Create a merge commit". Do NOT squash.

    scripts/release.sh computes the "unreleased PRs" list by walking the merge history of develop since the last sync-back from main. Squashing the release PR collapses the merge commits on main into a single commit that no longer shares SHAs with develop's history. The next run of release.sh then re-lists PRs that have already shipped. Using "Create a merge commit" preserves the first-parent chain and keeps the PR list accurate.

  3. Auto-tag fires. .github/workflows/auto-tag-release.yml runs on push to main, reads MARKETING_VERSION and CURRENT_PROJECT_VERSION from the pbxproj, and pushes tag vX.Y.Z-bN if it does not already exist. Each TestFlight build gets a unique v<version>-b<build> tag so hotfix build bumps trigger a fresh CircleCI upload under the same version.

  4. Sync-back PR opens. .github/workflows/sync-main-to-develop.yml fires on the same push and opens a PR merging main back into develop. Merge it promptly so the next release diff is accurate.

  5. Build and upload from the developer laptop. On the machine that holds the production signing auth and secrets:

    git checkout main && git pull
    bundle exec fastlane release_production

    The lane runs ensure_git_status_clean, ensure_git_branch('main'), scan, build_app, upload_to_testflight (falling back to local Apple auth), and sentry_debug_files_upload.

  6. Promote manually. Once the TestFlight build finishes processing, promote it to the App Store from App Store Connect when ready.

Target release flow (after PRs 2 through 5 land)

Steps 1 through 4 above are unchanged. Steps 5 and 6 become:

  1. CircleCI builds and uploads on tag push. Pushing vX.Y.Z triggers a release_build job that decodes secrets from env vars, runs match readonly, runs scan, runs build_app, uploads to TestFlight with the App Store Connect API key, and uploads dSYMs to Sentry. No developer laptop is involved.

  2. Promote manually. Unchanged.

The incremental rollout is:

  • PR 2: make the release_production Fastlane lane CI-runnable.
  • PR 3: add scripts/encode-secrets.sh and scripts/ci-write-secrets.sh.
  • PR 4: add a tag-filtered CircleCI job that builds but does not upload.
  • PR 5: enable upload_to_testflight and sentry_debug_files_upload.

Hotfix path

For a build-number-only bump on an existing version (for example, to fix a bug found by TestFlight testers on a shipped version, or to re-upload after a TestFlight processing failure), on a clean develop run:

./scripts/bump-build.sh

The script:

  1. Verifies you are on develop with a clean working tree.
  2. Fetches origin/main and origin/develop.
  3. Bumps the build number via agvtool (version stays the same).
  4. Creates hotfix/<version>-b<new_build>, commits, and pushes.
  5. Opens a PR targeting main with the PRs merged to develop since the last release tag.

Merge the hotfix PR using "Create a merge commit" (same rule as the release PR — do not squash). Auto-tag then fires with vX.Y.Z-bN and CircleCI uploads a new TestFlight build under the unchanged version.

If the fix warrants a version bump (not just a build rebuild), use ./scripts/release.sh with the patch option instead.

Secrets

Once automation is in place, the following env vars must be set in a CircleCI context (suggested name ios-release) and attached to the release workflow. Names only; values live in CircleCI.

Variable Purpose
APP_STORE_CONNECT_API_KEY_ID ASC API key identifier.
APP_STORE_CONNECT_API_ISSUER_ID ASC issuer UUID.
APP_STORE_CONNECT_API_KEY_CONTENT The .p8 private key for App Store Connect auth. Prefer base64 — set it to base64 -i AuthKey_XXXX.p8 | tr -d '\n'. (The raw .p8 also works, but CircleCI env vars often strip the multi-line PEM's newlines, which corrupts the key and fails the release with OpenSSL invalid curve name. The Fastfile auto-detects base64 vs raw via the -----BEGIN header.)
MATCH_PASSWORD Passphrase that decrypts the fastlane-match certificate repo.
MATCH_GIT_PRIVATE_KEY SSH key with read access to the fastlane-match-certs-and-profiles repo.
SECRETS_XCCONFIG_B64 Base64 of PlayolaRadio/Config/Secrets.xcconfig.
SECRETS_LOCAL_XCCONFIG_B64 Base64 of PlayolaRadio/Config/Secrets-Local.xcconfig.
SECRETS_DEVELOPMENT_XCCONFIG_B64 Base64 of PlayolaRadio/Config/Secrets-Development.xcconfig.
SECRETS_STAGING_XCCONFIG_B64 Base64 of PlayolaRadio/Config/Secrets-Staging.xcconfig.
SENTRY_AUTH_TOKEN Auth token used by sentry_debug_files_upload to push dSYMs.

Rotating or adding a secret

To rotate the xcconfig secrets, update the source of truth under ~/playola/playola-radio-ios/PlayolaRadio/Config/ on the release machine, then run ./scripts/encode-secrets.sh to print VAR=<base64> lines for each xcconfig. Paste each line's value into the matching CircleCI context variable. CircleCI decodes them at the start of the release job by running ./scripts/ci-write-secrets.sh, which reads the four SECRETS_*_B64 env vars and writes the xcconfig files back into PlayolaRadio/Config/.

For the ASC API key and Match passphrase, update the CircleCI context variable directly; there is no file to regenerate. After any rotation, re-run the release workflow on a test tag to confirm CI can still sign and upload.

Other developers can refresh their local xcconfig files from the release machine's ~/playola/playola-radio-ios/PlayolaRadio/Config/ by running ./scripts/setup-secrets.sh. That script skips files that already exist, so delete the stale xcconfig under PlayolaRadio/Config/ first when pulling a rotated value.