diff --git a/.agents/skills/e2e-test/SKILL.md b/.agents/skills/e2e-test/SKILL.md index a64fcf9b5f38..0ddc28bd84bf 100644 --- a/.agents/skills/e2e-test/SKILL.md +++ b/.agents/skills/e2e-test/SKILL.md @@ -89,10 +89,10 @@ Step 5 → Iterate (fix → lint → run) until green Documentation is split by **action**. Open only the reference that matches what you are doing. -| Action | File | When to open it | -| --------------------------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -| **Writing or updating a spec** | [references/writing-tests.md](references/writing-tests.md) | New spec file, spec structure, FixtureBuilder patterns, smoke/regression templates. | -| **Page Objects and selectors** | [references/page-objects.md](references/page-objects.md) | Create or update POM classes, selector/testId conventions, Matchers/Gestures/Assertions API. | -| **API and feature flag mocking** | [references/mocking.md](references/mocking.md) | testSpecificMock, setupRemoteFeatureFlagsMock, setupMockRequest, shared mock files. | +| Action | File | When to open it | +| --------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| **Writing or updating a spec** | [references/writing-tests.md](references/writing-tests.md) | New spec file, spec structure, FixtureBuilder patterns, smoke/regression templates. | +| **Page Objects and selectors** | [references/page-objects.md](references/page-objects.md) | Create or update POM classes, selector/testId conventions, Matchers/Gestures/Assertions API. | +| **API and feature flag mocking** | [references/mocking.md](references/mocking.md) | testSpecificMock, setupRemoteFeatureFlagsMock, setupMockRequest, shared mock files. | | **MetaMetrics / analytics expectations** | [tests/docs/analytics-e2e.md](../../../tests/docs/analytics-e2e.md) | `analyticsExpectations` on `withFixtures`, declarative checks, presets in `tests/helpers/analytics/expectations/`. | -| **Running tests, debugging, fixing failures** | [references/running-tests.md](references/running-tests.md) | Build check, detox run commands, lint/tsc, common failures table, retry patterns, iteration loop. | +| **Running tests, debugging, fixing failures** | [references/running-tests.md](references/running-tests.md) | Build check, detox run commands, lint/tsc, common failures table, retry patterns, iteration loop. | diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dd694bdd9fcf..bf59c2526ede 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,12 +14,12 @@ on: type: boolean default: false source_branch: - description: 'Branch, tag, or SHA to build' + description: 'Branch, tag, or SHA for version bump and prepare checkout. When non-empty, takes precedence over ref.' required: false type: string default: '' ref: - description: 'Git ref to checkout when skip_version_bump is true. Defaults to the triggering event ref.' + description: 'Git ref (branch) to run the build against. Used as base-branch for version bump and for checkout when skip_version_bump is true. Defaults to the triggering event ref.' required: false type: string default: '' @@ -65,7 +65,7 @@ jobs: contents: write id-token: write with: - base-branch: ${{ inputs.source_branch != '' && inputs.source_branch || github.ref_name }} + base-branch: ${{ inputs.source_branch != '' && inputs.source_branch || inputs.ref != '' && inputs.ref || github.ref_name }} secrets: PR_TOKEN: ${{ secrets.PR_TOKEN }} @@ -80,12 +80,12 @@ jobs: signing_aws_role: ${{ steps.config.outputs.signing_aws_role }} signing_aws_secret: ${{ steps.config.outputs.signing_aws_secret }} signing_android_keystore_path: ${{ steps.config.outputs.signing_android_keystore_path }} - checkout_ref_for_setup: ${{ !inputs.skip_version_bump && needs.update-build-version.outputs.commit-hash || (inputs.source_branch != '' && inputs.source_branch || github.ref_name) }} + checkout_ref_for_setup: ${{ !inputs.skip_version_bump && needs.update-build-version.outputs.commit-hash || (inputs.source_branch != '' && inputs.source_branch || inputs.ref != '' && inputs.ref || github.ref_name) }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - ref: ${{ !inputs.skip_version_bump && needs.update-build-version.outputs.commit-hash || (inputs.source_branch != '' && inputs.source_branch || github.ref_name) }} + ref: ${{ !inputs.skip_version_bump && needs.update-build-version.outputs.commit-hash || (inputs.source_branch != '' && inputs.source_branch || inputs.ref != '' && inputs.ref || github.ref_name) }} - name: Setup Node.js uses: actions/setup-node@v4 with: diff --git a/.github/workflows/runway_android_rc_workflow.yml b/.github/workflows/runway_android_rc_workflow.yml new file mode 100644 index 000000000000..20e842a01a1b --- /dev/null +++ b/.github/workflows/runway_android_rc_workflow.yml @@ -0,0 +1,148 @@ +############################################################################################## +# +# Runway Android RC Workflow +# +# Triggered from Runway to either: +# - Push an OTA update (when OTA_VERSION in app/constants/ota.ts line 9 is bumped), or +# - Build the mobile app (when there is no OTA version bump). +# +# When triggering workflow_dispatch, select the release branch (e.g. release/7.71.0). +# +############################################################################################## +name: Runway Android RC + +on: + workflow_dispatch: + inputs: + ref: + description: 'Optional git ref (branch) to run against. Defaults to the branch selected in the UI.' + required: false + type: string + +permissions: + contents: write # required by build.yml (update-build-version job) + pull-requests: read + actions: write + id-token: write # required by build.yml + +jobs: + decide: + name: Check OTA version and resolve inputs + runs-on: ubuntu-latest + outputs: + ota_bump: ${{ steps.decide.outputs.ota_bump }} + base_ref: ${{ steps.decide.outputs.base_ref }} + ota_version: ${{ steps.decide.outputs.ota_version }} + pr_number: ${{ steps.resolve-pr.outputs.pr_number }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ inputs.ref || github.ref }} + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + + - name: Resolve PR number for current branch + id: resolve-pr + run: | + BRANCH="${{ inputs.ref || github.ref_name }}" + # Strip refs/heads/ if present + BRANCH="${BRANCH#refs/heads/}" + echo "Resolving PR for branch: $BRANCH (repo: $GITHUB_REPOSITORY)" + + # Try same-repo head first, then owner:branch (required by API when listing pulls) + PR_NUMBER=$(gh pr list --repo "$GITHUB_REPOSITORY" --head "$BRANCH" --json number --jq '.[0].number' 2>/dev/null || echo "") + if [[ -z "$PR_NUMBER" ]]; then + PR_NUMBER=$(gh pr list --repo "$GITHUB_REPOSITORY" --head "$GITHUB_REPOSITORY_OWNER:$BRANCH" --json number --jq '.[0].number' 2>/dev/null || echo "") + fi + + echo "pr_number=${PR_NUMBER}" >> "$GITHUB_OUTPUT" + echo "Branch: $BRANCH, PR number: ${PR_NUMBER:-none}" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Decide OTA vs build + id: decide + run: | + set -e + # Version from package.json (e.g. 7.70.0) → base ref for OTA workflow is always v{VERSION} + VERSION=$(node -p "require('./package.json').version") + RELEASE_TAG="v${VERSION}" + echo "base_ref=${RELEASE_TAG}" >> "$GITHUB_OUTPUT" + + # Extract OTA_VERSION from line 9 (format: export const OTA_VERSION: string = 'vX.Y.Z';) + extract_ota() { sed -n '9p' "$1" | sed "s/.*'\\([^']*\\)'.*/\1/"; } + + # OTA_VERSION from current ref + CURRENT_OTA=$(extract_ota app/constants/ota.ts) + echo "ota_version=${CURRENT_OTA}" >> "$GITHUB_OUTPUT" + + # Ref to compare against for detecting bump: use release tag if it exists, else main + if git rev-parse "$RELEASE_TAG" >/dev/null 2>&1; then + COMPARE_REF="$RELEASE_TAG" + BASE_OTA=$(git show "${COMPARE_REF}:app/constants/ota.ts" 2>/dev/null | sed -n '9p' | sed "s/.*'\\([^']*\\)'.*/\1/" || echo "") + else + COMPARE_REF="main" + BASE_OTA=$(git show "origin/main:app/constants/ota.ts" 2>/dev/null | sed -n '9p' | sed "s/.*'\\([^']*\\)'.*/\1/" || echo "") + echo "Release tag ${RELEASE_TAG} not found; comparing OTA_VERSION to ${COMPARE_REF} to detect bump" + fi + + if [[ -n "$BASE_OTA" && "$CURRENT_OTA" != "$BASE_OTA" ]]; then + echo "ota_bump=true" >> "$GITHUB_OUTPUT" + echo "OTA_VERSION changed: $BASE_OTA -> $CURRENT_OTA → will trigger OTA update" + else + echo "ota_bump=false" >> "$GITHUB_OUTPUT" + echo "No OTA version bump (base: $BASE_OTA, current: $CURRENT_OTA) → will trigger build" + fi + + trigger-ota: + name: Trigger OTA update + needs: decide + if: needs.decide.outputs.ota_bump == 'true' + runs-on: ubuntu-latest + steps: + - name: Validate PR number + run: | + if [[ -z "${{ needs.decide.outputs.pr_number }}" ]]; then + echo "::error::No PR found for this branch. OTA update requires a PR number." + echo "::error::If you ran the workflow manually (workflow_dispatch), select your release branch in the 'Use workflow from' dropdown (e.g. release/7.71.0), not main." + exit 1 + fi + echo "Using PR #${{ needs.decide.outputs.pr_number }}" + + - name: Trigger Push OTA Update workflow + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const ref = '${{ inputs.ref || github.ref_name }}'.replace(/^refs\/heads\//, ''); + await github.rest.actions.createWorkflowDispatch({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: 'push-eas-update.yml', + ref: ref, + inputs: { + pr_number: '${{ needs.decide.outputs.pr_number }}', + base_branch: '${{ needs.decide.outputs.base_ref }}', + message: '${{ needs.decide.outputs.ota_version }}', + channel: 'rc', + platform: 'android' + } + }); + core.notice(`Triggered Push OTA Update on ${ref} (PR #${{ needs.decide.outputs.pr_number }}, base: ${{ needs.decide.outputs.base_ref }}, message: ${{ needs.decide.outputs.ota_version }})`); + + trigger-build: + name: Trigger build mobile app + needs: decide + if: needs.decide.outputs.ota_bump != 'true' + uses: ./.github/workflows/build.yml + with: + build_name: main-rc + platform: android + skip_version_bump: false + ref: ${{ inputs.ref || github.ref_name }} + secrets: inherit diff --git a/.github/workflows/runway_ios_rc_workflow.yml b/.github/workflows/runway_ios_rc_workflow.yml new file mode 100644 index 000000000000..ae2f39036e05 --- /dev/null +++ b/.github/workflows/runway_ios_rc_workflow.yml @@ -0,0 +1,238 @@ +############################################################################################## +# +# Runway iOS RC Workflow +# +# Triggered from Runway to either: +# - Push an OTA update (when OTA_VERSION in app/constants/ota.ts line 9 is bumped), or +# - Build the mobile app and upload the IPA to TestFlight (when there is no OTA version bump). +# +# When triggering workflow_dispatch, select the release branch (e.g. release/7.71.0). +# +############################################################################################## +name: Runway iOS RC + +on: + push: + branches: + - 'release/*' + workflow_dispatch: + inputs: + ref: + description: 'Optional git ref (branch) to run against. Defaults to the branch selected in the UI.' + required: false + type: string + +permissions: + contents: write # required by build.yml (update-build-version job) + pull-requests: read + actions: write + id-token: write # required by build.yml + +jobs: + decide: + name: Check OTA version and resolve inputs + runs-on: ubuntu-latest + outputs: + ota_bump: ${{ steps.decide.outputs.ota_bump }} + base_ref: ${{ steps.decide.outputs.base_ref }} + ota_version: ${{ steps.decide.outputs.ota_version }} + pr_number: ${{ steps.resolve-pr.outputs.pr_number }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ inputs.ref || github.ref }} + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + + - name: Resolve PR number for current branch + id: resolve-pr + run: | + BRANCH="${{ inputs.ref || github.ref_name }}" + # Strip refs/heads/ if present + BRANCH="${BRANCH#refs/heads/}" + echo "Resolving PR for branch: $BRANCH (repo: $GITHUB_REPOSITORY)" + + # Try same-repo head first, then owner:branch (required by API when listing pulls) + PR_NUMBER=$(gh pr list --repo "$GITHUB_REPOSITORY" --head "$BRANCH" --json number --jq '.[0].number' 2>/dev/null || echo "") + if [[ -z "$PR_NUMBER" ]]; then + PR_NUMBER=$(gh pr list --repo "$GITHUB_REPOSITORY" --head "$GITHUB_REPOSITORY_OWNER:$BRANCH" --json number --jq '.[0].number' 2>/dev/null || echo "") + fi + + echo "pr_number=${PR_NUMBER}" >> "$GITHUB_OUTPUT" + echo "Branch: $BRANCH, PR number: ${PR_NUMBER:-none}" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Decide OTA vs build + id: decide + run: | + set -e + # Version from package.json (e.g. 7.70.0) → base ref for OTA workflow is always v{VERSION} + VERSION=$(node -p "require('./package.json').version") + RELEASE_TAG="v${VERSION}" + echo "base_ref=${RELEASE_TAG}" >> "$GITHUB_OUTPUT" + + # Extract OTA_VERSION from line 9 (format: export const OTA_VERSION: string = 'vX.Y.Z';) + extract_ota() { sed -n '9p' "$1" | sed "s/.*'\\([^']*\\)'.*/\1/"; } + + # OTA_VERSION from current ref + CURRENT_OTA=$(extract_ota app/constants/ota.ts) + echo "ota_version=${CURRENT_OTA}" >> "$GITHUB_OUTPUT" + + # Ref to compare against for detecting bump: use release tag if it exists, else main + if git rev-parse "$RELEASE_TAG" >/dev/null 2>&1; then + COMPARE_REF="$RELEASE_TAG" + BASE_OTA=$(git show "${COMPARE_REF}:app/constants/ota.ts" 2>/dev/null | sed -n '9p' | sed "s/.*'\\([^']*\\)'.*/\1/" || echo "") + else + COMPARE_REF="main" + BASE_OTA=$(git show "origin/main:app/constants/ota.ts" 2>/dev/null | sed -n '9p' | sed "s/.*'\\([^']*\\)'.*/\1/" || echo "") + echo "Release tag ${RELEASE_TAG} not found; comparing OTA_VERSION to ${COMPARE_REF} to detect bump" + fi + + if [[ -n "$BASE_OTA" && "$CURRENT_OTA" != "$BASE_OTA" ]]; then + echo "ota_bump=true" >> "$GITHUB_OUTPUT" + echo "OTA_VERSION changed: $BASE_OTA -> $CURRENT_OTA → will trigger OTA update" + else + echo "ota_bump=false" >> "$GITHUB_OUTPUT" + echo "No OTA version bump (base: $BASE_OTA, current: $CURRENT_OTA) → will trigger build" + fi + + trigger-ota: + name: Trigger OTA update + needs: decide + if: needs.decide.outputs.ota_bump == 'true' + runs-on: ubuntu-latest + steps: + - name: Validate PR number + run: | + if [[ -z "${{ needs.decide.outputs.pr_number }}" ]]; then + echo "::error::No PR found for this branch. OTA update requires a PR number." + echo "::error::If you ran the workflow manually (workflow_dispatch), select your release branch in the 'Use workflow from' dropdown (e.g. release/7.71.0), not main." + exit 1 + fi + echo "Using PR #${{ needs.decide.outputs.pr_number }}" + + - name: Trigger Push OTA Update workflow + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const ref = '${{ inputs.ref || github.ref_name }}'.replace(/^refs\/heads\//, ''); + await github.rest.actions.createWorkflowDispatch({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: 'push-eas-update.yml', + ref: ref, + inputs: { + pr_number: '${{ needs.decide.outputs.pr_number }}', + base_branch: '${{ needs.decide.outputs.base_ref }}', + message: '${{ needs.decide.outputs.ota_version }}', + channel: 'rc', + platform: 'ios' + } + }); + core.notice(`Triggered Push OTA Update on ${ref} (PR #${{ needs.decide.outputs.pr_number }}, base: ${{ needs.decide.outputs.base_ref }}, message: ${{ needs.decide.outputs.ota_version }})`); + + trigger-build: + name: Trigger build mobile app + needs: decide + if: needs.decide.outputs.ota_bump != 'true' + uses: ./.github/workflows/build.yml + with: + build_name: main-rc + platform: ios + skip_version_bump: false + ref: ${{ inputs.ref || github.ref_name }} + secrets: inherit + + testflight-upload-summary: + name: TestFlight upload summary + needs: [trigger-build] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ inputs.ref || github.ref_name }} + - name: Display TestFlight upload summary + run: | + BUILD_VERSION=$(node -p "require('./package.json').version") + { + echo "### 📲 TestFlight Upload (Runway iOS RC)" + echo "" + echo "| Field | Value |" + echo "| --- | --- |" + echo "| **Ref** | ${{ inputs.ref || github.ref_name }} |" + echo "| **Build name** | main-rc |" + echo "| **Build version** | ${BUILD_VERSION} |" + echo "| **TestFlight group** | MetaMask BETA & Release Candidates |" + } >> "$GITHUB_STEP_SUMMARY" + + upload-ios-testflight: + name: Upload iOS to TestFlight + needs: [trigger-build, testflight-upload-summary] + runs-on: ghcr.io/cirruslabs/macos-runner:sequoia-xl + environment: apple + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ inputs.ref || github.ref_name }} + + - name: Setup Ruby (iOS) + uses: ruby/setup-ruby@44511735964dcb71245e7e55f72539531f7bc0eb #v1 + with: + ruby-version: '3.2.9' + working-directory: ios + bundler-cache: true + + - name: Download iOS build artifact + uses: actions/download-artifact@v4 + with: + name: ios-main-rc + + - name: Find IPA path + id: ipa + run: | + IPA=$(find . -name '*.ipa' -type f | head -1) + if [ -z "$IPA" ]; then + echo "::error::No .ipa file found in artifact" + exit 1 + fi + case "$IPA" in /*) ABS="$IPA" ;; *) ABS="$PWD/$IPA" ;; esac + echo "path=$ABS" >> "$GITHUB_OUTPUT" + + - name: Setup App Store Connect API Key + run: | + bash scripts/setup-app-store-connect-api-key.sh \ + "$APP_STORE_CONNECT_API_KEY_ISSUER_ID" \ + "$APP_STORE_CONNECT_API_KEY_KEY_ID" \ + "$APP_STORE_CONNECT_API_KEY_KEY_CONTENT" + env: + APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }} + APP_STORE_CONNECT_API_KEY_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_KEY_ID }} + APP_STORE_CONNECT_API_KEY_KEY_CONTENT: ${{ secrets.APP_STORE_CONNECT_API_KEY_KEY_CONTENT }} + + - name: Upload to TestFlight + env: + # Runway / GH apple env uses a restricted key; Bitrise keeps default external distribution. + TESTFLIGHT_DISTRIBUTE_EXTERNAL: 'false' + run: | + bash scripts/upload-to-testflight.sh \ + "github_actions_main-rc" \ + "${{ inputs.ref || github.ref_name }}" \ + "${{ steps.ipa.outputs.path }}" \ + "" \ + "false" + + - name: Cleanup API Key + if: always() + run: | + rm -f ios/AuthKey.p8 + echo "🧹 Cleaned up API key file" diff --git a/android/app/build.gradle b/android/app/build.gradle index 97165e049447..d556b01225ca 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -188,7 +188,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionName "7.71.0" - versionCode 3607 + versionCode 4127 testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" manifestPlaceholders.MM_BRANCH_KEY_TEST = "$System.env.MM_BRANCH_KEY_TEST" diff --git a/bitrise.yml b/bitrise.yml index c8f848cf5bda..034f86ef8e6c 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -3534,13 +3534,13 @@ app: VERSION_NAME: 7.71.0 - opts: is_expand: false - VERSION_NUMBER: 3911 + VERSION_NUMBER: 4127 - opts: is_expand: false FLASK_VERSION_NAME: 7.71.0 - opts: is_expand: false - FLASK_VERSION_NUMBER: 3911 + FLASK_VERSION_NUMBER: 4127 - opts: is_expand: false ANDROID_APK_LINK: '' diff --git a/ios/MetaMask.xcodeproj/project.pbxproj b/ios/MetaMask.xcodeproj/project.pbxproj index 6726013ffa4b..e3079cb5db1a 100644 --- a/ios/MetaMask.xcodeproj/project.pbxproj +++ b/ios/MetaMask.xcodeproj/project.pbxproj @@ -1281,7 +1281,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3911; + CURRENT_PROJECT_VERSION = 4127; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1350,7 +1350,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3911; + CURRENT_PROJECT_VERSION = 4127; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; @@ -1416,7 +1416,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3911; + CURRENT_PROJECT_VERSION = 4127; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1483,7 +1483,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3911; + CURRENT_PROJECT_VERSION = 4127; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; @@ -1646,7 +1646,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3911; + CURRENT_PROJECT_VERSION = 4127; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1716,7 +1716,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3911; + CURRENT_PROJECT_VERSION = 4127; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; diff --git a/ios/fastlane/Fastfile b/ios/fastlane/Fastfile index e0b667efe51a..037e92165e47 100644 --- a/ios/fastlane/Fastfile +++ b/ios/fastlane/Fastfile @@ -94,14 +94,19 @@ platform :ios do ) # Upload to TestFlight with API key from app_store_connect_api_key action - upload_to_testflight( + upload_params = { api_key: api_key, ipa: ipa_path, distribute_external: distribute_external, +<<<<<<< HEAD +======= groups: groups, +>>>>>>> wsun/create-runway-rc-ios-workflow notify_external_testers: notify_external_testers, changelog: changelog_message - ) + } + upload_params[:groups] = groups if distribute_external + upload_to_testflight(**upload_params) end end diff --git a/ios/fastlane/README.md b/ios/fastlane/README.md index 889574b8cd02..ceac8873c85b 100644 --- a/ios/fastlane/README.md +++ b/ios/fastlane/README.md @@ -4,7 +4,7 @@ This Fastlane configuration handles uploading iOS builds to TestFlight via Bitri ## Overview -The `upload_to_testflight_only` lane uploads pre-built IPA files to TestFlight and distributes them to external testing groups. +The `upload_to_testflight_only` lane uploads pre-built IPA files to TestFlight and, by default, distributes them to external testing groups (same as historical Bitrise behavior). Set `TESTFLIGHT_DISTRIBUTE_EXTERNAL=false` (or `0`, `no`, `off`) for upload-only when the App Store Connect API key cannot manage external beta groups. ## GitHub Actions diff --git a/scripts/upload-to-testflight.sh b/scripts/upload-to-testflight.sh index f8c167b3f93e..5540b67452ce 100644 --- a/scripts/upload-to-testflight.sh +++ b/scripts/upload-to-testflight.sh @@ -15,6 +15,8 @@ # # Environment variables: # IPA_PATH - IPA path (set by find-ipa-file.sh if not provided as argument) +# TESTFLIGHT_DISTRIBUTE_EXTERNAL - Default "true": distribute to external TestFlight groups after upload. +# Set to "false" (or 0/no/off) for upload-only when the API key lacks App Manager access to beta groups. set -e diff --git a/tests/framework/fixtures/README.md b/tests/framework/fixtures/README.md index 4c56c2545621..fff3e8697bba 100644 --- a/tests/framework/fixtures/README.md +++ b/tests/framework/fixtures/README.md @@ -42,7 +42,7 @@ describe('My Test Suite', () => { | `languageAndLocale` | `LanguageAndLocale` | `false` | - | Set the device Language and Locale of the device | | `permissions` | `object` | `false` | - | Allows setting specific device permissions | | `endTestfn` | `fn()` | `false` | - | Allows providing a function that is executed at the end of the test before the cleanup | -| `analyticsExpectations` | `AnalyticsExpectations` | `false` | - | Optional MetaMetrics checks after `endTestfn`, before mock drain; see `tests/docs/analytics-e2e.md` | +| `analyticsExpectations` | `AnalyticsExpectations` | `false` | - | Optional MetaMetrics checks after `endTestfn`, before mock drain; see `tests/docs/analytics-e2e.md` | | `skipReactNativeReload` | `boolean` | `false` | `false` | Skip React Native reload during cleanup to preserve app state between tests | | `useCommandQueueServer` | `boolean` | `false` | `false` | Launches an instance of CommandQueueServer to create a queue of items the app consumes on E2E context |