create-svc 0.1.10 → 0.1.11
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/README.md +46 -43
- package/bin/create-service.mjs +2 -0
- package/package.json +12 -9
- package/src/cli.test.ts +28 -10
- package/src/cli.ts +195 -30
- package/src/git-bootstrap.test.ts +40 -0
- package/src/git-bootstrap.ts +110 -0
- package/src/naming.test.ts +1 -0
- package/src/naming.ts +23 -0
- package/src/post-scaffold.test.ts +19 -0
- package/src/post-scaffold.ts +17 -4
- package/src/profiles.ts +2 -5
- package/src/scaffold.test.ts +231 -40
- package/src/scaffold.ts +84 -29
- package/src/vault.test.ts +61 -1
- package/src/vault.ts +77 -15
- package/templates/shared/.github/workflows/ci.yml +2 -1
- package/templates/shared/.github/workflows/deploy.yml +2 -0
- package/templates/shared/README.md +124 -47
- package/templates/shared/grafana/alerts.yaml +54 -0
- package/templates/shared/grafana/waitlist-dashboard.json +63 -0
- package/templates/shared/scripts/authctl.ts +231 -0
- package/templates/shared/scripts/cloudrun/bootstrap.ts +14 -5
- package/templates/shared/scripts/cloudrun/cleanup.ts +64 -4
- package/templates/shared/scripts/cloudrun/cli.ts +324 -7
- package/templates/shared/scripts/cloudrun/config.ts +11 -4
- package/templates/shared/scripts/cloudrun/deploy.ts +0 -4
- package/templates/shared/scripts/cloudrun/lib.ts +174 -41
- package/templates/shared/scripts/cloudrun/neon.ts +45 -0
- package/templates/shared/scripts/dev.ts +22 -0
- package/templates/shared/scripts/ensure-local-db.ts +3 -0
- package/templates/shared/scripts/local-docker.ts +63 -0
- package/templates/shared/scripts/local-env.ts +27 -0
- package/templates/shared/scripts/seed.ts +73 -0
- package/templates/shared/scripts/wait-for-db.ts +32 -0
- package/templates/shared/service.config.ts +59 -0
- package/templates/shared/service.yaml +24 -44
- package/templates/targets/workers/.github/workflows/ci.yml +19 -0
- package/templates/targets/workers/.github/workflows/deploy.yml +19 -0
- package/templates/targets/workers/Makefile +33 -0
- package/templates/targets/workers/README.md +75 -0
- package/templates/targets/workers/package.json +35 -0
- package/templates/targets/workers/scripts/workers/cli.ts +397 -0
- package/templates/targets/workers/src/auth.ts +178 -0
- package/templates/targets/workers/src/index.ts +198 -0
- package/templates/targets/workers/src/storage.ts +370 -0
- package/templates/targets/workers/test/app.test.ts +108 -0
- package/templates/targets/workers/tsconfig.json +11 -0
- package/templates/targets/workers/wrangler.toml +24 -0
- package/templates/variants/bun-connectrpc/Makefile +14 -8
- package/templates/variants/bun-connectrpc/gen/protos/waitlist/v1/waitlist_pb.ts +424 -0
- package/templates/variants/bun-connectrpc/migrations/0000_init.sql +12 -55
- package/templates/variants/bun-connectrpc/package.json +12 -5
- package/templates/variants/bun-connectrpc/protos/waitlist/v1/waitlist.proto +91 -0
- package/templates/variants/bun-connectrpc/scripts/codegen.ts +1 -1
- package/templates/variants/bun-connectrpc/scripts/migrate.ts +4 -1
- package/templates/variants/bun-connectrpc/src/auth.ts +200 -0
- package/templates/variants/bun-connectrpc/src/db/repository.ts +67 -420
- package/templates/variants/bun-connectrpc/src/db/schema.ts +15 -64
- package/templates/variants/bun-connectrpc/src/index.ts +76 -176
- package/templates/variants/bun-connectrpc/src/temporal/activities.ts +14 -0
- package/templates/variants/bun-connectrpc/src/temporal/worker.ts +38 -0
- package/templates/variants/bun-connectrpc/src/temporal/workflows.ts +10 -0
- package/templates/variants/bun-connectrpc/src/waitlist/service.ts +172 -0
- package/templates/variants/bun-connectrpc/src/waitlist/types.ts +45 -0
- package/templates/variants/bun-connectrpc/test/app.test.ts +4 -4
- package/templates/variants/bun-connectrpc/test/waitlist.integration.test.ts +71 -0
- package/templates/variants/bun-hono/Makefile +14 -8
- package/templates/variants/bun-hono/migrations/0000_init.sql +12 -55
- package/templates/variants/bun-hono/package.json +12 -5
- package/templates/variants/bun-hono/scripts/migrate.ts +4 -1
- package/templates/variants/bun-hono/src/auth.ts +181 -0
- package/templates/variants/bun-hono/src/db/repository.ts +68 -421
- package/templates/variants/bun-hono/src/db/schema.ts +15 -64
- package/templates/variants/bun-hono/src/index.ts +65 -180
- package/templates/variants/bun-hono/src/temporal/activities.ts +14 -0
- package/templates/variants/bun-hono/src/temporal/worker.ts +38 -0
- package/templates/variants/bun-hono/src/temporal/workflows.ts +10 -0
- package/templates/variants/bun-hono/src/waitlist/service.ts +166 -0
- package/templates/variants/bun-hono/src/waitlist/types.ts +50 -0
- package/templates/variants/bun-hono/test/app.test.ts +72 -41
- package/templates/variants/bun-hono/test/waitlist.integration.test.ts +102 -0
- package/templates/variants/go-chi/Makefile +27 -11
- package/templates/variants/go-chi/atlas.hcl +8 -0
- package/templates/variants/go-chi/cmd/server/main.go +21 -10
- package/templates/variants/go-chi/go.mod +1 -3
- package/templates/variants/go-chi/internal/app/service.go +202 -685
- package/templates/variants/go-chi/internal/auth/middleware.go +289 -0
- package/templates/variants/go-chi/internal/auth/middleware_test.go +38 -0
- package/templates/variants/go-chi/internal/config/config.go +27 -11
- package/templates/variants/go-chi/internal/httpapi/routes.go +78 -157
- package/templates/variants/go-chi/internal/httpapi/waitlist_integration_test.go +199 -0
- package/templates/variants/go-chi/internal/temporal/activities.go +27 -0
- package/templates/variants/go-chi/internal/temporal/worker.go +42 -0
- package/templates/variants/go-chi/internal/temporal/workflows.go +18 -0
- package/templates/variants/go-chi/migrations/0000_init.sql +12 -55
- package/templates/variants/go-chi/migrations/atlas.sum +2 -0
- package/templates/variants/go-chi/package.json +7 -1
- package/templates/variants/go-connectrpc/Makefile +26 -9
- package/templates/variants/go-connectrpc/atlas.hcl +8 -0
- package/templates/variants/go-connectrpc/buf.gen.yaml +2 -2
- package/templates/variants/go-connectrpc/cmd/server/main.go +23 -12
- package/templates/variants/go-connectrpc/gen/waitlist/v1/waitlist.pb.go +960 -0
- package/templates/variants/go-connectrpc/gen/waitlist/v1/waitlistv1connect/waitlist.connect.go +283 -0
- package/templates/variants/go-connectrpc/go.mod +1 -1
- package/templates/variants/go-connectrpc/internal/app/service.go +202 -685
- package/templates/variants/go-connectrpc/internal/auth/middleware.go +289 -0
- package/templates/variants/go-connectrpc/internal/auth/middleware_test.go +38 -0
- package/templates/variants/go-connectrpc/internal/config/config.go +27 -11
- package/templates/variants/go-connectrpc/internal/connectapi/handler.go +78 -201
- package/templates/variants/go-connectrpc/internal/connectapi/waitlist_integration_test.go +122 -0
- package/templates/variants/go-connectrpc/internal/httpapi/routes.go +147 -9
- package/templates/variants/go-connectrpc/internal/temporal/activities.go +27 -0
- package/templates/variants/go-connectrpc/internal/temporal/worker.go +42 -0
- package/templates/variants/go-connectrpc/internal/temporal/workflows.go +18 -0
- package/templates/variants/go-connectrpc/migrations/0000_init.sql +12 -55
- package/templates/variants/go-connectrpc/migrations/atlas.sum +2 -0
- package/templates/variants/go-connectrpc/package.json +7 -1
- package/templates/variants/go-connectrpc/protos/waitlist/v1/waitlist.proto +93 -0
- package/templates/root/.github/workflows/buf-publish.yml +0 -19
- package/templates/root/.github/workflows/ci.yml +0 -26
- package/templates/root/.github/workflows/deploy.yml +0 -22
- package/templates/root/Dockerfile +0 -23
- package/templates/root/README.md +0 -69
- package/templates/root/buf.gen.yaml +0 -10
- package/templates/root/buf.yaml +0 -9
- package/templates/root/cmd/server/main.go +0 -44
- package/templates/root/gen/dns/v1/dns.pb.go +0 -623
- package/templates/root/gen/dns/v1/dnsv1connect/dns.connect.go +0 -192
- package/templates/root/go.mod +0 -10
- package/templates/root/internal/app/service.go +0 -152
- package/templates/root/internal/app/token_source.go +0 -50
- package/templates/root/internal/cloudflare/client.go +0 -160
- package/templates/root/internal/config/config.go +0 -55
- package/templates/root/internal/connectapi/handler.go +0 -79
- package/templates/root/internal/httpapi/routes.go +0 -93
- package/templates/root/internal/vault/client.go +0 -148
- package/templates/root/package.json +0 -12
- package/templates/root/protos/dns/v1/dns.proto +0 -58
- package/templates/root/scripts/cloudrun/bootstrap.ts +0 -65
- package/templates/root/scripts/cloudrun/config.ts +0 -50
- package/templates/root/scripts/cloudrun/deploy.ts +0 -41
- package/templates/root/scripts/cloudrun/lib.ts +0 -244
- package/templates/root/service.yaml +0 -50
- package/templates/root/test/go.test.ts +0 -19
- package/templates/shared/scripts/cloudrun/integrations.ts +0 -111
- package/templates/variants/bun-connectrpc/gen/protos/chat/v1/chat_pb.ts +0 -1078
- package/templates/variants/bun-connectrpc/protos/chat/v1/chat.proto +0 -228
- package/templates/variants/bun-connectrpc/src/chat/service.ts +0 -384
- package/templates/variants/bun-connectrpc/src/chat/types.ts +0 -142
- package/templates/variants/bun-connectrpc/src/storage.ts +0 -72
- package/templates/variants/bun-connectrpc/src/webhooks.ts +0 -35
- package/templates/variants/bun-connectrpc/test/list-messages.integration.test.ts +0 -182
- package/templates/variants/bun-hono/src/chat/service.ts +0 -384
- package/templates/variants/bun-hono/src/chat/types.ts +0 -142
- package/templates/variants/bun-hono/src/storage.ts +0 -72
- package/templates/variants/bun-hono/src/webhooks.ts +0 -35
- package/templates/variants/bun-hono/test/list-messages.integration.test.ts +0 -256
- package/templates/variants/go-chi/buf.gen.yaml +0 -12
- package/templates/variants/go-chi/buf.yaml +0 -9
- package/templates/variants/go-chi/cmd/migrate/main.go +0 -101
- package/templates/variants/go-chi/internal/httpapi/list_messages_integration_test.go +0 -298
- package/templates/variants/go-chi/protos/chat/v1/chat.proto +0 -219
- package/templates/variants/go-connectrpc/cmd/migrate/main.go +0 -101
- package/templates/variants/go-connectrpc/gen/chat/v1/chat.pb.go +0 -2512
- package/templates/variants/go-connectrpc/gen/chat/v1/chatv1connect/chat.connect.go +0 -571
- package/templates/variants/go-connectrpc/internal/connectapi/list_messages_integration_test.go +0 -216
- package/templates/variants/go-connectrpc/protos/chat/v1/chat.proto +0 -232
package/src/cli.ts
CHANGED
|
@@ -16,13 +16,16 @@ import { readdirSync } from "node:fs";
|
|
|
16
16
|
import { basename, dirname, resolve } from "node:path";
|
|
17
17
|
import { fileURLToPath } from "node:url";
|
|
18
18
|
import { runPostScaffoldFlow } from "./post-scaffold";
|
|
19
|
+
import { bootstrapGitHubRepository, buildGitBootstrapConfig, commitAndPushGeneratedArtifacts } from "./git-bootstrap";
|
|
19
20
|
import { listOpenBillingAccounts, listAccessibleProjects, type BillingAccount, type GcpProject } from "./gcp";
|
|
20
21
|
import {
|
|
21
22
|
BILLING_ACCOUNT_DEFAULT,
|
|
22
|
-
FRAMEWORKS_BY_RUNTIME,
|
|
23
23
|
QUOTA_PROJECT_DEFAULT,
|
|
24
24
|
deriveDefaults,
|
|
25
|
+
frameworksForTargetRuntime,
|
|
26
|
+
parseDeployTarget,
|
|
25
27
|
slugify,
|
|
28
|
+
type DeployTarget,
|
|
26
29
|
type Framework,
|
|
27
30
|
type GcpProjectMode,
|
|
28
31
|
type Runtime,
|
|
@@ -37,6 +40,7 @@ import {
|
|
|
37
40
|
|
|
38
41
|
type ParsedArgs = {
|
|
39
42
|
directory?: string;
|
|
43
|
+
target?: DeployTarget;
|
|
40
44
|
runtime?: Runtime;
|
|
41
45
|
framework?: Framework;
|
|
42
46
|
modulePath?: string;
|
|
@@ -46,6 +50,9 @@ type ParsedArgs = {
|
|
|
46
50
|
billingAccount?: string;
|
|
47
51
|
quotaProjectId?: string;
|
|
48
52
|
autoDeploy?: boolean;
|
|
53
|
+
autoUpdate?: boolean;
|
|
54
|
+
noUpdateCheck?: boolean;
|
|
55
|
+
noGit?: boolean;
|
|
49
56
|
profile: Profile;
|
|
50
57
|
yes: boolean;
|
|
51
58
|
help: boolean;
|
|
@@ -67,7 +74,9 @@ export async function run(argv: string[]) {
|
|
|
67
74
|
return;
|
|
68
75
|
}
|
|
69
76
|
|
|
70
|
-
|
|
77
|
+
await maybeCheckForUpdate(args);
|
|
78
|
+
|
|
79
|
+
intro(`${pc.bold("create-service")} ${pc.dim("microservice bootstrap")}`);
|
|
71
80
|
|
|
72
81
|
const config = await resolveConfig(args);
|
|
73
82
|
const targetDir = resolve(process.cwd(), config.directory);
|
|
@@ -75,10 +84,12 @@ export async function run(argv: string[]) {
|
|
|
75
84
|
note(
|
|
76
85
|
[
|
|
77
86
|
`${pc.bold("Output")}: ${targetDir}`,
|
|
87
|
+
`${pc.bold("Target")}: ${config.target}`,
|
|
78
88
|
`${pc.bold("Runtime")}: ${config.runtime} + ${config.framework}`,
|
|
79
89
|
`${pc.bold("Project")}: ${config.gcpProjectMode === "create_new" ? "create" : "use"} ${config.gcpProjectName} (${config.gcpProject})`,
|
|
80
90
|
`${pc.bold("API")}: https://${config.apiHostname}`,
|
|
81
91
|
`${pc.bold("Local DB")}: docker compose postgres`,
|
|
92
|
+
`${pc.bold("GitHub")}: ${config.git.enabled ? `anmho/${config.git.repository}` : "disabled"}`,
|
|
82
93
|
].join("\n"),
|
|
83
94
|
"Scaffold"
|
|
84
95
|
);
|
|
@@ -88,6 +99,17 @@ export async function run(argv: string[]) {
|
|
|
88
99
|
await scaffoldProject(config);
|
|
89
100
|
buildSpinner.stop("Project files generated");
|
|
90
101
|
|
|
102
|
+
const gitSpinner = spinner();
|
|
103
|
+
gitSpinner.start("Preparing git repository");
|
|
104
|
+
const gitResult = await bootstrapGitHubRepository(targetDir, config.git);
|
|
105
|
+
if (gitResult.status === "created") {
|
|
106
|
+
gitSpinner.stop(`GitHub repository created: ${gitResult.url}`);
|
|
107
|
+
} else if (gitResult.status === "skipped-existing-worktree") {
|
|
108
|
+
gitSpinner.stop(`Existing git worktree detected: ${gitResult.root}`);
|
|
109
|
+
} else {
|
|
110
|
+
gitSpinner.stop("Git bootstrap disabled");
|
|
111
|
+
}
|
|
112
|
+
|
|
91
113
|
const shouldRunPostScaffoldFlow = config.autoDeploy;
|
|
92
114
|
if (shouldRunPostScaffoldFlow) {
|
|
93
115
|
const automationSpinner = spinner();
|
|
@@ -96,8 +118,15 @@ export async function run(argv: string[]) {
|
|
|
96
118
|
const result = await runPostScaffoldFlow(config, targetDir);
|
|
97
119
|
automationSpinner.stop(result.message);
|
|
98
120
|
} catch (error) {
|
|
99
|
-
automationSpinner.stop("Post-scaffold automation
|
|
100
|
-
|
|
121
|
+
automationSpinner.stop("Post-scaffold automation failed");
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (gitResult.status === "created") {
|
|
126
|
+
const publishSpinner = spinner();
|
|
127
|
+
publishSpinner.start("Publishing generated artifacts");
|
|
128
|
+
const result = commitAndPushGeneratedArtifacts(targetDir, "Record generated deployment artifacts");
|
|
129
|
+
publishSpinner.stop(result.committed ? "Generated artifacts committed and pushed" : "Generated artifacts already committed");
|
|
101
130
|
}
|
|
102
131
|
}
|
|
103
132
|
|
|
@@ -105,18 +134,19 @@ export async function run(argv: string[]) {
|
|
|
105
134
|
outro(
|
|
106
135
|
[
|
|
107
136
|
`Next: ${pc.cyan(`cd ${config.directory}`)}`,
|
|
108
|
-
`Local DB: ${pc.cyan("
|
|
137
|
+
`Local DB: ${pc.cyan("started by local dev command")}`,
|
|
109
138
|
`Migrate: ${pc.cyan(isBun ? "bun run migrate" : "make migrate")}`,
|
|
110
139
|
`Local dev: ${pc.cyan(isBun ? "bun run dev" : "make dev")}`,
|
|
111
|
-
`
|
|
112
|
-
`Deploy: ${pc.cyan(isBun ? "bun run deploy" : "make deploy")}`,
|
|
140
|
+
`Create: ${pc.cyan(isBun ? "bun run service -- create" : "make create")}`,
|
|
141
|
+
`Deploy: ${pc.cyan(isBun ? "bun run service -- deploy" : "make deploy")}`,
|
|
142
|
+
config.git.enabled ? `Repository: ${pc.cyan(`https://github.com/anmho/${config.git.repository}`)}` : undefined,
|
|
113
143
|
`Personal env: ${pc.cyan(
|
|
114
144
|
isBun
|
|
115
145
|
? `bun run deploy -- --environment personal --name ${config.serviceName}`
|
|
116
146
|
: `make deploy ARGS="--environment personal --name ${config.serviceName}"`
|
|
117
147
|
)}`,
|
|
118
148
|
`Production API: ${pc.cyan(`https://${config.apiHostname}`)}`,
|
|
119
|
-
].join("\n")
|
|
149
|
+
].filter(Boolean).join("\n")
|
|
120
150
|
);
|
|
121
151
|
} catch (error) {
|
|
122
152
|
handleCliError(error);
|
|
@@ -160,6 +190,21 @@ export function parseArgs(argv: string[]): ParsedArgs {
|
|
|
160
190
|
continue;
|
|
161
191
|
}
|
|
162
192
|
|
|
193
|
+
if (token === "--auto-update") {
|
|
194
|
+
parsed.autoUpdate = true;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (token === "--no-update-check") {
|
|
199
|
+
parsed.noUpdateCheck = true;
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (token === "--no-git") {
|
|
204
|
+
parsed.noGit = true;
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
|
|
163
208
|
if (token === "--runtime") {
|
|
164
209
|
parsed.runtime = readValue() as Runtime;
|
|
165
210
|
continue;
|
|
@@ -170,6 +215,16 @@ export function parseArgs(argv: string[]): ParsedArgs {
|
|
|
170
215
|
continue;
|
|
171
216
|
}
|
|
172
217
|
|
|
218
|
+
if (token === "--target") {
|
|
219
|
+
parsed.target = parseDeployTarget(readValue());
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (token.startsWith("--target=")) {
|
|
224
|
+
parsed.target = parseDeployTarget(token.slice("--target=".length));
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
|
|
173
228
|
if (token === "--framework") {
|
|
174
229
|
parsed.framework = readValue() as Framework;
|
|
175
230
|
continue;
|
|
@@ -260,11 +315,6 @@ export function parseArgs(argv: string[]): ParsedArgs {
|
|
|
260
315
|
continue;
|
|
261
316
|
}
|
|
262
317
|
|
|
263
|
-
if (token === "--bootstrap") {
|
|
264
|
-
parsed.autoDeploy = true;
|
|
265
|
-
continue;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
318
|
if (token === "--no-auto-deploy") {
|
|
269
319
|
parsed.autoDeploy = false;
|
|
270
320
|
continue;
|
|
@@ -276,6 +326,69 @@ export function parseArgs(argv: string[]): ParsedArgs {
|
|
|
276
326
|
return parsed;
|
|
277
327
|
}
|
|
278
328
|
|
|
329
|
+
const CURRENT_VERSION = "0.1.9";
|
|
330
|
+
const PACKAGE_NAME = "create-svc";
|
|
331
|
+
|
|
332
|
+
async function maybeCheckForUpdate(args: ParsedArgs) {
|
|
333
|
+
if (args.noUpdateCheck || shouldSkipUpdateCheck()) {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const latest = await resolveLatestVersion().catch(() => "");
|
|
338
|
+
if (!latest || !isVersionGreater(latest, CURRENT_VERSION)) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const command = `bunx ${PACKAGE_NAME}@latest ${Bun.argv.slice(2).filter((arg) => arg !== "--auto-update").join(" ")}`.trim();
|
|
343
|
+
if (!args.autoUpdate) {
|
|
344
|
+
log.info(`A newer ${PACKAGE_NAME} is available: ${CURRENT_VERSION} -> ${latest}. Run ${command}`);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const result = Bun.spawnSync(["bunx", `${PACKAGE_NAME}@latest`, ...Bun.argv.slice(2).filter((arg) => arg !== "--auto-update")], {
|
|
349
|
+
stdin: "inherit",
|
|
350
|
+
stdout: "inherit",
|
|
351
|
+
stderr: "inherit",
|
|
352
|
+
env: {
|
|
353
|
+
...process.env,
|
|
354
|
+
CREATE_SERVICE_NO_UPDATE_CHECK: "1",
|
|
355
|
+
},
|
|
356
|
+
});
|
|
357
|
+
process.exit(result.exitCode);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function shouldSkipUpdateCheck() {
|
|
361
|
+
return Boolean(
|
|
362
|
+
process.env.CI ||
|
|
363
|
+
process.env.CODEX_CI ||
|
|
364
|
+
process.env.CREATE_SERVICE_NO_UPDATE_CHECK ||
|
|
365
|
+
process.env.BUN_TEST ||
|
|
366
|
+
process.env.npm_lifecycle_event
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
async function resolveLatestVersion() {
|
|
371
|
+
const response = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, {
|
|
372
|
+
signal: AbortSignal.timeout(1_500),
|
|
373
|
+
});
|
|
374
|
+
if (!response.ok) {
|
|
375
|
+
return "";
|
|
376
|
+
}
|
|
377
|
+
const payload = (await response.json()) as { version?: string };
|
|
378
|
+
return payload.version?.trim() ?? "";
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function isVersionGreater(left: string, right: string) {
|
|
382
|
+
const parse = (value: string) => value.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
383
|
+
const [leftMajor = 0, leftMinor = 0, leftPatch = 0] = parse(left);
|
|
384
|
+
const [rightMajor = 0, rightMinor = 0, rightPatch = 0] = parse(right);
|
|
385
|
+
return (
|
|
386
|
+
leftMajor > rightMajor ||
|
|
387
|
+
(leftMajor === rightMajor && leftMinor > rightMinor) ||
|
|
388
|
+
(leftMajor === rightMajor && leftMinor === rightMinor && leftPatch > rightPatch)
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
|
|
279
392
|
export async function resolveConfig(args: ParsedArgs): Promise<ScaffoldConfig> {
|
|
280
393
|
const inferredName = slugify(basename(args.directory ?? "my-service"));
|
|
281
394
|
const serviceName = args.yes
|
|
@@ -287,14 +400,17 @@ export async function resolveConfig(args: ParsedArgs): Promise<ScaffoldConfig> {
|
|
|
287
400
|
|
|
288
401
|
const discoveryPromise = discoverCloudInputs();
|
|
289
402
|
const defaults = deriveDefaults(serviceName);
|
|
290
|
-
const
|
|
291
|
-
const
|
|
403
|
+
const target = await resolveTarget(args);
|
|
404
|
+
const runtime = await resolveRuntime(args, target);
|
|
405
|
+
const framework = await resolveFramework(args, target, runtime);
|
|
406
|
+
validateTargetRuntimeFramework(target, runtime, framework);
|
|
292
407
|
const modulePath = await resolveModulePath(args, runtime, defaults.modulePath);
|
|
293
408
|
const discovery = await waitForDiscovery(discoveryPromise);
|
|
294
409
|
const gcpSelection = await resolveGcpSelection(args, defaults, discovery);
|
|
295
410
|
const region = args.region ?? DEFAULT_REGION;
|
|
296
411
|
const billingAccount = chooseBillingAccount(args.billingAccount, discovery.billingAccounts);
|
|
297
412
|
const autoDeploy = resolveAutoDeploy(args.autoDeploy);
|
|
413
|
+
const git = buildGitBootstrapConfig(serviceName, args.noGit);
|
|
298
414
|
|
|
299
415
|
if (!args.yes) {
|
|
300
416
|
const okay = await confirm({
|
|
@@ -315,6 +431,7 @@ export async function resolveConfig(args: ParsedArgs): Promise<ScaffoldConfig> {
|
|
|
315
431
|
directory,
|
|
316
432
|
serviceName,
|
|
317
433
|
modulePath,
|
|
434
|
+
target,
|
|
318
435
|
runtime,
|
|
319
436
|
framework,
|
|
320
437
|
profile: args.profile,
|
|
@@ -325,6 +442,7 @@ export async function resolveConfig(args: ParsedArgs): Promise<ScaffoldConfig> {
|
|
|
325
442
|
billingAccount,
|
|
326
443
|
quotaProjectId: args.quotaProjectId ?? QUOTA_PROJECT_DEFAULT,
|
|
327
444
|
autoDeploy,
|
|
445
|
+
git,
|
|
328
446
|
neonDatabaseName: defaults.neonDatabaseName,
|
|
329
447
|
apiHostname: defaults.apiHostname,
|
|
330
448
|
generatorRoot: resolve(dirname(fileURLToPath(import.meta.url)), ".."),
|
|
@@ -344,22 +462,55 @@ async function waitForDiscovery(discoveryPromise: Promise<DiscoveryState>) {
|
|
|
344
462
|
}
|
|
345
463
|
}
|
|
346
464
|
|
|
347
|
-
async function
|
|
465
|
+
async function resolveTarget(args: ParsedArgs): Promise<DeployTarget> {
|
|
466
|
+
if (args.target) {
|
|
467
|
+
return args.target;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (args.yes) {
|
|
471
|
+
return "cloudrun";
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
const value = await select({
|
|
475
|
+
message: "Deploy target",
|
|
476
|
+
initialValue: "cloudrun",
|
|
477
|
+
options: [
|
|
478
|
+
{ value: "cloudrun", label: "Cloud Run", hint: "Default" },
|
|
479
|
+
{ value: "workers", label: "Cloudflare Workers" },
|
|
480
|
+
],
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
if (isCancel(value)) {
|
|
484
|
+
cancel("Aborted");
|
|
485
|
+
process.exit(1);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
return value as DeployTarget;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
async function resolveRuntime(args: ParsedArgs, target: DeployTarget): Promise<Runtime> {
|
|
348
492
|
if (args.runtime) {
|
|
349
493
|
return args.runtime;
|
|
350
494
|
}
|
|
351
495
|
|
|
352
|
-
if (
|
|
496
|
+
if (target === "workers") {
|
|
353
497
|
return "bun";
|
|
354
498
|
}
|
|
355
499
|
|
|
500
|
+
if (args.yes) {
|
|
501
|
+
return "go";
|
|
502
|
+
}
|
|
503
|
+
|
|
356
504
|
const value = await select({
|
|
357
505
|
message: "Runtime",
|
|
358
|
-
initialValue: "bun",
|
|
359
|
-
options:
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
506
|
+
initialValue: target === "cloudrun" ? "go" : "bun",
|
|
507
|
+
options:
|
|
508
|
+
target === "cloudrun"
|
|
509
|
+
? [
|
|
510
|
+
{ value: "go", label: "Go", hint: "Default" },
|
|
511
|
+
{ value: "bun", label: "Bun" },
|
|
512
|
+
]
|
|
513
|
+
: [{ value: "bun", label: "Bun/TypeScript", hint: "Workers runtime" }],
|
|
363
514
|
});
|
|
364
515
|
|
|
365
516
|
if (isCancel(value)) {
|
|
@@ -370,17 +521,17 @@ async function resolveRuntime(args: ParsedArgs): Promise<Runtime> {
|
|
|
370
521
|
return value as Runtime;
|
|
371
522
|
}
|
|
372
523
|
|
|
373
|
-
async function resolveFramework(args: ParsedArgs, runtime: Runtime): Promise<Framework> {
|
|
374
|
-
const allowed =
|
|
524
|
+
async function resolveFramework(args: ParsedArgs, target: DeployTarget, runtime: Runtime): Promise<Framework> {
|
|
525
|
+
const allowed = frameworksForTargetRuntime(target, runtime);
|
|
375
526
|
if (args.framework) {
|
|
376
527
|
if (allowed.some((framework) => framework === args.framework)) {
|
|
377
528
|
return args.framework;
|
|
378
529
|
}
|
|
379
|
-
throw new Error(`Framework ${args.framework} is not valid for runtime ${runtime}`);
|
|
530
|
+
throw new Error(`Framework ${args.framework} is not valid for target ${target} and runtime ${runtime}`);
|
|
380
531
|
}
|
|
381
532
|
|
|
382
533
|
if (args.yes) {
|
|
383
|
-
return allowed[0]
|
|
534
|
+
return target === "cloudrun" && runtime === "go" ? "connectrpc" : allowed[0]!;
|
|
384
535
|
}
|
|
385
536
|
|
|
386
537
|
const value = await select({
|
|
@@ -639,6 +790,17 @@ export function normalizeValidationResult(result: true | string): string | undef
|
|
|
639
790
|
return result === true ? undefined : result;
|
|
640
791
|
}
|
|
641
792
|
|
|
793
|
+
export function validateProfileRuntimeFramework(profile: Profile, runtime: Runtime, framework: Framework) {
|
|
794
|
+
validateTargetRuntimeFramework("cloudrun", runtime, framework);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
export function validateTargetRuntimeFramework(target: DeployTarget, runtime: Runtime, framework: Framework) {
|
|
798
|
+
const allowed = frameworksForTargetRuntime(target, runtime);
|
|
799
|
+
if (!allowed.some((candidate) => candidate === framework)) {
|
|
800
|
+
throw new Error(`Framework ${framework} is not valid for target ${target} and runtime ${runtime}`);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
642
804
|
export function validateServiceNameInput(rawValue: string, directoryOverride?: string) {
|
|
643
805
|
const serviceName = slugify(rawValue);
|
|
644
806
|
if (!serviceName) {
|
|
@@ -665,10 +827,11 @@ export function validateServiceNameInput(rawValue: string, directoryOverride?: s
|
|
|
665
827
|
function printHelp() {
|
|
666
828
|
log.message(`
|
|
667
829
|
Usage:
|
|
668
|
-
|
|
830
|
+
create-service [service_id] [options]
|
|
669
831
|
|
|
670
832
|
Options:
|
|
671
|
-
--
|
|
833
|
+
--target <cloudrun|workers> Deploy target for the generated service
|
|
834
|
+
--profile <microservice> Compatibility no-op; app workspaces moved out
|
|
672
835
|
--runtime <go|bun> Runtime scaffold to generate
|
|
673
836
|
--framework <name> Framework for the selected runtime
|
|
674
837
|
--module-path <path> Go module path for generated Go scaffolds
|
|
@@ -677,9 +840,11 @@ Options:
|
|
|
677
840
|
--billing-account <name> Billing account resource name
|
|
678
841
|
--quota-project <id> Billing quota project for gcloud calls
|
|
679
842
|
--region <region> Cloud Run region
|
|
680
|
-
--auto-deploy Run
|
|
681
|
-
--bootstrap Alias for --auto-deploy
|
|
843
|
+
--auto-deploy Run service create and service deploy after scaffold
|
|
682
844
|
--no-auto-deploy Scaffold only
|
|
845
|
+
--no-git Skip git init, initial commit, GitHub repo creation, and push
|
|
846
|
+
--auto-update Re-run through create-svc@latest when a newer version exists
|
|
847
|
+
--no-update-check Skip the best-effort npm update check
|
|
683
848
|
--yes, -y Accept defaults without prompts
|
|
684
849
|
--help, -h Show this message
|
|
685
850
|
`);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import { mkdir, mkdtemp, realpath } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { buildGitBootstrapConfig, findExistingGitWorktree } from "./git-bootstrap";
|
|
6
|
+
|
|
7
|
+
test("buildGitBootstrapConfig defaults to anmho private repo creation", () => {
|
|
8
|
+
expect(buildGitBootstrapConfig("launch-api", undefined)).toEqual({
|
|
9
|
+
enabled: true,
|
|
10
|
+
owner: "anmho",
|
|
11
|
+
repository: "launch-api",
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test("buildGitBootstrapConfig honors --no-git", () => {
|
|
16
|
+
expect(buildGitBootstrapConfig("launch-api", true)).toEqual({
|
|
17
|
+
enabled: false,
|
|
18
|
+
owner: "anmho",
|
|
19
|
+
repository: "launch-api",
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("findExistingGitWorktree detects parent repositories", async () => {
|
|
24
|
+
const root = await mkdtemp(join(tmpdir(), "create-svc-git-"));
|
|
25
|
+
run(["git", "init", "-b", "main"], root);
|
|
26
|
+
await mkdir(join(root, "apps", "launch-api"), { recursive: true });
|
|
27
|
+
|
|
28
|
+
expect(findExistingGitWorktree(join(root, "apps", "launch-api"))).toBe(await realpath(root));
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
function run(command: string[], cwd: string) {
|
|
32
|
+
const result = Bun.spawnSync(command, {
|
|
33
|
+
cwd,
|
|
34
|
+
stdout: "pipe",
|
|
35
|
+
stderr: "pipe",
|
|
36
|
+
});
|
|
37
|
+
if (result.exitCode !== 0) {
|
|
38
|
+
throw new Error(result.stderr.toString());
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
|
|
4
|
+
export type GitBootstrapConfig = {
|
|
5
|
+
enabled: boolean;
|
|
6
|
+
owner: string;
|
|
7
|
+
repository: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type GitBootstrapResult =
|
|
11
|
+
| { status: "disabled" }
|
|
12
|
+
| { status: "skipped-existing-worktree"; root: string }
|
|
13
|
+
| { status: "created"; url: string };
|
|
14
|
+
|
|
15
|
+
export function buildGitBootstrapConfig(serviceName: string, noGit: boolean | undefined): GitBootstrapConfig {
|
|
16
|
+
return {
|
|
17
|
+
enabled: !noGit,
|
|
18
|
+
owner: "anmho",
|
|
19
|
+
repository: serviceName,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function bootstrapGitHubRepository(targetDir: string, config: GitBootstrapConfig): Promise<GitBootstrapResult> {
|
|
24
|
+
if (!config.enabled) {
|
|
25
|
+
return { status: "disabled" };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const existingRoot = findExistingGitWorktree(targetDir);
|
|
29
|
+
if (existingRoot) {
|
|
30
|
+
return { status: "skipped-existing-worktree", root: existingRoot };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
run(["git", "--version"], targetDir, "git is required to initialize the generated repository");
|
|
34
|
+
run(["gh", "--version"], targetDir, "GitHub CLI `gh` is required to create the generated repository");
|
|
35
|
+
run(["gh", "auth", "status"], targetDir, "Authenticate GitHub CLI with `gh auth login` before creating the repository");
|
|
36
|
+
|
|
37
|
+
run(["git", "init", "-b", "main"], targetDir);
|
|
38
|
+
run(["git", "add", "."], targetDir);
|
|
39
|
+
|
|
40
|
+
if (hasStagedChanges(targetDir)) {
|
|
41
|
+
run(["git", "commit", "-m", "Initial commit"], targetDir);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const repository = `${config.owner}/${config.repository}`;
|
|
45
|
+
run(["gh", "repo", "create", repository, "--private", "--source", ".", "--remote", "origin", "--push"], targetDir);
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
status: "created",
|
|
49
|
+
url: `https://github.com/${repository}`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function commitAndPushGeneratedArtifacts(targetDir: string, message: string) {
|
|
54
|
+
run(["git", "add", "."], targetDir);
|
|
55
|
+
if (!hasStagedChanges(targetDir)) {
|
|
56
|
+
return { committed: false };
|
|
57
|
+
}
|
|
58
|
+
run(["git", "commit", "-m", message], targetDir);
|
|
59
|
+
run(["git", "push"], targetDir);
|
|
60
|
+
return { committed: true };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function findExistingGitWorktree(targetDir: string) {
|
|
64
|
+
const cwd = existingPath(targetDir);
|
|
65
|
+
const result = Bun.spawnSync(["git", "-C", cwd, "rev-parse", "--show-toplevel"], {
|
|
66
|
+
stdout: "pipe",
|
|
67
|
+
stderr: "pipe",
|
|
68
|
+
});
|
|
69
|
+
if (result.exitCode !== 0) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
return result.stdout.toString().trim() || undefined;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function existingPath(path: string): string {
|
|
76
|
+
if (existsSync(path)) {
|
|
77
|
+
return path;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const parent = dirname(path);
|
|
81
|
+
if (parent === path) {
|
|
82
|
+
return path;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return existingPath(parent);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function hasStagedChanges(cwd: string) {
|
|
89
|
+
const result = Bun.spawnSync(["git", "diff", "--cached", "--quiet"], {
|
|
90
|
+
cwd,
|
|
91
|
+
stdout: "pipe",
|
|
92
|
+
stderr: "pipe",
|
|
93
|
+
});
|
|
94
|
+
return result.exitCode === 1;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function run(command: string[], cwd: string, message?: string) {
|
|
98
|
+
const result = Bun.spawnSync(command, {
|
|
99
|
+
cwd,
|
|
100
|
+
stdin: "inherit",
|
|
101
|
+
stdout: "inherit",
|
|
102
|
+
stderr: "pipe",
|
|
103
|
+
});
|
|
104
|
+
if (result.exitCode === 0) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const detail = result.stderr.toString().trim();
|
|
109
|
+
throw new Error([message, `Command failed: ${command.join(" ")}`, detail].filter(Boolean).join("\n"));
|
|
110
|
+
}
|
package/src/naming.test.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { buildGcpProjectOptions, compactDatabaseName, compactIdentifier, deriveD
|
|
|
3
3
|
|
|
4
4
|
test("deriveDefaults uses the service name for project, repo, and database naming", () => {
|
|
5
5
|
expect(deriveDefaults("edge-api")).toEqual({
|
|
6
|
+
serviceId: "edge-api",
|
|
6
7
|
serviceName: "edge-api",
|
|
7
8
|
projectName: "edge-api",
|
|
8
9
|
projectId: "anmho-edge-api",
|
package/src/naming.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
export const BILLING_ACCOUNT_DEFAULT = "billingAccounts/01BD2E-3A6949-8F4C84";
|
|
2
2
|
export const QUOTA_PROJECT_DEFAULT = "anmho-infra-prod";
|
|
3
3
|
|
|
4
|
+
export const DEPLOY_TARGETS = ["cloudrun", "workers"] as const;
|
|
5
|
+
|
|
6
|
+
export type DeployTarget = (typeof DEPLOY_TARGETS)[number];
|
|
7
|
+
|
|
4
8
|
export const FRAMEWORKS_BY_RUNTIME = {
|
|
5
9
|
go: ["chi", "connectrpc"],
|
|
6
10
|
bun: ["hono", "connectrpc"],
|
|
@@ -10,6 +14,24 @@ export type Runtime = keyof typeof FRAMEWORKS_BY_RUNTIME;
|
|
|
10
14
|
export type Framework = (typeof FRAMEWORKS_BY_RUNTIME)[Runtime][number];
|
|
11
15
|
export type GcpProjectMode = "create_new" | "use_existing";
|
|
12
16
|
|
|
17
|
+
export function parseDeployTarget(value: string): DeployTarget {
|
|
18
|
+
if (DEPLOY_TARGETS.includes(value as DeployTarget)) {
|
|
19
|
+
return value as DeployTarget;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
throw new Error(`Unknown target: ${value}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function frameworksForTargetRuntime(target: DeployTarget, runtime: Runtime): readonly Framework[] {
|
|
26
|
+
if (target === "workers") {
|
|
27
|
+
if (runtime === "bun") {
|
|
28
|
+
return ["hono"];
|
|
29
|
+
}
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
return FRAMEWORKS_BY_RUNTIME[runtime];
|
|
33
|
+
}
|
|
34
|
+
|
|
13
35
|
export function slugify(value: string, maxLength = 63) {
|
|
14
36
|
return value
|
|
15
37
|
.trim()
|
|
@@ -64,6 +86,7 @@ export function deriveDefaults(serviceName: string) {
|
|
|
64
86
|
const normalizedServiceName = slugify(serviceName) || "my-service";
|
|
65
87
|
|
|
66
88
|
return {
|
|
89
|
+
serviceId: normalizedServiceName,
|
|
67
90
|
serviceName: normalizedServiceName,
|
|
68
91
|
projectName: normalizedServiceName,
|
|
69
92
|
projectId: compactIdentifier(`anmho-${normalizedServiceName}`, 30),
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { buildPostScaffoldCommands } from "./post-scaffold";
|
|
3
|
+
|
|
4
|
+
describe("buildPostScaffoldCommands", () => {
|
|
5
|
+
test("runs create and deploy for HTTP services", () => {
|
|
6
|
+
expect(buildPostScaffoldCommands({ framework: "hono" })).toEqual([
|
|
7
|
+
{ command: "bun", args: ["run", "service", "--", "create"] },
|
|
8
|
+
{ command: "bun", args: ["run", "service", "--", "deploy"] },
|
|
9
|
+
]);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test("builds SDK artifacts before create and deploy for ConnectRPC services", () => {
|
|
13
|
+
expect(buildPostScaffoldCommands({ framework: "connectrpc" })).toEqual([
|
|
14
|
+
{ command: "bun", args: ["run", "service", "--", "sdk", "build"] },
|
|
15
|
+
{ command: "bun", args: ["run", "service", "--", "create"] },
|
|
16
|
+
{ command: "bun", args: ["run", "service", "--", "deploy"] },
|
|
17
|
+
]);
|
|
18
|
+
});
|
|
19
|
+
});
|
package/src/post-scaffold.ts
CHANGED
|
@@ -12,21 +12,34 @@ type CommandResult = {
|
|
|
12
12
|
stderr: string;
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
+
type PostScaffoldCommand = {
|
|
16
|
+
command: string;
|
|
17
|
+
args: string[];
|
|
18
|
+
};
|
|
19
|
+
|
|
15
20
|
const decoder = new TextDecoder();
|
|
16
21
|
const encoder = new TextEncoder();
|
|
17
22
|
|
|
18
23
|
export async function runPostScaffoldFlow(config: ScaffoldConfig, cwd: string) {
|
|
19
24
|
if (config.autoDeploy) {
|
|
20
25
|
installProjectDependencies(cwd);
|
|
21
|
-
const command
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return { message: "Dependencies installed and
|
|
26
|
+
for (const command of buildPostScaffoldCommands(config)) {
|
|
27
|
+
run(command.command, command.args, { cwd });
|
|
28
|
+
}
|
|
29
|
+
return { message: "Dependencies installed, service created, and service deployed" };
|
|
25
30
|
}
|
|
26
31
|
|
|
27
32
|
return { message: "Backend package generated" };
|
|
28
33
|
}
|
|
29
34
|
|
|
35
|
+
export function buildPostScaffoldCommands(config: Pick<ScaffoldConfig, "framework">): PostScaffoldCommand[] {
|
|
36
|
+
return [
|
|
37
|
+
...(config.framework === "connectrpc" ? [{ command: "bun", args: ["run", "service", "--", "sdk", "build"] }] : []),
|
|
38
|
+
{ command: "bun", args: ["run", "service", "--", "create"] },
|
|
39
|
+
{ command: "bun", args: ["run", "service", "--", "deploy"] },
|
|
40
|
+
];
|
|
41
|
+
}
|
|
42
|
+
|
|
30
43
|
function installProjectDependencies(cwd: string) {
|
|
31
44
|
requireCommand("bun");
|
|
32
45
|
run("bun", ["install"], { cwd });
|
package/src/profiles.ts
CHANGED
|
@@ -9,17 +9,14 @@ export function parseProfile(value: string): Profile {
|
|
|
9
9
|
|
|
10
10
|
if (value === "app") {
|
|
11
11
|
throw new Error(
|
|
12
|
-
|
|
13
|
-
"The app profile moved out of create-svc.",
|
|
14
|
-
"Use the private GitHub template repos anmho/create-app-consumer or anmho/create-app-saas instead.",
|
|
15
|
-
].join(" ")
|
|
12
|
+
"The app profile has moved out of create-service. Use the private create-app template repositories instead."
|
|
16
13
|
);
|
|
17
14
|
}
|
|
18
15
|
|
|
19
16
|
throw new Error(`Unknown profile: ${value}`);
|
|
20
17
|
}
|
|
21
18
|
|
|
22
|
-
export function exampleForProfile(
|
|
19
|
+
export function exampleForProfile(_profile: Profile) {
|
|
23
20
|
return {
|
|
24
21
|
kind: "microservice",
|
|
25
22
|
domain: "waitlist",
|