Skip to content

Commit

Permalink
feat: support adding secrets in non-interactive mode (#559)
Browse files Browse the repository at this point in the history
* feat: support adding secrets in non-interactive mode

Now the user can pipe in the secret value to the `wrangler secret put` command.
For example:

```
cat my-secret.txt | wrangler secret put secret-key --name worker-name
```

This requires that the user is logged in, and has only one account, or that the `account_id` has been set in `wrangler.toml`.

Fixes #170
  • Loading branch information
petebacondarwin committed Mar 9, 2022
1 parent 6e9a219 commit 16fb5e6
Show file tree
Hide file tree
Showing 6 changed files with 413 additions and 116 deletions.
16 changes: 16 additions & 0 deletions .changeset/famous-pets-complain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
"wrangler": patch
---

feat: support adding secrets in non-interactive mode

Now the user can pipe in the secret value to the `wrangler secret put` command.
For example:

```
cat my-secret.txt | wrangler secret put secret-key --name worker-name
```

This requires that the user is logged in, and has only one account, or that the `account_id` has been set in `wrangler.toml`.

Fixes #170
22 changes: 18 additions & 4 deletions packages/wrangler/src/__tests__/helpers/mock-account-id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@ const ORIGINAL_CLOUDFLARE_ACCOUNT_ID = process.env.CLOUDFLARE_ACCOUNT_ID;

/**
* Mock the API token so that we don't need to read it from user configuration files.
*
* Note that you can remove any API token from the environment by setting the value to `null`.
* This is useful if a higher `describe()` block has already called `mockApiToken()`.
*/
export function mockApiToken({
apiToken = "some-api-token",
}: { apiToken?: string } = {}) {
}: { apiToken?: string | null } = {}) {
beforeEach(() => {
process.env.CLOUDFLARE_API_TOKEN = apiToken;
if (apiToken === null) {
delete process.env.CLOUDFLARE_API_TOKEN;
} else {
process.env.CLOUDFLARE_API_TOKEN = apiToken;
}
});
afterEach(() => {
process.env.CLOUDFLARE_API_TOKEN = ORIGINAL_CLOUDFLARE_API_TOKEN;
Expand All @@ -17,12 +24,19 @@ export function mockApiToken({

/**
* Mock the current account ID so that we don't need to read it from configuration files.
*
* Note that you can remove any account ID from the environment by setting the value to `null`.
* This is useful if a higher `describe()` block has already called `mockAccountId()`.
*/
export function mockAccountId({
accountId = "some-account-id",
}: { accountId?: string } = {}) {
}: { accountId?: string | null } = {}) {
beforeEach(() => {
process.env.CLOUDFLARE_ACCOUNT_ID = accountId;
if (accountId === null) {
delete process.env.CLOUDFLARE_ACCOUNT_ID;
} else {
process.env.CLOUDFLARE_ACCOUNT_ID = accountId;
}
});
afterEach(() => {
process.env.CLOUDFLARE_ACCOUNT_ID = ORIGINAL_CLOUDFLARE_ACCOUNT_ID;
Expand Down
103 changes: 103 additions & 0 deletions packages/wrangler/src/__tests__/helpers/mock-stdin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
const ORIGINAL_STDIN = process.stdin;

/**
* Mock process.stdin so that we can pipe in text for non-interactive mode tests.
*/
export function useMockStdin({ isTTY }: { isTTY: boolean }) {
const mockStdin = new MockStdIn(isTTY);

beforeEach(() => {
mockStdin.reset();
Object.defineProperty(process, "stdin", {
value: mockStdin,
configurable: true,
writable: false,
});
});

afterEach(() => {
Object.defineProperty(process, "stdin", {
value: ORIGINAL_STDIN,
configurable: true,
writable: false,
});
});

return mockStdin;
}

const failCallback: (value: unknown) => void = () => {
throw new Error(
"TEST FAILURE: stdin callback called before being initialized."
);
};

/**
* A mock version of `process.std`, which can be used to simulate piping data
* into the wrangler process in non-interactive mode.
*/
class MockStdIn {
private endCallback = failCallback;
private errorCallback = failCallback;
private chunks: string[] = [];
private error: Error | undefined;

/**
* Set this to true if you want the stdin stream to error.
*/
throwError(error: Error) {
this.error = error;
}

/**
* Call this to clean out the chunks that are queued for sending.
*/
reset() {
this.chunks.length = 0;
this.error = undefined;
}

/**
* Queue up some chunks to be sent.
*/
send(...chunks: string[]) {
this.chunks.push(...chunks);
}

constructor(
/**
* Used by wrangler to check whether stdin is interactive.
*/
readonly isTTY: boolean
) {}

/**
* Used by wrangler to add event listeners.
*/
on(eventName: string, callback: () => void) {
switch (eventName) {
case "readable":
setImmediate(callback);
break;
case "end":
this.endCallback = callback;
break;
case "error":
this.errorCallback = callback;
break;
}
}

/**
* Used by wrangler to get the next chunk of data in the stream.
*/
read() {
if (this.error) {
setImmediate(() => this.errorCallback(this.error));
}
if (this.chunks.length === 0) {
setImmediate(this.endCallback);
}
return this.chunks.shift() ?? null;
}
}
Loading

0 comments on commit 16fb5e6

Please sign in to comment.