Skip to content

Commit a5d1615

Browse files
Add Bitrise E2E pipeline skeleton
Assisted-By: devx/396ca0c4-44ad-415d-8d80-7200df4e42a4
1 parent 71e91c0 commit a5d1615

8 files changed

Lines changed: 302 additions & 2 deletions

File tree

bitrise.yml

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
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: osx-xcode-26.0.x-edge
8+
9+
pipelines:
10+
e2e:
11+
workflows:
12+
e2e-package-suite: {}
13+
e2e-build-react-native-ios: {}
14+
e2e-build-react-native-android: {}
15+
e2e-run-browserstack:
16+
depends_on:
17+
- e2e-package-suite
18+
- e2e-build-react-native-ios
19+
- e2e-build-react-native-android
20+
parallel: $E2E_RUN_COUNT
21+
e2e-report:
22+
depends_on:
23+
- e2e-run-browserstack
24+
should_always_run: workflow
25+
26+
workflows:
27+
e2e-package-suite:
28+
steps:
29+
- git-clone@8: {}
30+
- script@1:
31+
title: Validate E2E matrix and create suite placeholder
32+
inputs:
33+
- content: |-
34+
set -euo pipefail
35+
ruby e2e/scripts/e2e_matrix validate
36+
ruby e2e/scripts/e2e_matrix expand > "$BITRISE_DEPLOY_DIR/e2e-matrix.json"
37+
mkdir -p "$BITRISE_DEPLOY_DIR/e2e"
38+
echo "Phase 2 placeholder for BrowserStack Maestro suite zip" > "$BITRISE_DEPLOY_DIR/e2e/maestro-suite.zip"
39+
envman add --key E2E_MAESTRO_SUITE_ZIP --value "$BITRISE_DEPLOY_DIR/e2e/maestro-suite.zip"
40+
- deploy-to-bitrise-io@2:
41+
inputs:
42+
- pipeline_intermediate_files: |-
43+
$E2E_MAESTRO_SUITE_ZIP:E2E_MAESTRO_SUITE_ZIP
44+
$BITRISE_DEPLOY_DIR/e2e-matrix.json:E2E_MATRIX_JSON
45+
46+
e2e-build-react-native-ios:
47+
steps:
48+
- git-clone@8: {}
49+
- restore-cache@1:
50+
inputs:
51+
- key: |-
52+
rn-ios-{{ checksum "platforms/react-native/pnpm-lock.yaml" }}-{{ checksum "platforms/react-native/sample/ios/Podfile.lock" }}
53+
- activate-build-cache-for-xcode: {}
54+
- script@1:
55+
title: Create React Native iOS artifact placeholder
56+
inputs:
57+
- content: |-
58+
set -euo pipefail
59+
mkdir -p "$BITRISE_DEPLOY_DIR/e2e"
60+
echo "Phase 2 placeholder for React Native iOS IPA" > "$BITRISE_DEPLOY_DIR/e2e/react-native-ios.ipa"
61+
envman add --key E2E_REACT_NATIVE_IOS_APP_PATH --value "$BITRISE_DEPLOY_DIR/e2e/react-native-ios.ipa"
62+
- deploy-to-bitrise-io@2:
63+
inputs:
64+
- pipeline_intermediate_files: |-
65+
$E2E_REACT_NATIVE_IOS_APP_PATH:E2E_REACT_NATIVE_IOS_APP_PATH
66+
- save-cache@1:
67+
inputs:
68+
- key: |-
69+
rn-ios-{{ checksum "platforms/react-native/pnpm-lock.yaml" }}-{{ checksum "platforms/react-native/sample/ios/Podfile.lock" }}
70+
- paths: |-
71+
platforms/react-native/node_modules
72+
platforms/react-native/sample/ios/Pods
73+
74+
e2e-build-react-native-android:
75+
steps:
76+
- git-clone@8: {}
77+
- restore-cache@1:
78+
inputs:
79+
- key: |-
80+
rn-android-{{ checksum "platforms/react-native/pnpm-lock.yaml" }}-{{ checksum "platforms/react-native/sample/android/**/*.gradle*" }}
81+
- activate-build-cache-for-gradle: {}
82+
- script@1:
83+
title: Create React Native Android 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 Android APK" > "$BITRISE_DEPLOY_DIR/e2e/react-native-android.apk"
89+
envman add --key E2E_REACT_NATIVE_ANDROID_APP_PATH --value "$BITRISE_DEPLOY_DIR/e2e/react-native-android.apk"
90+
- deploy-to-bitrise-io@2:
91+
inputs:
92+
- pipeline_intermediate_files: |-
93+
$E2E_REACT_NATIVE_ANDROID_APP_PATH:E2E_REACT_NATIVE_ANDROID_APP_PATH
94+
- save-cache@1:
95+
inputs:
96+
- key: |-
97+
rn-android-{{ checksum "platforms/react-native/pnpm-lock.yaml" }}-{{ checksum "platforms/react-native/sample/android/**/*.gradle*" }}
98+
- paths: |-
99+
platforms/react-native/node_modules
100+
~/.gradle/caches
101+
~/.gradle/wrapper
102+
103+
e2e-run-browserstack:
104+
steps:
105+
- git-clone@8: {}
106+
- pull-intermediate-files@1:
107+
inputs:
108+
- artifact_sources: |-
109+
e2e-package-suite
110+
e2e-build-react-native-*
111+
- script@1:
112+
title: Resolve E2E matrix row placeholder
113+
inputs:
114+
- content: |-
115+
set -euo pipefail
116+
run_index="${BITRISE_IO_PARALLEL_INDEX:-0}"
117+
mkdir -p "$BITRISE_DEPLOY_DIR/e2e/results"
118+
ruby e2e/scripts/e2e_matrix expand --index "$run_index" > "$BITRISE_DEPLOY_DIR/e2e/results/run-${run_index}.json"
119+
- deploy-to-bitrise-io@2:
120+
inputs:
121+
- pipeline_intermediate_files: |-
122+
$BITRISE_DEPLOY_DIR/e2e/results:E2E_BROWSERSTACK_RESULTS_DIR
123+
124+
e2e-report:
125+
steps:
126+
- git-clone@8: {}
127+
- pull-intermediate-files@1:
128+
inputs:
129+
- artifact_sources: |-
130+
e2e-run-browserstack
131+
- script@1:
132+
title: Placeholder E2E report
133+
inputs:
134+
- content: |-
135+
set -euo pipefail
136+
echo "Phase 2 placeholder for aggregate E2E reporting"

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: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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+
## Pipeline
18+
19+
The initial repo-owned pipeline is `e2e` in `bitrise.yml`.
20+
21+
Phase 2 only validates the graph shape and intermediate artifact wiring. BrowserStack execution is added in a later phase.
22+
23+
## Required app environment variables
24+
25+
| Variable | Initial value | Purpose |
26+
|---|---|---|
27+
| `E2E_RUN_COUNT` | `2` | Number of expanded matrix rows to run in parallel. Keep this aligned with `ruby e2e/scripts/e2e_matrix count`. |
28+
29+
## Future secrets
30+
31+
Do not add BrowserStack credentials until the BrowserStack integration phase.
32+
33+
Future secret names:
34+
35+
| Secret | Purpose |
36+
|---|---|
37+
| `BROWSERSTACK_USERNAME` | BrowserStack API username |
38+
| `BROWSERSTACK_ACCESS_KEY` | BrowserStack API access key |
39+
40+
## PR trigger
41+
42+
Configure the Bitrise app to run the `e2e` pipeline for pull requests once the skeleton is ready to validate in Bitrise.
43+
44+
## Code signing
45+
46+
React Native iOS IPA generation is added in a later phase and will require Bitrise iOS code signing setup for the sample app.
47+
48+
## Caching
49+
50+
The skeleton includes placeholders for:
51+
52+
- key-based pnpm/CocoaPods/Gradle cache paths
53+
- Bitrise Build Cache for Xcode
54+
- Bitrise Build Cache for Gradle
55+
56+
The real artifact build phases should tune cache keys and paths against measured build behavior.

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/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/e2e_matrix

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ options = {
1010
}
1111

1212
parser = OptionParser.new do |opts|
13-
opts.banner = "Usage: e2e/scripts/e2e_matrix validate|expand [options]"
13+
opts.banner = "Usage: e2e/scripts/e2e_matrix validate|expand|count [options]"
1414
opts.on("--config PATH") { |path| options[:config] = path }
1515
opts.on("--index INDEX", Integer) { |index| options[:index] = index }
1616
end
1717

1818
command = ARGV.shift
1919
parser.parse!(ARGV)
2020

21-
unless ["validate", "expand"].include?(command)
21+
unless ["validate", "expand", "count"].include?(command)
2222
warn parser
2323
exit 1
2424
end
@@ -48,4 +48,12 @@ when "expand"
4848
end
4949

5050
puts JSON.pretty_generate(output)
51+
when "count"
52+
errors = matrix.validation_errors
53+
unless errors.empty?
54+
warn errors.join("\n")
55+
exit 1
56+
end
57+
58+
puts matrix.count
5159
end

e2e/test/bitrise_pipeline_test.rb

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# frozen_string_literal: true
2+
3+
require "minitest/autorun"
4+
require "yaml"
5+
6+
class BitrisePipelineTest < Minitest::Test
7+
def test_e2e_pipeline_uses_expected_graph
8+
config = load_bitrise_config
9+
workflows = config.fetch("pipelines").fetch("e2e").fetch("workflows")
10+
11+
assert_equal({}, workflows.fetch("e2e-package-suite"))
12+
assert_equal({}, workflows.fetch("e2e-build-react-native-ios"))
13+
assert_equal({}, workflows.fetch("e2e-build-react-native-android"))
14+
assert_equal [
15+
"e2e-package-suite",
16+
"e2e-build-react-native-ios",
17+
"e2e-build-react-native-android"
18+
], workflows.fetch("e2e-run-browserstack").fetch("depends_on")
19+
assert_equal "$E2E_RUN_COUNT", workflows.fetch("e2e-run-browserstack").fetch("parallel")
20+
assert_equal ["e2e-run-browserstack"], workflows.fetch("e2e-report").fetch("depends_on")
21+
assert_equal "workflow", workflows.fetch("e2e-report").fetch("should_always_run")
22+
end
23+
24+
def test_e2e_workflows_exist
25+
config = load_bitrise_config
26+
workflows = config.fetch("workflows")
27+
28+
expected_workflows.each do |workflow|
29+
assert workflows.key?(workflow), "missing workflow #{workflow}"
30+
end
31+
end
32+
33+
def test_pipeline_skeleton_does_not_call_browserstack_yet
34+
bitrise_yml = File.read("bitrise.yml")
35+
36+
refute_includes bitrise_yml, "api-cloud.browserstack.com"
37+
refute_includes bitrise_yml, "BROWSERSTACK_USERNAME"
38+
refute_includes bitrise_yml, "BROWSERSTACK_ACCESS_KEY"
39+
end
40+
41+
def test_pipeline_uses_intermediate_file_sharing_placeholders
42+
config = load_bitrise_config
43+
44+
assert_workflow_has_step(config, "e2e-package-suite", "deploy-to-bitrise-io")
45+
assert_workflow_has_step(config, "e2e-build-react-native-ios", "deploy-to-bitrise-io")
46+
assert_workflow_has_step(config, "e2e-build-react-native-android", "deploy-to-bitrise-io")
47+
assert_workflow_has_step(config, "e2e-run-browserstack", "pull-intermediate-files")
48+
assert_workflow_has_step(config, "e2e-report", "pull-intermediate-files")
49+
end
50+
51+
def test_dev_up_installs_bitrise_cli
52+
packages = YAML.safe_load_file("dev.yml").fetch("up").find { |entry| entry.key?("packages") }.fetch("packages")
53+
54+
assert_includes packages, "bitrise"
55+
end
56+
57+
def test_bitrise_setup_docs_exist
58+
assert File.exist?("e2e/BITRISE.md")
59+
end
60+
61+
private
62+
63+
def load_bitrise_config
64+
YAML.safe_load_file("bitrise.yml")
65+
end
66+
67+
def expected_workflows
68+
[
69+
"e2e-package-suite",
70+
"e2e-build-react-native-ios",
71+
"e2e-build-react-native-android",
72+
"e2e-run-browserstack",
73+
"e2e-report"
74+
]
75+
end
76+
77+
def assert_workflow_has_step(config, workflow, step_name)
78+
steps = config.fetch("workflows").fetch(workflow).fetch("steps")
79+
step_names = steps.flat_map(&:keys)
80+
81+
assert step_names.any? { |name| name.start_with?(step_name) }, "expected #{workflow} to include #{step_name}"
82+
end
83+
end

e2e/test/e2e_matrix_test.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ def test_returns_stable_indexed_run
3434
assert_equal "E2E_REACT_NATIVE_ANDROID_APP_PATH", run.fetch("artifact_env")
3535
end
3636

37+
def test_counts_expanded_runs
38+
matrix = E2EMatrix.load("e2e/config/matrix.yml")
39+
40+
assert_equal 2, matrix.count
41+
end
42+
3743
def test_reports_missing_suite_file
3844
Dir.mktmpdir do |dir|
3945
config_dir = File.join(dir, "config")

0 commit comments

Comments
 (0)