From ff4896a9012fc04de5f4b8ace59cf3f6b98debc9 Mon Sep 17 00:00:00 2001 From: Conrad Kramer Date: Sun, 7 Jun 2026 20:34:59 -0700 Subject: [PATCH] Keep internal TestFlight off external beta review --- .../workflows/apple-distribute-testers.yml | 1 - .forgejo/workflows/distribute-testflight.yml | 1 - .forgejo/workflows/publish-store-uploads.yml | 2 -- Scripts/ci/distribute-testflight.sh | 33 ++++++++++++++++--- ...nfrastructure-and-store-upload-pipeline.md | 6 ++++ store-metadata/app-store-connect/README.md | 7 ++-- 6 files changed, 40 insertions(+), 10 deletions(-) diff --git a/.forgejo/workflows/apple-distribute-testers.yml b/.forgejo/workflows/apple-distribute-testers.yml index f27ad96..f108237 100644 --- a/.forgejo/workflows/apple-distribute-testers.yml +++ b/.forgejo/workflows/apple-distribute-testers.yml @@ -478,7 +478,6 @@ jobs: BUILD_NUMBER: ${{ needs.prepare.outputs.build_number }} TESTFLIGHT_DISTRIBUTE_EXTERNAL: ${{ github.event.inputs.testflight_distribute_external || 'false' }} TESTFLIGHT_GROUPS: ${{ github.event.inputs.testflight_groups || '' }} - TESTFLIGHT_INTERNAL_GROUP: Internal IOS_USES_NON_EXEMPT_ENCRYPTION: ${{ github.event.inputs.ios_uses_non_exempt_encryption || 'false' }} run: Scripts/ci/distribute-testflight.sh diff --git a/.forgejo/workflows/distribute-testflight.yml b/.forgejo/workflows/distribute-testflight.yml index 54af649..8962c9a 100644 --- a/.forgejo/workflows/distribute-testflight.yml +++ b/.forgejo/workflows/distribute-testflight.yml @@ -67,6 +67,5 @@ jobs: BUILD_NUMBER: ${{ inputs.build_number }} TESTFLIGHT_DISTRIBUTE_EXTERNAL: ${{ inputs.testflight_distribute_external || 'false' }} TESTFLIGHT_GROUPS: ${{ inputs.testflight_groups || '' }} - TESTFLIGHT_INTERNAL_GROUP: Internal IOS_USES_NON_EXEMPT_ENCRYPTION: ${{ inputs.ios_uses_non_exempt_encryption || 'false' }} run: Scripts/ci/distribute-testflight.sh diff --git a/.forgejo/workflows/publish-store-uploads.yml b/.forgejo/workflows/publish-store-uploads.yml index 1d59954..825177d 100644 --- a/.forgejo/workflows/publish-store-uploads.yml +++ b/.forgejo/workflows/publish-store-uploads.yml @@ -262,7 +262,6 @@ jobs: BUILD_NUMBER: ${{ inputs.build_number }} TESTFLIGHT_DISTRIBUTE_EXTERNAL: ${{ inputs.testflight_distribute_external || 'false' }} TESTFLIGHT_GROUPS: ${{ inputs.testflight_groups || '' }} - TESTFLIGHT_INTERNAL_GROUP: Internal IOS_USES_NON_EXEMPT_ENCRYPTION: ${{ inputs.ios_uses_non_exempt_encryption || 'false' }} run: Scripts/ci/distribute-testflight.sh @@ -300,7 +299,6 @@ jobs: BUILD_NUMBER: ${{ inputs.build_number }} TESTFLIGHT_DISTRIBUTE_EXTERNAL: ${{ inputs.testflight_distribute_external || 'false' }} TESTFLIGHT_GROUPS: ${{ inputs.testflight_groups || '' }} - TESTFLIGHT_INTERNAL_GROUP: Internal IOS_USES_NON_EXEMPT_ENCRYPTION: ${{ inputs.ios_uses_non_exempt_encryption || 'false' }} run: Scripts/ci/distribute-testflight.sh diff --git a/Scripts/ci/distribute-testflight.sh b/Scripts/ci/distribute-testflight.sh index 3d6badc..7c09ee1 100755 --- a/Scripts/ci/distribute-testflight.sh +++ b/Scripts/ci/distribute-testflight.sh @@ -6,16 +6,31 @@ Scripts/ci/check-release-config.sh app-store build_number="${BUILD_NUMBER:?BUILD_NUMBER is required}" distribute_external="${TESTFLIGHT_DISTRIBUTE_EXTERNAL:-false}" testflight_groups="${TESTFLIGHT_GROUPS:-}" -testflight_internal_group="${TESTFLIGHT_INTERNAL_GROUP:-Internal}" uses_non_exempt="${IOS_USES_NON_EXEMPT_ENCRYPTION:-false}" wait_processing_timeout="${TESTFLIGHT_WAIT_PROCESSING_TIMEOUT_SECONDS:-7200}" if [[ -z "$distribute_external" ]]; then distribute_external="false" fi +case "$distribute_external" in + true|false) + ;; + *) + echo "::error ::TESTFLIGHT_DISTRIBUTE_EXTERNAL must be true or false." >&2 + exit 1 + ;; +esac if [[ -z "$uses_non_exempt" ]]; then uses_non_exempt="false" fi +case "$uses_non_exempt" in + true|false) + ;; + *) + echo "::error ::IOS_USES_NON_EXEMPT_ENCRYPTION must be true or false." >&2 + exit 1 + ;; +esac case "$wait_processing_timeout" in ""|*[!0-9]*) echo "::error ::TESTFLIGHT_WAIT_PROCESSING_TIMEOUT_SECONDS must be an integer number of seconds." >&2 @@ -51,10 +66,20 @@ args=( "uses_non_exempt_encryption:${uses_non_exempt}" ) -if [[ -n "$testflight_groups" ]]; then +if [[ "$distribute_external" == "true" ]]; then + if [[ -z "$testflight_groups" ]]; then + echo "::error ::TESTFLIGHT_GROUPS is required for external TestFlight distribution." >&2 + exit 1 + fi args+=("groups:${testflight_groups}") -elif [[ "$distribute_external" != "true" && -n "$testflight_internal_group" ]]; then - args+=("groups:${testflight_internal_group}") +else + args+=( + "skip_submission:true" + "notify_external_testers:false" + ) + if [[ -n "$testflight_groups" ]]; then + echo "::warning ::Ignoring TESTFLIGHT_GROUPS for internal-only TestFlight distribution; App Store Connect manages internal tester group availability." >&2 + fi fi nix run nixpkgs#fastlane -- "${args[@]}" diff --git a/evolution/proposals/BEP-0009-release-infrastructure-and-store-upload-pipeline.md b/evolution/proposals/BEP-0009-release-infrastructure-and-store-upload-pipeline.md index 30e8a37..ae22d16 100644 --- a/evolution/proposals/BEP-0009-release-infrastructure-and-store-upload-pipeline.md +++ b/evolution/proposals/BEP-0009-release-infrastructure-and-store-upload-pipeline.md @@ -205,6 +205,12 @@ The same change also makes the forge itself the release authority: release tags upload/signing jobs cannot suppress distribution of an already-uploaded build. `distribute-testflight` is the narrower one-job retry workflow when only App Store Connect processing and tester assignment need to be retried. +- Internal TestFlight distribution must not submit the build for external beta + review. The internal lane waits for processing, sets export-compliance + metadata, uses Fastlane `skip_submission:true`, and relies on App Store + Connect's configured internal tester group availability. External TestFlight + distribution requires explicit groups plus beta-review metadata before it can + be enabled. - Add `infra/releases` as the OpenTofu boundary for the GCS release and package repository backup buckets. Garage is the required S3-compatible primary distribution target after host bootstrap. `infra/identity` remains the boundary for Google KMS signing keys, the Authentik WIF pool/provider, and the runner service account. - Serve `releases.burrow.net` and `packages.burrow.net` from the forge Caddy edge as stable Burrow domains backed by the GCS release and package buckets. diff --git a/store-metadata/app-store-connect/README.md b/store-metadata/app-store-connect/README.md index 2552de0..e565b9a 100644 --- a/store-metadata/app-store-connect/README.md +++ b/store-metadata/app-store-connect/README.md @@ -14,5 +14,8 @@ Forgejo secrets should only provide the runner age identity fallback, not the App Store key itself. The initial release pipeline can upload `.ipa` and `.pkg` artifacts once the -Apple build lane produces signed outputs. TestFlight external distribution must -remain explicit until review metadata, groups, and tester policy are complete. +Apple build lane produces signed outputs. Internal TestFlight distribution waits +for processing and skips external beta-review submission; App Store Connect then +exposes the valid build through the configured internal tester group. TestFlight +external distribution must remain explicit until review metadata, groups, and +tester policy are complete.