-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Expand file tree
/
Copy pathbuild.sh
More file actions
executable file
·987 lines (867 loc) · 35.4 KB
/
build.sh
File metadata and controls
executable file
·987 lines (867 loc) · 35.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
#!/bin/bash
set -o pipefail
readonly __DIRNAME__="$( cd "${BASH_SOURCE[0]%/*}" && pwd )"
readonly REPO_ROOT_DIR="$(dirname "${__DIRNAME__}")"
PLATFORM=$1
MODE=$2
ENVIRONMENT=$3
PRE_RELEASE=false
JS_ENV_FILE=".js.env"
ANDROID_ENV_FILE=".android.env"
IOS_ENV_FILE=".ios.env"
IS_LOCAL=false
SHOULD_CLEAN_WATCHER_CACHE=false
WATCHER_PORT=${WATCHER_PORT:-8081}
loadJSEnv(){
# Load JS specific env variables
if [ "$PRE_RELEASE" = false ] ; then
if [ -e $JS_ENV_FILE ]
then
source $JS_ENV_FILE
fi
fi
}
# Load JS env variables
loadJSEnv
if [ "$PLATFORM" != "watcher" ]; then
# Use the values from the environment variables when platform is watcher
export METAMASK_BUILD_TYPE=${MODE:-"$METAMASK_BUILD_TYPE"}
export METAMASK_ENVIRONMENT=${ENVIRONMENT:-"$METAMASK_ENVIRONMENT"}
fi
# Enable Sentry to auto upload source maps and debug symbols
export SENTRY_DISABLE_AUTO_UPLOAD=${SENTRY_DISABLE_AUTO_UPLOAD:-"true"}
export EXPO_NO_TYPESCRIPT_SETUP=1
echo "PLATFORM = $PLATFORM"
echo "MODE = $METAMASK_BUILD_TYPE"
echo "ENVIRONMENT = $METAMASK_ENVIRONMENT"
envFileMissing() {
FILE="$1"
echo "'$FILE' is missing, you'll need to add it to the root of the project."
echo "For convenience you can rename '$FILE.example' and fill in the parameters."
echo ""
exit 1
}
displayHelp() {
echo ''
echo "Usage: $0 {platform} ${--device}" >&2
echo ''
echo "Platform is required. Can be android or ios"
echo ''
echo "Mode is required. Can be debug or release"
echo ''
echo "Target is optional and valid for iOS only"
echo ''
echo "examples: $0 ios debug"
echo ''
echo " $0 ios debug --device"
echo ''
echo " $0 android debug"
echo ''
echo " $0 android release"
echo ''
exit 1
}
printTitle(){
echo ''
echo '-------------------------------------------'
echo ''
echo " 🚀 BUILDING $PLATFORM for $METAMASK_BUILD_TYPE target with $METAMASK_ENVIRONMENT environment" | tr [a-z] [A-Z]
echo ''
echo '-------------------------------------------'
echo ''
}
printError(){
ERROR_ICON=$'\342\235\214'
echo ''
echo " $ERROR_ICON $1"
echo ''
}
checkParameters(){
if [ "$#" -eq "0" ]
then
printError 'Platform is a required parameter'
displayHelp
exit 0;
elif [ "$1" == "--help" ]
then
displayHelp
exit 0;
elif [ "$1" == "-h" ]
then
displayHelp
exit 0;
elif [ -z "$1" ]
then
displayHelp
exit 0;
elif [ -z "$1" ]
then
printError 'No platform supplied'
displayHelp
exit 0;
fi
# Check if the --pre flag is present
if [[ "$*" == *"--pre"* ]]; then
PRE_RELEASE=true
fi
# Check if the --local flag is present
if [[ "$*" == *"--local"* ]]; then
# Script is running locally
IS_LOCAL=true
fi
# Check if the --clean flag is present
if [[ "$*" == *"--clean"* ]]; then
# Clean watcher cache
SHOULD_CLEAN_WATCHER_CACHE=true
fi
# Check if the METAMASK_ENVIRONMENT is valid
VALID_METAMASK_ENVIRONMENTS="production|beta|rc|exp|test|e2e|e2e-bs|dev"
case "${METAMASK_ENVIRONMENT}" in
production|beta|rc|exp|test|e2e|e2e-bs|dev)
# Valid environment - continue
;;
*)
# Invalid environment - exit with error
printError "METAMASK_ENVIRONMENT '${METAMASK_ENVIRONMENT}' is not valid. Please set it to one of the following: ${VALID_METAMASK_ENVIRONMENTS}"
exit 1
esac
VALID_METAMASK_BUILD_TYPES="main|flask"
# Check if the METAMASK_BUILD_TYPE is valid
case "${METAMASK_BUILD_TYPE}" in
main|flask)
# Valid build type - continue
;;
*)
# Invalid build type - exit with error
printError "METAMASK_BUILD_TYPE '${METAMASK_BUILD_TYPE}' is not valid. Please set it to one of the following: ${VALID_METAMASK_BUILD_TYPES}"
exit 1
esac
#TODO: Add check for valid METAMASK_BUILD_TYPE once commands are fully refactored
}
# ─────────────────────────────────────────────────────────────────────────────
# Load build configuration from builds.yml
# Used when GITHUB_ACTIONS is set (workflow already set secrets; this fills CONFIGURATION, IS_SIM_BUILD, etc.)
# ─────────────────────────────────────────────────────────────────────────────
loadBuildConfig() {
local build_type="$1"
local environment="$2"
# Normalize environment name (production -> prod for build name)
local normalized_env="$environment"
case "$environment" in
production) normalized_env="prod" ;;
esac
# Construct build name (e.g., main-prod, flask-dev)
local build_name="${build_type}-${normalized_env}"
echo ""
echo "📦 Loading configuration from builds.yml for '${build_name}'..."
echo ""
# Load config using apply-build-config.js
local config_output
config_output=$(node "${__DIRNAME__}/apply-build-config.js" "${build_name}" --export 2>&1)
local exit_code=$?
if [ $exit_code -ne 0 ]; then
echo "❌ Failed to load build configuration"
echo ""
echo "Error: ${config_output}"
echo ""
echo "Available builds can be found in builds.yml"
echo "Run 'node scripts/validate-build-config.js' to check config validity."
return 1
fi
# Apply the configuration (exports environment variables)
eval "$config_output"
echo "✅ Configuration loaded from builds.yml"
echo " Build: ${build_name}"
echo ""
return 0
}
# ─────────────────────────────────────────────────────────────────────────────
# Legacy env remapping. Used only when GITHUB_ACTIONS is not set.
# GitHub Actions uses loadBuildConfig + builds.yml; secrets are set with canonical names.
# ─────────────────────────────────────────────────────────────────────────────
# Remap legacy per-env vars (*_DEV, *_PROD) to canonical names. Skip when source is unset
# (local / builds.yml use canonical names in .js.env; no _DEV/_PROD needed).
# Legacy path (not GHA, not builds.yml): missing source var fails fast. Local: set BUILDS_ENABLED_WITH_GH_ACTIONS_TEMPORARY in .js.env to use builds.yml and skip.
remapEnvVariable() {
local old_var_name="$1"
local new_var_name="$2"
if [ -z "${!old_var_name}" ]; then
if [ -z "${GITHUB_ACTIONS:-}" ] && [ "${BUILDS_ENABLED_WITH_GH_ACTIONS_TEMPORARY:-false}" != "true" ]; then
echo "❌ Required legacy secret is missing: $old_var_name"
return 1
fi
return 0
fi
export $new_var_name="${!old_var_name}"
unset $old_var_name
echo "Successfully remapped $old_var_name to $new_var_name."
}
# Mapping for Main env variables in the dev environment
remapMainDevEnvVariables() {
echo "Remapping Main target environment variables for the dev environment"
remapEnvVariable "SEGMENT_WRITE_KEY_DEV" "SEGMENT_WRITE_KEY"
remapEnvVariable "SEGMENT_PROXY_URL_DEV" "SEGMENT_PROXY_URL"
remapEnvVariable "SEGMENT_DELETE_API_SOURCE_ID_QA" "SEGMENT_DELETE_API_SOURCE_ID"
remapEnvVariable "SEGMENT_REGULATIONS_ENDPOINT_QA" "SEGMENT_REGULATIONS_ENDPOINT"
# Only dev environment uses the dev DSN, this is for the Sentry project test-metamask-mobile
remapEnvVariable "MM_SENTRY_DSN_DEV" "MM_SENTRY_DSN"
remapEnvVariable "MM_CARD_BAANX_API_CLIENT_KEY_DEV" "MM_CARD_BAANX_API_CLIENT_KEY"
}
# Mapping for Main env variables in the e2e environment
remapMainE2EEnvVariables() {
echo "Remapping Main target environment variables for the e2e environment"
remapEnvVariable "SEGMENT_WRITE_KEY_QA" "SEGMENT_WRITE_KEY"
remapEnvVariable "SEGMENT_PROXY_URL_QA" "SEGMENT_PROXY_URL"
remapEnvVariable "SEGMENT_DELETE_API_SOURCE_ID_QA" "SEGMENT_DELETE_API_SOURCE_ID"
remapEnvVariable "SEGMENT_REGULATIONS_ENDPOINT_QA" "SEGMENT_REGULATIONS_ENDPOINT"
remapEnvVariable "MM_CARD_BAANX_API_CLIENT_KEY_UAT" "MM_CARD_BAANX_API_CLIENT_KEY"
}
# Mapping for Main env variables in the test environment
remapMainTestEnvVariables() {
echo "Remapping Main target environment variables for the test environment"
remapEnvVariable "SEGMENT_WRITE_KEY_QA" "SEGMENT_WRITE_KEY"
remapEnvVariable "SEGMENT_PROXY_URL_QA" "SEGMENT_PROXY_URL"
remapEnvVariable "SEGMENT_DELETE_API_SOURCE_ID_QA" "SEGMENT_DELETE_API_SOURCE_ID"
remapEnvVariable "SEGMENT_REGULATIONS_ENDPOINT_QA" "SEGMENT_REGULATIONS_ENDPOINT"
remapEnvVariable "MM_CARD_BAANX_API_CLIENT_KEY_UAT" "MM_CARD_BAANX_API_CLIENT_KEY"
}
# Mapping for Main env variables in the production environment
remapMainProdEnvVariables() {
echo "Remapping release env variable names to match production values"
remapEnvVariable "SEGMENT_WRITE_KEY_PROD" "SEGMENT_WRITE_KEY"
remapEnvVariable "SEGMENT_PROXY_URL_PROD" "SEGMENT_PROXY_URL"
remapEnvVariable "SEGMENT_DELETE_API_SOURCE_ID_PROD" "SEGMENT_DELETE_API_SOURCE_ID"
remapEnvVariable "SEGMENT_REGULATIONS_ENDPOINT_PROD" "SEGMENT_REGULATIONS_ENDPOINT"
remapEnvVariable "MM_CARD_BAANX_API_CLIENT_KEY_PROD" "MM_CARD_BAANX_API_CLIENT_KEY"
}
# Mapping for Flask env variables in the production environment
remapFlaskProdEnvVariables() {
echo "Remapping Flask target environment variables for the production environment"
remapEnvVariable "SEGMENT_WRITE_KEY_FLASK" "SEGMENT_WRITE_KEY"
remapEnvVariable "SEGMENT_PROXY_URL_FLASK" "SEGMENT_PROXY_URL"
remapEnvVariable "SEGMENT_DELETE_API_SOURCE_ID_FLASK" "SEGMENT_DELETE_API_SOURCE_ID"
remapEnvVariable "SEGMENT_REGULATIONS_ENDPOINT_FLASK" "SEGMENT_REGULATIONS_ENDPOINT"
remapEnvVariable "MM_CARD_BAANX_API_CLIENT_KEY_PROD" "MM_CARD_BAANX_API_CLIENT_KEY"
}
# Mapping for Flask env variables in the test environment
remapFlaskTestEnvVariables() {
echo "Remapping Flask target environment variables for the test environment"
remapEnvVariable "SEGMENT_WRITE_KEY_QA" "SEGMENT_WRITE_KEY"
remapEnvVariable "SEGMENT_PROXY_URL_QA" "SEGMENT_PROXY_URL"
remapEnvVariable "SEGMENT_DELETE_API_SOURCE_ID_QA" "SEGMENT_DELETE_API_SOURCE_ID"
remapEnvVariable "SEGMENT_REGULATIONS_ENDPOINT_QA" "SEGMENT_REGULATIONS_ENDPOINT"
remapEnvVariable "MM_CARD_BAANX_API_CLIENT_KEY_UAT" "MM_CARD_BAANX_API_CLIENT_KEY"
}
# Mapping for Flask env variables in the e2e environment
remapFlaskE2EEnvVariables() {
echo "Remapping Flask target environment variables for the e2e environment"
remapEnvVariable "SEGMENT_WRITE_KEY_QA" "SEGMENT_WRITE_KEY"
remapEnvVariable "SEGMENT_PROXY_URL_QA" "SEGMENT_PROXY_URL"
remapEnvVariable "SEGMENT_DELETE_API_SOURCE_ID_QA" "SEGMENT_DELETE_API_SOURCE_ID"
remapEnvVariable "SEGMENT_REGULATIONS_ENDPOINT_QA" "SEGMENT_REGULATIONS_ENDPOINT"
remapEnvVariable "MM_CARD_BAANX_API_CLIENT_KEY_UAT" "MM_CARD_BAANX_API_CLIENT_KEY"
}
# Mapping for Main env variables in the beta environment
remapMainBetaEnvVariables() {
echo "Remapping Main target environment variables for the beta environment"
remapEnvVariable "SEGMENT_WRITE_KEY_BETA" "SEGMENT_WRITE_KEY"
remapEnvVariable "SEGMENT_PROXY_URL_BETA" "SEGMENT_PROXY_URL"
remapEnvVariable "SEGMENT_DELETE_API_SOURCE_ID_QA" "SEGMENT_DELETE_API_SOURCE_ID"
remapEnvVariable "SEGMENT_REGULATIONS_ENDPOINT_QA" "SEGMENT_REGULATIONS_ENDPOINT"
remapEnvVariable "MM_CARD_BAANX_API_CLIENT_KEY_PROD" "MM_CARD_BAANX_API_CLIENT_KEY"
}
# Mapping for Main env variables in the release candidate environment
remapMainReleaseCandidateEnvVariables() {
echo "Remapping Main target environment variables for the release candidate environment"
remapEnvVariable "SEGMENT_WRITE_KEY_QA" "SEGMENT_WRITE_KEY"
remapEnvVariable "SEGMENT_PROXY_URL_QA" "SEGMENT_PROXY_URL"
remapEnvVariable "SEGMENT_DELETE_API_SOURCE_ID_QA" "SEGMENT_DELETE_API_SOURCE_ID"
remapEnvVariable "SEGMENT_REGULATIONS_ENDPOINT_QA" "SEGMENT_REGULATIONS_ENDPOINT"
remapEnvVariable "MM_CARD_BAANX_API_CLIENT_KEY_PROD" "MM_CARD_BAANX_API_CLIENT_KEY"
}
# Mapping for Main env variables in the experimental environment
remapMainExperimentalEnvVariables() {
echo "Remapping Main target environment variables for the experimental environment"
remapEnvVariable "SEGMENT_WRITE_KEY_QA" "SEGMENT_WRITE_KEY"
remapEnvVariable "SEGMENT_PROXY_URL_QA" "SEGMENT_PROXY_URL"
remapEnvVariable "SEGMENT_DELETE_API_SOURCE_ID_QA" "SEGMENT_DELETE_API_SOURCE_ID"
remapEnvVariable "SEGMENT_REGULATIONS_ENDPOINT_QA" "SEGMENT_REGULATIONS_ENDPOINT"
remapEnvVariable "MAIN_WEB3AUTH_NETWORK_PROD" "WEB3AUTH_NETWORK"
remapEnvVariable "MM_CARD_BAANX_API_CLIENT_KEY_UAT" "MM_CARD_BAANX_API_CLIENT_KEY"
}
prebuild_ios(){
# Generate xcconfig files for CircleCI
if [ "$PRE_RELEASE" = true ] ; then
echo "" > ios/debug.xcconfig
echo "" > ios/release.xcconfig
fi
# Required to install mixpanel dep
git submodule update --init --recursive
unset PREFIX
# Create GoogleService-Info.plist file to be used by the Firebase services.
# Check if GOOGLE_SERVICES_B64_IOS is set
if [ ! -z "$GOOGLE_SERVICES_B64_IOS" ]; then
echo -n $GOOGLE_SERVICES_B64_IOS | base64 -d > ./ios/GoogleServices/GoogleService-Info.plist
echo "GoogleService-Info.plist has been created successfully."
# Ensure the file has read and write permissions
chmod 664 ./ios/GoogleServices/GoogleService-Info.plist
else
echo "GOOGLE_SERVICES_B64_IOS is not set in the .env file."
exit 1
fi
}
prebuild_android(){
# Copy JS files for injection
yes | cp -rf app/core/InpageBridgeWeb3.js android/app/src/main/assets/.
# Copy fonts with iconset
yes | cp -rf ./app/fonts/Metamask.ttf ./android/app/src/main/assets/fonts/Metamask.ttf
#Create google-services.json file to be used by the Firebase services.
# Check if GOOGLE_SERVICES_B64_ANDROID is set
if [ ! -z "$GOOGLE_SERVICES_B64_ANDROID" ]; then
echo -n $GOOGLE_SERVICES_B64_ANDROID | base64 -d > ./android/app/google-services.json
echo "google-services.json has been created successfully."
# Ensure the file has read and write permissions
chmod 664 ./android/app/google-services.json
else
echo "GOOGLE_SERVICES_B64_ANDROID is not set in the .env file."
exit 1
fi
if [ "$PRE_RELEASE" = false ] ; then
if [ -e $ANDROID_ENV_FILE ]
then
source $ANDROID_ENV_FILE
fi
fi
}
# Builds and installs the Main APK for local development
buildAndroidMainLocal(){
prebuild_android
#react-native run-android --port=$WATCHER_PORT --variant=prodDebug --active-arch-only
yarn expo run:android --no-install --port $WATCHER_PORT --variant 'prodDebug' --device
}
# Builds and installs the Flask APK for local development
buildAndroidFlaskLocal(){
prebuild_android
#react-native run-android --port=$WATCHER_PORT --variant=flaskDebug --active-arch-only
yarn expo run:android --no-install --port $WATCHER_PORT --variant 'flaskDebug' --device
}
# Builds and installs the Main iOS app for local development
buildIosMainLocal(){
prebuild_ios
yarn expo run:ios --no-install --configuration Debug --port $WATCHER_PORT --scheme "MetaMask" --device "$IOS_SIMULATOR"
}
# Builds and installs the Flask iOS app for local development
buildIosFlaskLocal(){
prebuild_ios
yarn expo run:ios --no-install --configuration Debug --port $WATCHER_PORT --scheme "MetaMask-Flask" --device "$IOS_SIMULATOR"
}
# Generates the iOS binary for the given scheme and configuration
generateIosBinary() {
scheme="$1"
configuration="${CONFIGURATION:-"Release"}"
# Check if configuration is valid
if [ "$configuration" != "Debug" ] && [ "$configuration" != "Release" ] ; then
# Configuration is not recognized
echo "Configuration $configuration is not recognized! Only Debug and Release are supported"
exit 1
fi
# Check if scheme is valid
if [ "$scheme" != "MetaMask" ] && [ "$scheme" != "MetaMask-Flask" ] ; then
# Scheme is not recognized
echo "Scheme $scheme is not recognized! Only MetaMask, and MetaMask-Flask are supported"
exit 1
fi
# PROFILE: 'development' or 'release' (set in builds.yml, defaults to 'release')
local profile="${PROFILE:-release}"
if [ "$scheme" = "MetaMask" ] ; then
if [ "$profile" = "development" ] ; then
exportOptionsPlist="MetaMask/IosExportOptionsMetaMaskDevelopment.plist"
else
exportOptionsPlist="MetaMask/IosExportOptionsMetaMaskRelease.plist"
fi
elif [ "$scheme" = "MetaMask-Flask" ] ; then
if [ "$profile" = "development" ] ; then
exportOptionsPlist="MetaMask/IosExportOptionsMetaMaskFlaskDevelopment.plist"
else
exportOptionsPlist="MetaMask/IosExportOptionsMetaMaskFlaskRelease.plist"
fi
fi
echo "exportOptionsPlist: $exportOptionsPlist"
echo "Generating archive packages for $scheme in $configuration configuration"
if [ "$IS_SIM_BUILD" = "true" ]; then
echo "Binary build type: Simulator"
xcodebuild -workspace MetaMask.xcworkspace -scheme $scheme -configuration $configuration -sdk iphonesimulator -derivedDataPath build
fi
if [ "$IS_DEVICE_BUILD" = "true" ] || [ -z "$IS_SIM_BUILD" ]; then
echo "Binary build type: Device"
# When PROFILE=development, override the signing settings so a Release
# archive can be signed with the development certificate and profile
# instead of the distribution identity hardcoded in the Xcode project.
local -a archiveOverrides=()
if [ "$profile" = "development" ] && [ "$configuration" = "Release" ]; then
archiveOverrides=(
CODE_SIGN_STYLE=Manual
"PROVISIONING_PROFILE_SPECIFIER=development-metamask"
"CODE_SIGN_IDENTITY=Apple Development"
)
echo "Overriding signing: using development certificate and profile for Release archive"
fi
xcodebuild -workspace MetaMask.xcworkspace -scheme "$scheme" -configuration "$configuration" archive -archivePath "build/$scheme.xcarchive" -destination generic/platform=ios "${archiveOverrides[@]}"
echo "Generating ipa for $scheme"
xcodebuild -exportArchive -archivePath build/$scheme.xcarchive -exportPath build/output -exportOptionsPlist $exportOptionsPlist
fi
}
# Generates the Android binary for the given scheme and configuration
generateAndroidBinary() {
# Prod, Flask
local flavor="$1"
# Lowercase flavor string
local lowercaseFlavor=$(echo "$flavor" | tr '[:upper:]' '[:lower:]')
# Debug or Release configuration
local configuration="${CONFIGURATION:-"Release"}"
# Lowercase configuration string
local lowercaseConfiguration=$(echo "$configuration" | tr '[:upper:]' '[:lower:]')
# Construct assemble task
local assembleApkTask="app:assemble${flavor}${configuration}"
# Construct checksum command
local checkSumCommand="build:android:checksum:${lowercaseFlavor}"
# Create assemble test APK task
local assembleTestApkTask=""
# Define React Native Architecture arg
local reactNativeArchitecturesArg=""
# Define Test build type arg
local testBuildTypeArg=""
# Define Gradle logging flags for E2E builds
local gradleLoggingFlags=""
# Optional Gradle init script used by Namespace remote build cache trials.
local -a gradleInitScriptArg=()
# Check if configuration is valid
if [ "$configuration" != "Debug" ] && [ "$configuration" != "Release" ] ; then
# Configuration is not recognized
echo "Configuration $configuration is not recognized! Only Debug and Release are supported"
exit 1
fi
# Check if flavor is valid
if [ "$flavor" != "Prod" ] && [ "$flavor" != "Flask" ] ; then
# Flavor is not recognized
echo "Flavor $flavor is not recognized! Only Prod, Flask are supported"
exit 1
fi
if [ -n "${GRADLE_INIT_SCRIPT:-}" ] ; then
gradleInitScriptArg=(--init-script "$GRADLE_INIT_SCRIPT")
fi
if [ "$configuration" = "Debug" ] || [ "$METAMASK_ENVIRONMENT" = "e2e" ] ; then
# Define assemble test APK task
assembleTestApkTask="app:assemble${flavor}${configuration}AndroidTest"
# Define test build type arg
testBuildTypeArg="-DtestBuildType=${lowercaseConfiguration}"
# Memory optimization for E2E builds (Keep an eye out if this breaks outside of E2E CI builds)
# BrowserStack builds target real ARM devices, so skip x86_64-only restriction.
if [ "$METAMASK_ENVIRONMENT" = "e2e" ] && [ "${IS_BROWSERSTACK_BUILD:-false}" != "true" ] ; then
# CI uses x86_64 emulators only; local macOS (Apple Silicon) also needs arm64-v8a
if [ "${CI:-false}" = "true" ] ; then
reactNativeArchitecturesArg="-PreactNativeArchitectures=x86_64"
else
reactNativeArchitecturesArg="-PreactNativeArchitectures=x86_64,arm64-v8a"
fi
# Enable verbose logging for E2E builds to help diagnose build failures
gradleLoggingFlags="--stacktrace --info"
# Disable expo-updates delay-load-app to prevent Detox ANR.
# expo-updates defaults this to true, which causes a blocking launchAssetFile
# call when useDeveloperSupport=false (release-like E2E builds).
exUpdatesArgs="-PEX_UPDATES_ANDROID_DELAY_LOAD_APP=false"
fi
fi
# Generate Android APKs
echo "Generating Android binary for ($flavor) flavor with ($configuration) configuration"
./gradlew "${gradleInitScriptArg[@]}" $assembleApkTask $assembleTestApkTask $testBuildTypeArg $reactNativeArchitecturesArg $gradleLoggingFlags $exUpdatesArgs
# Skip AAB bundle for E2E environments - AAB cannot be installed on emulators
# and is only needed for Play Store distribution
if [ "$configuration" = "Release" ] && [ "$METAMASK_ENVIRONMENT" != "e2e" ] ; then
# Generate AAB bundle
bundleConfiguration="bundle${flavor}Release"
echo "Generating AAB bundle for ($flavor) flavor with ($configuration) configuration"
./gradlew $bundleConfiguration
# Generate checksum
echo "Generating checksum for ($flavor) flavor with ($configuration) configuration"
yarn $checkSumCommand
fi
# Verify APK files were created
echo ""
echo "📦 Verifying APK outputs..."
local appApkPath="app/build/outputs/apk/${lowercaseFlavor}/${lowercaseConfiguration}/app-${lowercaseFlavor}-${lowercaseConfiguration}.apk"
local testApkPath="app/build/outputs/apk/androidTest/${lowercaseFlavor}/${lowercaseConfiguration}/app-${lowercaseFlavor}-${lowercaseConfiguration}-androidTest.apk"
# Verify APK exists
if [ -f "$appApkPath" ]; then
echo "✅ App APK found: $appApkPath ($(du -h "$appApkPath" | cut -f1))"
else
echo "❌ App APK NOT found at: $appApkPath"
cd ..
return 1
fi
# Only verify test APK if it was supposed to be built
if [ -n "$assembleTestApkTask" ]; then
# Verify test APK exists
if [ -f "$testApkPath" ]; then
echo "✅ Test APK found: $testApkPath ($(du -h "$testApkPath" | cut -f1))"
else
echo "❌ Test APK NOT found at: $testApkPath"
cd ..
return 1
fi
fi
echo ""
# Change directory back out
cd ..
}
createEnvFile() {
echo "📝 Creating .env file from environment variables..."
# Derive build name from current METAMASK_BUILD_TYPE + METAMASK_ENVIRONMENT
local build_type="${METAMASK_BUILD_TYPE:-main}"
local environment="${METAMASK_ENVIRONMENT:-production}"
local normalized_env="$environment"
case "$environment" in
production) normalized_env="prod" ;;
esac
local build_name="${build_type}-${normalized_env}"
# Read env + secret keys from builds.yml (single source of truth).
# Any env var set in the shell (from builds.yml, workflow, or feature flags)
# that matches a key in builds.yml will be written to .env.
local builds_yml_keys
builds_yml_keys=$(node -e "
const yaml = require('js-yaml');
const fs = require('fs');
const config = yaml.load(fs.readFileSync('${__DIRNAME__}/../builds.yml', 'utf8'));
const build = config.builds['${build_name}'];
if (!build) { console.error('Build not found: ${build_name}'); process.exit(1); }
const keys = new Set([
...Object.keys(build.env || {}),
...Object.keys(build.secrets || {}),
]);
keys.forEach(k => console.log(k));
")
if [ $? -ne 0 ]; then
echo "❌ Failed to read builds.yml for build '${build_name}'"
return 1
fi
# Create .env file and export to GITHUB_ENV
> .env
local exported_count=0
while IFS= read -r var; do
if [ -n "${!var+x}" ]; then
value="${!var}"
echo "${var}=${value}" >> .env
if [ -n "${GITHUB_ENV:-}" ]; then
echo "${var}=${value}" >> "$GITHUB_ENV"
fi
exported_count=$((exported_count + 1))
fi
done <<< "$builds_yml_keys"
echo "📄 .env file created with ${exported_count} variables (from build: ${build_name})"
}
buildExpoUpdate() {
echo "Build Expo Update $METAMASK_BUILD_TYPE started..."
# Create .env file and export environment variables
createEnvFile
# Verify .env file was created and source it
if [ -f ".env" ]; then
echo "✅ .env file exists at $(pwd)/.env"
echo "📊 .env file contains $(wc -l < .env | tr -d ' ') lines"
# Show first few variables (without values for security)
echo "📝 Sample variables in .env:"
head -n 5 .env | cut -d= -f1 | sed 's/^/ - /'
# Source the .env file to ensure variables are loaded
echo "🔄 Sourcing .env file to load variables..."
set -a # automatically export all variables
source .env
set +a # turn off automatic export
echo "✅ .env file sourced successfully"
else
echo "⚠️ WARNING: .env file was not created!"
fi
if [ -z "${EXPO_TOKEN}" ]; then
echo "::error title=Missing EXPO_TOKEN::EXPO_TOKEN secret is not configured. Cannot authenticate with Expo." >&2
exit 1
fi
# Validate required Expo Update environment variables
if [ -z "${EXPO_CHANNEL}" ]; then
echo "::error title=Missing EXPO_CHANNEL::EXPO_CHANNEL environment variable is not set. Cannot publish update." >&2
exit 1
fi
if [ -z "${EXPO_KEY_PRIV}" ]; then
echo "::error title=Missing EXPO_KEY_PRIV::EXPO_KEY_PRIV secret is not configured. Cannot sign update." >&2
exit 1
fi
# Prepare Expo update signing key
mkdir -p keys
echo "Writing Expo private key to ./keys/private-key.pem"
printf '%s' "${EXPO_KEY_PRIV}" > keys/private-key.pem
if [ ! -f keys/private-key.pem ]; then
echo "::error title=Missing signing key::keys/private-key.pem not found. Ensure the signing key step ran successfully." >&2
exit 1
fi
echo "🚀 Publishing EAS update..."
echo "ℹ️ Git head: $(git rev-parse HEAD)"
echo "ℹ️ Checking for eas script in package.json..."
if ! grep -q '"eas": "eas"' package.json; then
echo "::error title=Missing eas script::package.json does not include an \"eas\" script. Commit hash: $(git rev-parse HEAD)." >&2
exit 1
fi
echo "ℹ️ Available yarn scripts containing eas:"
yarn run --json | grep '"name":"eas"' || true
# Run platforms based on OTA_PUSH_PLATFORM environment variable (default: all)
# Run sequentially to avoid LavaMoat lockdown serializer conflicts
# when bundling multiple platforms simultaneously
OTA_PUSH_PLATFORM="${OTA_PUSH_PLATFORM:-all}"
# Track exit codes to ensure failures propagate
local ios_exit_code=0
local android_exit_code=0
if [ "$OTA_PUSH_PLATFORM" = "all" ] || [ "$OTA_PUSH_PLATFORM" = "ios" ]; then
echo "📱 Publishing iOS update..."
yarn run eas update \
--platform ios \
--channel "${EXPO_CHANNEL}" \
--private-key-path "./keys/private-key.pem" \
--message "${UPDATE_MESSAGE}" \
--non-interactive
ios_exit_code=$?
if [ $ios_exit_code -ne 0 ]; then
echo "::error title=iOS update failed::iOS EAS update command failed with exit code ${ios_exit_code}" >&2
fi
fi
if [ "$OTA_PUSH_PLATFORM" = "all" ] || [ "$OTA_PUSH_PLATFORM" = "android" ]; then
echo "🤖 Publishing Android update..."
yarn run eas update \
--platform android \
--channel "${EXPO_CHANNEL}" \
--private-key-path "./keys/private-key.pem" \
--message "${UPDATE_MESSAGE}" \
--non-interactive
android_exit_code=$?
if [ $android_exit_code -ne 0 ]; then
echo "::error title=Android update failed::Android EAS update command failed with exit code ${android_exit_code}" >&2
fi
fi
# Check for failures and exit accordingly
if [ $ios_exit_code -ne 0 ] || [ $android_exit_code -ne 0 ]; then
echo "::error title=EAS update failed::One or more platform updates failed. iOS exit code: ${ios_exit_code}, Android exit code: ${android_exit_code}" >&2
exit 1
fi
if [ "$OTA_PUSH_PLATFORM" = "all" ]; then
echo "✅ EAS updates published for both platforms"
else
echo "✅ EAS update published for ${OTA_PUSH_PLATFORM}"
fi
}
buildAndroid() {
echo "Build Android $METAMASK_BUILD_TYPE started..."
if [ "$METAMASK_BUILD_TYPE" == "main" ] ; then
if [ "$IS_LOCAL" = true ] ; then
buildAndroidMainLocal
else
# Prepare Android dependencies
prebuild_android
# Go to android directory
cd android
# Generate Android binary
generateAndroidBinary "Prod"
fi
elif [ "$METAMASK_BUILD_TYPE" == "flask" ] ; then
if [ "$IS_LOCAL" = true ] ; then
buildAndroidFlaskLocal
else
# Prepare Android dependencies
prebuild_android
# Go to android directory
cd android
# Generate Android binary
generateAndroidBinary "Flask"
fi
else
printError "METAMASK_BUILD_TYPE '${METAMASK_BUILD_TYPE}' is not recognized."
exit 1
fi
}
buildIos() {
echo "Build iOS $METAMASK_BUILD_TYPE started..."
if [ "$METAMASK_BUILD_TYPE" == "main" ] ; then
if [ "$IS_LOCAL" = true ] ; then
buildIosMainLocal
else
# Prepare iOS dependencies
prebuild_ios
# Go to ios directory
cd ios
# Generate iOS binary
generateIosBinary "MetaMask"
fi
elif [ "$METAMASK_BUILD_TYPE" == "flask" ] ; then
if [ "$IS_LOCAL" = true ] ; then
buildIosFlaskLocal
else
# Prepare iOS dependencies
prebuild_ios
# Go to ios directory
cd ios
# Generate iOS binary
generateIosBinary "MetaMask-Flask"
fi
else
printError "METAMASK_BUILD_TYPE '${METAMASK_BUILD_TYPE}' is not recognized"
exit 1
fi
}
startWatcher() {
if [ "$SHOULD_CLEAN_WATCHER_CACHE" = true ]; then
# Clean watcher cache, then start the watcher
echo "Cleaning watcher cache and starting the watcher..."
watchman watch-del-all
rm -rf $TMPDIR/metro-cache
yarn expo start --port $WATCHER_PORT --clear
else
# Start the watcher
echo "Starting the watcher..."
yarn expo start --port $WATCHER_PORT
fi
}
# TODO: Refactor this check to be environment specific
checkAuthToken() {
local propertiesFileName="$1"
if [ -n "${MM_SENTRY_AUTH_TOKEN}" ]; then
# Use | as delimiter to avoid conflicts with special characters in auth token (e.g., /)
sed -i'' -e "s|auth.token.*|auth.token=${MM_SENTRY_AUTH_TOKEN}|" "./${propertiesFileName}";
elif ! grep -qE '^auth.token=[[:alnum:]]+$' "./${propertiesFileName}"; then
if [ "$METAMASK_ENVIRONMENT" == "production" ]; then
printError "Missing auth token in '${propertiesFileName}'; add the token, or set it as MM_SENTRY_AUTH_TOKEN"
exit 1
else
echo "Missing auth token in '${propertiesFileName}'; add the token, or set it as MM_SENTRY_AUTH_TOKEN"
fi
fi
if [ ! -e "./${propertiesFileName}" ]; then
if [ -n "${MM_SENTRY_AUTH_TOKEN}" ]; then
cp "./${propertiesFileName}.example" "./${propertiesFileName}"
# Use | as delimiter to avoid conflicts with special characters in auth token (e.g., /)
sed -i'' -e "s|auth.token.*|auth.token=${MM_SENTRY_AUTH_TOKEN}|" "./${propertiesFileName}";
else
if [ "$METAMASK_ENVIRONMENT" == "production" ]; then
printError "Missing '${propertiesFileName}' file (see '${propertiesFileName}.example' or set MM_SENTRY_AUTH_TOKEN to generate)"
exit 1
else
echo "Missing '${propertiesFileName}' file (see '${propertiesFileName}.example' or set MM_SENTRY_AUTH_TOKEN to generate)"
fi
fi
fi
}
checkParameters "$@"
printTitle
# ─────────────────────────────────────────────────────────────────────────────
# Load build configuration from builds.yml (all platforms including expo-update).
# Gated by BUILDS_ENABLED_WITH_GH_ACTIONS_TEMPORARY:
# true = GHA (set by workflow) and local (set in .js.env) → use builds.yml
# false = unset → skip builds.yml, use legacy remap only
# Local: .js.env is applied after loadBuildConfig so it overrides (see below).
# ─────────────────────────────────────────────────────────────────────────────
# Non-GHA: source .js.env early so BUILDS_ENABLED_WITH_GH_ACTIONS_TEMPORARY is set for the gate (local can opt in)
if [ -z "${GITHUB_ACTIONS:-}" ] && [ -e "$JS_ENV_FILE" ]; then
source "$JS_ENV_FILE"
fi
BUILD_TYPE_FOR_CONFIG=$(echo "$METAMASK_BUILD_TYPE" | tr '[:upper:]' '[:lower:]')
if [ "${BUILDS_ENABLED_WITH_GH_ACTIONS_TEMPORARY:-false}" = "true" ]; then
# builds.yml path: GHA or local with flag.
if ! loadBuildConfig "$BUILD_TYPE_FOR_CONFIG" "$METAMASK_ENVIRONMENT"; then
echo "❌ Build configuration failed. Exiting."
exit 1
fi
else
echo "⚠️ BUILDS_ENABLED_WITH_GH_ACTIONS_TEMPORARY is not true; skipping builds.yml, using legacy remap / .js.env"
echo ""
fi
# Local builds: .js.env overrides builds.yml (takes precedence)
if [ -z "${GITHUB_ACTIONS:-}" ] && [ -e "$JS_ENV_FILE" ]; then
source "$JS_ENV_FILE"
fi
# Native-build-specific flags and legacy env remap (not needed for expo-update)
if [ "$PLATFORM" != "expo-update" ]; then
# Set flags for main builds
if [ "$METAMASK_BUILD_TYPE" == "main" ]; then
export GENERATE_BUNDLE=true # Used only for Android
export PRE_RELEASE=true # Used mostly for iOS, for Android only deletes old APK and installs new one
fi
# Non-GHA CI / local: legacy env remapping (secrets use per-env names, e.g. SEGMENT_WRITE_KEY_PROD)
if [ -z "${GITHUB_ACTIONS:-}" ]; then
if [ "$METAMASK_BUILD_TYPE" == "main" ]; then
if [ "$METAMASK_ENVIRONMENT" == "production" ]; then
remapMainProdEnvVariables
elif [ "$METAMASK_ENVIRONMENT" == "beta" ]; then
remapMainBetaEnvVariables
elif [ "$METAMASK_ENVIRONMENT" == "rc" ]; then
remapMainReleaseCandidateEnvVariables
elif [ "$METAMASK_ENVIRONMENT" == "exp" ]; then
remapMainExperimentalEnvVariables
elif [ "$METAMASK_ENVIRONMENT" == "test" ]; then
remapMainTestEnvVariables
elif [ "$METAMASK_ENVIRONMENT" == "e2e" ]; then
remapMainE2EEnvVariables
elif [ "$METAMASK_ENVIRONMENT" == "dev" ]; then
remapMainDevEnvVariables
fi
elif [ "$METAMASK_BUILD_TYPE" == "flask" ]; then
if [ "$METAMASK_ENVIRONMENT" == "production" ]; then
remapFlaskProdEnvVariables
elif [ "$METAMASK_ENVIRONMENT" == "test" ]; then
remapFlaskTestEnvVariables
elif [ "$METAMASK_ENVIRONMENT" == "e2e" ]; then
remapFlaskE2EEnvVariables
fi
fi
fi
fi
if [ "$METAMASK_ENVIRONMENT" == "e2e" ]; then
if [ "${IS_BROWSERSTACK_BUILD:-false}" != "true" ]; then
# Build for simulator (local/CI emulator). BrowserStack builds target real devices, so skip this.
export IS_SIM_BUILD="true"
fi
# Ignore Boxlogs for E2E builds
export IGNORE_BOXLOGS_DEVELOPMENT="true"
fi
# BrowserStack builds target real devices: override IS_SIM_BUILD=true that loadBuildConfig may
# have set from the generic main-e2e config (which uses IS_SIM_BUILD=true for emulators).
if [ "${IS_BROWSERSTACK_BUILD:-false}" = "true" ]; then
export IS_SIM_BUILD="false"
fi
if [ "$METAMASK_ENVIRONMENT" == "production" ]; then
echo "RELEASE SENTRY PROPS"
checkAuthToken 'sentry.release.properties'
export SENTRY_PROPERTIES="${REPO_ROOT_DIR}/sentry.release.properties"
else
echo "DEBUG SENTRY PROPS"
checkAuthToken 'sentry.debug.properties'
export SENTRY_PROPERTIES="${REPO_ROOT_DIR}/sentry.debug.properties"
fi
# Update Expo channel configuration based on environment
# Skip when running Expo updates, as channel is managed externally in that flow
if [ "$PLATFORM" != "expo-update" ]; then
echo "Updating Expo channel configuration..."
node "${__DIRNAME__}/update-expo-channel.js"
fi
if [ "$PLATFORM" == "ios" ]; then
# we don't care about env file in CI
if [ -f "$IOS_ENV_FILE" ] || [ "$CI" = true ]; then
buildIos
else
envFileMissing $IOS_ENV_FILE
fi
elif [ "$PLATFORM" == "android" ]; then
# we don't care about env file in CI
if [ -f "$ANDROID_ENV_FILE" ] || [ "$CI" = true ]; then
buildAndroid
else
envFileMissing $ANDROID_ENV_FILE
fi
elif [ "$PLATFORM" == "expo-update" ]; then
# we don't care about env file in CI
buildExpoUpdate
elif [ "$PLATFORM" == "watcher" ]; then
startWatcher
fi