Skip to content
This repository was archived by the owner on Dec 9, 2024. It is now read-only.

Commit eda7b64

Browse files
authored
fix: Inject current environment variables to spawn process (#218)
Add current process environment variables to spawned process, inherit `stdio` for spawned process, append `.cmd` if windows
1 parent 15ca299 commit eda7b64

File tree

2 files changed

+50
-57
lines changed

2 files changed

+50
-57
lines changed

src/services/offlineService.test.ts

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,7 @@ import { MockFactory } from "../test/mockFactory";
77
import { OfflineService } from "./offlineService";
88

99
describe("Offline Service", () => {
10-
11-
const mySpawn = mockSpawn();
12-
require("child_process").spawn = mySpawn;
13-
mySpawn.setDefault(mySpawn.simple(0, "Exit code"));
10+
let mySpawn;
1411

1512
function createService(sls?: Serverless): OfflineService {
1613
return new OfflineService(
@@ -21,12 +18,16 @@ describe("Offline Service", () => {
2118

2219
beforeEach(() => {
2320
// Mocking the file system so that files are not created in project directory
24-
mockFs({})
21+
mockFs({});
22+
23+
mySpawn = mockSpawn();
24+
require("child_process").spawn = mySpawn;
25+
mySpawn.setDefault(mySpawn.simple(0, "Exit code"));
2526
});
2627

2728
afterEach(() => {
2829
mockFs.restore();
29-
})
30+
});
3031

3132
it("builds required files for offline execution", async () => {
3233
const sls = MockFactory.createTestServerless();
@@ -113,7 +114,12 @@ describe("Offline Service", () => {
113114
rmdirSpy.mockRestore();
114115
});
115116

116-
it("instructs users how to run locally", async () => {
117+
it("calls func host start on Mac OS", async () => {
118+
Object.defineProperty(process, "platform", {
119+
value: "darwin",
120+
writable: true,
121+
});
122+
117123
const sls = MockFactory.createTestServerless();
118124
const service = createService(sls);
119125
await service.start();
@@ -123,4 +129,20 @@ describe("Offline Service", () => {
123129
expect(call.command).toEqual("func");
124130
expect(call.args).toEqual(["host", "start"]);
125131
});
132+
133+
it("calls func host start on windows", async () => {
134+
Object.defineProperty(process, "platform", {
135+
value: "win32",
136+
writable: true,
137+
});
138+
139+
const sls = MockFactory.createTestServerless();
140+
const service = createService(sls);
141+
await service.start();
142+
const calls = mySpawn.calls;
143+
expect(calls).toHaveLength(1);
144+
const call = calls[0];
145+
expect(call.command).toEqual("func.cmd");
146+
expect(call.args).toEqual(["host", "start"]);
147+
});
126148
});

src/services/offlineService.ts

Lines changed: 21 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { spawn } from "child_process";
1+
import { spawn, SpawnOptions } from "child_process";
22
import fs from "fs";
33
import Serverless from "serverless";
44
import configConstants from "../config";
@@ -29,7 +29,7 @@ export class OfflineService extends BaseService {
2929
await this.packageService.createBindings();
3030
const filenames = Object.keys(this.localFiles);
3131
for (const filename of filenames) {
32-
if (!fs.existsSync(filename)){
32+
if (!fs.existsSync(filename)) {
3333
fs.writeFileSync(
3434
filename,
3535
this.localFiles[filename]
@@ -44,7 +44,7 @@ export class OfflineService extends BaseService {
4444
await this.packageService.cleanUp();
4545
const filenames = Object.keys(this.localFiles);
4646
for (const filename of filenames) {
47-
if (fs.existsSync(filename)){
47+
if (fs.existsSync(filename)) {
4848
this.log(`Removing file '${filename}'`);
4949
fs.unlinkSync(filename)
5050
}
@@ -62,59 +62,30 @@ export class OfflineService extends BaseService {
6262
/**
6363
* Spawn a Node child process with predefined environment variables
6464
* @param command CLI Command - NO ARGS
65-
* @param args Array of arguments for CLI command
66-
* @param env Additional environment variables to be set in addition to
67-
* predefined variables in `serverless.yml`
65+
* @param spawnArgs Array of arguments for CLI command
6866
*/
69-
private spawn(command: string, args?: string[], env?: any): Promise<void> {
70-
env = {
67+
private spawn(command: string, spawnArgs?: string[]): Promise<void> {
68+
if (process.platform === "win32") {
69+
command += ".cmd";
70+
}
71+
72+
const env = {
73+
// Inherit environment from current process, most importantly, the PATH
74+
...process.env,
75+
// Environment variables from serverless config are king
7176
...this.serverless.service.provider["environment"],
72-
...env
7377
}
74-
this.log(`Spawning process '${command} ${args.join(" ")}'`);
78+
this.log(`Spawning process '${command} ${spawnArgs.join(" ")}'`);
7579
return new Promise((resolve, reject) => {
76-
const childProcess = spawn(command, args, {env});
77-
78-
childProcess.stdout.on("data", (data) => {
79-
this.log(data, {
80-
color: configConstants.funcConsoleColor,
81-
}, command);
82-
});
83-
84-
childProcess.stderr.on("data", (data) => {
85-
this.log(data, {
86-
color: "red",
87-
}, command);
88-
})
89-
90-
childProcess.on("message", (message) => {
91-
this.log(message, {
92-
color: configConstants.funcConsoleColor,
93-
}, command);
94-
});
95-
96-
childProcess.on("error", (err) => {
97-
this.log(`${err}`, {
98-
color: "red"
99-
}, command);
100-
reject(err);
101-
});
80+
const spawnOptions: SpawnOptions = { env, stdio: "inherit" };
81+
const childProcess = spawn(command, spawnArgs, spawnOptions);
10282

10383
childProcess.on("exit", (code) => {
104-
this.log(`Exited with code: ${code}`, {
105-
color: (code === 0) ? "green" : "red",
106-
}, command);
107-
});
108-
109-
childProcess.on("close", (code) => {
110-
this.log(`Closed with code: ${code}`, {
111-
color: (code === 0) ? "green" : "red",
112-
}, command);
113-
resolve();
114-
});
115-
116-
childProcess.on("disconnect", () => {
117-
this.log("Process disconnected");
84+
if (code === 0) {
85+
resolve();
86+
} else {
87+
reject();
88+
}
11889
});
11990
});
12091
}

0 commit comments

Comments
 (0)