create-svc 0.1.54 → 0.1.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/git-bootstrap.test.ts +49 -1
- package/src/git-bootstrap.ts +13 -1
- package/src/scaffold.test.ts +8 -0
- package/src/scaffold.ts +7 -3
- package/src/service-runtime/cloudrun/lib.ts +0 -3
- package/templates/shared/.env.example +40 -0
- package/templates/shared/.github/workflows/deploy.yml +1 -0
- package/templates/shared/.github/workflows/preview.yml +1 -0
- package/templates/shared/_gitignore +13 -0
- package/templates/targets/workers/.github/workflows/deploy.yml +1 -0
- package/templates/variants/go-chi/Dockerfile +0 -1
package/package.json
CHANGED
|
@@ -2,7 +2,13 @@ import { expect, test } from "bun:test";
|
|
|
2
2
|
import { mkdir, mkdtemp, realpath, writeFile } from "node:fs/promises";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
buildGitBootstrapConfig,
|
|
7
|
+
commitAndPushGeneratedArtifacts,
|
|
8
|
+
findExistingGitWorktree,
|
|
9
|
+
manualGitHubDeleteCommand,
|
|
10
|
+
markGitHubRepositoryDeleteOnDestroy,
|
|
11
|
+
} from "./git-bootstrap";
|
|
6
12
|
|
|
7
13
|
test("buildGitBootstrapConfig defaults to anmho private repo creation", () => {
|
|
8
14
|
expect(buildGitBootstrapConfig("launch-api", undefined)).toEqual({
|
|
@@ -41,6 +47,36 @@ test("markGitHubRepositoryDeleteOnDestroy records generated repo ownership", asy
|
|
|
41
47
|
expect(await Bun.file(join(root, "service.jsonc")).text()).toContain('"delete_on_destroy": true');
|
|
42
48
|
});
|
|
43
49
|
|
|
50
|
+
test("commitAndPushGeneratedArtifacts excludes local dependencies and dev runtime files", async () => {
|
|
51
|
+
const root = await mkdtemp(join(tmpdir(), "create-svc-git-"));
|
|
52
|
+
const remote = await mkdtemp(join(tmpdir(), "create-svc-remote-"));
|
|
53
|
+
run(["git", "init", "--bare"], remote);
|
|
54
|
+
run(["git", "init", "-b", "main"], root);
|
|
55
|
+
run(["git", "config", "user.name", "create-svc test"], root);
|
|
56
|
+
run(["git", "config", "user.email", "create-svc@example.invalid"], root);
|
|
57
|
+
run(["git", "remote", "add", "origin", remote], root);
|
|
58
|
+
await writeFile(join(root, "README.md"), "# generated\n");
|
|
59
|
+
run(["git", "add", "."], root);
|
|
60
|
+
run(["git", "commit", "-m", "Initial commit"], root);
|
|
61
|
+
run(["git", "push", "-u", "origin", "main"], root);
|
|
62
|
+
|
|
63
|
+
await mkdir(join(root, "node_modules", "large-package"), { recursive: true });
|
|
64
|
+
await mkdir(join(root, ".service"), { recursive: true });
|
|
65
|
+
await mkdir(join(root, ".wrangler", "tmp"), { recursive: true });
|
|
66
|
+
await writeFile(join(root, "node_modules", "large-package", "artifact.bin"), "large");
|
|
67
|
+
await writeFile(join(root, ".service", "local-dev.log"), "log");
|
|
68
|
+
await writeFile(join(root, ".service", "local-dev.pid"), "123");
|
|
69
|
+
await writeFile(join(root, ".wrangler", "tmp", "bundle.js"), "bundle");
|
|
70
|
+
await writeFile(join(root, "service.jsonc"), '{ "deployed": true }\n');
|
|
71
|
+
|
|
72
|
+
expect(commitAndPushGeneratedArtifacts(root, "Record generated deployment artifacts")).toEqual({ committed: true });
|
|
73
|
+
|
|
74
|
+
expect(git(["ls-files"], root)).toContain("service.jsonc");
|
|
75
|
+
expect(git(["ls-files"], root)).not.toContain("node_modules");
|
|
76
|
+
expect(git(["ls-files"], root)).not.toContain(".service/local-dev.log");
|
|
77
|
+
expect(git(["ls-files"], root)).not.toContain(".wrangler");
|
|
78
|
+
});
|
|
79
|
+
|
|
44
80
|
function run(command: string[], cwd: string) {
|
|
45
81
|
const result = Bun.spawnSync(command, {
|
|
46
82
|
cwd,
|
|
@@ -51,3 +87,15 @@ function run(command: string[], cwd: string) {
|
|
|
51
87
|
throw new Error(result.stderr.toString());
|
|
52
88
|
}
|
|
53
89
|
}
|
|
90
|
+
|
|
91
|
+
function git(command: string[], cwd: string) {
|
|
92
|
+
const result = Bun.spawnSync(["git", ...command], {
|
|
93
|
+
cwd,
|
|
94
|
+
stdout: "pipe",
|
|
95
|
+
stderr: "pipe",
|
|
96
|
+
});
|
|
97
|
+
if (result.exitCode !== 0) {
|
|
98
|
+
throw new Error(result.stderr.toString());
|
|
99
|
+
}
|
|
100
|
+
return result.stdout.toString();
|
|
101
|
+
}
|
package/src/git-bootstrap.ts
CHANGED
|
@@ -58,7 +58,19 @@ export async function bootstrapGitHubRepository(targetDir: string, config: GitBo
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
export function commitAndPushGeneratedArtifacts(targetDir: string, message: string) {
|
|
61
|
-
run(
|
|
61
|
+
run(
|
|
62
|
+
[
|
|
63
|
+
"git",
|
|
64
|
+
"add",
|
|
65
|
+
"--all",
|
|
66
|
+
".",
|
|
67
|
+
":!node_modules",
|
|
68
|
+
":!.service/*.log",
|
|
69
|
+
":!.service/*.pid",
|
|
70
|
+
":!.wrangler",
|
|
71
|
+
],
|
|
72
|
+
targetDir
|
|
73
|
+
);
|
|
62
74
|
if (!hasStagedChanges(targetDir)) {
|
|
63
75
|
return { committed: false };
|
|
64
76
|
}
|
package/src/scaffold.test.ts
CHANGED
|
@@ -105,6 +105,8 @@ test("scaffolds all runtime/framework variants with shared cloudrun config", asy
|
|
|
105
105
|
|
|
106
106
|
const gitignore = await Bun.file(join(generatedRoot, ".gitignore")).text();
|
|
107
107
|
expect(gitignore).toContain("node_modules");
|
|
108
|
+
expect(gitignore).toContain(".service/*.log");
|
|
109
|
+
expect(gitignore).toContain(".wrangler");
|
|
108
110
|
expect(await Bun.file(join(generatedRoot, "website", "package.json")).exists()).toBeFalse();
|
|
109
111
|
|
|
110
112
|
const dockerCompose = await Bun.file(join(generatedRoot, "docker-compose.yml")).text();
|
|
@@ -147,6 +149,7 @@ test("scaffolds all runtime/framework variants with shared cloudrun config", asy
|
|
|
147
149
|
const deployWorkflow = await Bun.file(join(generatedRoot, ".github", "workflows", "deploy.yml")).text();
|
|
148
150
|
expect(deployWorkflow).toContain("branches:");
|
|
149
151
|
expect(deployWorkflow).toContain("- main");
|
|
152
|
+
expect(deployWorkflow).toContain("bun install -g create-svc@latest");
|
|
150
153
|
expect(deployWorkflow).toContain("service deploy --ci");
|
|
151
154
|
expect(deployWorkflow).toContain("bun run dashboards");
|
|
152
155
|
expect(deployWorkflow).toContain("GCX_ENABLED");
|
|
@@ -160,6 +163,11 @@ test("scaffolds all runtime/framework variants with shared cloudrun config", asy
|
|
|
160
163
|
expect(goSumExists).toBeTrue();
|
|
161
164
|
const dockerfile = await Bun.file(join(generatedRoot, "Dockerfile")).text();
|
|
162
165
|
expect(dockerfile).toContain("COPY go.mod go.sum ./");
|
|
166
|
+
if (variant.framework === "chi") {
|
|
167
|
+
expect(dockerfile).not.toContain("COPY gen ./gen");
|
|
168
|
+
} else {
|
|
169
|
+
expect(dockerfile).toContain("COPY gen ./gen");
|
|
170
|
+
}
|
|
163
171
|
expect(packageJson).toContain('"dev": "make dev"');
|
|
164
172
|
expect(packageJson).toContain('"service": "service"');
|
|
165
173
|
expect(packageJson).toContain('"migrate": "service migrate"');
|
package/src/scaffold.ts
CHANGED
|
@@ -69,7 +69,7 @@ export async function scaffoldProject(config: ScaffoldConfig) {
|
|
|
69
69
|
continue;
|
|
70
70
|
}
|
|
71
71
|
const sourcePath = join(template.root, relativePath);
|
|
72
|
-
const destinationPath = join(targetDir, relativePath);
|
|
72
|
+
const destinationPath = join(targetDir, templateDestinationPath(relativePath));
|
|
73
73
|
const raw = await Bun.file(sourcePath).text();
|
|
74
74
|
const rendered = renderTemplate(raw, replacements);
|
|
75
75
|
|
|
@@ -81,6 +81,10 @@ export async function scaffoldProject(config: ScaffoldConfig) {
|
|
|
81
81
|
await writeLocalEnvFile(targetDir, replacements);
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
function templateDestinationPath(relativePath: string) {
|
|
85
|
+
return relativePath === "_gitignore" ? ".gitignore" : relativePath;
|
|
86
|
+
}
|
|
87
|
+
|
|
84
88
|
function shouldSkipForTarget(target: DeployTarget, templateKind: "shared" | "variant" | "target", relativePath: string) {
|
|
85
89
|
if (
|
|
86
90
|
relativePath === "scripts/authctl.ts" ||
|
|
@@ -237,9 +241,9 @@ function buildReplacements(config: ScaffoldConfig) {
|
|
|
237
241
|
COMMAND_DEPLOY: "service deploy",
|
|
238
242
|
COMMAND_OBSERVABILITY_BOOTSTRAP:
|
|
239
243
|
config.runtime === "bun" ? "bun run observability-bootstrap" : "make observability-bootstrap",
|
|
240
|
-
WORKFLOW_DEPLOY_MAIN_COMMAND: "
|
|
244
|
+
WORKFLOW_DEPLOY_MAIN_COMMAND: "service deploy --ci",
|
|
241
245
|
WORKFLOW_DEPLOY_PREVIEW_COMMAND:
|
|
242
|
-
"
|
|
246
|
+
"service deploy --ci --environment preview --name ${{ github.ref_name }}",
|
|
243
247
|
WORKFLOW_DEPLOY_MAIN_DOC_COMMAND: "service deploy --ci",
|
|
244
248
|
WORKFLOW_DEPLOY_PREVIEW_DOC_COMMAND: "service deploy --ci --environment preview --name <branch-name>",
|
|
245
249
|
COMMAND_AUTH_RESOURCE: "service auth resource-server",
|
|
@@ -605,7 +605,6 @@ export function ensureProductionDomainMapping(serviceName: string) {
|
|
|
605
605
|
}
|
|
606
606
|
|
|
607
607
|
const result = gcloud([
|
|
608
|
-
"beta",
|
|
609
608
|
"run",
|
|
610
609
|
"domain-mappings",
|
|
611
610
|
"create",
|
|
@@ -627,7 +626,6 @@ export function describeProductionDomainMapping():
|
|
|
627
626
|
| undefined {
|
|
628
627
|
const result = gcloud(
|
|
629
628
|
[
|
|
630
|
-
"beta",
|
|
631
629
|
"run",
|
|
632
630
|
"domain-mappings",
|
|
633
631
|
"describe",
|
|
@@ -680,7 +678,6 @@ export function deleteProductionDomainMapping() {
|
|
|
680
678
|
deleteCloudflareDnsRecord();
|
|
681
679
|
gcloud(
|
|
682
680
|
[
|
|
683
|
-
"beta",
|
|
684
681
|
"run",
|
|
685
682
|
"domain-mappings",
|
|
686
683
|
"delete",
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Local development defaults.
|
|
2
|
+
# The scaffold also writes a ready-to-use .env.local for Docker Compose Postgres.
|
|
3
|
+
|
|
4
|
+
DATABASE_URL=postgres://{{LOCAL_DATABASE_USER}}:{{LOCAL_DATABASE_PASSWORD}}@127.0.0.1:{{LOCAL_DATABASE_PORT}}/{{LOCAL_DATABASE_NAME}}?sslmode=disable
|
|
5
|
+
|
|
6
|
+
TEMPORAL_ENABLED=false
|
|
7
|
+
TEMPORAL_ADDRESS=localhost:7233
|
|
8
|
+
TEMPORAL_NAMESPACE=default
|
|
9
|
+
TEMPORAL_TASK_QUEUE={{SERVICE_ID}}
|
|
10
|
+
# Optional for Temporal Cloud. `service create` writes this to Secret Manager.
|
|
11
|
+
TEMPORAL_API_KEY=
|
|
12
|
+
|
|
13
|
+
AUTH_ENABLED=false
|
|
14
|
+
AUTH_ISSUER=https://auth.anmho.com
|
|
15
|
+
AUTH_AUDIENCE=api://{{SERVICE_ID}}
|
|
16
|
+
AUTH_JWKS_URL=https://auth.anmho.com/api/auth/jwks
|
|
17
|
+
|
|
18
|
+
# Production authctl operations such as `service create` and
|
|
19
|
+
# `service auth client create` load Cloudflare Access values from Vault by
|
|
20
|
+
# default. Direct env values still override Vault when set.
|
|
21
|
+
AUTH_INTERNAL_BASE_URL=https://auth.anmho.com/internal
|
|
22
|
+
CLOUDFLARE_ACCESS_SERVICE_TOKEN_CLIENT_ID=
|
|
23
|
+
CLOUDFLARE_ACCESS_SERVICE_TOKEN_CLIENT_SECRET=
|
|
24
|
+
|
|
25
|
+
# Remote bootstrap and deploy can use Neon admin credentials directly or through Vault.
|
|
26
|
+
# Do not commit VAULT_TOKEN. Prefer `vault login`; the CLI will also use
|
|
27
|
+
# VAULT_TOKEN_FILE or ~/.vault-token when available.
|
|
28
|
+
|
|
29
|
+
VAULT_ADDR=https://vault.example.com
|
|
30
|
+
VAULT_SECRET_MOUNT=secret
|
|
31
|
+
VAULT_AUTHCTL_ACCESS_PATH=prod/apps/auth/authctl/cloudflare-access
|
|
32
|
+
VAULT_AUTHCTL_ACCESS_BASE_URL_FIELD=AUTH_INTERNAL_BASE_URL
|
|
33
|
+
VAULT_AUTHCTL_ACCESS_CLIENT_ID_FIELD=CLOUDFLARE_ACCESS_SERVICE_TOKEN_CLIENT_ID
|
|
34
|
+
VAULT_AUTHCTL_ACCESS_CLIENT_SECRET_FIELD=CLOUDFLARE_ACCESS_SERVICE_TOKEN_CLIENT_SECRET
|
|
35
|
+
VAULT_NEON_API_KEY_PATH=prod/providers/neon
|
|
36
|
+
VAULT_NEON_API_KEY_FIELD=api_key
|
|
37
|
+
|
|
38
|
+
# Optional provider credentials can be read from Vault or environment by
|
|
39
|
+
# generated adapters you add later. The base waitlist service does not require
|
|
40
|
+
# provider secrets.
|
|
@@ -25,6 +25,7 @@ jobs:
|
|
|
25
25
|
service_account: ${{ vars.GCP_DEPLOYER_SERVICE_ACCOUNT }}
|
|
26
26
|
- uses: google-github-actions/setup-gcloud@v2
|
|
27
27
|
- run: bun install
|
|
28
|
+
- run: bun install -g create-svc@latest
|
|
28
29
|
- run: {{WORKFLOW_DEPLOY_MAIN_COMMAND}}
|
|
29
30
|
env:
|
|
30
31
|
NEON_API_KEY: ${{ secrets.NEON_API_KEY }}
|
|
@@ -25,6 +25,7 @@ jobs:
|
|
|
25
25
|
service_account: ${{ vars.GCP_DEPLOYER_SERVICE_ACCOUNT }}
|
|
26
26
|
- uses: google-github-actions/setup-gcloud@v2
|
|
27
27
|
- run: bun install
|
|
28
|
+
- run: bun install -g create-svc@latest
|
|
28
29
|
- run: {{WORKFLOW_DEPLOY_PREVIEW_COMMAND}}
|
|
29
30
|
env:
|
|
30
31
|
NEON_API_KEY: ${{ secrets.NEON_API_KEY }}
|