create-svc 0.1.10 → 0.1.12
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 +51 -47
- package/index.ts +2 -2
- package/package.json +10 -9
- package/src/cli.test.ts +28 -10
- package/src/cli.ts +196 -33
- 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 +232 -41
- package/src/scaffold.ts +81 -36
- package/src/service.test.ts +30 -0
- package/src/service.ts +65 -0
- 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 +329 -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 +402 -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/bin/{create-svc.mjs → service.mjs} +0 -0
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("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,17 @@ 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(
|
|
140
|
+
`Create: ${pc.cyan("service create")}`,
|
|
141
|
+
`Deploy: ${pc.cyan("service deploy")}`,
|
|
142
|
+
config.git.enabled ? `Repository: ${pc.cyan(`https://github.com/anmho/${config.git.repository}`)}` : undefined,
|
|
113
143
|
`Personal env: ${pc.cyan(
|
|
114
|
-
|
|
115
|
-
? `bun run deploy -- --environment personal --name ${config.serviceName}`
|
|
116
|
-
: `make deploy ARGS="--environment personal --name ${config.serviceName}"`
|
|
144
|
+
`service deploy --environment personal --name ${config.serviceName}`
|
|
117
145
|
)}`,
|
|
118
146
|
`Production API: ${pc.cyan(`https://${config.apiHostname}`)}`,
|
|
119
|
-
].join("\n")
|
|
147
|
+
].filter(Boolean).join("\n")
|
|
120
148
|
);
|
|
121
149
|
} catch (error) {
|
|
122
150
|
handleCliError(error);
|
|
@@ -160,6 +188,21 @@ export function parseArgs(argv: string[]): ParsedArgs {
|
|
|
160
188
|
continue;
|
|
161
189
|
}
|
|
162
190
|
|
|
191
|
+
if (token === "--auto-update") {
|
|
192
|
+
parsed.autoUpdate = true;
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (token === "--no-update-check") {
|
|
197
|
+
parsed.noUpdateCheck = true;
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (token === "--no-git") {
|
|
202
|
+
parsed.noGit = true;
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
|
|
163
206
|
if (token === "--runtime") {
|
|
164
207
|
parsed.runtime = readValue() as Runtime;
|
|
165
208
|
continue;
|
|
@@ -170,6 +213,16 @@ export function parseArgs(argv: string[]): ParsedArgs {
|
|
|
170
213
|
continue;
|
|
171
214
|
}
|
|
172
215
|
|
|
216
|
+
if (token === "--target") {
|
|
217
|
+
parsed.target = parseDeployTarget(readValue());
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (token.startsWith("--target=")) {
|
|
222
|
+
parsed.target = parseDeployTarget(token.slice("--target=".length));
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
|
|
173
226
|
if (token === "--framework") {
|
|
174
227
|
parsed.framework = readValue() as Framework;
|
|
175
228
|
continue;
|
|
@@ -260,11 +313,6 @@ export function parseArgs(argv: string[]): ParsedArgs {
|
|
|
260
313
|
continue;
|
|
261
314
|
}
|
|
262
315
|
|
|
263
|
-
if (token === "--bootstrap") {
|
|
264
|
-
parsed.autoDeploy = true;
|
|
265
|
-
continue;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
316
|
if (token === "--no-auto-deploy") {
|
|
269
317
|
parsed.autoDeploy = false;
|
|
270
318
|
continue;
|
|
@@ -276,6 +324,69 @@ export function parseArgs(argv: string[]): ParsedArgs {
|
|
|
276
324
|
return parsed;
|
|
277
325
|
}
|
|
278
326
|
|
|
327
|
+
const CURRENT_VERSION = "0.1.9";
|
|
328
|
+
const PACKAGE_NAME = "create-svc";
|
|
329
|
+
|
|
330
|
+
async function maybeCheckForUpdate(args: ParsedArgs) {
|
|
331
|
+
if (args.noUpdateCheck || shouldSkipUpdateCheck()) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const latest = await resolveLatestVersion().catch(() => "");
|
|
336
|
+
if (!latest || !isVersionGreater(latest, CURRENT_VERSION)) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const command = `bunx ${PACKAGE_NAME}@latest ${Bun.argv.slice(2).filter((arg) => arg !== "--auto-update").join(" ")}`.trim();
|
|
341
|
+
if (!args.autoUpdate) {
|
|
342
|
+
log.info(`A newer ${PACKAGE_NAME} is available: ${CURRENT_VERSION} -> ${latest}. Run ${command}`);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const result = Bun.spawnSync(["bunx", `${PACKAGE_NAME}@latest`, ...Bun.argv.slice(2).filter((arg) => arg !== "--auto-update")], {
|
|
347
|
+
stdin: "inherit",
|
|
348
|
+
stdout: "inherit",
|
|
349
|
+
stderr: "inherit",
|
|
350
|
+
env: {
|
|
351
|
+
...process.env,
|
|
352
|
+
CREATE_SERVICE_NO_UPDATE_CHECK: "1",
|
|
353
|
+
},
|
|
354
|
+
});
|
|
355
|
+
process.exit(result.exitCode);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function shouldSkipUpdateCheck() {
|
|
359
|
+
return Boolean(
|
|
360
|
+
process.env.CI ||
|
|
361
|
+
process.env.CODEX_CI ||
|
|
362
|
+
process.env.CREATE_SERVICE_NO_UPDATE_CHECK ||
|
|
363
|
+
process.env.BUN_TEST ||
|
|
364
|
+
process.env.npm_lifecycle_event
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
async function resolveLatestVersion() {
|
|
369
|
+
const response = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, {
|
|
370
|
+
signal: AbortSignal.timeout(1_500),
|
|
371
|
+
});
|
|
372
|
+
if (!response.ok) {
|
|
373
|
+
return "";
|
|
374
|
+
}
|
|
375
|
+
const payload = (await response.json()) as { version?: string };
|
|
376
|
+
return payload.version?.trim() ?? "";
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function isVersionGreater(left: string, right: string) {
|
|
380
|
+
const parse = (value: string) => value.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
381
|
+
const [leftMajor = 0, leftMinor = 0, leftPatch = 0] = parse(left);
|
|
382
|
+
const [rightMajor = 0, rightMinor = 0, rightPatch = 0] = parse(right);
|
|
383
|
+
return (
|
|
384
|
+
leftMajor > rightMajor ||
|
|
385
|
+
(leftMajor === rightMajor && leftMinor > rightMinor) ||
|
|
386
|
+
(leftMajor === rightMajor && leftMinor === rightMinor && leftPatch > rightPatch)
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
|
|
279
390
|
export async function resolveConfig(args: ParsedArgs): Promise<ScaffoldConfig> {
|
|
280
391
|
const inferredName = slugify(basename(args.directory ?? "my-service"));
|
|
281
392
|
const serviceName = args.yes
|
|
@@ -287,14 +398,17 @@ export async function resolveConfig(args: ParsedArgs): Promise<ScaffoldConfig> {
|
|
|
287
398
|
|
|
288
399
|
const discoveryPromise = discoverCloudInputs();
|
|
289
400
|
const defaults = deriveDefaults(serviceName);
|
|
290
|
-
const
|
|
291
|
-
const
|
|
401
|
+
const target = await resolveTarget(args);
|
|
402
|
+
const runtime = await resolveRuntime(args, target);
|
|
403
|
+
const framework = await resolveFramework(args, target, runtime);
|
|
404
|
+
validateTargetRuntimeFramework(target, runtime, framework);
|
|
292
405
|
const modulePath = await resolveModulePath(args, runtime, defaults.modulePath);
|
|
293
406
|
const discovery = await waitForDiscovery(discoveryPromise);
|
|
294
407
|
const gcpSelection = await resolveGcpSelection(args, defaults, discovery);
|
|
295
408
|
const region = args.region ?? DEFAULT_REGION;
|
|
296
409
|
const billingAccount = chooseBillingAccount(args.billingAccount, discovery.billingAccounts);
|
|
297
410
|
const autoDeploy = resolveAutoDeploy(args.autoDeploy);
|
|
411
|
+
const git = buildGitBootstrapConfig(serviceName, args.noGit);
|
|
298
412
|
|
|
299
413
|
if (!args.yes) {
|
|
300
414
|
const okay = await confirm({
|
|
@@ -315,6 +429,7 @@ export async function resolveConfig(args: ParsedArgs): Promise<ScaffoldConfig> {
|
|
|
315
429
|
directory,
|
|
316
430
|
serviceName,
|
|
317
431
|
modulePath,
|
|
432
|
+
target,
|
|
318
433
|
runtime,
|
|
319
434
|
framework,
|
|
320
435
|
profile: args.profile,
|
|
@@ -325,6 +440,7 @@ export async function resolveConfig(args: ParsedArgs): Promise<ScaffoldConfig> {
|
|
|
325
440
|
billingAccount,
|
|
326
441
|
quotaProjectId: args.quotaProjectId ?? QUOTA_PROJECT_DEFAULT,
|
|
327
442
|
autoDeploy,
|
|
443
|
+
git,
|
|
328
444
|
neonDatabaseName: defaults.neonDatabaseName,
|
|
329
445
|
apiHostname: defaults.apiHostname,
|
|
330
446
|
generatorRoot: resolve(dirname(fileURLToPath(import.meta.url)), ".."),
|
|
@@ -344,22 +460,55 @@ async function waitForDiscovery(discoveryPromise: Promise<DiscoveryState>) {
|
|
|
344
460
|
}
|
|
345
461
|
}
|
|
346
462
|
|
|
347
|
-
async function
|
|
463
|
+
async function resolveTarget(args: ParsedArgs): Promise<DeployTarget> {
|
|
464
|
+
if (args.target) {
|
|
465
|
+
return args.target;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (args.yes) {
|
|
469
|
+
return "cloudrun";
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const value = await select({
|
|
473
|
+
message: "Deploy target",
|
|
474
|
+
initialValue: "cloudrun",
|
|
475
|
+
options: [
|
|
476
|
+
{ value: "cloudrun", label: "Cloud Run", hint: "Default" },
|
|
477
|
+
{ value: "workers", label: "Cloudflare Workers" },
|
|
478
|
+
],
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
if (isCancel(value)) {
|
|
482
|
+
cancel("Aborted");
|
|
483
|
+
process.exit(1);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
return value as DeployTarget;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
async function resolveRuntime(args: ParsedArgs, target: DeployTarget): Promise<Runtime> {
|
|
348
490
|
if (args.runtime) {
|
|
349
491
|
return args.runtime;
|
|
350
492
|
}
|
|
351
493
|
|
|
352
|
-
if (
|
|
494
|
+
if (target === "workers") {
|
|
353
495
|
return "bun";
|
|
354
496
|
}
|
|
355
497
|
|
|
498
|
+
if (args.yes) {
|
|
499
|
+
return "go";
|
|
500
|
+
}
|
|
501
|
+
|
|
356
502
|
const value = await select({
|
|
357
503
|
message: "Runtime",
|
|
358
|
-
initialValue: "bun",
|
|
359
|
-
options:
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
504
|
+
initialValue: target === "cloudrun" ? "go" : "bun",
|
|
505
|
+
options:
|
|
506
|
+
target === "cloudrun"
|
|
507
|
+
? [
|
|
508
|
+
{ value: "go", label: "Go", hint: "Default" },
|
|
509
|
+
{ value: "bun", label: "Bun" },
|
|
510
|
+
]
|
|
511
|
+
: [{ value: "bun", label: "Bun/TypeScript", hint: "Workers runtime" }],
|
|
363
512
|
});
|
|
364
513
|
|
|
365
514
|
if (isCancel(value)) {
|
|
@@ -370,17 +519,17 @@ async function resolveRuntime(args: ParsedArgs): Promise<Runtime> {
|
|
|
370
519
|
return value as Runtime;
|
|
371
520
|
}
|
|
372
521
|
|
|
373
|
-
async function resolveFramework(args: ParsedArgs, runtime: Runtime): Promise<Framework> {
|
|
374
|
-
const allowed =
|
|
522
|
+
async function resolveFramework(args: ParsedArgs, target: DeployTarget, runtime: Runtime): Promise<Framework> {
|
|
523
|
+
const allowed = frameworksForTargetRuntime(target, runtime);
|
|
375
524
|
if (args.framework) {
|
|
376
525
|
if (allowed.some((framework) => framework === args.framework)) {
|
|
377
526
|
return args.framework;
|
|
378
527
|
}
|
|
379
|
-
throw new Error(`Framework ${args.framework} is not valid for runtime ${runtime}`);
|
|
528
|
+
throw new Error(`Framework ${args.framework} is not valid for target ${target} and runtime ${runtime}`);
|
|
380
529
|
}
|
|
381
530
|
|
|
382
531
|
if (args.yes) {
|
|
383
|
-
return allowed[0]
|
|
532
|
+
return target === "cloudrun" && runtime === "go" ? "connectrpc" : allowed[0]!;
|
|
384
533
|
}
|
|
385
534
|
|
|
386
535
|
const value = await select({
|
|
@@ -639,6 +788,17 @@ export function normalizeValidationResult(result: true | string): string | undef
|
|
|
639
788
|
return result === true ? undefined : result;
|
|
640
789
|
}
|
|
641
790
|
|
|
791
|
+
export function validateProfileRuntimeFramework(profile: Profile, runtime: Runtime, framework: Framework) {
|
|
792
|
+
validateTargetRuntimeFramework("cloudrun", runtime, framework);
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
export function validateTargetRuntimeFramework(target: DeployTarget, runtime: Runtime, framework: Framework) {
|
|
796
|
+
const allowed = frameworksForTargetRuntime(target, runtime);
|
|
797
|
+
if (!allowed.some((candidate) => candidate === framework)) {
|
|
798
|
+
throw new Error(`Framework ${framework} is not valid for target ${target} and runtime ${runtime}`);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
642
802
|
export function validateServiceNameInput(rawValue: string, directoryOverride?: string) {
|
|
643
803
|
const serviceName = slugify(rawValue);
|
|
644
804
|
if (!serviceName) {
|
|
@@ -665,10 +825,11 @@ export function validateServiceNameInput(rawValue: string, directoryOverride?: s
|
|
|
665
825
|
function printHelp() {
|
|
666
826
|
log.message(`
|
|
667
827
|
Usage:
|
|
668
|
-
|
|
828
|
+
service create [service_id] [options]
|
|
669
829
|
|
|
670
830
|
Options:
|
|
671
|
-
--
|
|
831
|
+
--target <cloudrun|workers> Deploy target for the generated service
|
|
832
|
+
--profile <microservice> Compatibility no-op; app workspaces moved out
|
|
672
833
|
--runtime <go|bun> Runtime scaffold to generate
|
|
673
834
|
--framework <name> Framework for the selected runtime
|
|
674
835
|
--module-path <path> Go module path for generated Go scaffolds
|
|
@@ -677,9 +838,11 @@ Options:
|
|
|
677
838
|
--billing-account <name> Billing account resource name
|
|
678
839
|
--quota-project <id> Billing quota project for gcloud calls
|
|
679
840
|
--region <region> Cloud Run region
|
|
680
|
-
--auto-deploy Run
|
|
681
|
-
--bootstrap Alias for --auto-deploy
|
|
841
|
+
--auto-deploy Run service create and service deploy after scaffold
|
|
682
842
|
--no-auto-deploy Scaffold only
|
|
843
|
+
--no-git Skip git init, initial commit, GitHub repo creation, and push
|
|
844
|
+
--auto-update Re-run through create-svc@latest when a newer version exists
|
|
845
|
+
--no-update-check Skip the best-effort npm update check
|
|
683
846
|
--yes, -y Accept defaults without prompts
|
|
684
847
|
--help, -h Show this message
|
|
685
848
|
`);
|
|
@@ -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",
|