create-svc 0.1.66 → 0.1.68
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/scaffold.test.ts +12 -0
- package/src/service-runtime/cloudrun/cli.ts +4 -4
- package/src/service-runtime/cloudrun/sdk.test.ts +107 -0
- package/templates/shared/.github/workflows/deploy.yml +2 -0
- package/templates/shared/.github/workflows/personal.yml +2 -0
- package/templates/shared/.github/workflows/preview.yml +2 -0
- package/templates/shared/README.md +12 -0
package/package.json
CHANGED
package/src/scaffold.test.ts
CHANGED
|
@@ -146,6 +146,9 @@ test("scaffolds all runtime/framework variants with shared cloudrun config", asy
|
|
|
146
146
|
expect(previewWorkflow).toContain("steps.pr.outputs.number");
|
|
147
147
|
expect(previewWorkflow).toContain("NEON_API_KEY");
|
|
148
148
|
expect(previewWorkflow).toContain("CLOUDFLARE_API_TOKEN");
|
|
149
|
+
if (variant.runtime === "go") {
|
|
150
|
+
expect(previewWorkflow).toContain("ariga/setup-atlas");
|
|
151
|
+
}
|
|
149
152
|
|
|
150
153
|
const previewCleanupWorkflow = await Bun.file(join(generatedRoot, ".github", "workflows", "preview-cleanup.yml")).text();
|
|
151
154
|
expect(previewCleanupWorkflow).toContain("pull_request:");
|
|
@@ -155,6 +158,9 @@ test("scaffolds all runtime/framework variants with shared cloudrun config", asy
|
|
|
155
158
|
const deployWorkflow = await Bun.file(join(generatedRoot, ".github", "workflows", "deploy.yml")).text();
|
|
156
159
|
expect(deployWorkflow).toContain("branches:");
|
|
157
160
|
expect(deployWorkflow).toContain("- main");
|
|
161
|
+
if (variant.runtime === "go") {
|
|
162
|
+
expect(deployWorkflow).toContain("ariga/setup-atlas");
|
|
163
|
+
}
|
|
158
164
|
expect(deployWorkflow).toContain("gcloud components install beta --quiet");
|
|
159
165
|
expect(deployWorkflow).toContain("bun install -g create-svc@latest");
|
|
160
166
|
expect(deployWorkflow).toContain("service deploy --ci");
|
|
@@ -166,6 +172,9 @@ test("scaffolds all runtime/framework variants with shared cloudrun config", asy
|
|
|
166
172
|
expect(personalWorkflow).toContain("workflow_dispatch:");
|
|
167
173
|
expect(personalWorkflow).toContain("service deploy --ci --environment personal --name");
|
|
168
174
|
expect(personalWorkflow).toContain("service deploy --ci --destroy --environment personal --name");
|
|
175
|
+
if (variant.runtime === "go") {
|
|
176
|
+
expect(personalWorkflow).toContain("ariga/setup-atlas");
|
|
177
|
+
}
|
|
169
178
|
|
|
170
179
|
if (variant.runtime === "go") {
|
|
171
180
|
const goMod = await Bun.file(join(generatedRoot, "go.mod")).text();
|
|
@@ -327,6 +336,9 @@ test("scaffolds a backend package cleanly into a nested monorepo-style directory
|
|
|
327
336
|
expect(readme).toContain("GCP_DEPLOYER_SERVICE_ACCOUNT");
|
|
328
337
|
expect(readme).toContain("NEON_API_KEY");
|
|
329
338
|
expect(readme).toContain("CLOUDFLARE_API_TOKEN");
|
|
339
|
+
expect(readme).toContain("ConnectRPC service builds import the generated bindings checked into this repo");
|
|
340
|
+
expect(readme).toContain("does not rewrite this");
|
|
341
|
+
expect(readme).toContain("service's Go imports away from local generated packages");
|
|
330
342
|
|
|
331
343
|
const packageJson = await Bun.file(join(generatedRoot, "package.json")).text();
|
|
332
344
|
expect(packageJson).toContain('"hono"');
|
|
@@ -351,7 +351,7 @@ async function runSdk(args: string[]) {
|
|
|
351
351
|
requireCommand("buf");
|
|
352
352
|
run("buf", ["push"]);
|
|
353
353
|
await writeSdkMode("remote");
|
|
354
|
-
return "Schema pushed to Buf Schema Registry";
|
|
354
|
+
return "Schema pushed to Buf Schema Registry and recorded for consumers";
|
|
355
355
|
}
|
|
356
356
|
|
|
357
357
|
if (subcommand === "build") {
|
|
@@ -361,18 +361,18 @@ async function runSdk(args: string[]) {
|
|
|
361
361
|
run("make", ["gen"]);
|
|
362
362
|
}
|
|
363
363
|
await writeSdkMode("local");
|
|
364
|
-
return "Local SDK artifacts generated and
|
|
364
|
+
return "Local SDK artifacts generated and recorded";
|
|
365
365
|
}
|
|
366
366
|
|
|
367
367
|
if (subcommand === "use-local") {
|
|
368
368
|
await assertLocalSdkArtifacts();
|
|
369
369
|
await writeSdkMode("local");
|
|
370
|
-
return "Local SDK artifacts
|
|
370
|
+
return "Local SDK artifacts recorded";
|
|
371
371
|
}
|
|
372
372
|
|
|
373
373
|
if (subcommand === "use-remote") {
|
|
374
374
|
await writeSdkMode("remote");
|
|
375
|
-
return `Remote Buf SDK
|
|
375
|
+
return `Remote Buf SDK recorded for consumers: ${bufModule()}`;
|
|
376
376
|
}
|
|
377
377
|
|
|
378
378
|
throw new Error("Usage: service sdk <build|publish|use-local|use-remote>");
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import { chmod, mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { scaffoldProject, type ScaffoldConfig } from "../../scaffold";
|
|
6
|
+
|
|
7
|
+
function baseConfig(directory: string): ScaffoldConfig {
|
|
8
|
+
return {
|
|
9
|
+
directory,
|
|
10
|
+
serviceName: "sdk-proof",
|
|
11
|
+
modulePath: "github.com/anmho/sdk-proof",
|
|
12
|
+
target: "cloudrun",
|
|
13
|
+
runtime: "go",
|
|
14
|
+
framework: "connectrpc",
|
|
15
|
+
region: "us-west1",
|
|
16
|
+
gcpProjectMode: "use_existing",
|
|
17
|
+
gcpProject: "anmho-services",
|
|
18
|
+
gcpProjectName: "services",
|
|
19
|
+
billingAccount: "",
|
|
20
|
+
quotaProjectId: "anmho-infra-prod",
|
|
21
|
+
profile: "microservice",
|
|
22
|
+
git: {
|
|
23
|
+
enabled: false,
|
|
24
|
+
owner: "anmho",
|
|
25
|
+
repository: "sdk-proof",
|
|
26
|
+
},
|
|
27
|
+
neonDatabaseName: "sdk_proof",
|
|
28
|
+
apiHostname: "api.sdk-proof.anmho.com",
|
|
29
|
+
generatorRoot: join(import.meta.dir, "..", "..", ".."),
|
|
30
|
+
autoDeploy: false,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
test("service sdk publish pushes the named Buf module and selects remote SDK mode", async () => {
|
|
35
|
+
const root = await mkdtemp(join(tmpdir(), "create-svc-sdk-"));
|
|
36
|
+
const generatedRoot = join(root, "sdk-proof");
|
|
37
|
+
const fakeBin = join(root, "bin");
|
|
38
|
+
const bufLog = join(root, "buf.log");
|
|
39
|
+
|
|
40
|
+
await scaffoldProject(baseConfig(generatedRoot));
|
|
41
|
+
await mkdir(join(generatedRoot, "node_modules"));
|
|
42
|
+
await mkdir(fakeBin);
|
|
43
|
+
await writeFile(
|
|
44
|
+
join(fakeBin, "buf"),
|
|
45
|
+
["#!/bin/sh", `echo "$@" > "${bufLog}"`, "exit 0", ""].join("\n")
|
|
46
|
+
);
|
|
47
|
+
await chmod(join(fakeBin, "buf"), 0o755);
|
|
48
|
+
|
|
49
|
+
const result = Bun.spawnSync(["bun", join(import.meta.dir, "..", "..", "..", "index.ts"), "sdk", "publish"], {
|
|
50
|
+
cwd: generatedRoot,
|
|
51
|
+
env: { ...process.env, PATH: `${fakeBin}:${process.env.PATH ?? ""}` },
|
|
52
|
+
stdout: "pipe",
|
|
53
|
+
stderr: "pipe",
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
expect(result.success, [result.stdout.toString(), result.stderr.toString()].join("\n")).toBeTrue();
|
|
57
|
+
expect(result.stdout.toString()).toContain("recorded for consumers");
|
|
58
|
+
expect((await readFile(bufLog, "utf8")).trim()).toBe("push");
|
|
59
|
+
const sdkState = JSON.parse(await Bun.file(join(generatedRoot, ".service", "sdk.json")).text());
|
|
60
|
+
expect(sdkState).toMatchObject({
|
|
61
|
+
mode: "remote",
|
|
62
|
+
module: "buf.build/anmho/sdk-proof",
|
|
63
|
+
localPath: "./gen/waitlist/v1",
|
|
64
|
+
});
|
|
65
|
+
const bufConfig = await Bun.file(join(generatedRoot, "buf.yaml")).text();
|
|
66
|
+
expect(bufConfig).toContain("name: buf.build/anmho/sdk-proof");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("service sdk publish leaves local SDK mode when Buf push fails", async () => {
|
|
70
|
+
const root = await mkdtemp(join(tmpdir(), "create-svc-sdk-"));
|
|
71
|
+
const generatedRoot = join(root, "sdk-proof");
|
|
72
|
+
const fakeBin = join(root, "bin");
|
|
73
|
+
|
|
74
|
+
await scaffoldProject(baseConfig(generatedRoot));
|
|
75
|
+
await mkdir(join(generatedRoot, "node_modules"));
|
|
76
|
+
await mkdir(fakeBin);
|
|
77
|
+
await mkdir(join(generatedRoot, ".service"));
|
|
78
|
+
await Bun.write(
|
|
79
|
+
join(generatedRoot, ".service", "sdk.json"),
|
|
80
|
+
`${JSON.stringify(
|
|
81
|
+
{
|
|
82
|
+
mode: "local",
|
|
83
|
+
module: "buf.build/anmho/sdk-proof",
|
|
84
|
+
localPath: "./gen/waitlist/v1",
|
|
85
|
+
updatedAt: "2026-05-25T00:00:00.000Z",
|
|
86
|
+
},
|
|
87
|
+
null,
|
|
88
|
+
2
|
|
89
|
+
)}\n`
|
|
90
|
+
);
|
|
91
|
+
await writeFile(join(fakeBin, "buf"), ["#!/bin/sh", "echo denied >&2", "exit 1", ""].join("\n"));
|
|
92
|
+
await chmod(join(fakeBin, "buf"), 0o755);
|
|
93
|
+
|
|
94
|
+
const before = JSON.parse(await Bun.file(join(generatedRoot, ".service", "sdk.json")).text());
|
|
95
|
+
expect(before.mode).toBe("local");
|
|
96
|
+
|
|
97
|
+
const result = Bun.spawnSync(["bun", join(import.meta.dir, "..", "..", "..", "index.ts"), "sdk", "publish"], {
|
|
98
|
+
cwd: generatedRoot,
|
|
99
|
+
env: { ...process.env, PATH: `${fakeBin}:${process.env.PATH ?? ""}` },
|
|
100
|
+
stdout: "pipe",
|
|
101
|
+
stderr: "pipe",
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
expect(result.success).toBeFalse();
|
|
105
|
+
const after = JSON.parse(await Bun.file(join(generatedRoot, ".service", "sdk.json")).text());
|
|
106
|
+
expect(after.mode).toBe("local");
|
|
107
|
+
});
|
|
@@ -258,6 +258,18 @@ secrets only when you add a provider adapter. A generic adapter can honor:
|
|
|
258
258
|
|
|
259
259
|
- `WEBHOOK_<PROVIDER>_SECRET`
|
|
260
260
|
|
|
261
|
+
## ConnectRPC SDK publishing
|
|
262
|
+
|
|
263
|
+
ConnectRPC service builds import the generated bindings checked into this repo
|
|
264
|
+
under `gen/`. That keeps the API and worker images self-contained and does not
|
|
265
|
+
change when SDK metadata changes.
|
|
266
|
+
|
|
267
|
+
Use `service sdk build` after editing protobufs to regenerate local
|
|
268
|
+
bindings for this service. Use `service sdk publish` to push the
|
|
269
|
+
schema to the Buf Schema Registry for external consumers. A successful publish
|
|
270
|
+
records the remote module in `.service/sdk.json`; it does not rewrite this
|
|
271
|
+
service's Go imports away from local generated packages.
|
|
272
|
+
|
|
261
273
|
## GitHub Actions deployment
|
|
262
274
|
|
|
263
275
|
The scaffold emits a minimal deployment workflow slice for Cloud Run:
|