Skip to content

Commit

Permalink
feat: add support for reading build time env variables from a .env
Browse files Browse the repository at this point in the history
…file (#882)

This change will automatically load up a `.env` file, if found, and apply its
values to the current environment. An example would be to provide a specific
CLOUDFLARE_ACCOUNT_ID value.

Related to #190
  • Loading branch information
petebacondarwin authored May 4, 2022
1 parent bae5ba4 commit 1ad7570
Show file tree
Hide file tree
Showing 10 changed files with 155 additions and 130 deletions.
11 changes: 11 additions & 0 deletions .changeset/spotty-waves-think.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"wrangler": patch
---

feat: add support for reading build time env variables from a `.env` file

This change will automatically load up a `.env` file, if found, and apply its
values to the current environment. An example would be to provide a specific
CLOUDFLARE_ACCOUNT_ID value.

Related to cloudflare#190
1 change: 1 addition & 0 deletions examples/local-mode-tests/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
FOO="the value of foo"
6 changes: 6 additions & 0 deletions examples/local-mode-tests/src/wrangler.dotenv.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
name = "local-mode-tests"
compatibility_date = "2022-03-27"

# This custom build command will show whether the FOO
# environment variable was read from the `.env` file.
build.command = "node -e \"console.log(process.env.FOO)\""
15 changes: 15 additions & 0 deletions examples/local-mode-tests/tests/dotenv.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { spawnWranglerDev } from "./helpers";

it("should use the environment variable from the .env file", async () => {
const { wranglerProcess, fetchWhenReady, terminateProcess } =
spawnWranglerDev("src/module.ts", "src/wrangler.dotenv.toml", 9002);

try {
await fetchWhenReady("http://localhost");
expect(wranglerProcess.stdout?.read().toString()).toContain(
"the value of foo"
);
} finally {
await terminateProcess();
}
});
77 changes: 77 additions & 0 deletions examples/local-mode-tests/tests/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { spawn } from "node:child_process";
import { fetch } from "undici";
import type { Response } from "undici";

const isWindows = process.platform === "win32";

export async function sleep(ms: number): Promise<void> {
await new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
}

/**
* Spawn a child process that is running `wrangler dev`.
*
* @returns two helper functions:
* - `fetchWhenReady()` will run a fetch against the preview Worker, once it is up and running,
* and return its response.
* - `terminateProcess()` send a signal to the `wrangler dev` child process to kill itself.
*/
export function spawnWranglerDev(
srcPath: string,
wranglerTomlPath: string,
port: number
) {
const wranglerProcess = spawn(
"npx",
[
"wrangler",
"dev",
srcPath,
"--local",
"--config",
wranglerTomlPath,
"--port",
port.toString(),
],
{
shell: isWindows,
stdio: "pipe",
}
);

const fetchWhenReady = async (url: string): Promise<Response> => {
const MAX_ATTEMPTS = 50;
const SLEEP_MS = 100;
let attempts = MAX_ATTEMPTS;
while (attempts-- > 0) {
await sleep(SLEEP_MS);
try {
return await fetch(`${url}:${port}`);
} catch {}
}
throw new Error(
`Failed to connect to "${url}:${port}" within ${
(MAX_ATTEMPTS * SLEEP_MS) / 1000
} seconds.`
);
};

const terminateProcess = () => {
return new Promise((resolve, reject) => {
wranglerProcess.once("exit", (code) => {
if (!code) {
resolve(code);
} else {
reject(code);
}
});
wranglerProcess.kill();
});
};

return {
wranglerProcess,
fetchWhenReady,
terminateProcess,
};
}
78 changes: 13 additions & 65 deletions examples/local-mode-tests/tests/module.test.ts
Original file line number Diff line number Diff line change
@@ -1,71 +1,16 @@
import { spawn } from "child_process";
import { fetch } from "undici";
import type { ChildProcess } from "child_process";
import type { Response } from "undici";

const waitUntilReady = async (url: string): Promise<Response> => {
let response: Response | undefined = undefined;

while (response === undefined) {
await new Promise((resolvePromise) => setTimeout(resolvePromise, 100));

try {
response = await fetch(url);
} catch {}
}

return response as Response;
};
const isWindows = process.platform === "win32";

let wranglerProcess: ChildProcess;

beforeAll(async () => {
// These tests break in CI for windows, so we're disabling them for now
if (isWindows) return;

wranglerProcess = spawn(
"npx",
[
"wrangler",
"dev",
"src/module.ts",
"--local",
"--config",
"src/wrangler.module.toml",
"--port",
"9001",
],
{
shell: isWindows,
stdio: "inherit",
}
);
});

afterAll(async () => {
// These tests break in CI for windows, so we're disabling them for now
if (isWindows) return;

await new Promise((resolve, reject) => {
wranglerProcess.once("exit", (code) => {
if (!code) {
resolve(code);
} else {
reject(code);
}
});
wranglerProcess.kill();
});
});
import { spawnWranglerDev } from "./helpers";

it("renders", async () => {
// These tests break in CI for windows, so we're disabling them for now
if (isWindows) return;
const { fetchWhenReady, terminateProcess } = spawnWranglerDev(
"src/module.ts",
"src/wrangler.module.toml",
9001
);

const response = await waitUntilReady("http://localhost:9001/");
const text = await response.text();
expect(text).toMatchInlineSnapshot(`
try {
const response = await fetchWhenReady("http://localhost");
const text = await response.text();
expect(text).toMatchInlineSnapshot(`
"{
\\"VAR1\\": \\"value1\\",
\\"VAR2\\": 123,
Expand All @@ -76,4 +21,7 @@ it("renders", async () => {
\\"data\\": \\"Here be some data\\"
}"
`);
} finally {
await terminateProcess();
}
});
78 changes: 13 additions & 65 deletions examples/local-mode-tests/tests/sw.test.ts
Original file line number Diff line number Diff line change
@@ -1,71 +1,16 @@
import { spawn } from "child_process";
import { fetch } from "undici";
import type { ChildProcess } from "child_process";
import type { Response } from "undici";

const waitUntilReady = async (url: string): Promise<Response> => {
let response: Response | undefined = undefined;

while (response === undefined) {
await new Promise((resolvePromise) => setTimeout(resolvePromise, 100));

try {
response = await fetch(url);
} catch {}
}

return response as Response;
};
const isWindows = process.platform === "win32";

let wranglerProcess: ChildProcess;

beforeAll(async () => {
// These tests break in CI for windows, so we're disabling them for now
if (isWindows) return;

wranglerProcess = spawn(
"npx",
[
"wrangler",
"dev",
"src/sw.ts",
"--local",
"--config",
"src/wrangler.sw.toml",
"--port",
"9002",
],
{
shell: isWindows,
stdio: "inherit",
}
);
});

afterAll(async () => {
// These tests break in CI for windows, so we're disabling them for now
if (isWindows) return;

await new Promise((resolve, reject) => {
wranglerProcess.once("exit", (code) => {
if (!code) {
resolve(code);
} else {
reject(code);
}
});
wranglerProcess.kill();
});
});
import { spawnWranglerDev } from "./helpers";

it("renders", async () => {
// These tests break in CI for windows, so we're disabling them for now
if (isWindows) return;
const { fetchWhenReady, terminateProcess } = spawnWranglerDev(
"src/sw.ts",
"src/wrangler.sw.toml",
9000
);

const response = await waitUntilReady("http://localhost:9002/");
const text = await response.text();
expect(text).toMatchInlineSnapshot(`
try {
const response = await fetchWhenReady("http://localhost");
const text = await response.text();
expect(text).toMatchInlineSnapshot(`
"{
\\"VAR1\\": \\"value1\\",
\\"VAR2\\": 123,
Expand All @@ -78,4 +23,7 @@ it("renders", async () => {
\\"DATA\\": \\"Here be some data\\"
}"
`);
} finally {
await terminateProcess();
}
});
17 changes: 17 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/wrangler/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"cmd-shim": "^4.1.0",
"command-exists": "^1.2.9",
"devtools-protocol": "^0.0.955664",
"dotenv": "^16.0.0",
"execa": "^6.1.0",
"faye-websocket": "^0.11.4",
"finalhandler": "^1.2.0",
Expand Down
1 change: 1 addition & 0 deletions packages/wrangler/src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import "dotenv/config"; // Grab locally specified env params from a `.env` file.
import process from "process";
import { hideBin } from "yargs/helpers";
import { FatalError } from "./errors";
Expand Down

0 comments on commit 1ad7570

Please sign in to comment.