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

transfer launch params (env and envFIle) to packager #1248

Merged
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,16 @@ If you use Android, you need to change the debug server by:

**NOTE:** Some aspects of React Native hard-code the port to the default as specified in [this issue](https://github.com/facebook/react-native/issues/9145).

### Custom environment variables
Extension supports passing custom environment variables for the React Native Packager process context. To add custom variables you can create `.env` file in the root folder of your project and add needed environment variables in the following format:
SounD120 marked this conversation as resolved.
Show resolved Hide resolved

```
Variable1_name=Variable1_value
Variable2_name=Variable2_value
```
Variables that are declared in this `.env` file can override the original environment variables from `process.env` of the Packager process.

It is possible to transfer environment variables (via `env` and `envFile` arguments in `launch.json`) from the `launch` or `attach` debug scenarios to the Packager. If these variables are defined, then they will be used, otherwise the `.env` file is used.

## Change project root

Expand Down
29 changes: 22 additions & 7 deletions src/common/packager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.

import {IRunOptions} from "./../extension/launchArgs";
import {GeneralMobilePlatform} from "./../extension/generalMobilePlatform";
import {ChildProcess} from "child_process";
import {CommandExecutor} from "./commandExecutor";
import {ExponentHelper} from "../extension/exponent/exponentHelper";
Expand Down Expand Up @@ -45,26 +47,31 @@ export class Packager {
private static OPN_PACKAGE_MAIN_FILENAME = "index.js";
private static fs: FileSystem = new Node.FileSystem();
private expoHelper: ExponentHelper;
private runOptions: IRunOptions;
SounD120 marked this conversation as resolved.
Show resolved Hide resolved

constructor(private workspacePath: string, private projectPath: string, private packagerPort?: number, packagerStatusIndicator?: PackagerStatusIndicator) {
this.packagerStatus = PackagerStatus.PACKAGER_STOPPED;
this.packagerStatusIndicator = packagerStatusIndicator || new PackagerStatusIndicator();
this.expoHelper = new ExponentHelper(this.workspacePath, this.projectPath);
}

public get port(): number {
public getPort(): number {
return this.packagerPort || SettingsHelper.getPackagerPort(this.workspacePath);
}

public setRunOptions(runOptions: IRunOptions) {
this.runOptions = runOptions;
}

public static getHostForPort(port: number): string {
return `localhost:${port}`;
}

public get statusIndicator(): PackagerStatusIndicator {
public getStatusIndicator(): PackagerStatusIndicator {
return this.packagerStatusIndicator;
}
public getHost(): string {
return Packager.getHostForPort(this.port);
return Packager.getHostForPort(this.getPort());
}

public getPackagerStatus(): PackagerStatus {
Expand All @@ -76,7 +83,7 @@ export class Packager {
}

public getPackagerArgs(rnVersion: string, resetCache: boolean = false): Q.Promise<string[]> {
let args: string[] = ["--port", this.port.toString()];
let args: string[] = ["--port", this.getPort().toString()];

if (resetCache) {
args = args.concat("--resetCache");
Expand Down Expand Up @@ -136,7 +143,15 @@ export class Packager {
// This bug will be fixed in 0.41
const failedRNVersions: string[] = ["0.38.0", "0.39.0", "0.40.0"];

let reactEnv = Object.assign({}, process.env, {
let env = process.env;
if (this.runOptions && (this.runOptions.env || this.runOptions.envFile)) {
env = GeneralMobilePlatform.getEnvArgument(env, this.runOptions.env, this.runOptions.envFile);
} else {
const rootEnv = path.join(this.getProjectPath(), ".env");
env = GeneralMobilePlatform.getEnvArgument(env, null, rootEnv);
}

let reactEnv = Object.assign({}, env, {
REACT_DEBUGGER: "echo A debugger is not needed: ",
REACT_EDITOR: failedRNVersions.indexOf(rnVersion) < 0 ? "code" : this.openFileAtLocationCommand(),
});
Expand Down Expand Up @@ -196,8 +211,8 @@ export class Packager {
}

public restart(port: number): Q.Promise<void> {
if (this.port && this.port !== port) {
return Q.reject<void>(ErrorHelper.getInternalError(InternalErrorCode.PackagerRunningInDifferentPort, port, this.port));
if (this.getPort() && this.getPort() !== port) {
return Q.reject<void>(ErrorHelper.getInternalError(InternalErrorCode.PackagerRunningInDifferentPort, port, this.getPort()));
}

return this.isRunning()
Expand Down
2 changes: 1 addition & 1 deletion src/extension/android/androidPlatform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export class AndroidPlatform extends GeneralMobilePlatform {
extProps = TelemetryHelper.addPropertyToTelemetryProperties(this.runOptions.reactNativeVersions.reactNativeVersion, "reactNativeVersion", extProps);

return TelemetryHelper.generate("AndroidPlatform.runApp", extProps, () => {
const env = this.getEnvArgument();
const env = GeneralMobilePlatform.getEnvArgument(process.env, this.runOptions.env, this.runOptions.envFile);

if (
!semver.valid(this.runOptions.reactNativeVersions.reactNativeVersion) /*Custom RN implementations should support this flag*/ ||
Expand Down
2 changes: 1 addition & 1 deletion src/extension/exponent/exponentPlatform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class ExponentPlatform extends GeneralMobilePlatform {
return TelemetryHelper.generate("ExponentPlatform.runApp", extProps, () => {
return this.loginToExponentOrSkip(this.runOptions.expoHostType)
.then(() =>
XDL.setOptions(this.projectPath, { packagerPort: this.packager.port })
XDL.setOptions(this.projectPath, { packagerPort: this.packager.getPort() })
)
.then(() =>
XDL.startExponentServer(this.projectPath)
Expand Down
2 changes: 1 addition & 1 deletion src/extension/extensionServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export class ExtensionServer implements vscode.Disposable {
this.serverInstance = null;
}

this.reactNativePackager.statusIndicator.dispose();
this.reactNativePackager.getStatusIndicator().dispose();
this.reactNativePackager.stop(true);
this.stopMonitoringLogCat();
}
Expand Down
58 changes: 37 additions & 21 deletions src/extension/generalMobilePlatform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export class GeneralMobilePlatform {
this.platformName = this.runOptions.platform;
this.projectPath = this.runOptions.projectRoot;
this.packager = platformDeps.packager || new Packager(this.runOptions.workspaceRoot, this.projectPath, SettingsHelper.getPackagerPort(this.runOptions.workspaceRoot), new PackagerStatusIndicator());
this.packager.setRunOptions(runOptions);
this.logger = OutputChannelLogger.getChannel(localize("ReactNativeRunPlatform", "React Native: Run {0}", this.platformName), true);
this.logger.clear();
this.runArguments = this.getRunArguments();
Expand Down Expand Up @@ -132,12 +133,36 @@ export class GeneralMobilePlatform {
throw new Error("Not yet implemented: GeneralMobilePlatform.getRunArguments");
}

public getEnvArgument(): any {
let args = this.runOptions;
let env = process.env;
public static getEnvArgument(processEnv: any, env?: any, envFile?: string): any {
let modifyEnv = Object.assign({}, processEnv);

if (args.envFile) {
let buffer = fs.readFileSync(args.envFile, "utf8");
if (envFile) {
// .env variables never overwrite existing variables
const argsFromEnvFile = this.readEnvFile(envFile);
if (argsFromEnvFile != null) {
for (let key in argsFromEnvFile) {
if (!modifyEnv[key] && argsFromEnvFile.hasOwnProperty(key)) {
modifyEnv[key] = argsFromEnvFile[key];
}
}
}
}

if (env) {
// launch config env vars overwrite .env vars
for (let key in env) {
if (env.hasOwnProperty(key)) {
modifyEnv[key] = env[key];
}
}
}
return modifyEnv;
}

private static readEnvFile(filePath: string): any {
if (fs.existsSync(filePath)) {
let buffer = fs.readFileSync(filePath, "utf8");
let result = {};

// Strip BOM
if (buffer && buffer[0] === "\uFEFF") {
Expand All @@ -148,26 +173,17 @@ export class GeneralMobilePlatform {
const r = line.match(/^\s*([\w\.\-]+)\s*=\s*(.*)?\s*$/);
if (r !== null) {
const key = r[1];
if (!env[key]) { // .env variables never overwrite existing variables
let value = r[2] || "";
if (value.length > 0 && value.charAt(0) === "\"" && value.charAt(value.length - 1) === "\"") {
value = value.replace(/\\n/gm, "\n");
}
env[key] = value.replace(/(^['"]|['"]$)/g, "");
let value = r[2] || "";
if (value.length > 0 && value.charAt(0) === "\"" && value.charAt(value.length - 1) === "\"") {
value = value.replace(/\\n/gm, "\n");
}
result[key] = value.replace(/(^['"]|['"]$)/g, "");
}
});
}

if (args.env) {
// launch config env vars overwrite .env vars
for (let key in args.env) {
if (args.env.hasOwnProperty(key)) {
env[key] = args.env[key];
}
}
return result;
} else {
return null;
}

return env;
}
}
2 changes: 1 addition & 1 deletion src/extension/ios/iOSPlatform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export class IOSPlatform extends GeneralMobilePlatform {

return TelemetryHelper.generate("iOSPlatform.runApp", extProps, () => {
// Compile, deploy, and launch the app on either a simulator or a device
const env = this.getEnvArgument();
const env = GeneralMobilePlatform.getEnvArgument(process.env, this.runOptions.env, this.runOptions.envFile);

if (!semver.valid(this.runOptions.reactNativeVersions.reactNativeVersion) /*Custom RN implementations should support this flag*/ || semver.gte(this.runOptions.reactNativeVersions.reactNativeVersion, IOSPlatform.NO_PACKAGER_VERSION)) {
this.runArguments.push("--no-packager");
Expand Down
2 changes: 1 addition & 1 deletion src/extension/windows/windowsPlatform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export class WindowsPlatform extends GeneralMobilePlatform {
extProps = TelemetryHelper.addPropertyToTelemetryProperties(this.runOptions.reactNativeVersions.reactNativeWindowsVersion, "reactNativeWindowsVersion", extProps);

return TelemetryHelper.generate("WindowsPlatform.runApp", extProps, () => {
const env = this.getEnvArgument();
const env = GeneralMobilePlatform.getEnvArgument(process.env, this.runOptions.env, this.runOptions.envFile);

if (enableDebug) {
this.runArguments.push("--proxy");
Expand Down
3 changes: 2 additions & 1 deletion src/extension/windows/wpfPlatform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as Q from "q";
import * as semver from "semver";
import * as path from "path";

import {GeneralMobilePlatform} from "../generalMobilePlatform";
import {MobilePlatformDeps} from "../generalMobilePlatform";
import {IWindowsRunOptions} from "../launchArgs";
import {TelemetryHelper} from "../../common/telemetryHelper";
Expand Down Expand Up @@ -34,7 +35,7 @@ export class WpfPlatform extends WindowsPlatform {
extProps = TelemetryHelper.addPropertyToTelemetryProperties(this.runOptions.reactNativeVersions.reactNativeWindowsVersion, "reactNativeWindowsVersion", extProps);

return TelemetryHelper.generate("WpfPlatform.runApp", extProps, () => {
const env = this.getEnvArgument();
const env = GeneralMobilePlatform.getEnvArgument(process.env, this.runOptions.env, this.runOptions.envFile);

if (enableDebug) {
this.runArguments.push("--proxy");
Expand Down
52 changes: 52 additions & 0 deletions test/extension/generalMobilePlatform.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

import * as assert from "assert";
import { GeneralMobilePlatform } from "../../src/extension/generalMobilePlatform";
import * as fs from "fs";
import * as path from "path";

suite("generalMobilePlatform", function () {
suite("extensionContext", function () {
Expand All @@ -29,5 +31,55 @@ suite("generalMobilePlatform", function () {
assert.equal(GeneralMobilePlatform.getOptFromRunArgs(args, "param4", false), undefined);
});
});

suite("getEnvArgument", function() {
const origEnv: any = {"test1": "origEnv", "test2": "origEnv", "test3": "origEnv"};

const env: any = {"test2": "env", "test3": "env", "test4": "env"};

const envForFile: string = "test3=envFile\ntest4=envFile\ntest5=envFile";
const envFile: string = path.join(__dirname, "..", "resources", "auxiliaryFiles", ".env");
const fakeEnvFile: string = path.join(__dirname, "..", "resources", "auxiliaryFiles", ".envFake");

setup(() => {
fs.writeFileSync(envFile, envForFile);
});

teardown(() => {
fs.unlinkSync(envFile);
});

test("existing args should not should not depend on the existence of the envFile", function() {
assert.deepEqual(GeneralMobilePlatform.getEnvArgument(origEnv, undefined, fakeEnvFile), {
"test1": "origEnv",
"test2": "origEnv",
"test3": "origEnv"});
});

test("existing args should not depend on null or undefined env and envFile", function() {
assert.deepEqual(GeneralMobilePlatform.getEnvArgument(origEnv, undefined, undefined), {
"test1": "origEnv",
"test2": "origEnv",
"test3": "origEnv"});
});

test("args from envFile should not overwrite existing variables", function() {
assert.deepEqual(GeneralMobilePlatform.getEnvArgument(origEnv, null, envFile), {
"test1": "origEnv",
"test2": "origEnv",
"test3": "origEnv",
"test4": "envFile",
"test5": "envFile"});
});

test("args from envFile and original args should be overwritten by env args", function() {
assert.deepEqual(GeneralMobilePlatform.getEnvArgument(origEnv, env, envFile), {
"test1": "origEnv",
"test2": "env",
"test3": "env",
"test4": "env",
"test5": "envFile"});
});
});
});
});