Skip to content
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
57 changes: 57 additions & 0 deletions apps/cli/src/__tests__/command-output/machine-environment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,61 @@ describe("machine env commands", () => {
});
}
});
it.each([
["split UTF-8", "café € 🌍\n", "café € 🌍"],
["empty input", "", ""],
["CRLF", "value\r\n", "value"],
["one final newline", "value\n\n", "value\n"],
["ASCII byte limit", "a".repeat(65536), "a".repeat(65536)],
["UTF-8 byte limit", "é".repeat(32768), "é".repeat(32768)],
["over byte limit", "é".repeat(32768) + "a", null],
["limit before newline removal", "a".repeat(65536) + "\n", null],
] as const)(
"preserves stdin semantics: %s",
async (_label, input, expected) => {
const requests: Request[] = [];
vi.mocked(fetch).mockImplementation(async (url, init) => {
requests.push(new Request(url, init));
return Response.json(result);
});
const bytes = Buffer.from(input);
vi.spyOn(process.stdin, Symbol.asyncIterator).mockImplementation(
async function* () {
const start = Math.max(0, bytes.length - 16);
yield bytes.subarray(0, start);
for (let i = start; i < bytes.length; i++)
yield bytes.subarray(i, i + 1);
},
);
const descriptor = Object.getOwnPropertyDescriptor(
process.stdin,
"isTTY",
)!;
Object.defineProperty(process.stdin, "isTTY", { value: false });
try {
const run = runCommand(
["machine", "env", "set", "VALUE", "--json"],
register,
);
if (expected === null) {
await expect(run).rejects.toThrow("process.exit:1");
expect(requests.map((request) => request.method)).toEqual(["GET"]);
expect(collectLogPayloads(vi.mocked(console.error))).toContain(
"Error: Environment value exceeds 65536 bytes.",
);
} else {
await run;
expect(await requests[1].json()).toEqual({
variables: [
{ name: "GH_TOKEN", value: null, note: null },
{ name: "VALUE", value: expected, note: null },
],
});
expect(requests[1].method).toBe("PUT");
}
} finally {
Object.defineProperty(process.stdin, "isTTY", descriptor);
}
},
);
});
13 changes: 9 additions & 4 deletions apps/cli/src/commands/machine-environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,18 @@ async function readValue(): Promise<string> {
throw new Error(
"Pipe the value to stdin; environment values are never accepted in command arguments.",
);
let value = "";
const chunks: Buffer[] = [];
let bytes = 0;
for await (const chunk of process.stdin) {
value += String(chunk);
if (Buffer.byteLength(value) > 65536)
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
bytes += buffer.byteLength;
if (bytes > 65536)
throw new Error("Environment value exceeds 65536 bytes.");
chunks.push(buffer);
}
return value.replace(/\r?\n$/u, "");
return Buffer.concat(chunks)
.toString("utf8")
.replace(/\r?\n$/u, "");
}

export function registerMachineEnvironmentCommands(
Expand Down
Loading