Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle eas build locally thorugh buildCommand in launch.json #833

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion packages/vscode-extension/src/builders/buildAndroid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,12 @@ export async function buildAndroid(
getTelemetryReporter().sendTelemetryEvent("build:custom-build-requested", {
platform: DevicePlatform.Android,
});
const apkPath = await runExternalBuild(cancelToken, customBuild.android.buildCommand, env);
const apkPath = await runExternalBuild(
cancelToken,
customBuild.android.buildCommand,
env,
DevicePlatform.Android
);
if (!apkPath) {
throw new Error("Failed to build Android app using custom script.");
}
Expand Down
7 changes: 6 additions & 1 deletion packages/vscode-extension/src/builders/buildIOS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,12 @@ export async function buildIos(
});
// We don't autoinstall Pods here to make custom build scripts more flexible

const appPath = await runExternalBuild(cancelToken, customBuild.ios.buildCommand, env);
const appPath = await runExternalBuild(
cancelToken,
customBuild.ios.buildCommand,
env,
DevicePlatform.IOS
);
if (!appPath) {
throw new Error("Failed to build iOS app using custom script.");
}
Expand Down
43 changes: 41 additions & 2 deletions packages/vscode-extension/src/builders/customBuild.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,66 @@
import path from "path";
import fs from "fs";
import os from "os";
import { mkdtemp } from "fs/promises";
import { Logger } from "../Logger";
import { command, lineReader } from "../utilities/subprocess";
import { CancelToken } from "./cancelToken";
import { getAppRootFolder } from "../utilities/extensionContext";
import { extractTarApp, isApkFile, isAppFile } from "./utils";
import { DevicePlatform } from "../common/DeviceManager";

type Env = Record<string, string> | undefined;

export async function runExternalBuild(cancelToken: CancelToken, buildCommand: string, env: Env) {
// Extracts all paths from the last line, both Unix and Windows format
const BUILD_PATH_REGEX = /(\/.*?\.\S*)|([a-zA-Z]:\\.*?\.\S*)/g;

export async function runExternalBuild(
cancelToken: CancelToken,
buildCommand: string,
env: Env,
platform: DevicePlatform
) {
const output = await runExternalScript(buildCommand, env, cancelToken);

if (!output) {
return undefined;
}

const binaryPath = output.lastLine;
let binaryPath = output.lastLine;

// We run regex to extract paths from the first line and we take the first one
const groups = output.lastLine.match(BUILD_PATH_REGEX);
if (groups?.[0]) {
binaryPath = groups[0];
}

if (binaryPath && !fs.existsSync(binaryPath)) {
Logger.error(
`External script: ${buildCommand} failed to output any existing app path, got: ${binaryPath}`
);
return undefined;
}

const shouldExtractArchive = binaryPath.endsWith(".tar.gz");
if (shouldExtractArchive) {
const tmpDirectory = await mkdtemp(path.join(os.tmpdir(), "rn-ide-custom-build-"));
const extractedFile = await extractTarApp(binaryPath, tmpDirectory, cancelToken, platform);

Logger.info(`External script: ${buildCommand} output app path: ${binaryPath}`);
return extractedFile;
}

if (platform === DevicePlatform.Android && !isApkFile(binaryPath)) {
Logger.error(`External script: ${buildCommand} failed to output .apk file, got: ${binaryPath}`);
return undefined;
}

if (platform === DevicePlatform.IOS && !isAppFile(binaryPath)) {
Logger.error(`External script: ${buildCommand} failed to output .app file, got: ${binaryPath}`);
return undefined;
}

Logger.info(`External script: ${buildCommand} output app path: ${binaryPath}`);
return binaryPath;
}

Expand Down
32 changes: 3 additions & 29 deletions packages/vscode-extension/src/builders/eas.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import path from "path";
import os from "os";
import { mkdtemp, readdir } from "fs/promises";
import { mkdtemp } from "fs/promises";
import maxBy from "lodash/maxBy";

import { DevicePlatform } from "../common/DeviceManager";
import { EasConfig } from "../common/LaunchConfig";
import { Logger } from "../Logger";
import { CancelToken } from "./cancelToken";
import { exec } from "../utilities/subprocess";
import { downloadBinary } from "../utilities/common";
import { EASBuild, listEasBuilds, viewEasBuild } from "./easCommand";
import { extractTarApp } from "./utils";

export async function fetchEasBuild(
cancelToken: CancelToken,
Expand Down Expand Up @@ -84,10 +84,6 @@ async function downloadAppFromEas(
platform: DevicePlatform,
cancelToken: CancelToken
) {
function isAppFile(name: string) {
return name.endsWith(".app");
}

const { id, binaryUrl } = build;

const tmpDirectory = await mkdtemp(path.join(os.tmpdir(), "rn-ide-eas-build-"));
Expand All @@ -107,27 +103,5 @@ async function downloadAppFromEas(
return binaryPath;
}

const { failed } = await cancelToken.adapt(
tarCommand({ archivePath: binaryPath, extractDir: tmpDirectory })
);
if (failed) {
Logger.error(`Failed to extract archive '${binaryPath}' to '${tmpDirectory}'.`);
return undefined;
}

// assuming that the archive contains only one .app file
const appName = (await readdir(tmpDirectory)).find(isAppFile);
if (!appName) {
Logger.error(`Failed to find .app in extracted archive '${binaryPath}'.`);
return undefined;
}

const appPath = path.join(tmpDirectory, appName);
Logger.debug(`Extracted app archive to '${appPath}'.`);
return appPath;
}

type TarCommandArgs = { archivePath: string; extractDir: string };
function tarCommand({ archivePath, extractDir }: TarCommandArgs) {
return exec("tar", ["-xf", archivePath, "-C", extractDir]);
return await extractTarApp(binaryPath, tmpDirectory, cancelToken, DevicePlatform.IOS);
}
52 changes: 52 additions & 0 deletions packages/vscode-extension/src/builders/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { readdir } from "fs/promises";
import path from "path";
import { Logger } from "../Logger";
import { exec } from "../utilities/subprocess";
import { CancelToken } from "./cancelToken";
import { DevicePlatform } from "../common/DeviceManager";

export function isAppFile(name: string) {
return name.endsWith(".app");
}

export function isApkFile(name: string) {
return name.endsWith(".apk");
}

export async function extractTarApp(
binaryPath: string,
pathToExtract: string,
cancelToken: CancelToken,
platform: DevicePlatform
) {
const { failed } = await cancelToken.adapt(
tarCommand({ archivePath: binaryPath, extractDir: pathToExtract })
);

if (failed) {
Logger.error(`Failed to extract archive '${binaryPath}' to '${pathToExtract}'.`);
return undefined;
}

// assuming that the archive contains only one app file
const appName = (await readdir(pathToExtract)).find(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of having this here both here and in runExternalBuild maybe we should just make this metod return a single extracted file and then only make the file extension checks in runExternalBuild

platform === DevicePlatform.Android ? isApkFile : isAppFile
);
if (!appName) {
Logger.error(
`Failed to find the ${
platform === DevicePlatform.Android ? ".apk" : ".app"
} file in extracted archive '${binaryPath}'.`
);
return undefined;
}

const appPath = path.join(pathToExtract, appName);
Logger.debug(`Extracted app archive to '${appPath}'.`);
return appPath;
}

type TarCommandArgs = { archivePath: string; extractDir: string };
function tarCommand({ archivePath, extractDir }: TarCommandArgs) {
return exec("tar", ["-xf", archivePath, "-C", extractDir]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps would be better to use some existing module like node-tar this way making this work cross platform and resistant to some PATH related issues

}
Loading