create-svc 0.1.29 → 0.1.31

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-svc",
3
- "version": "0.1.29",
3
+ "version": "0.1.31",
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",
package/src/cli.ts CHANGED
@@ -3,7 +3,7 @@ import pc from "picocolors";
3
3
  import { readdirSync } from "node:fs";
4
4
  import { basename, dirname, resolve } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
- import { buildDeploymentVerificationCommands, runPostScaffoldFlow } from "./post-scaffold";
6
+ import { buildDeploymentVerificationCommands, buildLocalVerificationCommands, runPostScaffoldFlow } from "./post-scaffold";
7
7
  import {
8
8
  bootstrapGitHubRepository,
9
9
  buildGitBootstrapConfig,
@@ -184,13 +184,20 @@ function formatCompletionSummary(config: ScaffoldConfig, targetDir: string, gitR
184
184
  ` Temporal API key secret: ${config.serviceName}-temporal-api-key`,
185
185
  config.runtime === "go" ? ` Go module: ${config.modulePath}` : undefined,
186
186
  "",
187
- config.autoDeploy ? "Verified after deploy:" : "After deploy, verify with:",
187
+ config.autoDeploy ? "Verified production after deploy:" : "After deploy, verify production with:",
188
188
  ...buildDeploymentVerificationCommands(config).map(formatShellCommand),
189
+ config.autoDeploy ? "" : undefined,
190
+ config.autoDeploy ? "Local dev started:" : undefined,
191
+ config.autoDeploy ? " .service/local-dev.pid" : undefined,
192
+ config.autoDeploy ? " .service/local-dev.log" : undefined,
193
+ config.autoDeploy ? "" : undefined,
194
+ config.autoDeploy ? "Verified local dev:" : undefined,
195
+ ...(config.autoDeploy ? buildLocalVerificationCommands(config).map(formatShellCommand) : []),
189
196
  "",
190
- "We suggest that you begin by typing:",
197
+ config.autoDeploy ? "Next:" : "We suggest that you begin by typing:",
191
198
  "",
192
199
  ` cd ${config.directory}`,
193
- ` ${devCommand}`,
200
+ config.autoDeploy ? " tail -f .service/local-dev.log" : ` ${devCommand}`,
194
201
  "",
195
202
  repository ? `Repository: ${repository}` : undefined,
196
203
  `Production API: https://${config.apiHostname}`,
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { buildDeploymentVerificationCommands, buildPostScaffoldCommands } from "./post-scaffold";
2
+ import { buildDeploymentVerificationCommands, buildLocalVerificationCommands, buildPostScaffoldCommands } from "./post-scaffold";
3
3
 
4
4
  describe("buildPostScaffoldCommands", () => {
5
5
  test("runs create and deploy for HTTP services", () => {
@@ -25,6 +25,32 @@ describe("buildPostScaffoldCommands", () => {
25
25
  });
26
26
  });
27
27
 
28
+ describe("buildLocalVerificationCommands", () => {
29
+ test("uses local curl checks for Bun Hono services", () => {
30
+ expect(buildLocalVerificationCommands({ apiHostname: "api.launch.anmho.com", framework: "hono", runtime: "bun" })).toEqual([
31
+ { command: "sh", args: ["-c", 'curl --fail --show-error --silent "http://127.0.0.1:3000/"'] },
32
+ { command: "sh", args: ["-c", 'curl --fail --show-error --silent "http://127.0.0.1:3000/readyz"'] },
33
+ {
34
+ command: "sh",
35
+ args: [
36
+ "-c",
37
+ 'TOKEN="$(service auth token)" && curl --fail --show-error --silent -H "Authorization: Bearer $TOKEN" "http://127.0.0.1:3000/v1/admin/waitlist?limit=1"',
38
+ ],
39
+ },
40
+ ]);
41
+ });
42
+
43
+ test("uses plaintext grpcurl for local Go ConnectRPC services", () => {
44
+ expect(buildLocalVerificationCommands({ apiHostname: "api.launch.anmho.com", framework: "connectrpc", runtime: "go" })).toContainEqual({
45
+ command: "sh",
46
+ args: [
47
+ "-c",
48
+ 'TOKEN="$(service auth token)" && grpcurl -plaintext -H "Authorization: Bearer $TOKEN" -d \'{"limit":1}\' -proto protos/waitlist/v1/waitlist.proto "127.0.0.1:8080" waitlist.v1.WaitlistService/ListWaitlistEntries',
49
+ ],
50
+ });
51
+ });
52
+ });
53
+
28
54
  describe("buildDeploymentVerificationCommands", () => {
29
55
  test("uses curl health checks for HTTP services", () => {
30
56
  expect(buildDeploymentVerificationCommands({ apiHostname: "api.launch.anmho.com", framework: "hono", runtime: "bun" })).toEqual([
@@ -33,12 +33,26 @@ export async function runPostScaffoldFlow(config: ScaffoldConfig, cwd: string) {
33
33
  for (const command of buildDeploymentVerificationCommands(config)) {
34
34
  runWithRetries(command, { cwd, quiet: true }, DEPLOYMENT_VERIFY_ATTEMPTS, DEPLOYMENT_VERIFY_DELAY_MS);
35
35
  }
36
- return { message: "Dependencies installed, service created, service deployed, and production health verified" };
36
+ startLocalDevelopment(config, cwd);
37
+ for (const command of buildLocalVerificationCommands(config)) {
38
+ runWithRetries(command, { cwd, quiet: true }, 18, 5_000);
39
+ }
40
+ return { message: "Dependencies installed, service created, service deployed, production verified, and local dev started" };
37
41
  }
38
42
 
39
43
  return { message: "Backend package generated" };
40
44
  }
41
45
 
46
+ function startLocalDevelopment(config: Pick<ScaffoldConfig, "target">, cwd: string) {
47
+ if (config.target === "workers") {
48
+ run("sh", ["-c", "mkdir -p .service && nohup bun run dev > .service/local-dev.log 2>&1 < /dev/null & echo $! > .service/local-dev.pid"], { cwd });
49
+ return;
50
+ }
51
+
52
+ run("bun", ["run", "migrate"], { cwd });
53
+ run("sh", ["-c", "mkdir -p .service && nohup bun run dev > .service/local-dev.log 2>&1 < /dev/null & echo $! > .service/local-dev.pid"], { cwd });
54
+ }
55
+
42
56
  function runWithRetries(command: PostScaffoldCommand, options: CommandOptions, attempts: number, delayMs: number) {
43
57
  let lastError: unknown;
44
58
  for (let attempt = 1; attempt <= attempts; attempt += 1) {
@@ -68,6 +82,18 @@ export function buildDeploymentVerificationCommands(
68
82
  ];
69
83
  }
70
84
 
85
+ export function buildLocalVerificationCommands(
86
+ config: Pick<ScaffoldConfig, "apiHostname" | "framework" | "runtime"> & Partial<Pick<ScaffoldConfig, "target">>
87
+ ): PostScaffoldCommand[] {
88
+ const origin = localVerificationOrigin(config);
89
+ const tokenCommand = 'TOKEN="$(service auth token)"';
90
+ return [
91
+ shellVerificationCommand(`curl --fail --show-error --silent "${origin}/"`),
92
+ shellVerificationCommand(`curl --fail --show-error --silent "${origin}/readyz"`),
93
+ protectedLocalVerificationCommand(config, origin, tokenCommand),
94
+ ];
95
+ }
96
+
71
97
  function protectedVerificationCommand(
72
98
  config: Pick<ScaffoldConfig, "apiHostname" | "framework" | "runtime"> &
73
99
  Partial<Pick<ScaffoldConfig, "target" | "serviceName" | "gcpProject" | "region">>,
@@ -124,6 +150,33 @@ function protectedVerificationCommand(
124
150
  };
125
151
  }
126
152
 
153
+ function protectedLocalVerificationCommand(
154
+ config: Pick<ScaffoldConfig, "apiHostname" | "framework" | "runtime"> & Partial<Pick<ScaffoldConfig, "target">>,
155
+ origin: string,
156
+ tokenCommand: string
157
+ ): PostScaffoldCommand {
158
+ if (config.framework === "connectrpc" && config.runtime === "go") {
159
+ const host = localVerificationHost(config);
160
+ return {
161
+ command: "sh",
162
+ args: [
163
+ "-c",
164
+ [
165
+ `${tokenCommand} &&`,
166
+ "grpcurl -plaintext",
167
+ '-H "Authorization: Bearer $TOKEN"',
168
+ "-d '{\"limit\":1}'",
169
+ "-proto protos/waitlist/v1/waitlist.proto",
170
+ `"${host}"`,
171
+ "waitlist.v1.WaitlistService/ListWaitlistEntries",
172
+ ].join(" "),
173
+ ],
174
+ };
175
+ }
176
+
177
+ return protectedVerificationCommand(config, origin, tokenCommand);
178
+ }
179
+
127
180
  function shellVerificationCommand(script: string): PostScaffoldCommand {
128
181
  return { command: "sh", args: ["-c", script] };
129
182
  }
@@ -146,6 +199,20 @@ function verificationHost(
146
199
  return config.apiHostname;
147
200
  }
148
201
 
202
+ function localVerificationOrigin(config: Partial<Pick<ScaffoldConfig, "target" | "runtime" | "framework">>) {
203
+ if (config.target === "workers") {
204
+ return "http://127.0.0.1:8787";
205
+ }
206
+ if (config.runtime === "bun" && config.framework === "hono") {
207
+ return "http://127.0.0.1:3000";
208
+ }
209
+ return "http://127.0.0.1:8080";
210
+ }
211
+
212
+ function localVerificationHost(config: Partial<Pick<ScaffoldConfig, "target" | "runtime" | "framework">>) {
213
+ return localVerificationOrigin(config).replace(/^https?:\/\//, "");
214
+ }
215
+
149
216
  export function buildPostScaffoldCommands(
150
217
  config: Pick<ScaffoldConfig, "framework"> & Partial<Pick<ScaffoldConfig, "target">>
151
218
  ): PostScaffoldCommand[] {