Skip to content

Commit 7ff0633

Browse files
committed
download artifact for expo dev builds from github action expo dev build
1 parent 4797726 commit 7ff0633

5 files changed

Lines changed: 517 additions & 502 deletions

File tree

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@
3838
"start:android:flask": "./scripts/build.sh android flask dev --local",
3939
"start:api-logging-server": "node -r @babel/register scripts/start-api-logging-server.js",
4040
"ramps:debug-dashboard": "bash scripts/money-movement/debug-dashboard/run.sh",
41-
"install:ios:runway": "./scripts/install-ios-runway-app.sh",
42-
"install:android:runway": "./scripts/install-android-runway-app.sh",
41+
"install:ios:gh": "./scripts/install-ios-gh-expo-dev.sh",
42+
"install:android:gh": "./scripts/install-android-gh-expo-dev.sh",
4343
"build:announce": "node ./scripts/metamask-bot-build-announce-bitrise.js",
4444
"build:android:main:prod": "./scripts/build.sh android main production",
4545
"build:android:main:beta": "./scripts/build.sh android main beta",
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
#!/bin/bash
2+
3+
set -euo pipefail
4+
5+
# Install the latest expo-dev-build Android APK from GitHub Actions artifacts.
6+
# Replaces the Runway-bucket flow with a direct `gh run download` from
7+
# .github/workflows/expo-dev-build.yml.
8+
#
9+
# The Android artifact uploads a single APK (no zipping required), so we just
10+
# copy it to a stable path under build/.
11+
#
12+
# Verify step by step:
13+
# Step 1 (Resolve): Run with --skipInstall and watch the resolved run id / artifact size.
14+
# Step 2 (Download): Inspect build/gh-expo-dev-build/android/*.apk and build/app-prod-debug.apk.
15+
# Step 3 (Install): Run with --skip-download to install build/app-prod-debug.apk on a booted emulator.
16+
17+
RED='\033[0;31m'
18+
GREEN='\033[0;32m'
19+
YELLOW='\033[1;33m'
20+
BLUE='\033[0;34m'
21+
NC='\033[0m'
22+
23+
REPO="MetaMask/metamask-mobile"
24+
WORKFLOW="expo-dev-build.yml"
25+
ARTIFACT_NAME="android-apk-main-dev-expo"
26+
PACKAGE_ID="io.metamask"
27+
28+
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
29+
readonly REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
30+
readonly BUILD_DIR="$REPO_ROOT/build"
31+
readonly DOWNLOAD_DIR="$BUILD_DIR/gh-expo-dev-build/android"
32+
readonly STABLE_APK="$BUILD_DIR/app-prod-debug.apk"
33+
34+
if [[ "$(pwd)" != "$REPO_ROOT" ]]; then
35+
echo -e "${RED}❌ This script must be run from the repository root${NC}"
36+
echo -e "${YELLOW}Current directory: $(pwd)${NC}"
37+
echo -e "${YELLOW}Expected directory: $REPO_ROOT${NC}"
38+
echo -e "${YELLOW}Run: cd $REPO_ROOT && yarn install:android:gh-expo-dev${NC}"
39+
exit 1
40+
fi
41+
42+
UNINSTALL=false
43+
SKIP_DOWNLOAD=false
44+
SKIP_INSTALL=false
45+
BRANCH="main"
46+
RUN_ID=""
47+
48+
DOWNLOAD_SUCCESS=false
49+
50+
cleanup() {
51+
if [[ "$DOWNLOAD_SUCCESS" == true && -d "$DOWNLOAD_DIR" && "$DOWNLOAD_DIR" == "$BUILD_DIR"/* ]]; then
52+
rm -rf "$DOWNLOAD_DIR"
53+
fi
54+
}
55+
trap cleanup EXIT
56+
57+
while [[ $# -gt 0 ]]; do
58+
case $1 in
59+
--uninstall)
60+
UNINSTALL=true
61+
shift
62+
;;
63+
--skip-download)
64+
SKIP_DOWNLOAD=true
65+
shift
66+
;;
67+
--skipInstall)
68+
SKIP_INSTALL=true
69+
shift
70+
;;
71+
--branch)
72+
if [[ -z "${2:-}" ]]; then
73+
echo -e "${RED}❌ --branch requires a value${NC}"
74+
exit 1
75+
fi
76+
BRANCH="$2"
77+
shift 2
78+
;;
79+
--run)
80+
if [[ -z "${2:-}" ]]; then
81+
echo -e "${RED}❌ --run requires a value${NC}"
82+
exit 1
83+
fi
84+
RUN_ID="$2"
85+
shift 2
86+
;;
87+
*)
88+
echo -e "${RED}Unknown option: $1${NC}"
89+
echo "Usage: $0 [--skip-download] [--skipInstall] [--uninstall] [--branch <name>] [--run <id>]"
90+
exit 1
91+
;;
92+
esac
93+
done
94+
95+
require_cmd() {
96+
if ! command -v "$1" &> /dev/null; then
97+
echo -e "${RED}$1 is required but not installed${NC}"
98+
echo -e "${YELLOW}$2${NC}"
99+
exit 1
100+
fi
101+
}
102+
103+
download_latest_app() {
104+
echo -e "${BLUE}━━━ Step 1: Resolving expo-dev-build run ━━━${NC}"
105+
106+
require_cmd gh "Install with: brew install gh (then run: gh auth login)"
107+
require_cmd jq "Install with: brew install jq"
108+
109+
if ! gh auth status &> /dev/null; then
110+
echo -e "${RED}❌ gh CLI is not authenticated${NC}"
111+
echo -e "${YELLOW}Run: gh auth login${NC}"
112+
exit 1
113+
fi
114+
115+
if [[ -n "$RUN_ID" ]]; then
116+
if [[ ! "$RUN_ID" =~ ^[0-9]+$ ]]; then
117+
echo -e "${RED}❌ Invalid run id: $RUN_ID (must be numeric)${NC}"
118+
exit 1
119+
fi
120+
echo -e "${GREEN}✓ Using explicit run id: $RUN_ID${NC}"
121+
else
122+
echo -e "${BLUE}Looking up latest successful run on '$BRANCH' for $WORKFLOW...${NC}"
123+
RUN_ID=$(gh run list \
124+
--repo "$REPO" \
125+
--workflow "$WORKFLOW" \
126+
--branch "$BRANCH" \
127+
--status success \
128+
--limit 1 \
129+
--json databaseId \
130+
--jq '.[0].databaseId' || true)
131+
132+
if [[ -z "$RUN_ID" || "$RUN_ID" == "null" ]]; then
133+
echo -e "${RED}❌ No successful '$WORKFLOW' runs found on branch '$BRANCH'${NC}"
134+
echo -e "${YELLOW}Trigger one via the workflow_dispatch UI or push to '$BRANCH', or pass --run <id>.${NC}"
135+
exit 1
136+
fi
137+
echo -e "${GREEN}✓ Latest successful run id: $RUN_ID${NC}"
138+
fi
139+
140+
echo -e "${BLUE}━━━ Step 2: Looking up artifact '$ARTIFACT_NAME' ━━━${NC}"
141+
local artifact_info
142+
artifact_info=$(gh api "repos/$REPO/actions/runs/$RUN_ID/artifacts" --paginate \
143+
--jq ".artifacts[] | select(.name==\"$ARTIFACT_NAME\")" || true)
144+
145+
if [[ -z "$artifact_info" ]]; then
146+
echo -e "${RED}❌ Artifact '$ARTIFACT_NAME' not found in run $RUN_ID${NC}"
147+
echo -e "${YELLOW}Inspect: https://github.com/$REPO/actions/runs/$RUN_ID${NC}"
148+
exit 1
149+
fi
150+
151+
local expired
152+
expired=$(echo "$artifact_info" | jq -r '.expired')
153+
if [[ "$expired" == "true" ]]; then
154+
echo -e "${RED}❌ Artifact '$ARTIFACT_NAME' has expired for run $RUN_ID${NC}"
155+
echo -e "${YELLOW}Re-run the workflow or pass --run <id> for a newer run.${NC}"
156+
exit 1
157+
fi
158+
159+
local artifact_size_bytes
160+
artifact_size_bytes=$(echo "$artifact_info" | jq -r '.size_in_bytes')
161+
local artifact_size_mb=$((artifact_size_bytes / 1024 / 1024))
162+
echo -e "${GREEN}✓ Artifact size: ${artifact_size_mb}MB${NC}"
163+
echo -e "${BLUE}🔗 https://github.com/$REPO/actions/runs/$RUN_ID${NC}"
164+
165+
echo -e "${BLUE}━━━ Step 3: Downloading $ARTIFACT_NAME ━━━${NC}"
166+
rm -rf "$DOWNLOAD_DIR"
167+
mkdir -p "$DOWNLOAD_DIR"
168+
169+
if ! gh run download "$RUN_ID" --repo "$REPO" --name "$ARTIFACT_NAME" --dir "$DOWNLOAD_DIR"; then
170+
echo -e "${RED}❌ Failed to download artifact${NC}"
171+
exit 1
172+
fi
173+
174+
# The Android artifact is uploaded as a single .apk (no zip), so we expect exactly one file.
175+
local apk_count
176+
apk_count=$(find "$DOWNLOAD_DIR" -maxdepth 1 -type f -name "*.apk" 2>/dev/null | wc -l | tr -d ' ')
177+
if [[ "$apk_count" != "1" ]]; then
178+
echo -e "${RED}❌ Expected exactly one .apk in $DOWNLOAD_DIR, found $apk_count${NC}"
179+
find "$DOWNLOAD_DIR" -maxdepth 2 -type f
180+
exit 1
181+
fi
182+
183+
local downloaded_apk
184+
downloaded_apk=$(find "$DOWNLOAD_DIR" -maxdepth 1 -type f -name "*.apk" | head -1)
185+
echo -e "${GREEN}✓ Downloaded: $downloaded_apk${NC}"
186+
187+
echo -e "${BLUE}━━━ Step 4: Copying to stable path ━━━${NC}"
188+
cp -f "$downloaded_apk" "$STABLE_APK"
189+
echo -e "${GREEN}✓ Copied to $STABLE_APK${NC}"
190+
DOWNLOAD_SUCCESS=true
191+
}
192+
193+
if [[ "$SKIP_DOWNLOAD" == false ]]; then
194+
download_latest_app
195+
fi
196+
197+
if [[ "$SKIP_INSTALL" == true ]]; then
198+
echo -e "${GREEN}✓ Download complete. Installation skipped (--skipInstall). APK at $STABLE_APK${NC}"
199+
exit 0
200+
fi
201+
202+
echo -e "${BLUE}━━━ Step 5: Installing on emulator/device ━━━${NC}"
203+
204+
require_cmd adb "Ensure Android SDK platform-tools are on PATH."
205+
206+
BOOTED_DEVICES=$(adb devices | grep -E '^[^ ]+\s+device$' | awk '{print $1}' || true)
207+
if [[ -z "$BOOTED_DEVICES" ]]; then
208+
echo -e "${RED}❌ No emulator/device in 'device' state.${NC}"
209+
echo -e "${YELLOW}Start an Android emulator and run: adb devices${NC}"
210+
exit 1
211+
fi
212+
213+
echo -e "${GREEN}✓ Device(s):${NC}"
214+
echo "$BOOTED_DEVICES" | while read -r dev; do echo " $dev"; done
215+
216+
if [[ ! -f "$STABLE_APK" ]]; then
217+
echo -e "${RED}❌ No APK found at $STABLE_APK${NC}"
218+
echo -e "${YELLOW}Run without --skip-download to download first${NC}"
219+
exit 1
220+
fi
221+
222+
if [[ "$UNINSTALL" == true ]]; then
223+
echo -e "${YELLOW}Uninstalling existing app...${NC}"
224+
adb uninstall "$PACKAGE_ID" 2>/dev/null || true
225+
echo -e "${GREEN}✓ Uninstall complete${NC}"
226+
fi
227+
228+
echo -e "${BLUE}Installing APK on device...${NC}"
229+
set +e
230+
INSTALL_OUTPUT=$(adb install -r "$STABLE_APK" 2>&1)
231+
INSTALL_EXIT=$?
232+
set -e
233+
234+
echo "$INSTALL_OUTPUT"
235+
if [[ $INSTALL_EXIT -ne 0 ]]; then
236+
if [[ "$INSTALL_OUTPUT" == *"INSTALL_FAILED_UPDATE_INCOMPATIBLE"* ]]; then
237+
echo -e "${YELLOW}Existing app was signed with a different key. Uninstall it first:${NC}"
238+
echo -e " yarn install:android:gh-expo-dev --skip-download --uninstall"
239+
fi
240+
exit $INSTALL_EXIT
241+
fi
242+
243+
echo -e "${GREEN}✓ Successfully installed app on emulator/device.${NC}"

0 commit comments

Comments
 (0)