From 58082970955e98b37143e20905d300abff70348c Mon Sep 17 00:00:00 2001 From: EzioLi Date: Mon, 30 Oct 2023 16:26:21 +0800 Subject: [PATCH 1/5] Install application --- src/common/downloadHelper.ts | 1 + src/common/installHelper.ts | 63 +++++++++++++++++++ .../commands/installExpoGoApplication.ts | 35 ++++++----- src/extension/settingsHelper.ts | 3 +- 4 files changed, 86 insertions(+), 16 deletions(-) create mode 100644 src/common/installHelper.ts diff --git a/src/common/downloadHelper.ts b/src/common/downloadHelper.ts index 8c0dba215..be6c9c4dc 100644 --- a/src/common/downloadHelper.ts +++ b/src/common/downloadHelper.ts @@ -1,5 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for details. + import * as fs from "fs"; import * as https from "https"; import * as vscode from "vscode"; diff --git a/src/common/installHelper.ts b/src/common/installHelper.ts new file mode 100644 index 000000000..bf1eb449a --- /dev/null +++ b/src/common/installHelper.ts @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for details. + +// import * as fs from "fs"; +// import * as https from "https"; +// import * as vscode from "vscode"; +import { AdbHelper } from "../extension/android/adb"; +import { AppLauncher } from "../extension/appLauncher"; +import { OutputChannelLogger } from "../extension/log/OutputChannelLogger"; +import { ChildProcess } from "./node/childProcess"; + +export async function installAndroidApplication(project: AppLauncher, appPath: string) { + const logger = OutputChannelLogger.getMainChannel(); + const childProcess: ChildProcess = new ChildProcess(); + const adbHelper = new AdbHelper( + project.getPackager().getProjectPath(), + project.getOrUpdateNodeModulesRoot(), + ); + + const targets = await adbHelper.getOnlineTargets(); + if (targets.length == 0) { + throw new Error("No online target found, please check your emulator status."); + } else if (targets.length > 1) { + logger.logStream( + `Found ${targets.length} online emulators, installing application on ${targets[0].id}. \n`, + ); + } else { + logger.logStream(`Installing application on ${targets[0].id}. \n`); + try { + await childProcess.execToString(`adb install ${appPath}`); + } catch { + throw new Error(`Failed to install application: ${appPath}.`); + } + logger.logStream(`Install Android application is completed. \n`); + } +} + +export async function installiOSApplication(project: AppLauncher, appPath: string) { + const logger = OutputChannelLogger.getMainChannel(); + const childProcess: ChildProcess = new ChildProcess(); + const getBootedSimulatorCommand = "xcrun simctl list | awk -F'[()]' '/(Booted)/ { print $2 }'"; + + const targetResult = await childProcess.execToString(getBootedSimulatorCommand); + const targets = targetResult.split("\n"); + console.log(targets); + + if (targets.length == 1) { + throw new Error("No booted iOS simulator found, please check your simulator status."); + } else if (targets.length > 2) { + logger.logStream( + `Found ${targets.length - 1} booted simulators, installing application on ${ + targets[0] + }. \n`, + ); + } else { + logger.logStream(`Installing application on ${targets[0]}. \n`); + try { + } catch { + throw new Error(`Failed to install application: ${appPath}.`); + } + logger.logStream(`Install Android application is completed. \n`); + } +} diff --git a/src/extension/commands/installExpoGoApplication.ts b/src/extension/commands/installExpoGoApplication.ts index 3db202caa..96e4113ef 100644 --- a/src/extension/commands/installExpoGoApplication.ts +++ b/src/extension/commands/installExpoGoApplication.ts @@ -11,6 +11,7 @@ import { ErrorHelper } from "../../common/error/errorHelper"; import { InternalErrorCode } from "../../common/error/internalErrorCode"; import { downloadExpoGo } from "../../common/downloadHelper"; import { getTimestamp } from "../../common/utils"; +import { installiOSApplication } from "../../common/installHelper"; import { Command } from "./util/command"; nls.config({ @@ -48,20 +49,26 @@ export class InstallExpoGoApplication extends Command { localize("DownloadAndroidExpoGo", "\nDownloading Expo Go for Android. \n"), ); - const targetUrl = expoUrlInfo.androidClientUrl; - const androidClientVersion = expoUrlInfo.androidClientVersion as string; - try { - await downloadExpoGo( - targetUrl, - `${this.project - .getPackager() - .getProjectPath()}/expogo_${androidClientVersion}_${getTimestamp()}.apk`, - ); - } catch { - throw new Error( - localize("FailedToDownloadExpoGo", "Failed to download Expo Go."), - ); - } + // const targetUrl = expoUrlInfo.androidClientUrl; + // const androidClientVersion = expoUrlInfo.androidClientVersion as string; + // try { + // await downloadExpoGo( + // targetUrl, + // `${this.project + // .getPackager() + // .getProjectPath()}/expogo_${androidClientVersion}_${getTimestamp()}.apk`, + // ); + // } catch { + // throw new Error( + // localize("FailedToDownloadExpoGo", "Failed to download Expo Go."), + // ); + // } + + // await installAndroidApplication(this.project, "/Users/ezio/Desktop/expo_49/expogo_2.29.8_20231030102618.apk"); + await installiOSApplication( + this.project, + "/Users/ezio/Desktop/expo_49/expogo_2.29.6_20231030103215.tar.gz", + ); } else if (item == "iOS") { if (os.platform() != "darwin") { logger.warning( diff --git a/src/extension/settingsHelper.ts b/src/extension/settingsHelper.ts index da7d7c4c8..6a1043221 100644 --- a/src/extension/settingsHelper.ts +++ b/src/extension/settingsHelper.ts @@ -216,9 +216,8 @@ export class SettingsHelper { if (workspaceSettingsContent.settings) { const exclude = workspaceSettingsContent.settings["react-native.workspace.exclude"]; return exclude ? exclude : []; - } else { - return []; } + return []; } return []; } From 06f1b657ad73c88cbff53845e900ea248df9cf51 Mon Sep 17 00:00:00 2001 From: EzioLi Date: Tue, 31 Oct 2023 16:13:36 +0800 Subject: [PATCH 2/5] Update --- src/common/downloadHelper.ts | 2 +- src/common/installHelper.ts | 23 +++- .../commands/installExpoGoApplication.ts | 103 ++++++++++++------ 3 files changed, 93 insertions(+), 35 deletions(-) diff --git a/src/common/downloadHelper.ts b/src/common/downloadHelper.ts index be6c9c4dc..76856cff7 100644 --- a/src/common/downloadHelper.ts +++ b/src/common/downloadHelper.ts @@ -37,7 +37,7 @@ export async function downloadFile(url: any, targetFile: any) { file.on("finish", async () => { file.close(); - logger.logStream(`Download Expo Go Completed: ${targetFile as string}`); + logger.logStream(`Download Expo Go Completed: ${targetFile as string} \n`); void vscode.window.showInformationMessage("Download Expo Go Completed."); }); diff --git a/src/common/installHelper.ts b/src/common/installHelper.ts index bf1eb449a..d95358487 100644 --- a/src/common/installHelper.ts +++ b/src/common/installHelper.ts @@ -42,8 +42,21 @@ export async function installiOSApplication(project: AppLauncher, appPath: strin const targetResult = await childProcess.execToString(getBootedSimulatorCommand); const targets = targetResult.split("\n"); - console.log(targets); + try { + await childProcess.execToString( + `mkdir ${project.getPackager().getProjectPath()}/expoApp.app`, + ); + await childProcess.execToString( + `tar -xf ${appPath} -C ${project.getPackager().getProjectPath()}/expoApp.app`, + ); + } catch (e) { + throw e; + } + + const installCommand = `xcrun simctl install ${targets[0]} ${project + .getPackager() + .getProjectPath()}/expoApp.app`; if (targets.length == 1) { throw new Error("No booted iOS simulator found, please check your simulator status."); } else if (targets.length > 2) { @@ -52,12 +65,18 @@ export async function installiOSApplication(project: AppLauncher, appPath: strin targets[0] }. \n`, ); + try { + await childProcess.execToString(installCommand); + } catch { + throw new Error(`Failed to install application: ${appPath}.`); + } } else { logger.logStream(`Installing application on ${targets[0]}. \n`); try { + await childProcess.execToString(installCommand); } catch { throw new Error(`Failed to install application: ${appPath}.`); } - logger.logStream(`Install Android application is completed. \n`); + logger.logStream(`Install iOS application is completed. \n`); } } diff --git a/src/extension/commands/installExpoGoApplication.ts b/src/extension/commands/installExpoGoApplication.ts index 96e4113ef..af5e8e99f 100644 --- a/src/extension/commands/installExpoGoApplication.ts +++ b/src/extension/commands/installExpoGoApplication.ts @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for details. import * as assert from "assert"; +import * as fs from "fs"; import * as https from "https"; import * as os from "os"; import * as vscode from "vscode"; @@ -10,8 +11,7 @@ import { OutputChannelLogger } from "../log/OutputChannelLogger"; import { ErrorHelper } from "../../common/error/errorHelper"; import { InternalErrorCode } from "../../common/error/internalErrorCode"; import { downloadExpoGo } from "../../common/downloadHelper"; -import { getTimestamp } from "../../common/utils"; -import { installiOSApplication } from "../../common/installHelper"; +import { installAndroidApplication, installiOSApplication } from "../../common/installHelper"; import { Command } from "./util/command"; nls.config({ @@ -31,6 +31,9 @@ export class InstallExpoGoApplication extends Command { const item = await vscode.window.showQuickPick(["Android", "iOS"], { placeHolder: "Select type for mobile OS", }); + const installItem = await vscode.window.showQuickPick(["Manual", "Auto"], { + placeHolder: "How to install application", + }); const expoHelper = this.project.getExponentHelper(); logger.info(localize("CheckExpoEnvironment", "Checking Expo project environment.")); const isExpo = await expoHelper.isExpoManagedApp(true); @@ -49,26 +52,38 @@ export class InstallExpoGoApplication extends Command { localize("DownloadAndroidExpoGo", "\nDownloading Expo Go for Android. \n"), ); - // const targetUrl = expoUrlInfo.androidClientUrl; - // const androidClientVersion = expoUrlInfo.androidClientVersion as string; - // try { - // await downloadExpoGo( - // targetUrl, - // `${this.project - // .getPackager() - // .getProjectPath()}/expogo_${androidClientVersion}_${getTimestamp()}.apk`, - // ); - // } catch { - // throw new Error( - // localize("FailedToDownloadExpoGo", "Failed to download Expo Go."), - // ); - // } - - // await installAndroidApplication(this.project, "/Users/ezio/Desktop/expo_49/expogo_2.29.8_20231030102618.apk"); - await installiOSApplication( - this.project, - "/Users/ezio/Desktop/expo_49/expogo_2.29.6_20231030103215.tar.gz", - ); + const targetUrl = expoUrlInfo.androidClientUrl; + const androidClientVersion = expoUrlInfo.androidClientVersion as string; + const fileName = `${this.project + .getPackager() + .getProjectPath()}/expogo_${androidClientVersion}.apk`; + + if (!fs.existsSync(fileName)) { + try { + await downloadExpoGo(targetUrl, fileName); + } catch { + throw new Error( + localize("FailedToDownloadExpoGo", "Failed to download Expo Go."), + ); + } + } + + if (installItem == "Auto") { + try { + await installAndroidApplication(this.project, fileName); + } catch { + throw new Error( + localize("FailedToInstallExpoGo", "Failed to install Expo Go."), + ); + } + } else { + logger.logStream( + localize( + "ManualInstall", + "Please manually install Expo Go from project root path. \n", + ), + ); + } } else if (item == "iOS") { if (os.platform() != "darwin") { logger.warning( @@ -85,16 +100,40 @@ export class InstallExpoGoApplication extends Command { const targetUrl = expoUrlInfo.iosClientUrl; const iOSClientVersion = expoUrlInfo.iosClientVersion as string; - try { - await downloadExpoGo( - targetUrl, - `${this.project - .getPackager() - .getProjectPath()}/expogo_${iOSClientVersion}_${getTimestamp()}.tar.gz`, - ); - } catch { - throw new Error( - localize("FailedToDownloadExpoGo", "Failed to download Expo Go."), + + const tarFile = `${this.project + .getPackager() + .getProjectPath()}/expogo_${iOSClientVersion}.tar.gz`; + + if (!fs.existsSync(tarFile)) { + try { + await downloadExpoGo( + targetUrl, + `${this.project + .getPackager() + .getProjectPath()}/expogo_${iOSClientVersion}.tar.gz`, + ); + } catch { + throw new Error( + localize("FailedToDownloadExpoGo", "Failed to download Expo Go."), + ); + } + } + + if (installItem == "Auto") { + try { + await installiOSApplication(this.project, tarFile); + } catch { + throw new Error( + localize("FailedToInstallExpoGo", "Failed to install Expo Go."), + ); + } + } else { + logger.logStream( + localize( + "ManualInstall", + "Please manually install Expo Go from project root path. \n", + ), ); } } else { From 20256568b3e40caee16e3e5018b0587468df4171 Mon Sep 17 00:00:00 2001 From: EzioLi Date: Tue, 31 Oct 2023 16:15:07 +0800 Subject: [PATCH 3/5] Update --- src/common/installHelper.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/common/installHelper.ts b/src/common/installHelper.ts index d95358487..8c7c2e2a6 100644 --- a/src/common/installHelper.ts +++ b/src/common/installHelper.ts @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for details. -// import * as fs from "fs"; -// import * as https from "https"; -// import * as vscode from "vscode"; import { AdbHelper } from "../extension/android/adb"; import { AppLauncher } from "../extension/appLauncher"; import { OutputChannelLogger } from "../extension/log/OutputChannelLogger"; From 20186d8035e314bccfb53003de399aeda8632559 Mon Sep 17 00:00:00 2001 From: EzioLi Date: Tue, 31 Oct 2023 16:21:09 +0800 Subject: [PATCH 4/5] Update --- src/common/downloadHelper.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/common/downloadHelper.ts b/src/common/downloadHelper.ts index 76856cff7..f89889f90 100644 --- a/src/common/downloadHelper.ts +++ b/src/common/downloadHelper.ts @@ -42,7 +42,9 @@ export async function downloadFile(url: any, targetFile: any) { }); response.on("end", function () { - console.log("Progress end."); + resolve(() => { + console.log("Progress end."); + }); }); }) .on("error", error => { From 4c05ff4f02a17ca3e98ecdc040911b7aa70d5e94 Mon Sep 17 00:00:00 2001 From: EzioLi Date: Wed, 1 Nov 2023 10:12:41 +0800 Subject: [PATCH 5/5] Update --- src/common/installHelper.ts | 29 ++++++++++++------- src/extension/android/adb.ts | 4 +++ .../commands/installExpoGoApplication.ts | 6 ++-- src/extension/ios/simctl.ts | 25 ++++++++++++++++ 4 files changed, 51 insertions(+), 13 deletions(-) create mode 100644 src/extension/ios/simctl.ts diff --git a/src/common/installHelper.ts b/src/common/installHelper.ts index 8c7c2e2a6..9effcb4b5 100644 --- a/src/common/installHelper.ts +++ b/src/common/installHelper.ts @@ -3,12 +3,12 @@ import { AdbHelper } from "../extension/android/adb"; import { AppLauncher } from "../extension/appLauncher"; +import { SimctrlHelper } from "../extension/ios/simctl"; import { OutputChannelLogger } from "../extension/log/OutputChannelLogger"; import { ChildProcess } from "./node/childProcess"; export async function installAndroidApplication(project: AppLauncher, appPath: string) { const logger = OutputChannelLogger.getMainChannel(); - const childProcess: ChildProcess = new ChildProcess(); const adbHelper = new AdbHelper( project.getPackager().getProjectPath(), project.getOrUpdateNodeModulesRoot(), @@ -21,10 +21,15 @@ export async function installAndroidApplication(project: AppLauncher, appPath: s logger.logStream( `Found ${targets.length} online emulators, installing application on ${targets[0].id}. \n`, ); + try { + await adbHelper.installApplicationToEmulator(appPath); + } catch { + throw new Error(`Failed to install application: ${appPath}.`); + } } else { logger.logStream(`Installing application on ${targets[0].id}. \n`); try { - await childProcess.execToString(`adb install ${appPath}`); + await adbHelper.installApplicationToEmulator(appPath); } catch { throw new Error(`Failed to install application: ${appPath}.`); } @@ -35,15 +40,16 @@ export async function installAndroidApplication(project: AppLauncher, appPath: s export async function installiOSApplication(project: AppLauncher, appPath: string) { const logger = OutputChannelLogger.getMainChannel(); const childProcess: ChildProcess = new ChildProcess(); - const getBootedSimulatorCommand = "xcrun simctl list | awk -F'[()]' '/(Booted)/ { print $2 }'"; - const targetResult = await childProcess.execToString(getBootedSimulatorCommand); - const targets = targetResult.split("\n"); + const targets = await SimctrlHelper.getBootediOSSimulatorList(); try { + // Create dir to iOS app await childProcess.execToString( `mkdir ${project.getPackager().getProjectPath()}/expoApp.app`, ); + + // Unpack .tar.gz file await childProcess.execToString( `tar -xf ${appPath} -C ${project.getPackager().getProjectPath()}/expoApp.app`, ); @@ -51,9 +57,6 @@ export async function installiOSApplication(project: AppLauncher, appPath: strin throw e; } - const installCommand = `xcrun simctl install ${targets[0]} ${project - .getPackager() - .getProjectPath()}/expoApp.app`; if (targets.length == 1) { throw new Error("No booted iOS simulator found, please check your simulator status."); } else if (targets.length > 2) { @@ -63,14 +66,20 @@ export async function installiOSApplication(project: AppLauncher, appPath: strin }. \n`, ); try { - await childProcess.execToString(installCommand); + await SimctrlHelper.installApplicationToSimulator( + targets[0], + `${project.getPackager().getProjectPath()}/expoApp.app`, + ); } catch { throw new Error(`Failed to install application: ${appPath}.`); } } else { logger.logStream(`Installing application on ${targets[0]}. \n`); try { - await childProcess.execToString(installCommand); + await SimctrlHelper.installApplicationToSimulator( + targets[0], + `${project.getPackager().getProjectPath()}/expoApp.app`, + ); } catch { throw new Error(`Failed to install application: ${appPath}.`); } diff --git a/src/extension/android/adb.ts b/src/extension/android/adb.ts index da733b5db..ec39ed663 100644 --- a/src/extension/android/adb.ts +++ b/src/extension/android/adb.ts @@ -256,6 +256,10 @@ export class AdbHelper { return this.executeQuery(deviceId, `shell "${command}"`); } + public installApplicationToEmulator(appPath: string): Promise { + return this.childProcess.execToString(`adb install ${appPath}`); + } + public executeQuery(deviceId: string, command: string): Promise { return this.childProcess.execToString(this.generateCommandForTarget(deviceId, command)); } diff --git a/src/extension/commands/installExpoGoApplication.ts b/src/extension/commands/installExpoGoApplication.ts index af5e8e99f..9c415c025 100644 --- a/src/extension/commands/installExpoGoApplication.ts +++ b/src/extension/commands/installExpoGoApplication.ts @@ -120,7 +120,7 @@ export class InstallExpoGoApplication extends Command { } } - if (installItem == "Auto") { + if (installItem == "Auto" && os.platform() == "darwin") { try { await installiOSApplication(this.project, tarFile); } catch { @@ -131,8 +131,8 @@ export class InstallExpoGoApplication extends Command { } else { logger.logStream( localize( - "ManualInstall", - "Please manually install Expo Go from project root path. \n", + "CannotAutoInstall", + "Cannot auto install Expo Go, selected manual install or target machine is not MacOS. \n", ), ); } diff --git a/src/extension/ios/simctl.ts b/src/extension/ios/simctl.ts new file mode 100644 index 000000000..ad002e953 --- /dev/null +++ b/src/extension/ios/simctl.ts @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for details. + +import { ChildProcess } from "../../common/node/childProcess"; + +const childProcess: ChildProcess = new ChildProcess(); + +export class SimctrlHelper { + public static async getBootediOSSimulatorList(): Promise { + const getBootedSimulatorCommand = + "xcrun simctl list | awk -F'[()]' '/(Booted)/ { print $2 }'"; + + const targetResult = await childProcess.execToString(getBootedSimulatorCommand); + const targetList = targetResult.split("\n"); + return targetList; + } + + public static async installApplicationToSimulator( + targetId: string, + appPath: string, + ): Promise { + const installCommand = `xcrun simctl install ${targetId} ${appPath}`; + await childProcess.execToString(installCommand); + } +}