Skip to content

Commit

Permalink
[cli] Improve Snack support for older SDKs
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrieldonadel committed Dec 22, 2023
1 parent e9fc2ad commit 2233b0f
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 40 deletions.
42 changes: 37 additions & 5 deletions apps/cli/src/commands/LaunchSnack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,7 @@ export async function launchSnackAsync(
snackURL: string,
{ platform, deviceId }: launchSnackAsyncOptions
) {
// Attempts to extract sdk version from url. Match "sdk.", followed by one or more digits and dots, before a hyphen
// e.g. exp://exp.host/@snack/sdk.48.0.0-ChmcDz6VUr
const regex = /sdk\.([\d.]+)(?=-)/;
const match = snackURL.match(regex);
const version = match ? match[1] : undefined;
const version = await getSDKVersionForSnack(snackURL);

if (platform === 'android') {
await launchSnackOnAndroidAsync(snackURL, deviceId, version);
Expand Down Expand Up @@ -48,3 +44,39 @@ async function launchSnackOnIOSAsync(snackURL: string, deviceId: string, version
await AppleDevice.ensureExpoClientInstalledAsync(deviceId);
await AppleDevice.openSnackURLAsync(deviceId, snackURL);
}

export async function getSDKVersionForSnack(snackURL: string): Promise<string | undefined> {
// Attempts to extract sdk version from url. Match "sdk.", followed by one or more digits and dots, before a hyphen
// e.g. exp://exp.host/@snack/sdk.48.0.0-ChmcDz6VUr
const versionRegex = /sdk\.([\d.]+)(?=-)/;
const match = snackURL.match(versionRegex);
if (match?.[1]) {
return match[1];
}

// For snacks saved to accounts the ID can be `@snack/<hashId>` or `@<username>/<hashId>`.
const snackIdentifierRegex = /(@[^\/]+\/[^\/+]+)/;
const snackIdentifier = snackURL.match(snackIdentifierRegex)?.[0];
if (!snackIdentifier) {
return;
}

const snackId = snackIdentifier.startsWith('@snack/')
? snackIdentifier.substring('@snack/'.length)
: snackIdentifier;

// Get the SDK version for a specific snack from the Snack API.
try {
const response = await fetch(`https://exp.host/--/api/v2/snack/${snackId}`, {
method: 'GET',
headers: {
'Snack-Api-Version': '3.0.0',
},
});
const { sdkVersion } = await response.json();

return sdkVersion;
} catch (err) {
console.error(`Failed fetch snack with identifier: ${snackId}`, err);
}
}
56 changes: 48 additions & 8 deletions packages/eas-shared/src/run/android/emulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,19 @@ export async function installAppAsync(
Log.succeed('Successfully installed your app!');
}

export async function uninstallAppAsync(
emulator: AndroidConnectedDevice | AndroidEmulator,
bundleId: string
): Promise<void> {
Log.newLine();
Log.log(`Uninstalling ${bundleId}...`);

assert(emulator.pid);
await adbAsync('-s', emulator.pid, 'uninstall', bundleId);

Log.succeed(`Successfully uninstalled ${bundleId}!`);
}

export async function startAppAsync(
emulator: AndroidConnectedDevice | AndroidEmulator,
packageName: string,
Expand Down Expand Up @@ -296,16 +309,34 @@ async function expoVersionOnEmulatorAsync(pid: string): Promise<string | null> {
return regexMatch[1];
}

async function doesExpoClientNeedUpdatedAsync(pid: string, sdkVersion?: string): Promise<boolean> {
/**
* Checks Expo Go compatibility with an SDK version by verifying
* the latest Expo Go version released for that SDK. On Android
* we can't directly check supported SDKs of the installed app
* like we do on iOS.
*/
async function checkExpoClientCompatibilityAsync(
pid: string,
sdkVersion?: string
): Promise<{ compatible: boolean; requiresDowngrade?: boolean }> {
const versions = await Versions.versionsAsync();
const clientForSdk = await getClientForSDK(sdkVersion);
const latestVersionForSdk = clientForSdk?.version ?? versions.androidClientVersion;

const installedVersion = await expoVersionOnEmulatorAsync(pid);
if (installedVersion && semver.lt(installedVersion, latestVersionForSdk)) {
return true;
if (!installedVersion) {
return { compatible: true };
}
return false;

const isCompatible = semver.satisfies(
installedVersion,
`${semver.major(latestVersionForSdk)}.${semver.minor(latestVersionForSdk)}.x`
);

return {
compatible: isCompatible,
requiresDowngrade: semver.gt(installedVersion, latestVersionForSdk),
};
}

export async function installExpoOnEmulatorAsync({
Expand Down Expand Up @@ -346,12 +377,21 @@ export async function installExpoOnEmulatorAsync({
}

export async function ensureExpoClientInstalledAsync(pid: string, sdkVersion?: string) {
let isInstalled = await isExpoClientInstalledOnEmulatorAsync(pid);
if (isInstalled && (await doesExpoClientNeedUpdatedAsync(pid, sdkVersion))) {
isInstalled = false;
let isCompatible = true;
let requiresDowngrade = false;

const isInstalled = await isExpoClientInstalledOnEmulatorAsync(pid);
if (isInstalled) {
const compatibility = await checkExpoClientCompatibilityAsync(pid, sdkVersion);
isCompatible = compatibility.compatible;
requiresDowngrade = compatibility.requiresDowngrade ?? false;
}

if (!isInstalled) {
if (!isInstalled || !isCompatible) {
if (requiresDowngrade) {
await uninstallAppAsync({ pid } as AndroidEmulator, EXPO_GO_BUNDLE_IDENTIFIER);
}

const androidClient = await getClientForSDK(sdkVersion);
const versions = await Versions.versionsAsync();
const url = androidClient?.url ?? versions.androidUrl;
Expand Down
50 changes: 23 additions & 27 deletions packages/eas-shared/src/run/ios/simulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import * as osascript from '@expo/osascript';
import spawnAsync from '@expo/spawn-async';
import fs from 'fs-extra';
import path from 'path';
import semver from 'semver';
import { IosSimulator } from 'common-types/build/devices';

import * as CoreSimulator from './CoreSimulator';
Expand All @@ -15,6 +14,7 @@ import { delayAsync } from '../../utils/delayAsync';
import { sleepAsync } from '../../utils/promise';
import * as Versions from '../../versions';
import { EXPO_GO_BUNDLE_IDENTIFIER } from './constants';
import { parseBinaryPlistAsync } from '../../utils/parseBinaryPlistAsync';

const INSTALL_WARNING_TIMEOUT = 60 * 1000;

Expand Down Expand Up @@ -270,27 +270,26 @@ async function getClientForSDK(sdkVersionString?: string) {
};
}

export async function expoVersionOnSimulatorAsync(udid: string): Promise<string | null> {
const localPath = await getContainerPathAsync({
udid,
bundleIdentifier: EXPO_GO_BUNDLE_IDENTIFIER,
});
if (!localPath) {
return null;
}
export async function expoSDKSupportedVersionsOnSimulatorAsync(
udid: string
): Promise<string[] | null> {
try {
const localPath = await getContainerPathAsync({
udid,
bundleIdentifier: EXPO_GO_BUNDLE_IDENTIFIER,
});
if (!localPath) {
return null;
}

const regex = /Exponent-([0-9.]+).*\.app$/;
const regexMatch = regex.exec(localPath);
if (!regexMatch) {
return null;
}
const builtInfoPlistPath = path.join(localPath, 'EXSDKVersions.plist');
const { sdkVersions }: { sdkVersions: string[] } =
await parseBinaryPlistAsync(builtInfoPlistPath);

let matched = regexMatch[1];
// If the value is matched like 1.0.0. then remove the trailing dot.
if (matched.endsWith('.')) {
matched = matched.substr(0, matched.length - 1);
return sdkVersions;
} catch (error) {
return null;
}
return matched;
}

function simulatorCacheDirectory() {
Expand Down Expand Up @@ -377,15 +376,12 @@ export async function doesExpoClientNeedUpdatedAsync(
udid: string,
sdkVersion?: string
): Promise<boolean> {
const versions = await Versions.versionsAsync();
const clientForSdk = await getClientForSDK(sdkVersion);
const latestVersionForSdk = clientForSdk?.version ?? versions.iosVersion;

const installedVersion = await expoVersionOnSimulatorAsync(udid);
if (installedVersion && semver.lt(installedVersion, latestVersionForSdk)) {
return true;
const supportedSDKs = await expoSDKSupportedVersionsOnSimulatorAsync(udid);
if (!sdkVersion || !supportedSDKs) {
return false;
}
return false;

return !supportedSDKs?.includes(sdkVersion);
}

export async function waitForExpoClientInstalledOnSimulatorAsync(udid: string): Promise<boolean> {
Expand Down

0 comments on commit 2233b0f

Please sign in to comment.