fix: update path for TS test helpers
This commit is contained in:
parent
4bbeb50dd5
commit
25b7225a5b
@ -1,2 +1,3 @@
|
|||||||
# hub
|
# hub
|
||||||
|
|
||||||
Publish Coder modules and templates for other developers to use.
|
Publish Coder modules and templates for other developers to use.
|
||||||
|
|||||||
28
package.json
Normal file
28
package.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": "modules",
|
||||||
|
"scripts": {
|
||||||
|
"test": "bun test",
|
||||||
|
"fmt": "bun x prettier -w **/*.sh .sample/run.sh new.sh **/*.ts **/*.md *.md && terraform fmt **/*.tf .sample/main.tf",
|
||||||
|
"fmt:ci": "bun x prettier --check **/*.sh .sample/run.sh new.sh **/*.ts **/*.md *.md && terraform fmt -check **/*.tf .sample/main.tf",
|
||||||
|
"lint": "bun run lint.ts && ./terraform_validate.sh",
|
||||||
|
"update-version": "./update-version.sh"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "^1.2.9",
|
||||||
|
"bun-types": "^1.1.23",
|
||||||
|
"gray-matter": "^4.0.3",
|
||||||
|
"marked": "^12.0.2",
|
||||||
|
"prettier": "^3.3.3",
|
||||||
|
"prettier-plugin-sh": "^0.13.1",
|
||||||
|
"prettier-plugin-terraform-formatter": "^1.2.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5.5.4"
|
||||||
|
},
|
||||||
|
"prettier": {
|
||||||
|
"plugins": [
|
||||||
|
"prettier-plugin-sh",
|
||||||
|
"prettier-plugin-terraform-formatter"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,7 +3,7 @@ import {
|
|||||||
runTerraformApply,
|
runTerraformApply,
|
||||||
runTerraformInit,
|
runTerraformInit,
|
||||||
testRequiredVariables,
|
testRequiredVariables,
|
||||||
} from "../test";
|
} from "~test";
|
||||||
|
|
||||||
describe("aws-region", async () => {
|
describe("aws-region", async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import {
|
|||||||
runTerraformApply,
|
runTerraformApply,
|
||||||
runTerraformInit,
|
runTerraformInit,
|
||||||
testRequiredVariables,
|
testRequiredVariables,
|
||||||
} from "../test";
|
} from "~test";
|
||||||
|
|
||||||
describe("azure-region", async () => {
|
describe("azure-region", async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import {
|
|||||||
runTerraformApply,
|
runTerraformApply,
|
||||||
runTerraformInit,
|
runTerraformInit,
|
||||||
testRequiredVariables,
|
testRequiredVariables,
|
||||||
} from "../test";
|
} from "~test";
|
||||||
|
|
||||||
describe("code-server", async () => {
|
describe("code-server", async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { describe } from "bun:test";
|
import { describe } from "bun:test";
|
||||||
import { runTerraformInit, testRequiredVariables } from "../test";
|
import { runTerraformInit, testRequiredVariables } from "~test";
|
||||||
|
|
||||||
describe("coder-login", async () => {
|
describe("coder-login", async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import {
|
|||||||
runTerraformApply,
|
runTerraformApply,
|
||||||
runTerraformInit,
|
runTerraformInit,
|
||||||
testRequiredVariables,
|
testRequiredVariables,
|
||||||
} from "../test";
|
} from "~test";
|
||||||
|
|
||||||
describe("cursor", async () => {
|
describe("cursor", async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import {
|
|||||||
runTerraformApply,
|
runTerraformApply,
|
||||||
runTerraformInit,
|
runTerraformInit,
|
||||||
testRequiredVariables,
|
testRequiredVariables,
|
||||||
} from "../test";
|
} from "~test";
|
||||||
|
|
||||||
describe("dotfiles", async () => {
|
describe("dotfiles", async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import {
|
|||||||
runTerraformApply,
|
runTerraformApply,
|
||||||
runTerraformInit,
|
runTerraformInit,
|
||||||
testRequiredVariables,
|
testRequiredVariables,
|
||||||
} from "../test";
|
} from "~test";
|
||||||
|
|
||||||
describe("fly-region", async () => {
|
describe("fly-region", async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import {
|
|||||||
runTerraformApply,
|
runTerraformApply,
|
||||||
runTerraformInit,
|
runTerraformInit,
|
||||||
testRequiredVariables,
|
testRequiredVariables,
|
||||||
} from "../test";
|
} from "~test";
|
||||||
|
|
||||||
describe("gcp-region", async () => {
|
describe("gcp-region", async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import {
|
|||||||
runTerraformApply,
|
runTerraformApply,
|
||||||
runTerraformInit,
|
runTerraformInit,
|
||||||
testRequiredVariables,
|
testRequiredVariables,
|
||||||
} from "../test";
|
} from "~test";
|
||||||
|
|
||||||
describe("git-clone", async () => {
|
describe("git-clone", async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import {
|
|||||||
runTerraformApply,
|
runTerraformApply,
|
||||||
runTerraformInit,
|
runTerraformInit,
|
||||||
testRequiredVariables,
|
testRequiredVariables,
|
||||||
} from "../test";
|
} from "~test";
|
||||||
|
|
||||||
describe("git-config", async () => {
|
describe("git-config", async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import {
|
|||||||
runTerraformInit,
|
runTerraformInit,
|
||||||
testRequiredVariables,
|
testRequiredVariables,
|
||||||
writeCoder,
|
writeCoder,
|
||||||
} from "../test";
|
} from "~test";
|
||||||
|
|
||||||
describe("github-upload-public-key", async () => {
|
describe("github-upload-public-key", async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import {
|
|||||||
runTerraformInit,
|
runTerraformInit,
|
||||||
testRequiredVariables,
|
testRequiredVariables,
|
||||||
runTerraformApply,
|
runTerraformApply,
|
||||||
} from "../test";
|
} from "~test";
|
||||||
|
|
||||||
describe("jetbrains-gateway", async () => {
|
describe("jetbrains-gateway", async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import {
|
|||||||
runTerraformInit,
|
runTerraformInit,
|
||||||
runTerraformApply,
|
runTerraformApply,
|
||||||
testRequiredVariables,
|
testRequiredVariables,
|
||||||
} from "../test";
|
} from "~test";
|
||||||
|
|
||||||
describe("jfrog-oauth", async () => {
|
describe("jfrog-oauth", async () => {
|
||||||
type TestVariables = {
|
type TestVariables = {
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import {
|
|||||||
runTerraformInit,
|
runTerraformInit,
|
||||||
runTerraformApply,
|
runTerraformApply,
|
||||||
testRequiredVariables,
|
testRequiredVariables,
|
||||||
} from "../test";
|
} from "~test";
|
||||||
|
|
||||||
describe("jfrog-token", async () => {
|
describe("jfrog-token", async () => {
|
||||||
type TestVariables = {
|
type TestVariables = {
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import {
|
|||||||
runTerraformInit,
|
runTerraformInit,
|
||||||
testRequiredVariables,
|
testRequiredVariables,
|
||||||
type TerraformState,
|
type TerraformState,
|
||||||
} from "../test";
|
} from "~test";
|
||||||
|
|
||||||
// executes the coder script after installing pip
|
// executes the coder script after installing pip
|
||||||
const executeScriptInContainerWithPip = async (
|
const executeScriptInContainerWithPip = async (
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import {
|
|||||||
runTerraformApply,
|
runTerraformApply,
|
||||||
runTerraformInit,
|
runTerraformInit,
|
||||||
testRequiredVariables,
|
testRequiredVariables,
|
||||||
} from "../test";
|
} from "~test";
|
||||||
|
|
||||||
const allowedDesktopEnvs = ["xfce", "kde", "gnome", "lxde", "lxqt"] as const;
|
const allowedDesktopEnvs = ["xfce", "kde", "gnome", "lxde", "lxqt"] as const;
|
||||||
type AllowedDesktopEnv = (typeof allowedDesktopEnvs)[number];
|
type AllowedDesktopEnv = (typeof allowedDesktopEnvs)[number];
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import {
|
|||||||
runTerraformApply,
|
runTerraformApply,
|
||||||
runTerraformInit,
|
runTerraformInit,
|
||||||
testRequiredVariables,
|
testRequiredVariables,
|
||||||
} from "../test";
|
} from "~test";
|
||||||
|
|
||||||
describe("personalize", async () => {
|
describe("personalize", async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import {
|
|||||||
runTerraformInit,
|
runTerraformInit,
|
||||||
testRequiredVariables,
|
testRequiredVariables,
|
||||||
writeCoder,
|
writeCoder,
|
||||||
} from "../test";
|
} from "~test";
|
||||||
|
|
||||||
describe("slackme", async () => {
|
describe("slackme", async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { describe } from "bun:test";
|
import { describe } from "bun:test";
|
||||||
import { runTerraformInit, testRequiredVariables } from "../test";
|
import { runTerraformInit, testRequiredVariables } from "~test";
|
||||||
|
|
||||||
describe("vault-github", async () => {
|
describe("vault-github", async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { describe } from "bun:test";
|
import { describe } from "bun:test";
|
||||||
import { runTerraformInit, testRequiredVariables } from "../test";
|
import { runTerraformInit, testRequiredVariables } from "~test";
|
||||||
|
|
||||||
describe("vault-jwt", async () => {
|
describe("vault-jwt", async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { describe } from "bun:test";
|
import { describe } from "bun:test";
|
||||||
import { runTerraformInit, testRequiredVariables } from "../test";
|
import { runTerraformInit, testRequiredVariables } from "~test";
|
||||||
|
|
||||||
describe("vault-token", async () => {
|
describe("vault-token", async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import {
|
|||||||
runTerraformApply,
|
runTerraformApply,
|
||||||
runTerraformInit,
|
runTerraformInit,
|
||||||
testRequiredVariables,
|
testRequiredVariables,
|
||||||
} from "../test";
|
} from "~test";
|
||||||
|
|
||||||
describe("vscode-desktop", async () => {
|
describe("vscode-desktop", async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, it } from "bun:test";
|
import { describe, expect, it } from "bun:test";
|
||||||
import { runTerraformApply, runTerraformInit } from "../test";
|
import { runTerraformApply, runTerraformInit } from "~test";
|
||||||
|
|
||||||
describe("vscode-web", async () => {
|
describe("vscode-web", async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import {
|
|||||||
runTerraformApply,
|
runTerraformApply,
|
||||||
runTerraformInit,
|
runTerraformInit,
|
||||||
testRequiredVariables,
|
testRequiredVariables,
|
||||||
} from "../test";
|
} from "~test";
|
||||||
|
|
||||||
type TestVariables = Readonly<{
|
type TestVariables = Readonly<{
|
||||||
agent_id: string;
|
agent_id: string;
|
||||||
|
|||||||
@ -20,7 +20,7 @@ module "nodejs" {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Install multiple versions
|
## Install multiple versions
|
||||||
|
|
||||||
This installs multiple versions of Node.js:
|
This installs multiple versions of Node.js:
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { describe } from "bun:test";
|
import { describe } from "bun:test";
|
||||||
import { runTerraformInit, testRequiredVariables } from "../test";
|
import { runTerraformInit, testRequiredVariables } from "~test";
|
||||||
|
|
||||||
describe("nodejs", async () => {
|
describe("nodejs", async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import {
|
|||||||
runTerraformApply,
|
runTerraformApply,
|
||||||
runTerraformInit,
|
runTerraformInit,
|
||||||
testRequiredVariables,
|
testRequiredVariables,
|
||||||
} from "../test";
|
} from "~test";
|
||||||
|
|
||||||
describe("exoscale-instance-type", async () => {
|
describe("exoscale-instance-type", async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import {
|
|||||||
runTerraformApply,
|
runTerraformApply,
|
||||||
runTerraformInit,
|
runTerraformInit,
|
||||||
testRequiredVariables,
|
testRequiredVariables,
|
||||||
} from "../test";
|
} from "~test";
|
||||||
|
|
||||||
describe("exoscale-zone", async () => {
|
describe("exoscale-zone", async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
|
|||||||
271
test/test.ts
Normal file
271
test/test.ts
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
import { readableStreamToText, spawn } from "bun";
|
||||||
|
import { expect, it } from "bun:test";
|
||||||
|
import { readFile, unlink } from "node:fs/promises";
|
||||||
|
|
||||||
|
export const runContainer = async (
|
||||||
|
image: string,
|
||||||
|
init = "sleep infinity",
|
||||||
|
): Promise<string> => {
|
||||||
|
const proc = spawn([
|
||||||
|
"docker",
|
||||||
|
"run",
|
||||||
|
"--rm",
|
||||||
|
"-d",
|
||||||
|
"--label",
|
||||||
|
"modules-test=true",
|
||||||
|
"--network",
|
||||||
|
"host",
|
||||||
|
"--entrypoint",
|
||||||
|
"sh",
|
||||||
|
image,
|
||||||
|
"-c",
|
||||||
|
init,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const containerID = await readableStreamToText(proc.stdout);
|
||||||
|
const exitCode = await proc.exited;
|
||||||
|
if (exitCode !== 0) {
|
||||||
|
throw new Error(containerID);
|
||||||
|
}
|
||||||
|
return containerID.trim();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the only "coder_script" resource in the given state and runs it in a
|
||||||
|
* container.
|
||||||
|
*/
|
||||||
|
export const executeScriptInContainer = async (
|
||||||
|
state: TerraformState,
|
||||||
|
image: string,
|
||||||
|
shell = "sh",
|
||||||
|
): Promise<{
|
||||||
|
exitCode: number;
|
||||||
|
stdout: string[];
|
||||||
|
stderr: string[];
|
||||||
|
}> => {
|
||||||
|
const instance = findResourceInstance(state, "coder_script");
|
||||||
|
const id = await runContainer(image);
|
||||||
|
const resp = await execContainer(id, [shell, "-c", instance.script]);
|
||||||
|
const stdout = resp.stdout.trim().split("\n");
|
||||||
|
const stderr = resp.stderr.trim().split("\n");
|
||||||
|
return {
|
||||||
|
exitCode: resp.exitCode,
|
||||||
|
stdout,
|
||||||
|
stderr,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const execContainer = async (
|
||||||
|
id: string,
|
||||||
|
cmd: string[],
|
||||||
|
): Promise<{
|
||||||
|
exitCode: number;
|
||||||
|
stderr: string;
|
||||||
|
stdout: string;
|
||||||
|
}> => {
|
||||||
|
const proc = spawn(["docker", "exec", id, ...cmd], {
|
||||||
|
stderr: "pipe",
|
||||||
|
stdout: "pipe",
|
||||||
|
});
|
||||||
|
const [stderr, stdout] = await Promise.all([
|
||||||
|
readableStreamToText(proc.stderr),
|
||||||
|
readableStreamToText(proc.stdout),
|
||||||
|
]);
|
||||||
|
const exitCode = await proc.exited;
|
||||||
|
return {
|
||||||
|
exitCode,
|
||||||
|
stderr,
|
||||||
|
stdout,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type JsonValue =
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| boolean
|
||||||
|
| null
|
||||||
|
| JsonValue[]
|
||||||
|
| { [key: string]: JsonValue };
|
||||||
|
|
||||||
|
type TerraformStateResource = {
|
||||||
|
type: string;
|
||||||
|
name: string;
|
||||||
|
provider: string;
|
||||||
|
|
||||||
|
instances: [
|
||||||
|
{
|
||||||
|
attributes: Record<string, JsonValue>;
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
type TerraformOutput = {
|
||||||
|
type: string;
|
||||||
|
value: JsonValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface TerraformState {
|
||||||
|
outputs: Record<string, TerraformOutput>;
|
||||||
|
resources: [TerraformStateResource, ...TerraformStateResource[]];
|
||||||
|
}
|
||||||
|
|
||||||
|
type TerraformVariables = Record<string, JsonValue>;
|
||||||
|
|
||||||
|
export interface CoderScriptAttributes {
|
||||||
|
script: string;
|
||||||
|
agent_id: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ResourceInstance<T extends string = string> =
|
||||||
|
T extends "coder_script" ? CoderScriptAttributes : Record<string, string>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* finds the first instance of the given resource type in the given state. If
|
||||||
|
* name is specified, it will only find the instance with the given name.
|
||||||
|
*/
|
||||||
|
export const findResourceInstance = <T extends string>(
|
||||||
|
state: TerraformState,
|
||||||
|
type: T,
|
||||||
|
name?: string,
|
||||||
|
): ResourceInstance<T> => {
|
||||||
|
const resource = state.resources.find(
|
||||||
|
(resource) =>
|
||||||
|
resource.type === type && (name ? resource.name === name : true),
|
||||||
|
);
|
||||||
|
if (!resource) {
|
||||||
|
throw new Error(`Resource ${type} not found`);
|
||||||
|
}
|
||||||
|
if (resource.instances.length !== 1) {
|
||||||
|
throw new Error(
|
||||||
|
`Resource ${type} has ${resource.instances.length} instances`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resource.instances[0].attributes as ResourceInstance<T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a test-case for each variable provided and ensures that the apply
|
||||||
|
* fails without it.
|
||||||
|
*/
|
||||||
|
export const testRequiredVariables = <TVars extends TerraformVariables>(
|
||||||
|
dir: string,
|
||||||
|
vars: Readonly<TVars>,
|
||||||
|
) => {
|
||||||
|
// Ensures that all required variables are provided.
|
||||||
|
it("required variables", async () => {
|
||||||
|
await runTerraformApply(dir, vars);
|
||||||
|
});
|
||||||
|
|
||||||
|
const varNames = Object.keys(vars);
|
||||||
|
for (const varName of varNames) {
|
||||||
|
// Ensures that every variable provided is required!
|
||||||
|
it(`missing variable: ${varName}`, async () => {
|
||||||
|
const localVars: TerraformVariables = {};
|
||||||
|
for (const otherVarName of varNames) {
|
||||||
|
if (otherVarName !== varName) {
|
||||||
|
localVars[otherVarName] = vars[otherVarName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await runTerraformApply(dir, localVars);
|
||||||
|
} catch (ex) {
|
||||||
|
if (!(ex instanceof Error)) {
|
||||||
|
throw new Error("Unknown error generated");
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(ex.message).toContain(
|
||||||
|
`input variable \"${varName}\" is not set`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new Error(`${varName} is not a required variable!`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs terraform apply in the given directory with the given variables. It is
|
||||||
|
* fine to run in parallel with other instances of this function, as it uses a
|
||||||
|
* random state file.
|
||||||
|
*/
|
||||||
|
export const runTerraformApply = async <TVars extends TerraformVariables>(
|
||||||
|
dir: string,
|
||||||
|
vars: Readonly<TVars>,
|
||||||
|
customEnv?: Record<string, string>,
|
||||||
|
): Promise<TerraformState> => {
|
||||||
|
const stateFile = `${dir}/${crypto.randomUUID()}.tfstate`;
|
||||||
|
|
||||||
|
const childEnv: Record<string, string | undefined> = {
|
||||||
|
...process.env,
|
||||||
|
...(customEnv ?? {}),
|
||||||
|
};
|
||||||
|
for (const [key, value] of Object.entries(vars) as [string, JsonValue][]) {
|
||||||
|
if (value !== null) {
|
||||||
|
childEnv[`TF_VAR_${key}`] = String(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const proc = spawn(
|
||||||
|
[
|
||||||
|
"terraform",
|
||||||
|
"apply",
|
||||||
|
"-compact-warnings",
|
||||||
|
"-input=false",
|
||||||
|
"-auto-approve",
|
||||||
|
"-state",
|
||||||
|
"-no-color",
|
||||||
|
stateFile,
|
||||||
|
],
|
||||||
|
{
|
||||||
|
cwd: dir,
|
||||||
|
env: childEnv,
|
||||||
|
stderr: "pipe",
|
||||||
|
stdout: "pipe",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const text = await readableStreamToText(proc.stderr);
|
||||||
|
const exitCode = await proc.exited;
|
||||||
|
if (exitCode !== 0) {
|
||||||
|
throw new Error(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = await readFile(stateFile, "utf8");
|
||||||
|
await unlink(stateFile);
|
||||||
|
return JSON.parse(content);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs terraform init in the given directory.
|
||||||
|
*/
|
||||||
|
export const runTerraformInit = async (dir: string) => {
|
||||||
|
const proc = spawn(["terraform", "init"], {
|
||||||
|
cwd: dir,
|
||||||
|
});
|
||||||
|
const text = await readableStreamToText(proc.stdout);
|
||||||
|
const exitCode = await proc.exited;
|
||||||
|
if (exitCode !== 0) {
|
||||||
|
throw new Error(text);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createJSONResponse = (obj: object, statusCode = 200): Response => {
|
||||||
|
return new Response(JSON.stringify(obj), {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
status: statusCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const writeCoder = async (id: string, script: string) => {
|
||||||
|
const exec = await execContainer(id, [
|
||||||
|
"sh",
|
||||||
|
"-c",
|
||||||
|
`echo '${script}' > /usr/bin/coder && chmod +x /usr/bin/coder`,
|
||||||
|
]);
|
||||||
|
expect(exec.exitCode).toBe(0);
|
||||||
|
};
|
||||||
21
tsconfig.json
Normal file
21
tsconfig.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
// If we were just compiling for the tests, we could safely target ESNext at
|
||||||
|
// all times, but just because we've been starting to add more runtime logic
|
||||||
|
// files to some of the modules, erring on the side of caution by having a
|
||||||
|
// older compilation target
|
||||||
|
"target": "ES2024",
|
||||||
|
"module": "esnext",
|
||||||
|
"strict": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"types": ["bun-types"],
|
||||||
|
|
||||||
|
"paths": {
|
||||||
|
// Not the biggest fan of relative paths in TypeScript projects, but it
|
||||||
|
// does make things easier for non-Coder contributors to get tests
|
||||||
|
// imported and set up
|
||||||
|
"~test": ["./test/test.ts"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user