|
4 | 4 | # no Sentry, no performance metrics). Validates that core user flows (login, |
5 | 5 | # onboarding, swaps, etc.) work on real devices via BrowserStack. |
6 | 6 | # |
| 7 | +# Builds are triggered on Bitrise (same pipeline as performance tests) and |
| 8 | +# uploaded to BrowserStack automatically. |
| 9 | +# |
7 | 10 | # Triggers: |
8 | 11 | # - Daily at 5 AM UTC (1 hour after nightly build starts) |
9 | 12 | # - Manually via workflow_dispatch (optionally with pre-built BrowserStack URLs) |
|
33 | 36 | type: string |
34 | 37 |
|
35 | 38 | permissions: |
36 | | - contents: write |
| 39 | + contents: read |
37 | 40 | id-token: write |
38 | 41 |
|
39 | 42 | concurrency: |
40 | 43 | group: system-tests-${{ github.ref }} |
41 | 44 | cancel-in-progress: true |
| 45 | + |
42 | 46 | jobs: |
43 | | - build-with-srp: |
44 | | - name: Build with-SRP (login tests) |
| 47 | + trigger-android-dual-versions: |
| 48 | + name: Build Android (Bitrise + BrowserStack) |
| 49 | + uses: ./.github/workflows/build-android-upload-to-browserstack.yml |
45 | 50 | if: >- |
46 | 51 | github.event_name != 'workflow_dispatch' |
47 | 52 | || (!inputs.browserstack_app_url_android |
48 | | - && !inputs.browserstack_app_url_ios |
49 | | - && !inputs.browserstack_clean_app_url_android |
50 | | - && !inputs.browserstack_clean_app_url_ios) |
51 | | - uses: ./.github/workflows/build.yml |
| 53 | + && !inputs.browserstack_clean_app_url_android) |
52 | 54 | with: |
53 | | - build_name: main-rc-with-srp |
54 | | - platform: both |
55 | | - skip_version_bump: true |
56 | | - source_branch: ${{ github.ref_name }} |
| 55 | + branch_name: ${{ github.ref_name }} |
57 | 56 | secrets: inherit |
58 | 57 |
|
59 | | - build-clean: |
60 | | - name: Build clean RC (onboarding tests) |
| 58 | + trigger-ios-dual-versions: |
| 59 | + name: Build iOS (Bitrise + BrowserStack) |
| 60 | + uses: ./.github/workflows/build-ios-upload-to-browserstack.yml |
61 | 61 | if: >- |
62 | 62 | github.event_name != 'workflow_dispatch' |
63 | | - || (!inputs.browserstack_app_url_android |
64 | | - && !inputs.browserstack_app_url_ios |
65 | | - && !inputs.browserstack_clean_app_url_android |
| 63 | + || (!inputs.browserstack_app_url_ios |
66 | 64 | && !inputs.browserstack_clean_app_url_ios) |
67 | | - uses: ./.github/workflows/build.yml |
68 | 65 | with: |
69 | | - build_name: main-rc |
70 | | - platform: both |
71 | | - skip_version_bump: true |
72 | | - source_branch: ${{ github.ref_name }} |
| 66 | + branch_name: ${{ github.ref_name }} |
73 | 67 | secrets: inherit |
74 | 68 |
|
75 | | - upload-to-browserstack: |
76 | | - name: Upload builds to BrowserStack |
77 | | - needs: [build-with-srp, build-clean] |
78 | | - if: always() && !cancelled() |
79 | | - runs-on: ubuntu-latest |
80 | | - outputs: |
81 | | - android-login-url: ${{ steps.resolve.outputs.android-login-url }} |
82 | | - ios-login-url: ${{ steps.resolve.outputs.ios-login-url }} |
83 | | - android-onboarding-url: ${{ steps.resolve.outputs.android-onboarding-url }} |
84 | | - ios-onboarding-url: ${{ steps.resolve.outputs.ios-onboarding-url }} |
85 | | - env: |
86 | | - BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} |
87 | | - BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} |
88 | | - steps: |
89 | | - - name: Download Android APK (with-SRP) |
90 | | - if: needs.build-with-srp.result == 'success' |
91 | | - uses: actions/download-artifact@v8 |
92 | | - with: |
93 | | - name: android-apk-main-rc-with-srp |
94 | | - path: artifacts/android-with-srp |
95 | | - |
96 | | - - name: Download iOS IPA (with-SRP) |
97 | | - if: needs.build-with-srp.result == 'success' |
98 | | - uses: actions/download-artifact@v8 |
99 | | - with: |
100 | | - name: ios-ipa-main-rc-with-srp |
101 | | - path: artifacts/ios-with-srp |
102 | | - |
103 | | - - name: Download Android APK (clean) |
104 | | - if: needs.build-clean.result == 'success' |
105 | | - uses: actions/download-artifact@v8 |
106 | | - with: |
107 | | - name: android-apk-main-rc |
108 | | - path: artifacts/android-clean |
109 | | - |
110 | | - - name: Download iOS IPA (clean) |
111 | | - if: needs.build-clean.result == 'success' |
112 | | - uses: actions/download-artifact@v8 |
113 | | - with: |
114 | | - name: ios-ipa-main-rc |
115 | | - path: artifacts/ios-clean |
116 | | - |
117 | | - - name: Upload artifacts to BrowserStack and resolve URLs |
118 | | - id: resolve |
119 | | - run: | |
120 | | - # Helper: resolve a single URL from manual input or build artifact. |
121 | | - # Outputs the URL to GITHUB_OUTPUT, or empty string if unavailable. |
122 | | - # Upload failures are logged but do not abort other resolutions. |
123 | | - resolve_url() { |
124 | | - local output_key="$1" |
125 | | - local manual_url="$2" |
126 | | - local artifact_dir="$3" |
127 | | - local extension="$4" |
128 | | - local custom_id="$5" |
129 | | -
|
130 | | - if [[ -n "$manual_url" ]]; then |
131 | | - echo "${output_key}=${manual_url}" >> "$GITHUB_OUTPUT" |
132 | | - echo "Using manual URL for ${output_key}: ${manual_url}" |
133 | | - return 0 |
134 | | - fi |
135 | | -
|
136 | | - if [[ ! -d "$artifact_dir" ]]; then |
137 | | - echo "No build artifact for ${output_key} and no manual URL — test will be skipped" |
138 | | - echo "${output_key}=" >> "$GITHUB_OUTPUT" |
139 | | - return 0 |
140 | | - fi |
141 | | -
|
142 | | - local file |
143 | | - file=$(find "$artifact_dir" -name "*.${extension}" | head -1) |
144 | | - if [[ -z "$file" ]]; then |
145 | | - echo "::warning::No .${extension} found in ${artifact_dir} — ${output_key} will be empty" |
146 | | - echo "${output_key}=" >> "$GITHUB_OUTPUT" |
147 | | - return 0 |
148 | | - fi |
149 | | -
|
150 | | - echo "Uploading ${file} to BrowserStack..." |
151 | | - local response |
152 | | - response=$(curl -s -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" \ |
153 | | - -X POST "https://api-cloud.browserstack.com/app-automate/upload" \ |
154 | | - -F "file=@${file}" \ |
155 | | - -F "custom_id=${custom_id}") |
156 | | -
|
157 | | - local app_url |
158 | | - app_url=$(echo "$response" | jq -r '.app_url') |
159 | | -
|
160 | | - if [[ -z "$app_url" || "$app_url" == "null" ]]; then |
161 | | - echo "::error::BrowserStack upload failed for ${output_key} (${file}): ${response}" |
162 | | - echo "${output_key}=" >> "$GITHUB_OUTPUT" |
163 | | - return 0 |
164 | | - fi |
165 | | -
|
166 | | - echo "${output_key}=${app_url}" >> "$GITHUB_OUTPUT" |
167 | | - echo "${output_key}: ${app_url}" |
168 | | - } |
169 | | -
|
170 | | - resolve_url "android-login-url" \ |
171 | | - "${{ inputs.browserstack_app_url_android }}" \ |
172 | | - "artifacts/android-with-srp" "apk" \ |
173 | | - "system-test-android-login-${{ github.run_id }}" |
174 | | -
|
175 | | - resolve_url "ios-login-url" \ |
176 | | - "${{ inputs.browserstack_app_url_ios }}" \ |
177 | | - "artifacts/ios-with-srp" "ipa" \ |
178 | | - "system-test-ios-login-${{ github.run_id }}" |
179 | | -
|
180 | | - resolve_url "android-onboarding-url" \ |
181 | | - "${{ inputs.browserstack_clean_app_url_android }}" \ |
182 | | - "artifacts/android-clean" "apk" \ |
183 | | - "system-test-android-onboarding-${{ github.run_id }}" |
184 | | -
|
185 | | - resolve_url "ios-onboarding-url" \ |
186 | | - "${{ inputs.browserstack_clean_app_url_ios }}" \ |
187 | | - "artifacts/ios-clean" "ipa" \ |
188 | | - "system-test-ios-onboarding-${{ github.run_id }}" |
189 | | -
|
190 | | - echo "URL resolution complete" |
191 | | -
|
192 | 69 | run-android-login-tests: |
193 | 70 | name: Android Login Tests |
194 | | - needs: [upload-to-browserstack] |
| 71 | + needs: [trigger-android-dual-versions] |
195 | 72 | if: >- |
196 | 73 | always() && !cancelled() |
197 | | - && needs.upload-to-browserstack.outputs.android-login-url != '' |
| 74 | + && (needs.trigger-android-dual-versions.result == 'skipped' || needs.trigger-android-dual-versions.result == 'success') |
| 75 | + && (needs.trigger-android-dual-versions.outputs.with-srp-browserstack-url != '' || inputs.browserstack_app_url_android != '') |
198 | 76 | runs-on: ubuntu-latest |
199 | 77 | env: |
200 | | - BROWSERSTACK_ANDROID_APP_URL: ${{ needs.upload-to-browserstack.outputs.android-login-url }} |
| 78 | + BROWSERSTACK_ANDROID_APP_URL: ${{ needs.trigger-android-dual-versions.outputs.with-srp-browserstack-url || inputs.browserstack_app_url_android }} |
201 | 79 | steps: |
202 | 80 | - name: Checkout code |
203 | 81 | uses: actions/checkout@v6 |
@@ -289,13 +167,14 @@ jobs: |
289 | 167 |
|
290 | 168 | run-android-onboarding-tests: |
291 | 169 | name: Android Onboarding Tests |
292 | | - needs: [upload-to-browserstack] |
| 170 | + needs: [trigger-android-dual-versions] |
293 | 171 | if: >- |
294 | 172 | always() && !cancelled() |
295 | | - && needs.upload-to-browserstack.outputs.android-onboarding-url != '' |
| 173 | + && (needs.trigger-android-dual-versions.result == 'skipped' || needs.trigger-android-dual-versions.result == 'success') |
| 174 | + && (needs.trigger-android-dual-versions.outputs.without-srp-browserstack-url != '' || inputs.browserstack_clean_app_url_android != '') |
296 | 175 | runs-on: ubuntu-latest |
297 | 176 | env: |
298 | | - BROWSERSTACK_ANDROID_CLEAN_APP_URL: ${{ needs.upload-to-browserstack.outputs.android-onboarding-url }} |
| 177 | + BROWSERSTACK_ANDROID_CLEAN_APP_URL: ${{ needs.trigger-android-dual-versions.outputs.without-srp-browserstack-url || inputs.browserstack_clean_app_url_android }} |
299 | 178 | steps: |
300 | 179 | - name: Checkout code |
301 | 180 | uses: actions/checkout@v6 |
@@ -387,13 +266,14 @@ jobs: |
387 | 266 |
|
388 | 267 | run-ios-login-tests: |
389 | 268 | name: iOS Login Tests |
390 | | - needs: [upload-to-browserstack] |
| 269 | + needs: [trigger-ios-dual-versions] |
391 | 270 | if: >- |
392 | 271 | always() && !cancelled() |
393 | | - && needs.upload-to-browserstack.outputs.ios-login-url != '' |
| 272 | + && (needs.trigger-ios-dual-versions.result == 'skipped' || needs.trigger-ios-dual-versions.result == 'success') |
| 273 | + && (needs.trigger-ios-dual-versions.outputs.with-srp-browserstack-url != '' || inputs.browserstack_app_url_ios != '') |
394 | 274 | runs-on: ubuntu-latest |
395 | 275 | env: |
396 | | - BROWSERSTACK_IOS_APP_URL: ${{ needs.upload-to-browserstack.outputs.ios-login-url }} |
| 276 | + BROWSERSTACK_IOS_APP_URL: ${{ needs.trigger-ios-dual-versions.outputs.with-srp-browserstack-url || inputs.browserstack_app_url_ios }} |
397 | 277 | steps: |
398 | 278 | - name: Checkout code |
399 | 279 | uses: actions/checkout@v6 |
@@ -485,13 +365,14 @@ jobs: |
485 | 365 |
|
486 | 366 | run-ios-onboarding-tests: |
487 | 367 | name: iOS Onboarding Tests |
488 | | - needs: [upload-to-browserstack] |
| 368 | + needs: [trigger-ios-dual-versions] |
489 | 369 | if: >- |
490 | 370 | always() && !cancelled() |
491 | | - && needs.upload-to-browserstack.outputs.ios-onboarding-url != '' |
| 371 | + && (needs.trigger-ios-dual-versions.result == 'skipped' || needs.trigger-ios-dual-versions.result == 'success') |
| 372 | + && (needs.trigger-ios-dual-versions.outputs.without-srp-browserstack-url != '' || inputs.browserstack_clean_app_url_ios != '') |
492 | 373 | runs-on: ubuntu-latest |
493 | 374 | env: |
494 | | - BROWSERSTACK_IOS_CLEAN_APP_URL: ${{ needs.upload-to-browserstack.outputs.ios-onboarding-url }} |
| 375 | + BROWSERSTACK_IOS_CLEAN_APP_URL: ${{ needs.trigger-ios-dual-versions.outputs.without-srp-browserstack-url || inputs.browserstack_clean_app_url_ios }} |
495 | 376 | steps: |
496 | 377 | - name: Checkout code |
497 | 378 | uses: actions/checkout@v6 |
|
0 commit comments