Skip to content

Commit 69c152c

Browse files
Add Bitrise E2E pipeline skeleton
Assisted-By: devx/6c1e3ad5-96c8-4972-b087-da7ff7b195c3
1 parent 285bb9c commit 69c152c

10 files changed

Lines changed: 645 additions & 2 deletions

dev.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ up:
99
- swiftlint
1010
- swiftformat
1111
- sccache
12+
- bitrise
1213
- ruby
1314
- xcode:
1415
version: "26.2"

e2e/BITRISE.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Bitrise E2E Pipeline Setup
2+
3+
This document tracks the manual setup required for the Bitrise app that will run Checkout Kit E2E pipelines.
4+
5+
Parent issue: https://github.com/shop/issues-checkout-kit/issues/1096
6+
Epic: https://github.com/shop/issues-checkout-kit/issues/1084
7+
8+
## Bitrise app
9+
10+
Use the allocated Bitrise app and point it at the Checkout Kit repository:
11+
12+
- Bitrise app: https://app.bitrise.io/app/f51f9054-053e-40f1-81e9-ae727567ae76
13+
- Repository: `Shopify/checkout-kit`
14+
15+
The app is currently empty and can be reconfigured for this pipeline.
16+
17+
Useful Bitrise app URLs:
18+
19+
| Area | URL |
20+
|---|---|
21+
| App overview | https://app.bitrise.io/app/f51f9054-053e-40f1-81e9-ae727567ae76 |
22+
| Workflow/config editor | https://app.bitrise.io/app/f51f9054-053e-40f1-81e9-ae727567ae76/workflow |
23+
| Secrets/env vars | https://app.bitrise.io/app/f51f9054-053e-40f1-81e9-ae727567ae76/secrets |
24+
| Code signing | https://app.bitrise.io/app/f51f9054-053e-40f1-81e9-ae727567ae76/codesigning |
25+
| Build triggers | https://app.bitrise.io/app/f51f9054-053e-40f1-81e9-ae727567ae76/triggers |
26+
| Start build | https://app.bitrise.io/app/f51f9054-053e-40f1-81e9-ae727567ae76/build/start |
27+
28+
If a direct URL does not resolve in the current Bitrise UI, open the app overview and navigate to the matching area from the sidebar.
29+
30+
## Pipeline
31+
32+
The initial repo-owned pipeline is `e2e` in `e2e/bitrise.yml`.
33+
34+
Configure the Bitrise app to read the YAML from the repository path:
35+
36+
```text
37+
e2e/bitrise.yml
38+
```
39+
40+
Phase 2 only validates the graph shape and intermediate artifact wiring. BrowserStack execution is added in a later phase.
41+
42+
The default E2E stack is `linux-docker-android-22.04` on `g2.linux.medium`. Workflows should use Linux by default unless they require macOS-specific tooling. The React Native iOS artifact workflow overrides this default in a later phase because it requires Xcode and iOS signing.
43+
44+
Validate locally with:
45+
46+
```bash
47+
bitrise validate -c e2e/bitrise.yml
48+
```
49+
50+
## Required app environment variables
51+
52+
The non-secret E2E defaults are defined in `e2e/bitrise.yml` under `app.envs`. Do not edit these in the Bitrise Workflow Editor; change them in this repository and submit them through the Graphite stack.
53+
54+
| Variable | Initial value | Purpose |
55+
|---|---|---|
56+
| `E2E_RUN_COUNT` | `2` | Number of expanded matrix rows to run in parallel. Keep this aligned with `ruby e2e/scripts/e2e_matrix count`. |
57+
| `E2E_STRICT` | `false` | Soft/hard failure switch for BrowserStack Maestro runs. |
58+
| `E2E_BROWSERSTACK_API_RETRIES` | `1` | Retries for transient BrowserStack API responses. |
59+
| `E2E_BROWSERSTACK_TIMEOUT_SECONDS` | `1800` | BrowserStack build timeout. |
60+
| `E2E_BROWSERSTACK_POLL_SECONDS` | `30` | BrowserStack status polling interval. |
61+
| `E2E_IOS_EXPORT_METHOD` | `development` | Export method for the React Native iOS IPA. |
62+
| `E2E_PACKAGE_COMMAND_TIMEOUT_SECONDS` | `300` | Per-command timeout for package-suite matrix and suite commands. |
63+
| `E2E_RUBY_INSTALL_TIMEOUT_SECONDS` | `1800` | Timeout for installing the exact repository Ruby version from `.ruby-version`. |
64+
65+
Secrets still need to be configured in Bitrise.io.
66+
67+
## Future secrets
68+
69+
Do not add BrowserStack credentials until the BrowserStack integration phase.
70+
71+
Future secret names:
72+
73+
| Secret | Purpose |
74+
|---|---|
75+
| `BROWSERSTACK_USERNAME` | BrowserStack API username |
76+
| `BROWSERSTACK_ACCESS_KEY` | BrowserStack API access key |
77+
78+
## PR trigger
79+
80+
Configure the Bitrise app to run the `e2e` pipeline for pull requests once the skeleton is ready to validate in Bitrise.
81+
82+
## Code signing
83+
84+
React Native iOS IPA generation is added in a later phase and will require Bitrise iOS code signing setup for the sample app.
85+
86+
## Caching
87+
88+
The pipeline uses Bitrise cache steps for key-based pnpm/CocoaPods/Gradle cache paths.
89+
90+
Do not add `activate-build-cache-for-xcode` or `activate-build-cache-for-gradle`; the Bitrise Build Cache add-on is disabled for Shopify Bitrise apps.
91+
92+
Bitrise stacks can also have Ruby version managers installed without the repository `.ruby-version` Ruby being available. The pipeline sources `e2e/scripts/bitrise_ci_helpers`, installs the exact Ruby version from `.ruby-version` with `rbenv` or `asdf`, and verifies `RUBY_VERSION` before Ruby helpers run.

e2e/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ Expand a single run row by index:
3939
ruby e2e/scripts/e2e_matrix expand --index 0
4040
```
4141

42+
Count expanded run rows:
43+
44+
```bash
45+
ruby e2e/scripts/e2e_matrix count
46+
```
47+
4248
## Run locally
4349

4450
Install Maestro locally, launch the target sample app, then run the shared smoke flow with the same environment contract used by CI.

e2e/bitrise.yml

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
format_version: "23"
2+
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
3+
project_type: other
4+
5+
meta:
6+
bitrise.io:
7+
stack: linux-docker-android-22.04
8+
machine_type_id: g2.linux.medium
9+
10+
app:
11+
envs:
12+
- E2E_RUN_COUNT: "2"
13+
- E2E_STRICT: "false"
14+
- E2E_BROWSERSTACK_API_RETRIES: "1"
15+
- E2E_BROWSERSTACK_TIMEOUT_SECONDS: "1800"
16+
- E2E_BROWSERSTACK_POLL_SECONDS: "30"
17+
- E2E_IOS_EXPORT_METHOD: development
18+
- E2E_PACKAGE_COMMAND_TIMEOUT_SECONDS: "300"
19+
- E2E_RUBY_INSTALL_TIMEOUT_SECONDS: "1800"
20+
21+
pipelines:
22+
e2e:
23+
workflows:
24+
e2e-package-suite: {}
25+
e2e-build-react-native-ios: {}
26+
e2e-build-react-native-android: {}
27+
e2e-run-browserstack:
28+
depends_on:
29+
- e2e-package-suite
30+
- e2e-build-react-native-ios
31+
- e2e-build-react-native-android
32+
parallel: $E2E_RUN_COUNT
33+
e2e-report:
34+
depends_on:
35+
- e2e-run-browserstack
36+
should_always_run: workflow
37+
38+
workflows:
39+
e2e-package-suite:
40+
steps:
41+
- git-clone@8: {}
42+
- restore-cache@1:
43+
inputs:
44+
- key: |-
45+
e2e-ruby-linux-{{ checksum ".ruby-version" }}
46+
- script@1:
47+
title: Validate E2E matrix and create suite placeholder
48+
inputs:
49+
- content: |-
50+
set -euo pipefail
51+
source e2e/scripts/bitrise_ci_helpers
52+
e2e_prepare_ruby
53+
e2e_run_with_timeout "${E2E_PACKAGE_COMMAND_TIMEOUT_SECONDS:-300}" ruby e2e/scripts/e2e_matrix validate
54+
e2e_log "Expanding E2E matrix"
55+
e2e_run_with_timeout "${E2E_PACKAGE_COMMAND_TIMEOUT_SECONDS:-300}" ruby e2e/scripts/e2e_matrix expand > "$BITRISE_DEPLOY_DIR/e2e-matrix.json"
56+
e2e_log "Creating Maestro suite placeholder"
57+
mkdir -p "$BITRISE_DEPLOY_DIR/e2e"
58+
echo "Phase 2 placeholder for BrowserStack Maestro suite zip" > "$BITRISE_DEPLOY_DIR/e2e/maestro-suite.zip"
59+
e2e_log "Publishing Maestro suite path"
60+
envman add --key E2E_MAESTRO_SUITE_ZIP --value "$BITRISE_DEPLOY_DIR/e2e/maestro-suite.zip"
61+
- save-cache@1:
62+
is_always_run: true
63+
inputs:
64+
- key: |-
65+
e2e-ruby-linux-{{ checksum ".ruby-version" }}
66+
- paths: |-
67+
~/.asdf/installs/ruby
68+
~/.asdf/plugins/ruby
69+
- deploy-to-bitrise-io@2:
70+
inputs:
71+
- pipeline_intermediate_files: |-
72+
$E2E_MAESTRO_SUITE_ZIP:E2E_MAESTRO_SUITE_ZIP
73+
$BITRISE_DEPLOY_DIR/e2e-matrix.json:E2E_MATRIX_JSON
74+
75+
e2e-build-react-native-ios:
76+
steps:
77+
- git-clone@8: {}
78+
- restore-cache@1:
79+
inputs:
80+
- key: |-
81+
rn-ios-{{ checksum "platforms/react-native/pnpm-lock.yaml" }}-{{ checksum "platforms/react-native/sample/ios/Podfile.lock" }}
82+
- script@1:
83+
title: Create React Native iOS artifact placeholder
84+
inputs:
85+
- content: |-
86+
set -euo pipefail
87+
mkdir -p "$BITRISE_DEPLOY_DIR/e2e"
88+
echo "Phase 2 placeholder for React Native iOS IPA" > "$BITRISE_DEPLOY_DIR/e2e/react-native-ios.ipa"
89+
envman add --key E2E_REACT_NATIVE_IOS_APP_PATH --value "$BITRISE_DEPLOY_DIR/e2e/react-native-ios.ipa"
90+
- deploy-to-bitrise-io@2:
91+
inputs:
92+
- pipeline_intermediate_files: |-
93+
$E2E_REACT_NATIVE_IOS_APP_PATH:E2E_REACT_NATIVE_IOS_APP_PATH
94+
- save-cache@1:
95+
inputs:
96+
- key: |-
97+
rn-ios-{{ checksum "platforms/react-native/pnpm-lock.yaml" }}-{{ checksum "platforms/react-native/sample/ios/Podfile.lock" }}
98+
- paths: |-
99+
platforms/react-native/node_modules
100+
platforms/react-native/sample/ios/Pods
101+
102+
e2e-build-react-native-android:
103+
steps:
104+
- git-clone@8: {}
105+
- restore-cache@1:
106+
inputs:
107+
- key: |-
108+
rn-android-{{ checksum "platforms/react-native/pnpm-lock.yaml" }}-{{ checksum "platforms/react-native/sample/android/**/*.gradle*" }}
109+
- script@1:
110+
title: Create React Native Android artifact placeholder
111+
inputs:
112+
- content: |-
113+
set -euo pipefail
114+
mkdir -p "$BITRISE_DEPLOY_DIR/e2e"
115+
echo "Phase 2 placeholder for React Native Android APK" > "$BITRISE_DEPLOY_DIR/e2e/react-native-android.apk"
116+
envman add --key E2E_REACT_NATIVE_ANDROID_APP_PATH --value "$BITRISE_DEPLOY_DIR/e2e/react-native-android.apk"
117+
- deploy-to-bitrise-io@2:
118+
inputs:
119+
- pipeline_intermediate_files: |-
120+
$E2E_REACT_NATIVE_ANDROID_APP_PATH:E2E_REACT_NATIVE_ANDROID_APP_PATH
121+
- save-cache@1:
122+
inputs:
123+
- key: |-
124+
rn-android-{{ checksum "platforms/react-native/pnpm-lock.yaml" }}-{{ checksum "platforms/react-native/sample/android/**/*.gradle*" }}
125+
- paths: |-
126+
platforms/react-native/node_modules
127+
~/.gradle/caches
128+
~/.gradle/wrapper
129+
130+
e2e-run-browserstack:
131+
steps:
132+
- git-clone@8: {}
133+
- restore-cache@1:
134+
inputs:
135+
- key: |-
136+
e2e-ruby-linux-{{ checksum ".ruby-version" }}
137+
- pull-intermediate-files@1:
138+
inputs:
139+
- artifact_sources: |-
140+
e2e-package-suite
141+
e2e-build-react-native-*
142+
- script@1:
143+
title: Resolve E2E matrix row placeholder
144+
inputs:
145+
- content: |-
146+
set -euo pipefail
147+
source e2e/scripts/bitrise_ci_helpers
148+
e2e_prepare_ruby
149+
run_index="${BITRISE_IO_PARALLEL_INDEX:-0}"
150+
mkdir -p "$BITRISE_DEPLOY_DIR/e2e/results"
151+
e2e_log "Resolving E2E matrix row ${run_index}"
152+
e2e_run_with_timeout "${E2E_PACKAGE_COMMAND_TIMEOUT_SECONDS:-300}" ruby e2e/scripts/e2e_matrix expand --index "$run_index" > "$BITRISE_DEPLOY_DIR/e2e/results/run-${run_index}.json"
153+
- save-cache@1:
154+
is_always_run: true
155+
inputs:
156+
- key: |-
157+
e2e-ruby-linux-{{ checksum ".ruby-version" }}
158+
- paths: |-
159+
~/.asdf/installs/ruby
160+
~/.asdf/plugins/ruby
161+
- deploy-to-bitrise-io@2:
162+
inputs:
163+
- pipeline_intermediate_files: |-
164+
$BITRISE_DEPLOY_DIR/e2e/results:E2E_BROWSERSTACK_RESULTS_DIR
165+
166+
e2e-report:
167+
steps:
168+
- git-clone@8: {}
169+
- pull-intermediate-files@1:
170+
inputs:
171+
- artifact_sources: |-
172+
e2e-run-browserstack
173+
- script@1:
174+
title: Placeholder E2E report
175+
inputs:
176+
- content: |-
177+
set -euo pipefail
178+
echo "Phase 2 placeholder for aggregate E2E reporting"

e2e/lib/e2e_matrix.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ def run_at(index)
2929
runs.fetch(index)
3030
end
3131

32+
def count
33+
expand.length
34+
end
35+
3236
def validation_errors
3337
errors = []
3438
errors << "version must be 1" unless @config.fetch("version", nil) == 1

e2e/scripts/bitrise_ci_helpers

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
e2e_log() {
6+
printf '\n==> %s\n' "$*"
7+
}
8+
9+
e2e_run_with_timeout() {
10+
local timeout_seconds="$1"
11+
shift
12+
local command_name="$*"
13+
local heartbeat_seconds="${E2E_HEARTBEAT_SECONDS:-30}"
14+
local elapsed_seconds=0
15+
16+
e2e_log "Running: ${command_name}"
17+
"$@" &
18+
local command_pid="$!"
19+
20+
while kill -0 "$command_pid" 2>/dev/null; do
21+
if [ "$elapsed_seconds" -ge "$timeout_seconds" ]; then
22+
e2e_log "Timed out after ${timeout_seconds}s: ${command_name}"
23+
kill "$command_pid" 2>/dev/null || true
24+
wait "$command_pid" 2>/dev/null || true
25+
return 124
26+
fi
27+
28+
sleep "$heartbeat_seconds"
29+
elapsed_seconds=$((elapsed_seconds + heartbeat_seconds))
30+
31+
if kill -0 "$command_pid" 2>/dev/null; then
32+
e2e_log "Still running after ${elapsed_seconds}s: ${command_name}"
33+
fi
34+
done
35+
36+
wait "$command_pid"
37+
}
38+
39+
e2e_prepare_ruby() {
40+
local ruby_version
41+
ruby_version="$(tr -d "[:space:]" < .ruby-version)"
42+
local timeout_seconds="${E2E_RUBY_INSTALL_TIMEOUT_SECONDS:-1800}"
43+
44+
e2e_log "Preparing Ruby ${ruby_version}"
45+
46+
if command -v rbenv >/dev/null 2>&1; then
47+
eval "$(rbenv init - bash)"
48+
if ! rbenv versions --bare | grep -Fxq "$ruby_version"; then
49+
e2e_run_with_timeout "$timeout_seconds" rbenv install -s "$ruby_version"
50+
fi
51+
rbenv shell "$ruby_version"
52+
rbenv rehash
53+
elif command -v asdf >/dev/null 2>&1; then
54+
e2e_run_with_timeout "$timeout_seconds" asdf install ruby "$ruby_version"
55+
export ASDF_RUBY_VERSION="$ruby_version"
56+
asdf reshim ruby "$ruby_version"
57+
else
58+
echo "No supported Ruby version manager found. Expected rbenv or asdf to install Ruby ${ruby_version}." >&2
59+
return 1
60+
fi
61+
62+
e2e_log "Ruby executable: $(command -v ruby)"
63+
ruby --version
64+
65+
local actual_ruby_version
66+
actual_ruby_version="$(ruby -e 'print RUBY_VERSION')"
67+
if [ "$actual_ruby_version" != "$ruby_version" ]; then
68+
echo "Expected Ruby ${ruby_version}, got ${actual_ruby_version}." >&2
69+
return 1
70+
fi
71+
}

0 commit comments

Comments
 (0)