Repository navigation
Add internal provisioning profile support to iOS TestFlight workflow - #8185
Conversation
Support separate internal and beta provisioning profiles, selected based on bundle ID. Internal builds (dev.cmux.app.internal) now use the internal provisioning profile. Fixes export mismatch error when uploading internal builds. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThe iOS TestFlight workflow now selects an internal or beta provisioning profile from secrets based on ChangesiOS TestFlight provisioning profile handling
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 24 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (24 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR adds logic to select between a beta and an internal provisioning profile during the iOS TestFlight upload, choosing based on
Confidence Score: 2/5The change introduces a profile-selection branch that is permanently unreachable due to a variable scoping mistake, meaning every upload will still mismatch the bundle ID and fail at export — the core defect the PR was written to fix. The profile-selection logic gates on .github/workflows/ios-testflight.yml — specifically the Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Step1 as Install beta provisioning profile
participant Env as $GITHUB_ENV / Job env
participant Step2 as Archive, export, and upload
Note over Step1: IOS_BETA_BUNDLE_ID NOT set in job env
Step1->>Env: reads IOS_BETA_BUNDLE_ID (unset)
Note over Step1: Fallback: dev.cmux.app.beta
Step1->>Step1: Installs BETA profile (always)
Step2->>Step2: "env: IOS_BETA_BUNDLE_ID=dev.cmux.app.internal (step-scoped)"
Step2->>Step2: Archives with dev.cmux.app.internal bundle ID
Note over Step2: Mismatch: internal bundle ID + beta profile = export error
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Step1 as Install beta provisioning profile
participant Env as $GITHUB_ENV / Job env
participant Step2 as Archive, export, and upload
Note over Step1: IOS_BETA_BUNDLE_ID NOT set in job env
Step1->>Env: reads IOS_BETA_BUNDLE_ID (unset)
Note over Step1: Fallback: dev.cmux.app.beta
Step1->>Step1: Installs BETA profile (always)
Step2->>Step2: "env: IOS_BETA_BUNDLE_ID=dev.cmux.app.internal (step-scoped)"
Step2->>Step2: Archives with dev.cmux.app.internal bundle ID
Note over Step2: Mismatch: internal bundle ID + beta profile = export error
|
| if [ "${IOS_BETA_BUNDLE_ID:-dev.cmux.app.beta}" = "dev.cmux.app.internal" ]; then | ||
| PROFILE_BASE64="${IOS_BETA_PROVISIONING_PROFILE_INTERNAL_BASE64}" | ||
| EXPECTED_APP_ID="7WLXT3NR37.dev.cmux.app.internal" | ||
| PROFILE_TYPE="internal" | ||
| else | ||
| PROFILE_BASE64="${IOS_BETA_PROVISIONING_PROFILE_BASE64}" | ||
| EXPECTED_APP_ID="7WLXT3NR37.dev.cmux.app.beta" | ||
| PROFILE_TYPE="beta" | ||
| fi |
There was a problem hiding this comment.
IOS_BETA_BUNDLE_ID is not in scope when this step runs
IOS_BETA_BUNDLE_ID is defined only in the env: block of the later "Archive, export, and upload to TestFlight" step (line 497). In GitHub Actions, step-level env: variables are scoped exclusively to that step — they are never written to $GITHUB_ENV and are invisible to earlier steps. When "Install beta provisioning profile" executes, IOS_BETA_BUNDLE_ID is unset, so ${IOS_BETA_BUNDLE_ID:-dev.cmux.app.beta} always expands to dev.cmux.app.beta, the internal branch is never entered, IOS_BETA_PROVISIONING_PROFILE_INTERNAL_BASE64 is never installed, and the upload step still archives dev.cmux.app.internal with the beta profile — reproducing the exact mismatch this PR is meant to fix.
To resolve this, promote IOS_BETA_BUNDLE_ID to a job-level env: var (under the upload: job's top-level env: block, alongside ASC_API_KEY_ID etc.), so it is visible to every step in the job including the profile installation step. The unset IOS_BETA_BUNDLE_ID line in the marketing_version_override branch will still work correctly at the shell level.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/ios-testflight.yml:
- Around line 455-464: Update the PlistBuddy lookups assigning APP_ID and
APS_ENVIRONMENT in the provisioning-profile validation block to suppress stderr
and tolerate missing entitlement keys under set -euo pipefail, using the same
handling as install-app-store-provisioning-profile.sh. Preserve the existing
comparisons and descriptive validation errors so missing values fail through
those checks.
- Around line 431-435: Define IOS_BETA_BUNDLE_ID in the provisioning step’s env
block before the run script, using the same evaluation logic as the subsequent
Archive, export, and upload to TestFlight step, including the conditional unset
when INPUT_MARKETING_VERSION_OVERRIDE is present. Ensure the existing bundle-ID
check selects the internal provisioning profile when the resolved value is
dev.cmux.app.internal.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 77dce6dc-2c2e-4de2-ae93-cee3d0c37cd0
📒 Files selected for processing (1)
.github/workflows/ios-testflight.yml
| IOS_BETA_PROVISIONING_PROFILE_INTERNAL_BASE64: ${{ secrets.IOS_BETA_PROVISIONING_PROFILE_INTERNAL_BASE64 }} | ||
| run: | | ||
| set -euo pipefail | ||
| if [ -z "${IOS_BETA_PROVISIONING_PROFILE_BASE64:-}" ]; then | ||
| echo "Missing IOS_BETA_PROVISIONING_PROFILE_BASE64 secret" >&2 | ||
| # Determine which profile to use based on bundle ID | ||
| if [ "${IOS_BETA_BUNDLE_ID:-dev.cmux.app.beta}" = "dev.cmux.app.internal" ]; then |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
Pass the bundle ID to the provisioning step.
IOS_BETA_BUNDLE_ID is only defined in the env block of the subsequent Archive, export, and upload to TestFlight step (line 497). Because it is completely absent in the environment of this step, [ "${IOS_BETA_BUNDLE_ID:-dev.cmux.app.beta}" = "dev.cmux.app.internal" ] will always evaluate to false. As a result, the workflow will always install the beta profile and never the internal profile.
You must either define IOS_BETA_BUNDLE_ID in this step's env block or lift its evaluation to a prior step. Since the upload step dynamically unsets the variable when INPUT_MARKETING_VERSION_OVERRIDE is present, you can replicate that logic here to ensure the correct bundle ID is used.
🐛 Proposed fix to replicate the bundle ID logic
- name: Install beta provisioning profile
env:
IOS_BETA_PROVISIONING_PROFILE_BASE64: ${{ secrets.IOS_BETA_PROVISIONING_PROFILE_BASE64 }}
IOS_BETA_PROVISIONING_PROFILE_INTERNAL_BASE64: ${{ secrets.IOS_BETA_PROVISIONING_PROFILE_INTERNAL_BASE64 }}
+ INPUT_MARKETING_VERSION_OVERRIDE: ${{ github.event.inputs.marketing_version_override }}
run: |
set -euo pipefail
+
+ # Match the upload step's bundle ID selection logic
+ if [ -n "${INPUT_MARKETING_VERSION_OVERRIDE:-}" ]; then
+ IOS_BETA_BUNDLE_ID="dev.cmux.app.beta"
+ else
+ IOS_BETA_BUNDLE_ID="dev.cmux.app.internal"
+ fi
+
# Determine which profile to use based on bundle ID
if [ "${IOS_BETA_BUNDLE_ID:-dev.cmux.app.beta}" = "dev.cmux.app.internal" ]; then📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| IOS_BETA_PROVISIONING_PROFILE_INTERNAL_BASE64: ${{ secrets.IOS_BETA_PROVISIONING_PROFILE_INTERNAL_BASE64 }} | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${IOS_BETA_PROVISIONING_PROFILE_BASE64:-}" ]; then | |
| echo "Missing IOS_BETA_PROVISIONING_PROFILE_BASE64 secret" >&2 | |
| # Determine which profile to use based on bundle ID | |
| if [ "${IOS_BETA_BUNDLE_ID:-dev.cmux.app.beta}" = "dev.cmux.app.internal" ]; then | |
| IOS_BETA_PROVISIONING_PROFILE_INTERNAL_BASE64: ${{ secrets.IOS_BETA_PROVISIONING_PROFILE_INTERNAL_BASE64 }} | |
| INPUT_MARKETING_VERSION_OVERRIDE: ${{ github.event.inputs.marketing_version_override }} | |
| run: | | |
| set -euo pipefail | |
| # Match the upload step's bundle ID selection logic | |
| if [ -n "${INPUT_MARKETING_VERSION_OVERRIDE:-}" ]; then | |
| IOS_BETA_BUNDLE_ID="dev.cmux.app.beta" | |
| else | |
| IOS_BETA_BUNDLE_ID="dev.cmux.app.internal" | |
| fi | |
| # Determine which profile to use based on bundle ID | |
| if [ "${IOS_BETA_BUNDLE_ID:-dev.cmux.app.beta}" = "dev.cmux.app.internal" ]; then |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/ios-testflight.yml around lines 431 - 435, Define
IOS_BETA_BUNDLE_ID in the provisioning step’s env block before the run script,
using the same evaluation logic as the subsequent Archive, export, and upload to
TestFlight step, including the conditional unset when
INPUT_MARKETING_VERSION_OVERRIDE is present. Ensure the existing bundle-ID check
selects the internal provisioning profile when the resolved value is
dev.cmux.app.internal.
| APP_ID="$(/usr/libexec/PlistBuddy -c "Print :Entitlements:application-identifier" "$TMP_PLIST")" | ||
| if [ "$APP_ID" != "7WLXT3NR37.dev.cmux.app.beta" ]; then | ||
| echo "Beta provisioning profile targets unexpected app ID: $APP_ID" >&2 | ||
| if [ "$APP_ID" != "$EXPECTED_APP_ID" ]; then | ||
| echo "$PROFILE_TYPE provisioning profile targets unexpected app ID: $APP_ID (expected $EXPECTED_APP_ID)" >&2 | ||
| exit 1 | ||
| fi | ||
| APS_ENVIRONMENT="$(/usr/libexec/PlistBuddy -c "Print :Entitlements:aps-environment" "$TMP_PLIST")" | ||
| if [ "$APS_ENVIRONMENT" != "production" ]; then | ||
| echo "Beta provisioning profile aps-environment is '$APS_ENVIRONMENT', expected 'production'" >&2 | ||
| echo "$PROFILE_TYPE provisioning profile aps-environment is '$APS_ENVIRONMENT', expected 'production'" >&2 | ||
| exit 1 | ||
| fi |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Prevent PlistBuddy from crashing the script when entitlements are missing.
Because set -euo pipefail is active, if the provisioning profile happens to be missing the application-identifier or aps-environment entitlements entirely, PlistBuddy will exit with a non-zero status (1) and the script will terminate immediately. This bypasses the descriptive validation error messages on lines 457 and 462.
As seen in .github/scripts/install-app-store-provisioning-profile.sh, you should discard stderr and append || true to allow the script to proceed and fail gracefully with the intended error messages.
🛡️ Proposed fix to handle missing keys gracefully
- APP_ID="$(/usr/libexec/PlistBuddy -c "Print :Entitlements:application-identifier" "$TMP_PLIST")"
+ APP_ID="$(/usr/libexec/PlistBuddy -c "Print :Entitlements:application-identifier" "$TMP_PLIST" 2>/dev/null || true)"
if [ "$APP_ID" != "$EXPECTED_APP_ID" ]; then
echo "$PROFILE_TYPE provisioning profile targets unexpected app ID: $APP_ID (expected $EXPECTED_APP_ID)" >&2
exit 1
fi
- APS_ENVIRONMENT="$(/usr/libexec/PlistBuddy -c "Print :Entitlements:aps-environment" "$TMP_PLIST")"
+ APS_ENVIRONMENT="$(/usr/libexec/PlistBuddy -c "Print :Entitlements:aps-environment" "$TMP_PLIST" 2>/dev/null || true)"
if [ "$APS_ENVIRONMENT" != "production" ]; then
echo "$PROFILE_TYPE provisioning profile aps-environment is '$APS_ENVIRONMENT', expected 'production'" >&2
exit 1
fi📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| APP_ID="$(/usr/libexec/PlistBuddy -c "Print :Entitlements:application-identifier" "$TMP_PLIST")" | |
| if [ "$APP_ID" != "7WLXT3NR37.dev.cmux.app.beta" ]; then | |
| echo "Beta provisioning profile targets unexpected app ID: $APP_ID" >&2 | |
| if [ "$APP_ID" != "$EXPECTED_APP_ID" ]; then | |
| echo "$PROFILE_TYPE provisioning profile targets unexpected app ID: $APP_ID (expected $EXPECTED_APP_ID)" >&2 | |
| exit 1 | |
| fi | |
| APS_ENVIRONMENT="$(/usr/libexec/PlistBuddy -c "Print :Entitlements:aps-environment" "$TMP_PLIST")" | |
| if [ "$APS_ENVIRONMENT" != "production" ]; then | |
| echo "Beta provisioning profile aps-environment is '$APS_ENVIRONMENT', expected 'production'" >&2 | |
| echo "$PROFILE_TYPE provisioning profile aps-environment is '$APS_ENVIRONMENT', expected 'production'" >&2 | |
| exit 1 | |
| fi | |
| APP_ID="$(/usr/libexec/PlistBuddy -c "Print :Entitlements:application-identifier" "$TMP_PLIST" 2>/dev/null || true)" | |
| if [ "$APP_ID" != "$EXPECTED_APP_ID" ]; then | |
| echo "$PROFILE_TYPE provisioning profile targets unexpected app ID: $APP_ID (expected $EXPECTED_APP_ID)" >&2 | |
| exit 1 | |
| fi | |
| APS_ENVIRONMENT="$(/usr/libexec/PlistBuddy -c "Print :Entitlements:aps-environment" "$TMP_PLIST" 2>/dev/null || true)" | |
| if [ "$APS_ENVIRONMENT" != "production" ]; then | |
| echo "$PROFILE_TYPE provisioning profile aps-environment is '$APS_ENVIRONMENT', expected 'production'" >&2 | |
| exit 1 | |
| fi |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/ios-testflight.yml around lines 455 - 464, Update the
PlistBuddy lookups assigning APP_ID and APS_ENVIRONMENT in the
provisioning-profile validation block to suppress stderr and tolerate missing
entitlement keys under set -euo pipefail, using the same handling as
install-app-store-provisioning-profile.sh. Preserve the existing comparisons and
descriptive validation errors so missing values fail through those checks.
Support separate internal and beta provisioning profiles, selected based on bundle ID. Internal builds (dev.cmux.app.internal) now use the internal provisioning profile from GitHub Secrets. Fixes export mismatch error when uploading internal builds to TestFlight.
Need help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.Summary by cubic
Add internal iOS TestFlight support by selecting the correct provisioning profile based on the bundle ID. Fixes export mismatch errors when uploading
dev.cmux.app.internalbuilds.New Features
IOS_BETA_BUNDLE_ID(internal vs beta).IOS_BETA_PROVISIONING_PROFILE_INTERNAL_BASE64for internal builds and logs which profile is installed.aps-environment=productionto prevent mismatches.Migration
IOS_BETA_PROVISIONING_PROFILE_INTERNAL_BASE64with the internal profile (base64).IOS_BETA_BUNDLE_ID=dev.cmux.app.internalfor internal builds.Written for commit ed19d9b. Summary will update on new commits.
Summary by CodeRabbit