create-svc 0.1.24 → 0.1.25

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.
Files changed (32) hide show
  1. package/package.json +3 -1
  2. package/src/post-scaffold.test.ts +10 -10
  3. package/src/post-scaffold.ts +4 -9
  4. package/src/scaffold.test.ts +42 -101
  5. package/src/scaffold.ts +10 -3
  6. package/{templates/shared/scripts → src/service-runtime}/authctl.ts +3 -3
  7. package/{templates/shared/scripts → src/service-runtime}/cloudrun/cleanup.ts +1 -1
  8. package/{templates/shared/scripts → src/service-runtime}/cloudrun/cli.ts +1 -1
  9. package/src/service-runtime/cloudrun/config.ts +55 -0
  10. package/src/service-runtime/runtime.ts +8 -0
  11. package/{templates/targets/workers/scripts → src/service-runtime}/workers/cli.ts +7 -6
  12. package/src/service.test.ts +0 -2
  13. package/src/service.ts +13 -19
  14. package/templates/shared/README.md +1 -1
  15. package/templates/shared/service.config.ts +31 -0
  16. package/templates/targets/workers/Makefile +1 -1
  17. package/templates/targets/workers/package.json +8 -11
  18. package/templates/variants/bun-connectrpc/Makefile +1 -1
  19. package/templates/variants/bun-connectrpc/package.json +7 -10
  20. package/templates/variants/bun-hono/Makefile +1 -1
  21. package/templates/variants/bun-hono/package.json +7 -10
  22. package/templates/variants/go-chi/Makefile +1 -1
  23. package/templates/variants/go-chi/go.mod +30 -1
  24. package/templates/variants/go-chi/package.json +7 -10
  25. package/templates/variants/go-connectrpc/Makefile +1 -1
  26. package/templates/variants/go-connectrpc/go.mod +30 -2
  27. package/templates/variants/go-connectrpc/package.json +7 -10
  28. package/templates/shared/scripts/cloudrun/config.ts +0 -62
  29. /package/{templates/shared/scripts → src/service-runtime}/cloudrun/bootstrap.ts +0 -0
  30. /package/{templates/shared/scripts → src/service-runtime}/cloudrun/deploy.ts +0 -0
  31. /package/{templates/shared/scripts → src/service-runtime}/cloudrun/lib.ts +0 -0
  32. /package/{templates/shared/scripts → src/service-runtime}/cloudrun/neon.ts +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-svc",
3
- "version": "0.1.24",
3
+ "version": "0.1.25",
4
4
  "description": "Local microservice bootstrap CLI for Cloud Run and Workers services with Neon-backed data.",
5
5
  "module": "index.ts",
6
6
  "type": "module",
@@ -43,6 +43,7 @@
43
43
  ],
44
44
  "packageManager": "bun@1.3.2",
45
45
  "devDependencies": {
46
+ "@types/pg": "^8.16.0",
46
47
  "@types/bun": "latest"
47
48
  },
48
49
  "peerDependencies": {
@@ -51,6 +52,7 @@
51
52
  "dependencies": {
52
53
  "@clack/prompts": "^1.4.0",
53
54
  "@neondatabase/api-client": "^2.7.1",
55
+ "pg": "^8.16.3",
54
56
  "picocolors": "^1.1.1"
55
57
  }
56
58
  }
@@ -4,23 +4,23 @@ import { buildDeploymentVerificationCommands, buildPostScaffoldCommands } from "
4
4
  describe("buildPostScaffoldCommands", () => {
5
5
  test("runs create and deploy for HTTP services", () => {
6
6
  expect(buildPostScaffoldCommands({ framework: "hono" })).toEqual([
7
- { command: "bun", args: ["./scripts/cloudrun/cli.ts", "create"] },
8
- { command: "bun", args: ["./scripts/cloudrun/cli.ts", "deploy"] },
7
+ { command: "service", args: ["create"] },
8
+ { command: "service", args: ["deploy"] },
9
9
  ]);
10
10
  });
11
11
 
12
12
  test("builds SDK artifacts before create and deploy for ConnectRPC services", () => {
13
13
  expect(buildPostScaffoldCommands({ framework: "connectrpc" })).toEqual([
14
- { command: "bun", args: ["./scripts/cloudrun/cli.ts", "sdk", "build"] },
15
- { command: "bun", args: ["./scripts/cloudrun/cli.ts", "create"] },
16
- { command: "bun", args: ["./scripts/cloudrun/cli.ts", "deploy"] },
14
+ { command: "service", args: ["sdk", "build"] },
15
+ { command: "service", args: ["create"] },
16
+ { command: "service", args: ["deploy"] },
17
17
  ]);
18
18
  });
19
19
 
20
20
  test("uses the workers service CLI for workers services", () => {
21
21
  expect(buildPostScaffoldCommands({ target: "workers", framework: "hono" })).toEqual([
22
- { command: "bun", args: ["./scripts/workers/cli.ts", "create"] },
23
- { command: "bun", args: ["./scripts/workers/cli.ts", "deploy"] },
22
+ { command: "service", args: ["create"] },
23
+ { command: "service", args: ["deploy"] },
24
24
  ]);
25
25
  });
26
26
  });
@@ -34,7 +34,7 @@ describe("buildDeploymentVerificationCommands", () => {
34
34
  command: "sh",
35
35
  args: [
36
36
  "-c",
37
- 'TOKEN="$(bun ./scripts/cloudrun/cli.ts auth token)" && curl --fail --show-error --silent -H "Authorization: Bearer $TOKEN" "https://api.launch.anmho.com/v1/admin/waitlist?limit=1"',
37
+ 'TOKEN="$(service auth token)" && curl --fail --show-error --silent -H "Authorization: Bearer $TOKEN" "https://api.launch.anmho.com/v1/admin/waitlist?limit=1"',
38
38
  ],
39
39
  },
40
40
  ]);
@@ -64,7 +64,7 @@ describe("buildDeploymentVerificationCommands", () => {
64
64
  command: "sh",
65
65
  args: [
66
66
  "-c",
67
- 'TOKEN="$(bun ./scripts/cloudrun/cli.ts auth token)" && grpcurl -H "Authorization: Bearer $TOKEN" -d \'{"limit":1}\' -proto protos/waitlist/v1/waitlist.proto "api.launch.anmho.com:443" waitlist.v1.WaitlistService/ListWaitlistEntries',
67
+ 'TOKEN="$(service auth token)" && grpcurl -H "Authorization: Bearer $TOKEN" -d \'{"limit":1}\' -proto protos/waitlist/v1/waitlist.proto "api.launch.anmho.com:443" waitlist.v1.WaitlistService/ListWaitlistEntries',
68
68
  ],
69
69
  });
70
70
  });
@@ -81,7 +81,7 @@ describe("buildDeploymentVerificationCommands", () => {
81
81
  command: "sh",
82
82
  args: [
83
83
  "-c",
84
- 'TOKEN="$(bun ./scripts/workers/cli.ts auth token)" && curl --fail --show-error --silent -H "Authorization: Bearer $TOKEN" "https://api.launch.anmho.com/v1/admin/waitlist?limit=1"',
84
+ 'TOKEN="$(service auth token)" && curl --fail --show-error --silent -H "Authorization: Bearer $TOKEN" "https://api.launch.anmho.com/v1/admin/waitlist?limit=1"',
85
85
  ],
86
86
  });
87
87
  });
@@ -59,7 +59,7 @@ export function buildDeploymentVerificationCommands(
59
59
  Partial<Pick<ScaffoldConfig, "target" | "serviceName" | "gcpProject" | "region">>
60
60
  ): PostScaffoldCommand[] {
61
61
  const origin = verificationOrigin(config);
62
- const tokenCommand = `TOKEN="$(bun ${serviceCliPath(config)} auth token)"`;
62
+ const tokenCommand = 'TOKEN="$(service auth token)"';
63
63
  return [
64
64
  shellVerificationCommand(`curl --fail --show-error --silent "${origin}/"`),
65
65
  shellVerificationCommand(`curl --fail --show-error --silent "${origin}/readyz"`),
@@ -148,18 +148,13 @@ function verificationHost(
148
148
  export function buildPostScaffoldCommands(
149
149
  config: Pick<ScaffoldConfig, "framework"> & Partial<Pick<ScaffoldConfig, "target">>
150
150
  ): PostScaffoldCommand[] {
151
- const serviceCli = serviceCliPath(config);
152
151
  return [
153
- ...(config.target !== "workers" && config.framework === "connectrpc" ? [{ command: "bun", args: [serviceCli, "sdk", "build"] }] : []),
154
- { command: "bun", args: [serviceCli, "create"] },
155
- { command: "bun", args: [serviceCli, "deploy"] },
152
+ ...(config.target !== "workers" && config.framework === "connectrpc" ? [{ command: "service", args: ["sdk", "build"] }] : []),
153
+ { command: "service", args: ["create"] },
154
+ { command: "service", args: ["deploy"] },
156
155
  ];
157
156
  }
158
157
 
159
- function serviceCliPath(config: Partial<Pick<ScaffoldConfig, "target">>) {
160
- return config.target === "workers" ? "./scripts/workers/cli.ts" : "./scripts/cloudrun/cli.ts";
161
- }
162
-
163
158
  function installProjectDependencies(cwd: string) {
164
159
  requireCommand("bun");
165
160
  run("bun", ["install"], { cwd });
@@ -54,63 +54,31 @@ test("scaffolds all runtime/framework variants with shared cloudrun config", asy
54
54
  })
55
55
  );
56
56
 
57
- const configScript = await Bun.file(join(generatedRoot, "scripts", "cloudrun", "config.ts")).text();
58
57
  const serviceConfig = await Bun.file(join(generatedRoot, "service.config.ts")).text();
59
58
  expect(serviceConfig).toContain('service_id: "dns-api"');
60
59
  expect(serviceConfig).toContain('target: "cloudrun"');
60
+ expect(serviceConfig).toContain('profile: "microservice"');
61
+ expect(serviceConfig).toContain('domain: "waitlist"');
62
+ expect(serviceConfig).toContain('kind: "microservice"');
63
+ expect(serviceConfig).toContain(`runtime: "${variant.runtime}"`);
64
+ expect(serviceConfig).toContain(`framework: "${variant.framework}"`);
61
65
  expect(serviceConfig).toContain('module: "buf.build/anmho/dns-api"');
62
66
  expect(serviceConfig).toContain('cloudflare_vault_path: "prod/providers/cloudflare"');
63
67
  expect(serviceConfig).toContain('issuer: "https://auth.anmho.com/api/auth"');
64
68
  expect(serviceConfig).toContain('audience: "api://dns-api"');
65
69
  expect(serviceConfig).toContain('vault_path_prefix: "prod/apps/dns-api/server/oauth-clients"');
66
70
  expect(serviceConfig).toContain('api_key_secret_name: "dns-api-temporal-api-key"');
67
- expect(configScript).toContain('profile: "microservice"');
68
- expect(configScript).toContain('domain: "waitlist"');
69
- expect(configScript).toContain('kind: "microservice"');
70
- expect(configScript).toContain(`runtime: "${variant.runtime}"`);
71
- expect(configScript).toContain(`framework: "${variant.framework}"`);
72
- expect(configScript).toContain('mode: "create_new"');
73
- expect(configScript).toContain('quotaProjectId: "anmho-infra-prod"');
74
- expect(configScript).toContain('issuer: "https://auth.anmho.com/api/auth"');
75
- expect(configScript).toContain('audience: "api://dns-api"');
76
- expect(configScript).toContain('jwksUrl: "https://auth.anmho.com/api/auth/jwks"');
77
- expect(configScript).toContain('apiKeySecretName: "dns-api-temporal-api-key"');
78
- expect(configScript).toContain('projectId: ""');
79
- expect(configScript).toContain('baseBranchId: ""');
80
- expect(configScript).toContain('baseBranchName: "main"');
81
- expect(configScript).toContain('previewBranchPrefix: "dns-api-pr"');
82
- expect(configScript).toContain('hostname: "api.dns-api.anmho.com"');
83
- expect(configScript).toContain('cloudflareVaultPath: "prod/providers/cloudflare"');
84
- expect(configScript).not.toContain("github:");
85
- expect(configScript).not.toContain("attachmentBucket");
86
-
87
- const deployScript = await Bun.file(join(generatedRoot, "scripts", "cloudrun", "lib.ts")).text();
88
- expect(deployScript).toContain('--billing-project", config.project.quotaProjectId');
89
- expect(deployScript).toContain('projectMode === "use_existing"');
90
- expect(deployScript).toContain("serviceDomain");
91
- expect(deployScript).toContain("ensureProductionDomainMapping");
92
- expect(deployScript).toContain("ensureCloudflareDnsRecord");
93
- expect(deployScript).toContain("gcloudWithRetry");
94
- expect(deployScript).toContain("CLOUDFLARE_API_TOKEN");
95
- expect(deployScript).toContain('"domain-mappings",');
96
- expect(deployScript).toContain('"--region",');
97
- expect(deployScript).toContain("assertProductionDomainAvailable");
98
- expect(deployScript).toContain("assertServiceNameAvailable");
99
- expect(deployScript).not.toContain("ensureStorageBucket");
100
-
71
+ expect(serviceConfig).toContain('project_mode: "create_new"');
72
+ expect(serviceConfig).toContain('quota_project_id: "anmho-infra-prod"');
73
+ expect(serviceConfig).toContain('jwks_url: "https://auth.anmho.com/api/auth/jwks"');
74
+ expect(serviceConfig).toContain('project_id: ""');
75
+ expect(serviceConfig).toContain('base_branch_id: ""');
76
+ expect(serviceConfig).toContain('base_branch_name: "main"');
77
+ expect(serviceConfig).toContain('preview_branch_prefix: "dns-api-pr"');
78
+ expect(serviceConfig).toContain('hostname: "api.dns-api.anmho.com"');
79
+ expect(serviceConfig).not.toContain("github:");
80
+ expect(serviceConfig).not.toContain("attachmentBucket");
101
81
  expect(await Bun.file(join(generatedRoot, "scripts", "cloudrun", "integrations.ts")).exists()).toBeFalse();
102
- const destroyScript = await Bun.file(join(generatedRoot, "scripts", "cloudrun", "cleanup.ts")).text();
103
- expect(destroyScript).toContain("assertOwnedResource");
104
- expect(destroyScript).toContain("Planning resources to destroy");
105
- expect(destroyScript).toContain("Resources selected for destroy");
106
- expect(destroyScript).toContain("Destroy cannot continue until resource discovery succeeds");
107
- expect(destroyScript).toContain("deleteAuthResourceServer");
108
- expect(destroyScript).toContain("deleteGrafanaResources");
109
- expect(destroyScript).toContain('gcx", ["resources", "delete"');
110
- expect(destroyScript).toContain("config.temporal.apiKeySecretName");
111
- const neonScript = await Bun.file(join(generatedRoot, "scripts", "cloudrun", "neon.ts")).text();
112
- expect(neonScript).toContain("assertDatabaseOwned");
113
- expect(neonScript).toContain("assertDisposableBranchName");
114
82
  const seedScript = await Bun.file(join(generatedRoot, "scripts", "seed.ts")).text();
115
83
  expect(seedScript).toContain("SEED_PROD=true");
116
84
  expect(seedScript).toContain("waitlist_entries");
@@ -176,10 +144,11 @@ test("scaffolds all runtime/framework variants with shared cloudrun config", asy
176
144
  const dockerfile = await Bun.file(join(generatedRoot, "Dockerfile")).text();
177
145
  expect(dockerfile).toContain("COPY go.mod go.sum ./");
178
146
  expect(packageJson).toContain('"dev": "make dev"');
179
- expect(packageJson).toContain('"migrate": "make migrate"');
180
- expect(packageJson).toContain('"create": "bun run ./scripts/cloudrun/cli.ts create"');
181
- expect(packageJson).toContain('"deploy": "bun run ./scripts/cloudrun/cli.ts deploy"');
182
- expect(packageJson).toContain('"destroy": "bun run ./scripts/cloudrun/cli.ts destroy"');
147
+ expect(packageJson).toContain('"service": "service"');
148
+ expect(packageJson).toContain('"migrate": "service migrate"');
149
+ expect(packageJson).toContain('"create": "service create"');
150
+ expect(packageJson).toContain('"deploy": "service deploy"');
151
+ expect(packageJson).toContain('"destroy": "service destroy"');
183
152
 
184
153
  const mainGo = await Bun.file(join(generatedRoot, "cmd", "server", "main.go")).text();
185
154
  expect(mainGo).toContain("github.com/anmho/dns-api");
@@ -215,45 +184,26 @@ test("scaffolds all runtime/framework variants with shared cloudrun config", asy
215
184
  const packageJson = await Bun.file(join(generatedRoot, "package.json")).text();
216
185
  expect(packageJson).toContain('"@anmho/authctl": "0.1.1"');
217
186
  expect(packageJson).toContain("@temporalio/worker");
218
- expect(packageJson).toContain('"service": "./scripts/cloudrun/cli.ts"');
219
187
  expect(packageJson).toContain('"dev": "bun run ./scripts/dev.ts bun run ./src/index.ts"');
220
188
  expect(packageJson).toContain('"gen": "bun run ./scripts/codegen.ts"');
221
- expect(packageJson).toContain('"create": "bun run ./scripts/cloudrun/cli.ts create"');
222
- expect(packageJson).toContain('"deploy": "bun run ./scripts/cloudrun/cli.ts deploy"');
223
- expect(packageJson).toContain('"dashboards": "bun run ./scripts/cloudrun/cli.ts dashboards"');
224
- expect(packageJson).toContain('"auth": "bun run ./scripts/cloudrun/cli.ts auth"');
225
- expect(packageJson).toContain('"destroy": "bun run ./scripts/cloudrun/cli.ts destroy"');
226
- const serviceCli = await Bun.file(join(generatedRoot, "scripts", "cloudrun", "cli.ts")).text();
227
- expect(serviceCli).toContain("service <command> [args]");
228
- expect(serviceCli).toContain("Provision auth, database, migrations, and first deploy");
229
- expect(serviceCli).toContain("assertServiceNameAvailable(config.serviceName)");
230
- expect(serviceCli).toContain("ensureAuthResourceServer");
231
- expect(serviceCli).toContain("ensureAuthClient");
232
- expect(serviceCli).toContain("auth token");
233
- expect(serviceCli).toContain('["resources", "push", "--path", "./grafana"]');
234
- const cloudrunLib = await Bun.file(join(generatedRoot, "scripts", "cloudrun", "lib.ts")).text();
235
- expect(cloudrunLib).toContain("resolveTemporalRuntimeConfig");
236
- expect(cloudrunLib).toContain("TEMPORAL_API_KEY_ENV");
237
- expect(cloudrunLib).toContain("value === undefined");
238
-
239
- const authctlScript = await Bun.file(join(generatedRoot, "scripts", "authctl.ts")).text();
240
- expect(authctlScript).toContain("authctl");
241
- expect(authctlScript).toContain("resource-servers");
242
- expect(authctlScript).toContain("clients");
243
- expect(authctlScript).toContain("defaultClientTargetArgs");
244
- expect(authctlScript).toContain("ensureAuthClient");
245
- expect(authctlScript).toContain("mintAuthToken");
246
- expect(authctlScript).toContain("clientVaultPath");
247
- expect(authctlScript).toContain("deleteAuthResourceServer");
248
- expect(authctlScript).toContain("readAuthctlAccessVaultField");
249
- expect(authctlScript).toContain("prod/apps/auth/authctl/cloudflare-access");
250
- expect(authctlScript).toContain('existsSync("./node_modules/.bin/authctl") ? "./node_modules/.bin/authctl" : Bun.which("authctl")');
251
- expect(authctlScript).not.toContain('defaultAuthResourceServerArgs(), "--yes", "--json"');
189
+ expect(packageJson).toContain('"service": "service"');
190
+ expect(packageJson).toContain('"migrate": "service migrate"');
191
+ expect(packageJson).toContain('"create": "service create"');
192
+ expect(packageJson).toContain('"deploy": "service deploy"');
193
+ expect(packageJson).toContain('"dashboards": "service dashboards"');
194
+ expect(packageJson).toContain('"auth": "service auth"');
195
+ expect(packageJson).toContain('"destroy": "service destroy"');
196
+ expect(await Bun.file(join(generatedRoot, "scripts", "cloudrun", "cli.ts")).exists()).toBeFalse();
197
+ expect(await Bun.file(join(generatedRoot, "scripts", "authctl.ts")).exists()).toBeFalse();
198
+ const serviceConfig = await Bun.file(join(generatedRoot, "service.config.ts")).text();
199
+ expect(serviceConfig).toContain('service_id: "dns-api"');
200
+ expect(serviceConfig).toContain('project_id: "anmho-dns-api"');
201
+ expect(serviceConfig).toContain('database_name: "dns_api"');
252
202
  const authScript = await Bun.file(join(generatedRoot, "src", "auth.ts")).text();
253
203
  expect(authScript).toContain('"Ed25519"');
254
204
 
255
205
  const makefile = await Bun.file(join(generatedRoot, "Makefile")).text();
256
- expect(makefile).toContain("npx --no-install service");
206
+ expect(makefile).toContain("SERVICE := service");
257
207
  expect(makefile).toContain("dashboards:");
258
208
  expect(makefile).toContain("auth:");
259
209
  expect(makefile).toContain("bun run dev");
@@ -350,9 +300,9 @@ test("scaffolds the workers target with wrangler lifecycle commands", async () =
350
300
 
351
301
  const packageJson = await Bun.file(join(generatedRoot, "package.json")).text();
352
302
  expect(packageJson).toContain('"@anmho/authctl": "0.1.1"');
353
- expect(packageJson).toContain('"service": "./scripts/workers/cli.ts"');
354
303
  expect(packageJson).toContain('"dev": "wrangler dev"');
355
- expect(packageJson).toContain('"auth": "bun run ./scripts/workers/cli.ts auth"');
304
+ expect(packageJson).toContain('"service": "service"');
305
+ expect(packageJson).toContain('"auth": "service auth"');
356
306
  expect(packageJson).toContain('"wrangler"');
357
307
  expect(packageJson).toContain('"pg"');
358
308
 
@@ -374,28 +324,19 @@ test("scaffolds the workers target with wrangler lifecycle commands", async () =
374
324
  expect(readme).toContain("Cloudflare Workers");
375
325
  expect(readme).toContain("Hyperdrive binding for Neon-backed Postgres persistence");
376
326
  expect(readme).not.toContain("Cloud Run");
377
- const workerCli = await Bun.file(join(generatedRoot, "scripts", "workers", "cli.ts")).text();
378
- expect(workerCli).toContain("hyperdrive");
379
- expect(workerCli).toContain('["resources", "push", "--path", "./grafana"]');
380
- expect(workerCli).toContain("ensureAuthResourceServer");
381
- expect(workerCli).toContain("ensureAuthClient");
382
- expect(workerCli).toContain("auth token");
383
- expect(workerCli).toContain("Workers database schema applied");
384
- expect(workerCli).toContain("create table if not exists waitlist_entries");
385
- expect(workerCli).toContain("DATABASE_URL or NEON_API_KEY is required to provision the Hyperdrive binding");
386
- expect(workerCli).toContain("createProjectBranchDatabase");
387
- expect(workerCli).toContain("deleteNeonDatabase");
388
- expect(workerCli).toContain("deleteGrafanaResources");
389
- expect(workerCli).toContain("hyperdrive\", \"delete");
327
+ const serviceConfig = await Bun.file(join(generatedRoot, "service.config.ts")).text();
328
+ expect(serviceConfig).toContain('target: "workers"');
329
+ expect(serviceConfig).toContain('hostname: "api.dns-api.anmho.com"');
330
+ expect(serviceConfig).toContain('database_name: "dns_api"');
390
331
  const makefile = await Bun.file(join(generatedRoot, "Makefile")).text();
391
332
  expect(makefile).toContain('no generated code for workers');
392
333
  expect(makefile).toContain("auth:");
393
334
  expect(makefile).not.toContain("scripts/codegen.ts");
394
335
 
395
- expect(await Bun.file(join(generatedRoot, "scripts", "authctl.ts")).exists()).toBeTrue();
336
+ expect(await Bun.file(join(generatedRoot, "scripts", "authctl.ts")).exists()).toBeFalse();
396
337
  expect(await Bun.file(join(generatedRoot, "src", "auth.ts")).exists()).toBeTrue();
397
338
  expect(await Bun.file(join(generatedRoot, "src", "storage.ts")).exists()).toBeTrue();
398
- expect(await Bun.file(join(generatedRoot, "scripts", "workers", "cli.ts")).exists()).toBeTrue();
339
+ expect(await Bun.file(join(generatedRoot, "scripts", "workers", "cli.ts")).exists()).toBeFalse();
399
340
  expect(await Bun.file(join(generatedRoot, "scripts", "cloudrun", "cli.ts")).exists()).toBeFalse();
400
341
  expect(await Bun.file(join(generatedRoot, "scripts", "dev.ts")).exists()).toBeFalse();
401
342
  expect(await Bun.file(join(generatedRoot, "scripts", "ensure-local-db.ts")).exists()).toBeFalse();
package/src/scaffold.ts CHANGED
@@ -77,6 +77,14 @@ export async function scaffoldProject(config: ScaffoldConfig) {
77
77
  }
78
78
 
79
79
  function shouldSkipForTarget(target: DeployTarget, templateKind: "shared" | "variant" | "target", relativePath: string) {
80
+ if (
81
+ relativePath === "scripts/authctl.ts" ||
82
+ relativePath.startsWith("scripts/cloudrun/") ||
83
+ relativePath.startsWith("scripts/workers/")
84
+ ) {
85
+ return true;
86
+ }
87
+
80
88
  if (target === "workers") {
81
89
  if (templateKind === "target") {
82
90
  return false;
@@ -94,8 +102,7 @@ function shouldSkipForTarget(target: DeployTarget, templateKind: "shared" | "var
94
102
  relativePath === "scripts/local-docker.ts" ||
95
103
  relativePath === "scripts/local-env.ts" ||
96
104
  relativePath === "scripts/seed.ts" ||
97
- relativePath === "scripts/wait-for-db.ts" ||
98
- relativePath.startsWith("scripts/cloudrun/")
105
+ relativePath === "scripts/wait-for-db.ts"
99
106
  );
100
107
  }
101
108
 
@@ -110,7 +117,7 @@ function shouldSkipForTarget(target: DeployTarget, templateKind: "shared" | "var
110
117
  );
111
118
  }
112
119
 
113
- return relativePath.startsWith("scripts/workers/") || relativePath === "wrangler.toml";
120
+ return relativePath === "wrangler.toml";
114
121
  }
115
122
 
116
123
  async function ensureTargetDirectory(targetDir: string) {
@@ -1,5 +1,5 @@
1
- import serviceConfig from "../service.config";
2
1
  import { existsSync } from "node:fs";
2
+ import { serviceConfig } from "./runtime";
3
3
 
4
4
  type CommandResult = {
5
5
  success: boolean;
@@ -47,7 +47,7 @@ export function defaultAuthResourceServerArgs() {
47
47
  auth.resource_server.audience,
48
48
  "--stage",
49
49
  serviceConfig.stage_default,
50
- ...auth.resource_server.default_scopes.flatMap((scope) => ["--scope", scope]),
50
+ ...(auth.resource_server.default_scopes as string[]).flatMap((scope: string) => ["--scope", scope]),
51
51
  ];
52
52
  }
53
53
 
@@ -244,7 +244,7 @@ function defaultClientTargetArgs(rest: string[]) {
244
244
  const hasScope = hasFlag(rest, "--scope");
245
245
  return [
246
246
  ...(hasResourceServer ? [] : ["--resource-server", serviceConfig.auth.resource_server.id]),
247
- ...(hasScope ? [] : serviceConfig.auth.resource_server.default_scopes.flatMap((scope) => ["--scope", scope])),
247
+ ...(hasScope ? [] : (serviceConfig.auth.resource_server.default_scopes as string[]).flatMap((scope: string) => ["--scope", scope])),
248
248
  ];
249
249
  }
250
250
 
@@ -151,7 +151,7 @@ function planProductionDomainMapping(plan: DestroyPlan) {
151
151
  return;
152
152
  }
153
153
 
154
- const routeName = mapping.spec?.routeName;
154
+ const routeName = mapping.spec?.routeName ?? "";
155
155
  if (routeName !== config.serviceName) {
156
156
  plan.blockers.push(`${config.domain.hostname} maps to ${routeName || "an unknown service"}; refusing to delete ambiguous DNS mapping`);
157
157
  return;
@@ -23,7 +23,7 @@ import {
23
23
  serviceOrigin,
24
24
  } from "./lib";
25
25
 
26
- async function main(argv = Bun.argv.slice(2)) {
26
+ export async function main(argv = Bun.argv.slice(2)) {
27
27
  const [command, ...rest] = argv;
28
28
 
29
29
  if (!command || command === "--help" || command === "-h" || command === "help") {
@@ -0,0 +1,55 @@
1
+ import { serviceConfig } from "../runtime";
2
+
3
+ const cloudrun = serviceConfig.cloudrun;
4
+ const dns = serviceConfig.dns;
5
+ const neon = serviceConfig.neon;
6
+
7
+ export const config = {
8
+ serviceName: serviceConfig.service_id,
9
+ profile: serviceConfig.profile,
10
+ example: serviceConfig.example,
11
+ runtime: serviceConfig.runtime,
12
+ framework: serviceConfig.framework,
13
+ region: cloudrun.region,
14
+ artifactRepository: cloudrun.artifact_repository,
15
+ runtimeServiceAccount: cloudrun.service_account,
16
+ project: {
17
+ mode: cloudrun.project_mode,
18
+ id: cloudrun.project_id,
19
+ name: cloudrun.project_name,
20
+ createIfMissing: cloudrun.create_if_missing,
21
+ billingAccount: cloudrun.billing_account,
22
+ quotaProjectId: cloudrun.quota_project_id,
23
+ },
24
+ domain: {
25
+ hostname: dns.hostname,
26
+ baseDomain: dns.base_domain,
27
+ cloudflareApiBaseUrl: dns.cloudflare_api_base_url,
28
+ cloudflareVaultPath: dns.cloudflare_vault_path,
29
+ cloudflareVaultField: dns.cloudflare_vault_field,
30
+ },
31
+ auth: {
32
+ issuer: serviceConfig.auth.issuer,
33
+ audience: serviceConfig.auth.resource_server.audience,
34
+ jwksUrl: serviceConfig.auth.jwks_url,
35
+ },
36
+ temporal: {
37
+ enabled: serviceConfig.temporal.enabled,
38
+ address: serviceConfig.temporal.address,
39
+ namespace: serviceConfig.temporal.namespace,
40
+ taskQueue: serviceConfig.temporal.task_queue,
41
+ apiKeySecretName: serviceConfig.temporal.api_key_secret_name,
42
+ },
43
+ neon: {
44
+ projectId: neon.project_id,
45
+ baseBranchId: neon.base_branch_id,
46
+ baseBranchName: neon.base_branch_name,
47
+ databaseName: neon.database_name,
48
+ roleName: neon.role_name,
49
+ previewBranchPrefix: neon.preview_branch_prefix,
50
+ personalBranchPrefix: neon.personal_branch_prefix,
51
+ },
52
+ requiredApis: cloudrun.required_apis,
53
+ } as const;
54
+
55
+ export type DeployEnvironment = "main" | "preview" | "personal";
@@ -0,0 +1,8 @@
1
+ import { join } from "node:path";
2
+ import { pathToFileURL } from "node:url";
3
+
4
+ export const serviceRoot = process.env.CREATE_SVC_SERVICE_ROOT?.trim() || process.cwd();
5
+
6
+ export const serviceConfig = (
7
+ await import(pathToFileURL(join(serviceRoot, "service.config.ts")).href)
8
+ ).default;
@@ -4,17 +4,18 @@ import { confirm, intro, isCancel, log, outro } from "@clack/prompts";
4
4
  import { createApiClient } from "@neondatabase/api-client";
5
5
  import { Client } from "pg";
6
6
  import { ensureAuthClient, ensureAuthResourceServer, runAuthCommand, runAuthDoctor } from "../authctl";
7
+ import { serviceConfig } from "../runtime";
7
8
 
8
9
  const config = {
9
- serviceName: "{{SERVICE_NAME}}",
10
- hostname: "{{API_HOSTNAME}}",
11
- neonDatabaseName: "{{NEON_DATABASE_NAME}}",
12
- neonRoleName: "neondb_owner",
10
+ serviceName: serviceConfig.service_id,
11
+ hostname: serviceConfig.dns.hostname,
12
+ neonDatabaseName: serviceConfig.neon.database_name,
13
+ neonRoleName: serviceConfig.neon.role_name,
13
14
  };
14
15
 
15
16
  type DoctorStatus = "pass" | "warn" | "fail";
16
17
 
17
- async function main(argv = Bun.argv.slice(2)) {
18
+ export async function main(argv = Bun.argv.slice(2)) {
18
19
  const [command, ...rest] = argv;
19
20
 
20
21
  if (!command || command === "--help" || command === "-h" || command === "help") {
@@ -255,7 +256,7 @@ async function deleteNeonDatabase() {
255
256
 
256
257
  const payload = await neon.getProjectBranchDatabase(projectId, branchId, config.neonDatabaseName);
257
258
  const database = (payload.data as { database?: { name?: string; owner_name?: string } } | undefined)?.database;
258
- if (database?.name !== config.neonDatabaseName || (database.owner_name && database.owner_name !== config.neonRoleName)) {
259
+ if (!database || database.name !== config.neonDatabaseName || (database.owner_name && database.owner_name !== config.neonRoleName)) {
259
260
  throw new Error(`Refusing to delete Neon database ${database?.name ?? config.neonDatabaseName}; ownership metadata does not match`);
260
261
  }
261
262
 
@@ -20,10 +20,8 @@ test("findGeneratedServiceRoot detects generated service context from nested dir
20
20
  const root = await mkdtemp(join(tmpdir(), "create-svc-service-root-"));
21
21
  const serviceRoot = join(root, "generated-api");
22
22
  const nested = join(serviceRoot, "src", "waitlist");
23
- await mkdir(join(serviceRoot, "scripts", "cloudrun"), { recursive: true });
24
23
  await mkdir(nested, { recursive: true });
25
24
  await writeFile(join(serviceRoot, "service.config.ts"), "export default {}");
26
- await writeFile(join(serviceRoot, "scripts", "cloudrun", "cli.ts"), "");
27
25
 
28
26
  expect(findGeneratedServiceRoot(nested)).toBe(serviceRoot);
29
27
  expect(findGeneratedServiceRoot(root)).toBeUndefined();
package/src/service.ts CHANGED
@@ -7,7 +7,7 @@ const SCAFFOLD_COMMANDS = new Set(["create", "new", "init"]);
7
7
  export async function runServiceCommand(argv: string[], cwd = process.cwd()) {
8
8
  const serviceRoot = findGeneratedServiceRoot(cwd);
9
9
  if (serviceRoot) {
10
- delegateToGeneratedService(serviceRoot, argv);
10
+ await delegateToGeneratedService(serviceRoot, argv);
11
11
  return;
12
12
  }
13
13
 
@@ -41,29 +41,23 @@ export function findGeneratedServiceRoot(start: string): string | undefined {
41
41
  }
42
42
 
43
43
  function isGeneratedServiceRoot(path: string) {
44
- return (
45
- existsSync(join(path, "service.config.ts")) &&
46
- (existsSync(join(path, "scripts", "cloudrun", "cli.ts")) || existsSync(join(path, "scripts", "workers", "cli.ts")))
47
- );
44
+ return existsSync(join(path, "service.config.ts"));
48
45
  }
49
46
 
50
- function delegateToGeneratedService(serviceRoot: string, argv: string[]) {
47
+ async function delegateToGeneratedService(serviceRoot: string, argv: string[]) {
51
48
  ensureGeneratedDependencies(serviceRoot);
49
+ process.chdir(serviceRoot);
50
+ process.env.CREATE_SVC_SERVICE_ROOT = serviceRoot;
52
51
 
53
- const cliPath = existsSync(join(serviceRoot, "scripts", "cloudrun", "cli.ts"))
54
- ? "./scripts/cloudrun/cli.ts"
55
- : "./scripts/workers/cli.ts";
56
- const result = Bun.spawnSync(["bun", "run", cliPath, ...argv], {
57
- cwd: serviceRoot,
58
- env: process.env,
59
- stdin: "inherit",
60
- stdout: "inherit",
61
- stderr: "inherit",
62
- });
63
-
64
- if (!result.success) {
65
- process.exit(result.exitCode || 1);
52
+ const serviceConfig = (await import(`${serviceRoot}/service.config.ts`)).default;
53
+ if (serviceConfig.target === "workers") {
54
+ const { main } = await import("./service-runtime/workers/cli");
55
+ await main(argv);
56
+ return;
66
57
  }
58
+
59
+ const { main } = await import("./service-runtime/cloudrun/cli");
60
+ await main(argv);
67
61
  }
68
62
 
69
63
  export function generatedDependenciesInstalled(serviceRoot: string) {
@@ -59,7 +59,7 @@ No cloud credentials are required for local HTTP development after Docker and Po
59
59
 
60
60
  ## Remote provisioning
61
61
 
62
- The generated Cloud Run config lives in [scripts/cloudrun/config.ts](scripts/cloudrun/config.ts).
62
+ The generated service config lives in [service.config.ts](service.config.ts).
63
63
 
64
64
  Create, deploy, and destroy use:
65
65
 
@@ -3,7 +3,13 @@ export default {
3
3
  target: "{{TARGET}}",
4
4
  runtime: "{{RUNTIME}}",
5
5
  framework: "{{FRAMEWORK}}",
6
+ profile: "{{PROFILE}}",
6
7
  stage_default: "prod",
8
+ example: {
9
+ kind: "{{EXAMPLE_KIND}}",
10
+ domain: "{{EXAMPLE_DOMAIN}}",
11
+ label: "{{EXAMPLE_LABEL}}",
12
+ },
7
13
  dns: {
8
14
  hostname: "{{API_HOSTNAME}}",
9
15
  base_domain: "{{API_BASE_DOMAIN}}",
@@ -47,13 +53,38 @@ export default {
47
53
  temporal_path: "prod/providers/temporal",
48
54
  },
49
55
  },
56
+ neon: {
57
+ project_id: "{{NEON_PROJECT_ID}}",
58
+ base_branch_id: "{{NEON_BASE_BRANCH_ID}}",
59
+ base_branch_name: "{{NEON_BASE_BRANCH_NAME}}",
60
+ database_name: "{{NEON_DATABASE_NAME}}",
61
+ role_name: "{{NEON_ROLE_NAME}}",
62
+ preview_branch_prefix: "{{NEON_PREVIEW_BRANCH_PREFIX}}",
63
+ personal_branch_prefix: "{{NEON_PERSONAL_BRANCH_PREFIX}}",
64
+ },
50
65
  buf: {
51
66
  module: "buf.build/anmho/{{SERVICE_ID}}",
52
67
  },
53
68
  cloudrun: {
54
69
  project_id: "{{PROJECT_ID}}",
70
+ project_name: "{{PROJECT_NAME}}",
71
+ project_mode: "{{GCP_PROJECT_MODE}}",
72
+ create_if_missing: {{PROJECT_CREATE_IF_MISSING}},
73
+ billing_account: "{{BILLING_ACCOUNT}}",
74
+ quota_project_id: "{{QUOTA_PROJECT_ID}}",
55
75
  region: "{{REGION}}",
76
+ artifact_repository: "cloud-run",
56
77
  service_account: "{{RUNTIME_SERVICE_ACCOUNT}}",
78
+ required_apis: [
79
+ "run.googleapis.com",
80
+ "cloudbuild.googleapis.com",
81
+ "artifactregistry.googleapis.com",
82
+ "iam.googleapis.com",
83
+ "iamcredentials.googleapis.com",
84
+ "secretmanager.googleapis.com",
85
+ "serviceusage.googleapis.com",
86
+ "sts.googleapis.com",
87
+ ],
57
88
  },
58
89
  workers: {
59
90
  script_name: "{{SERVICE_ID}}",
@@ -1,6 +1,6 @@
1
1
  .PHONY: dev migrate gen lint test create deploy dashboards auth destroy
2
2
 
3
- SERVICE := npx --no-install service
3
+ SERVICE := service
4
4
 
5
5
  dev:
6
6
  bun run dev
@@ -2,21 +2,18 @@
2
2
  "name": "{{SERVICE_NAME}}",
3
3
  "private": true,
4
4
  "type": "module",
5
- "bin": {
6
- "service": "./scripts/workers/cli.ts"
7
- },
8
5
  "scripts": {
9
6
  "dev": "wrangler dev",
10
- "service": "bun run ./scripts/workers/cli.ts",
11
- "migrate": "bun run ./scripts/workers/cli.ts migrate",
12
- "seed": "bun run ./scripts/workers/cli.ts seed",
7
+ "service": "service",
8
+ "migrate": "service migrate",
9
+ "seed": "service seed",
13
10
  "lint": "tsc --noEmit",
14
11
  "test": "bun test",
15
- "create": "bun run ./scripts/workers/cli.ts create",
16
- "deploy": "bun run ./scripts/workers/cli.ts deploy",
17
- "dashboards": "bun run ./scripts/workers/cli.ts dashboards",
18
- "auth": "bun run ./scripts/workers/cli.ts auth",
19
- "destroy": "bun run ./scripts/workers/cli.ts destroy"
12
+ "create": "service create",
13
+ "deploy": "service deploy",
14
+ "dashboards": "service dashboards",
15
+ "auth": "service auth",
16
+ "destroy": "service destroy"
20
17
  },
21
18
  "dependencies": {
22
19
  "@anmho/authctl": "0.1.1",
@@ -1,6 +1,6 @@
1
1
  .PHONY: dev migrate gen lint test create deploy dashboards auth destroy
2
2
 
3
- SERVICE := npx --no-install service
3
+ SERVICE := service
4
4
 
5
5
  dev:
6
6
  bun run dev
@@ -2,21 +2,18 @@
2
2
  "name": "{{SERVICE_NAME}}",
3
3
  "private": true,
4
4
  "type": "module",
5
- "bin": {
6
- "service": "./scripts/cloudrun/cli.ts"
7
- },
8
5
  "scripts": {
9
6
  "dev": "bun run ./scripts/dev.ts bun run ./src/index.ts",
10
- "service": "bun run ./scripts/cloudrun/cli.ts",
11
- "migrate": "bun run ./scripts/migrate.ts",
7
+ "service": "service",
8
+ "migrate": "service migrate",
12
9
  "gen": "bun run ./scripts/codegen.ts",
13
10
  "lint": "tsc --noEmit",
14
11
  "test": "bun test",
15
- "create": "bun run ./scripts/cloudrun/cli.ts create",
16
- "deploy": "bun run ./scripts/cloudrun/cli.ts deploy",
17
- "dashboards": "bun run ./scripts/cloudrun/cli.ts dashboards",
18
- "auth": "bun run ./scripts/cloudrun/cli.ts auth",
19
- "destroy": "bun run ./scripts/cloudrun/cli.ts destroy"
12
+ "create": "service create",
13
+ "deploy": "service deploy",
14
+ "dashboards": "service dashboards",
15
+ "auth": "service auth",
16
+ "destroy": "service destroy"
20
17
  },
21
18
  "dependencies": {
22
19
  "@anmho/authctl": "0.1.1",
@@ -1,6 +1,6 @@
1
1
  .PHONY: dev migrate gen lint test create deploy dashboards auth destroy
2
2
 
3
- SERVICE := npx --no-install service
3
+ SERVICE := service
4
4
 
5
5
  dev:
6
6
  bun run dev
@@ -2,21 +2,18 @@
2
2
  "name": "{{SERVICE_NAME}}",
3
3
  "private": true,
4
4
  "type": "module",
5
- "bin": {
6
- "service": "./scripts/cloudrun/cli.ts"
7
- },
8
5
  "scripts": {
9
6
  "dev": "bun run ./scripts/dev.ts bun run ./src/index.ts",
10
- "service": "bun run ./scripts/cloudrun/cli.ts",
11
- "migrate": "bun run ./scripts/migrate.ts",
7
+ "service": "service",
8
+ "migrate": "service migrate",
12
9
  "gen": "bun run ./scripts/codegen.ts",
13
10
  "lint": "tsc --noEmit",
14
11
  "test": "bun test",
15
- "create": "bun run ./scripts/cloudrun/cli.ts create",
16
- "deploy": "bun run ./scripts/cloudrun/cli.ts deploy",
17
- "dashboards": "bun run ./scripts/cloudrun/cli.ts dashboards",
18
- "auth": "bun run ./scripts/cloudrun/cli.ts auth",
19
- "destroy": "bun run ./scripts/cloudrun/cli.ts destroy"
12
+ "create": "service create",
13
+ "deploy": "service deploy",
14
+ "dashboards": "service dashboards",
15
+ "auth": "service auth",
16
+ "destroy": "service destroy"
20
17
  },
21
18
  "dependencies": {
22
19
  "@anmho/authctl": "0.1.1",
@@ -1,6 +1,6 @@
1
1
  .PHONY: dev migrate migrate-lint gen lint test create deploy dashboards auth destroy
2
2
 
3
- SERVICE := npx --no-install service
3
+ SERVICE := service
4
4
  WITH_ENV := set -a; [ ! -f .env.local ] || . ./.env.local; set +a;
5
5
  ATLAS ?= atlas
6
6
 
@@ -7,5 +7,34 @@ require (
7
7
  github.com/jackc/pgx/v5 v5.7.5
8
8
  github.com/jmoiron/sqlx v1.4.0
9
9
  go.temporal.io/sdk v1.43.0
10
- golang.org/x/net v0.42.0
10
+ golang.org/x/net v0.49.0
11
+ )
12
+
13
+ require (
14
+ github.com/davecgh/go-spew v1.1.1 // indirect
15
+ github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect
16
+ github.com/gogo/protobuf v1.3.2 // indirect
17
+ github.com/golang/mock v1.6.0 // indirect
18
+ github.com/google/uuid v1.6.0 // indirect
19
+ github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect
20
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
21
+ github.com/jackc/pgpassfile v1.0.0 // indirect
22
+ github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
23
+ github.com/jackc/puddle/v2 v2.2.2 // indirect
24
+ github.com/nexus-rpc/sdk-go v0.6.0 // indirect
25
+ github.com/pmezard/go-difflib v1.0.0 // indirect
26
+ github.com/robfig/cron v1.2.0 // indirect
27
+ github.com/stretchr/objx v0.5.2 // indirect
28
+ github.com/stretchr/testify v1.10.0 // indirect
29
+ go.temporal.io/api v1.62.11 // indirect
30
+ golang.org/x/crypto v0.47.0 // indirect
31
+ golang.org/x/sync v0.19.0 // indirect
32
+ golang.org/x/sys v0.40.0 // indirect
33
+ golang.org/x/text v0.33.0 // indirect
34
+ golang.org/x/time v0.3.0 // indirect
35
+ google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516 // indirect
36
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect
37
+ google.golang.org/grpc v1.79.3 // indirect
38
+ google.golang.org/protobuf v1.36.11 // indirect
39
+ gopkg.in/yaml.v3 v3.0.1 // indirect
11
40
  )
@@ -2,21 +2,18 @@
2
2
  "name": "{{SERVICE_NAME}}",
3
3
  "private": true,
4
4
  "type": "module",
5
- "bin": {
6
- "service": "./scripts/cloudrun/cli.ts"
7
- },
8
5
  "scripts": {
9
6
  "dev": "make dev",
10
- "service": "bun run ./scripts/cloudrun/cli.ts",
11
- "migrate": "make migrate",
7
+ "service": "service",
8
+ "migrate": "service migrate",
12
9
  "gen": "make gen",
13
10
  "lint": "make lint",
14
11
  "test": "make test",
15
- "create": "bun run ./scripts/cloudrun/cli.ts create",
16
- "deploy": "bun run ./scripts/cloudrun/cli.ts deploy",
17
- "auth": "bun run ./scripts/cloudrun/cli.ts auth",
18
- "dashboards": "bun run ./scripts/cloudrun/cli.ts dashboards",
19
- "destroy": "bun run ./scripts/cloudrun/cli.ts destroy"
12
+ "create": "service create",
13
+ "deploy": "service deploy",
14
+ "auth": "service auth",
15
+ "dashboards": "service dashboards",
16
+ "destroy": "service destroy"
20
17
  },
21
18
  "dependencies": {
22
19
  "@anmho/authctl": "0.1.1",
@@ -1,6 +1,6 @@
1
1
  .PHONY: dev migrate migrate-lint gen lint test create deploy dashboards auth destroy
2
2
 
3
- SERVICE := npx --no-install service
3
+ SERVICE := service
4
4
  WITH_ENV := set -a; [ ! -f .env.local ] || . ./.env.local; set +a;
5
5
  ATLAS ?= atlas
6
6
 
@@ -9,6 +9,34 @@ require (
9
9
  github.com/jackc/pgx/v5 v5.7.5
10
10
  github.com/jmoiron/sqlx v1.4.0
11
11
  go.temporal.io/sdk v1.43.0
12
- golang.org/x/net v0.42.0
13
- google.golang.org/protobuf v1.36.10
12
+ golang.org/x/net v0.49.0
13
+ google.golang.org/protobuf v1.36.11
14
+ )
15
+
16
+ require (
17
+ github.com/davecgh/go-spew v1.1.1 // indirect
18
+ github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect
19
+ github.com/gogo/protobuf v1.3.2 // indirect
20
+ github.com/golang/mock v1.6.0 // indirect
21
+ github.com/google/uuid v1.6.0 // indirect
22
+ github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect
23
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
24
+ github.com/jackc/pgpassfile v1.0.0 // indirect
25
+ github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
26
+ github.com/jackc/puddle/v2 v2.2.2 // indirect
27
+ github.com/nexus-rpc/sdk-go v0.6.0 // indirect
28
+ github.com/pmezard/go-difflib v1.0.0 // indirect
29
+ github.com/robfig/cron v1.2.0 // indirect
30
+ github.com/stretchr/objx v0.5.2 // indirect
31
+ github.com/stretchr/testify v1.10.0 // indirect
32
+ go.temporal.io/api v1.62.11 // indirect
33
+ golang.org/x/crypto v0.47.0 // indirect
34
+ golang.org/x/sync v0.19.0 // indirect
35
+ golang.org/x/sys v0.40.0 // indirect
36
+ golang.org/x/text v0.33.0 // indirect
37
+ golang.org/x/time v0.3.0 // indirect
38
+ google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516 // indirect
39
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect
40
+ google.golang.org/grpc v1.79.3 // indirect
41
+ gopkg.in/yaml.v3 v3.0.1 // indirect
14
42
  )
@@ -2,21 +2,18 @@
2
2
  "name": "{{SERVICE_NAME}}",
3
3
  "private": true,
4
4
  "type": "module",
5
- "bin": {
6
- "service": "./scripts/cloudrun/cli.ts"
7
- },
8
5
  "scripts": {
9
6
  "dev": "make dev",
10
- "service": "bun run ./scripts/cloudrun/cli.ts",
11
- "migrate": "make migrate",
7
+ "service": "service",
8
+ "migrate": "service migrate",
12
9
  "gen": "make gen",
13
10
  "lint": "make lint",
14
11
  "test": "make test",
15
- "create": "bun run ./scripts/cloudrun/cli.ts create",
16
- "deploy": "bun run ./scripts/cloudrun/cli.ts deploy",
17
- "auth": "bun run ./scripts/cloudrun/cli.ts auth",
18
- "dashboards": "bun run ./scripts/cloudrun/cli.ts dashboards",
19
- "destroy": "bun run ./scripts/cloudrun/cli.ts destroy"
12
+ "create": "service create",
13
+ "deploy": "service deploy",
14
+ "auth": "service auth",
15
+ "dashboards": "service dashboards",
16
+ "destroy": "service destroy"
20
17
  },
21
18
  "dependencies": {
22
19
  "@anmho/authctl": "0.1.1",
@@ -1,62 +0,0 @@
1
- export const config = {
2
- serviceName: "{{SERVICE_NAME}}",
3
- profile: "{{PROFILE}}",
4
- example: {
5
- kind: "{{EXAMPLE_KIND}}",
6
- domain: "{{EXAMPLE_DOMAIN}}",
7
- label: "{{EXAMPLE_LABEL}}",
8
- },
9
- runtime: "{{RUNTIME}}",
10
- framework: "{{FRAMEWORK}}",
11
- region: "{{REGION}}",
12
- artifactRepository: "cloud-run",
13
- runtimeServiceAccount: "{{RUNTIME_SERVICE_ACCOUNT}}",
14
- project: {
15
- mode: "{{GCP_PROJECT_MODE}}",
16
- id: "{{PROJECT_ID}}",
17
- name: "{{PROJECT_NAME}}",
18
- createIfMissing: {{PROJECT_CREATE_IF_MISSING}},
19
- billingAccount: "{{BILLING_ACCOUNT}}",
20
- quotaProjectId: "{{QUOTA_PROJECT_ID}}",
21
- },
22
- domain: {
23
- hostname: "{{API_HOSTNAME}}",
24
- baseDomain: "{{API_BASE_DOMAIN}}",
25
- cloudflareApiBaseUrl: "https://api.cloudflare.com/client/v4",
26
- cloudflareVaultPath: "prod/providers/cloudflare",
27
- cloudflareVaultField: "api_token",
28
- },
29
- auth: {
30
- issuer: "https://auth.anmho.com/api/auth",
31
- audience: "api://{{SERVICE_ID}}",
32
- jwksUrl: "https://auth.anmho.com/api/auth/jwks",
33
- },
34
- temporal: {
35
- enabled: false,
36
- address: "localhost:7233",
37
- namespace: "default",
38
- taskQueue: "{{SERVICE_ID}}",
39
- apiKeySecretName: "{{SERVICE_ID}}-temporal-api-key",
40
- },
41
- neon: {
42
- projectId: "{{NEON_PROJECT_ID}}",
43
- baseBranchId: "{{NEON_BASE_BRANCH_ID}}",
44
- baseBranchName: "{{NEON_BASE_BRANCH_NAME}}",
45
- databaseName: "{{NEON_DATABASE_NAME}}",
46
- roleName: "{{NEON_ROLE_NAME}}",
47
- previewBranchPrefix: "{{NEON_PREVIEW_BRANCH_PREFIX}}",
48
- personalBranchPrefix: "{{NEON_PERSONAL_BRANCH_PREFIX}}",
49
- },
50
- requiredApis: [
51
- "run.googleapis.com",
52
- "cloudbuild.googleapis.com",
53
- "artifactregistry.googleapis.com",
54
- "iam.googleapis.com",
55
- "iamcredentials.googleapis.com",
56
- "secretmanager.googleapis.com",
57
- "serviceusage.googleapis.com",
58
- "sts.googleapis.com",
59
- ],
60
- } as const;
61
-
62
- export type DeployEnvironment = "main" | "preview" | "personal";