Skip to content

Commit 418f4f3

Browse files
runway-github[bot]weitingsunsethkfman
authored
chore(runway): cherry-pick chore: validate env expo (#25481)
- chore: validate env expo cp-7.63.0 (#25415) <!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** - Move creating .env from push-eas-update.yml to build.sh - Have eas push platform as an input so we can choose to push to all, ios or android ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: null ## **Related issues** Fixes: ## **Manual testing steps** ```gherkin Feature: my feature name Scenario: user [verb for user action] Given [describe expected initial app state] When user [verb for user action] Then [describe expected outcome] ``` ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes the automation that publishes EAS OTA updates by altering required env validation and how updates are executed per platform, so misconfiguration could block releases. No product/runtime logic changes, but CI deployment behavior is affected. > > **Overview** > Adds a `platform` input to the `push-eas-update` GitHub workflow and plumbs it through as `OTA_PUSH_PLATFORM` so release operators can publish OTA updates to `ios`, `android`, or `all`. > > Moves Expo-update environment preparation into `scripts/build.sh` by generating and sourcing a `.env` from selected CI variables (and exporting to `GITHUB_ENV`), tightens Expo credential validation (fails fast on missing `EXPO_TOKEN`), and publishes iOS/Android updates sequentially with per-platform error reporting. Also removes `EXPO_NO_LAVAMOAT` from the workflow and hardens Sentry auth token injection by using a safer `sed` delimiter. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit fd79d6c. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: sethkfman <seth.kaufman@consensys.net> [7e6bee4](7e6bee4) Co-authored-by: Wei Sun <wei.sun@consensys.net> Co-authored-by: sethkfman <seth.kaufman@consensys.net>
1 parent 67a50f5 commit 418f4f3

2 files changed

Lines changed: 184 additions & 33 deletions

File tree

.github/workflows/push-eas-update.yml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@ on:
2828
- rc
2929
- production
3030
default: exp
31+
platform:
32+
description: 'Platform to update (all, ios, android)'
33+
required: true
34+
type: choice
35+
options:
36+
- all
37+
- ios
38+
- android
39+
default: all
3140

3241
permissions:
3342
contents: read
@@ -39,6 +48,7 @@ env:
3948
BASE_BRANCH_REF: ${{ inputs.base_branch }}
4049
UPDATE_MESSAGE: ${{ inputs.message }}
4150
TARGET_CHANNEL: ${{ inputs.channel }}
51+
OTA_PUSH_PLATFORM: ${{ inputs.platform }}
4252

4353
jobs:
4454
setup-dependencies:
@@ -396,8 +406,6 @@ jobs:
396406
SKIP_TRANSFORM_LINT: 'true'
397407
# Increase Node heap to avoid OOM during Expo export in CI
398408
NODE_OPTIONS: '--max_old_space_size=8192'
399-
# Disable LavaMoat sandbox to prevent duplicate bundle executions in CI
400-
EXPO_NO_LAVAMOAT: '1'
401409
run: |
402410
echo "📦 Configuring EXPO key..."
403411
if [[ -z "$EXPO_KEY_PRIV_B64" ]]; then

scripts/build.sh

Lines changed: 174 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -628,53 +628,194 @@ generateAndroidBinary() {
628628
cd ..
629629
}
630630

631-
buildExpoUpdate() {
632-
echo "Build Expo Update $METAMASK_BUILD_TYPE started..."
633-
634-
if [ -z "${EXPO_TOKEN}" ]; then
635-
echo "EXPO_TOKEN is NOT set in build.sh env"
636-
else
637-
echo "EXPO_TOKEN is set in build.sh env (value masked by GitHub Actions logs)"
631+
createEnvFile() {
632+
echo "📝 Creating .env file from environment variables..."
633+
634+
# List of environment variable names to export
635+
local ENV_VARS=(
636+
"MM_MUSD_CONVERSION_FLOW_ENABLED"
637+
"MM_NETWORK_UI_REDESIGN_ENABLED"
638+
"MM_NOTIFICATIONS_UI_ENABLED"
639+
"MM_PERMISSIONS_SETTINGS_V1_ENABLED"
640+
"MM_PERPS_BLOCKED_REGIONS"
641+
"MM_PERPS_ENABLED"
642+
"MM_PERPS_HIP3_ALLOWLIST_MARKETS"
643+
"MM_PERPS_HIP3_BLOCKLIST_MARKETS"
644+
"MM_PERPS_HIP3_ENABLED"
645+
"MM_SECURITY_ALERTS_API_ENABLED"
646+
"BRIDGE_USE_DEV_APIS"
647+
"SEEDLESS_ONBOARDING_ENABLED"
648+
"RAMP_INTERNAL_BUILD"
649+
"FEATURES_ANNOUNCEMENTS_ACCESS_TOKEN"
650+
"FEATURES_ANNOUNCEMENTS_SPACE_ID"
651+
"SEGMENT_WRITE_KEY"
652+
"SEGMENT_PROXY_URL"
653+
"SEGMENT_DELETE_API_SOURCE_ID"
654+
"SEGMENT_REGULATIONS_ENDPOINT"
655+
"MM_SENTRY_DSN"
656+
"MM_SENTRY_AUTH_TOKEN"
657+
"IOS_GOOGLE_CLIENT_ID"
658+
"IOS_GOOGLE_REDIRECT_URI"
659+
"ANDROID_APPLE_CLIENT_ID"
660+
"ANDROID_GOOGLE_CLIENT_ID"
661+
"ANDROID_GOOGLE_SERVER_CLIENT_ID"
662+
"MM_INFURA_PROJECT_ID"
663+
"MM_BRANCH_KEY_LIVE"
664+
"MM_BRANCH_KEY_TEST"
665+
"MM_CARD_BAANX_API_CLIENT_KEY"
666+
"WALLET_CONNECT_PROJECT_ID"
667+
"MM_FOX_CODE"
668+
"FCM_CONFIG_API_KEY"
669+
"FCM_CONFIG_AUTH_DOMAIN"
670+
"FCM_CONFIG_STORAGE_BUCKET"
671+
"FCM_CONFIG_PROJECT_ID"
672+
"FCM_CONFIG_MESSAGING_SENDER_ID"
673+
"FCM_CONFIG_APP_ID"
674+
"FCM_CONFIG_MEASUREMENT_ID"
675+
"QUICKNODE_MAINNET_URL"
676+
"QUICKNODE_ARBITRUM_URL"
677+
"QUICKNODE_AVALANCHE_URL"
678+
"QUICKNODE_BASE_URL"
679+
"QUICKNODE_LINEA_MAINNET_URL"
680+
"QUICKNODE_MONAD_URL"
681+
"QUICKNODE_OPTIMISM_URL"
682+
"QUICKNODE_POLYGON_URL"
683+
)
684+
685+
# Create .env file and export to GITHUB_ENV
686+
> .env
687+
local exported_count=0
688+
for var in "${ENV_VARS[@]}"; do
689+
# Check if variable is set (defined), not just non-empty
690+
# This allows explicitly empty strings to be written to .env
691+
if [ -n "${!var+x}" ]; then
692+
value="${!var}"
693+
echo "${var}=${value}" >> .env
694+
695+
# Export to GITHUB_ENV if running in GitHub Actions
696+
if [ -n "${GITHUB_ENV:-}" ]; then
697+
echo "${var}=${value}" >> "$GITHUB_ENV"
698+
fi
699+
700+
exported_count=$((exported_count + 1))
638701
fi
702+
done
639703

640-
# Validate required Expo Update environment variables
641-
if [ -z "${EXPO_CHANNEL}" ]; then
642-
echo "::error title=Missing EXPO_CHANNEL::EXPO_CHANNEL environment variable is not set. Cannot publish update." >&2
643-
exit 1
644-
fi
704+
echo "📄 .env file created with ${exported_count} variables"
705+
}
706+
707+
buildExpoUpdate() {
708+
echo "Build Expo Update $METAMASK_BUILD_TYPE started..."
709+
710+
# Create .env file and export environment variables
711+
createEnvFile
712+
713+
# Verify .env file was created and source it
714+
if [ -f ".env" ]; then
715+
echo "✅ .env file exists at $(pwd)/.env"
716+
echo "📊 .env file contains $(wc -l < .env | tr -d ' ') lines"
717+
# Show first few variables (without values for security)
718+
echo "📝 Sample variables in .env:"
719+
head -n 5 .env | cut -d= -f1 | sed 's/^/ - /'
720+
721+
# Source the .env file to ensure variables are loaded
722+
echo "🔄 Sourcing .env file to load variables..."
723+
set -a # automatically export all variables
724+
source .env
725+
set +a # turn off automatic export
726+
echo "✅ .env file sourced successfully"
727+
else
728+
echo "⚠️ WARNING: .env file was not created!"
729+
fi
730+
731+
if [ -z "${EXPO_TOKEN}" ]; then
732+
echo "::error title=Missing EXPO_TOKEN::EXPO_TOKEN secret is not configured. Cannot authenticate with Expo." >&2
733+
exit 1
734+
fi
735+
736+
# Validate required Expo Update environment variables
737+
if [ -z "${EXPO_CHANNEL}" ]; then
738+
echo "::error title=Missing EXPO_CHANNEL::EXPO_CHANNEL environment variable is not set. Cannot publish update." >&2
739+
exit 1
740+
fi
645741

646742
if [ -z "${EXPO_KEY_PRIV}" ]; then
647743
echo "::error title=Missing EXPO_KEY_PRIV::EXPO_KEY_PRIV secret is not configured. Cannot sign update." >&2
648744
exit 1
649745
fi
650746

651-
# Prepare Expo update signing key
652-
mkdir -p keys
653-
echo "Writing Expo private key to ./keys/private-key.pem"
654-
printf '%s' "${EXPO_KEY_PRIV}" > keys/private-key.pem
747+
# Prepare Expo update signing key
748+
mkdir -p keys
749+
echo "Writing Expo private key to ./keys/private-key.pem"
750+
printf '%s' "${EXPO_KEY_PRIV}" > keys/private-key.pem
655751

656-
if [ ! -f keys/private-key.pem ]; then
657-
echo "::error title=Missing signing key::keys/private-key.pem not found. Ensure the signing key step ran successfully." >&2
658-
exit 1
659-
fi
752+
if [ ! -f keys/private-key.pem ]; then
753+
echo "::error title=Missing signing key::keys/private-key.pem not found. Ensure the signing key step ran successfully." >&2
754+
exit 1
755+
fi
660756

661-
echo "🚀 Publishing EAS update..."
757+
echo "🚀 Publishing EAS update..."
662758

663-
echo "ℹ️ Git head: $(git rev-parse HEAD)"
664-
echo "ℹ️ Checking for eas script in package.json..."
665-
if ! grep -q '"eas": "eas"' package.json; then
666-
echo "::error title=Missing eas script::package.json does not include an \"eas\" script. Commit hash: $(git rev-parse HEAD)." >&2
667-
exit 1
668-
fi
759+
echo "ℹ️ Git head: $(git rev-parse HEAD)"
760+
echo "ℹ️ Checking for eas script in package.json..."
761+
if ! grep -q '"eas": "eas"' package.json; then
762+
echo "::error title=Missing eas script::package.json does not include an \"eas\" script. Commit hash: $(git rev-parse HEAD)." >&2
763+
exit 1
764+
fi
765+
766+
echo "ℹ️ Available yarn scripts containing eas:"
767+
yarn run --json | grep '"name":"eas"' || true
669768

670-
echo "ℹ️ Available yarn scripts containing eas:"
671-
yarn run --json | grep '"name":"eas"' || true
769+
# Run platforms based on OTA_PUSH_PLATFORM environment variable (default: all)
770+
# Run sequentially to avoid LavaMoat lockdown serializer conflicts
771+
# when bundling multiple platforms simultaneously
772+
OTA_PUSH_PLATFORM="${OTA_PUSH_PLATFORM:-all}"
773+
774+
# Track exit codes to ensure failures propagate
775+
local ios_exit_code=0
776+
local android_exit_code=0
777+
778+
if [ "$OTA_PUSH_PLATFORM" = "all" ] || [ "$OTA_PUSH_PLATFORM" = "ios" ]; then
779+
echo "📱 Publishing iOS update..."
780+
yarn run eas update \
781+
--platform ios \
782+
--channel "${EXPO_CHANNEL}" \
783+
--private-key-path "./keys/private-key.pem" \
784+
--message "${UPDATE_MESSAGE}" \
785+
--non-interactive
786+
ios_exit_code=$?
787+
788+
if [ $ios_exit_code -ne 0 ]; then
789+
echo "::error title=iOS update failed::iOS EAS update command failed with exit code ${ios_exit_code}" >&2
790+
fi
791+
fi
672792

793+
if [ "$OTA_PUSH_PLATFORM" = "all" ] || [ "$OTA_PUSH_PLATFORM" = "android" ]; then
794+
echo "🤖 Publishing Android update..."
673795
yarn run eas update \
796+
--platform android \
674797
--channel "${EXPO_CHANNEL}" \
675798
--private-key-path "./keys/private-key.pem" \
676799
--message "${UPDATE_MESSAGE}" \
677800
--non-interactive
801+
android_exit_code=$?
802+
803+
if [ $android_exit_code -ne 0 ]; then
804+
echo "::error title=Android update failed::Android EAS update command failed with exit code ${android_exit_code}" >&2
805+
fi
806+
fi
807+
808+
# Check for failures and exit accordingly
809+
if [ $ios_exit_code -ne 0 ] || [ $android_exit_code -ne 0 ]; then
810+
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
811+
exit 1
812+
fi
813+
814+
if [ "$OTA_PUSH_PLATFORM" = "all" ]; then
815+
echo "✅ EAS updates published for both platforms"
816+
else
817+
echo "✅ EAS update published for ${OTA_PUSH_PLATFORM}"
818+
fi
678819
}
679820

680821
buildAndroid() {
@@ -778,7 +919,8 @@ checkAuthToken() {
778919
local propertiesFileName="$1"
779920

780921
if [ -n "${MM_SENTRY_AUTH_TOKEN}" ]; then
781-
sed -i'' -e "s/auth.token.*/auth.token=${MM_SENTRY_AUTH_TOKEN}/" "./${propertiesFileName}";
922+
# Use | as delimiter to avoid conflicts with special characters in auth token (e.g., /)
923+
sed -i'' -e "s|auth.token.*|auth.token=${MM_SENTRY_AUTH_TOKEN}|" "./${propertiesFileName}";
782924
elif ! grep -qE '^auth.token=[[:alnum:]]+$' "./${propertiesFileName}"; then
783925
if [ "$METAMASK_ENVIRONMENT" == "production" ]; then
784926
printError "Missing auth token in '${propertiesFileName}'; add the token, or set it as MM_SENTRY_AUTH_TOKEN"
@@ -791,7 +933,8 @@ checkAuthToken() {
791933
if [ ! -e "./${propertiesFileName}" ]; then
792934
if [ -n "${MM_SENTRY_AUTH_TOKEN}" ]; then
793935
cp "./${propertiesFileName}.example" "./${propertiesFileName}"
794-
sed -i'' -e "s/auth.token.*/auth.token=${MM_SENTRY_AUTH_TOKEN}/" "./${propertiesFileName}";
936+
# Use | as delimiter to avoid conflicts with special characters in auth token (e.g., /)
937+
sed -i'' -e "s|auth.token.*|auth.token=${MM_SENTRY_AUTH_TOKEN}|" "./${propertiesFileName}";
795938
else
796939
if [ "$METAMASK_ENVIRONMENT" == "production" ]; then
797940
printError "Missing '${propertiesFileName}' file (see '${propertiesFileName}.example' or set MM_SENTRY_AUTH_TOKEN to generate)"

0 commit comments

Comments
 (0)