create-svc 0.1.9 → 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.
Files changed (163) hide show
  1. package/README.md +138 -16
  2. package/bin/create-service.mjs +2 -0
  3. package/package.json +19 -11
  4. package/src/cli.test.ts +46 -7
  5. package/src/cli.ts +282 -84
  6. package/src/git-bootstrap.test.ts +40 -0
  7. package/src/git-bootstrap.ts +110 -0
  8. package/src/naming.test.ts +5 -2
  9. package/src/naming.ts +32 -1
  10. package/src/neon.ts +10 -8
  11. package/src/post-scaffold.test.ts +19 -0
  12. package/src/post-scaffold.ts +18 -26
  13. package/src/profiles.ts +25 -0
  14. package/src/scaffold.test.ts +320 -18
  15. package/src/scaffold.ts +154 -28
  16. package/src/vault.test.ts +94 -10
  17. package/src/vault.ts +81 -18
  18. package/templates/shared/.github/workflows/ci.yml +2 -1
  19. package/templates/shared/.github/workflows/deploy.yml +2 -0
  20. package/templates/shared/README.md +217 -29
  21. package/templates/shared/docker-compose.yml +19 -0
  22. package/templates/shared/grafana/alerts.yaml +54 -0
  23. package/templates/shared/grafana/waitlist-dashboard.json +63 -0
  24. package/templates/shared/scripts/authctl.ts +231 -0
  25. package/templates/shared/scripts/cloudrun/bootstrap.ts +24 -42
  26. package/templates/shared/scripts/cloudrun/cleanup.ts +81 -35
  27. package/templates/shared/scripts/cloudrun/cli.ts +324 -7
  28. package/templates/shared/scripts/cloudrun/config.ts +21 -19
  29. package/templates/shared/scripts/cloudrun/deploy.ts +16 -11
  30. package/templates/shared/scripts/cloudrun/lib.ts +232 -123
  31. package/templates/shared/scripts/cloudrun/neon.ts +127 -13
  32. package/templates/shared/scripts/dev.ts +22 -0
  33. package/templates/shared/scripts/ensure-local-db.ts +3 -0
  34. package/templates/shared/scripts/local-docker.ts +63 -0
  35. package/templates/shared/scripts/local-env.ts +27 -0
  36. package/templates/shared/scripts/seed.ts +73 -0
  37. package/templates/shared/scripts/wait-for-db.ts +32 -0
  38. package/templates/shared/service.config.ts +59 -0
  39. package/templates/shared/service.yaml +24 -1
  40. package/templates/targets/workers/.github/workflows/ci.yml +19 -0
  41. package/templates/targets/workers/.github/workflows/deploy.yml +19 -0
  42. package/templates/targets/workers/Makefile +33 -0
  43. package/templates/targets/workers/README.md +75 -0
  44. package/templates/targets/workers/package.json +35 -0
  45. package/templates/targets/workers/scripts/workers/cli.ts +397 -0
  46. package/templates/targets/workers/src/auth.ts +178 -0
  47. package/templates/targets/workers/src/index.ts +198 -0
  48. package/templates/targets/workers/src/storage.ts +370 -0
  49. package/templates/targets/workers/test/app.test.ts +108 -0
  50. package/templates/targets/workers/tsconfig.json +11 -0
  51. package/templates/targets/workers/wrangler.toml +24 -0
  52. package/templates/variants/bun-connectrpc/Dockerfile +1 -0
  53. package/templates/variants/bun-connectrpc/Makefile +17 -8
  54. package/templates/variants/bun-connectrpc/gen/protos/waitlist/v1/waitlist_pb.ts +424 -0
  55. package/templates/variants/bun-connectrpc/migrations/0000_init.sql +20 -0
  56. package/templates/variants/bun-connectrpc/package.json +25 -1
  57. package/templates/variants/bun-connectrpc/protos/waitlist/v1/waitlist.proto +91 -0
  58. package/templates/variants/bun-connectrpc/scripts/codegen.ts +31 -1
  59. package/templates/variants/bun-connectrpc/scripts/migrate.ts +49 -0
  60. package/templates/variants/bun-connectrpc/src/auth.ts +200 -0
  61. package/templates/variants/bun-connectrpc/src/db/client.ts +15 -0
  62. package/templates/variants/bun-connectrpc/src/db/repository.ts +126 -0
  63. package/templates/variants/bun-connectrpc/src/db/schema.ts +26 -0
  64. package/templates/variants/bun-connectrpc/src/index.ts +194 -22
  65. package/templates/variants/bun-connectrpc/src/temporal/activities.ts +14 -0
  66. package/templates/variants/bun-connectrpc/src/temporal/worker.ts +38 -0
  67. package/templates/variants/bun-connectrpc/src/temporal/workflows.ts +10 -0
  68. package/templates/variants/bun-connectrpc/src/waitlist/service.ts +172 -0
  69. package/templates/variants/bun-connectrpc/src/waitlist/types.ts +45 -0
  70. package/templates/variants/bun-connectrpc/test/app.test.ts +14 -13
  71. package/templates/variants/bun-connectrpc/test/waitlist.integration.test.ts +71 -0
  72. package/templates/variants/bun-connectrpc/tsconfig.json +2 -1
  73. package/templates/variants/bun-hono/Makefile +17 -8
  74. package/templates/variants/bun-hono/migrations/0000_init.sql +20 -0
  75. package/templates/variants/bun-hono/package.json +21 -1
  76. package/templates/variants/bun-hono/scripts/migrate.ts +49 -0
  77. package/templates/variants/bun-hono/src/auth.ts +181 -0
  78. package/templates/variants/bun-hono/src/db/client.ts +15 -0
  79. package/templates/variants/bun-hono/src/db/repository.ts +126 -0
  80. package/templates/variants/bun-hono/src/db/schema.ts +26 -0
  81. package/templates/variants/bun-hono/src/index.ts +141 -10
  82. package/templates/variants/bun-hono/src/temporal/activities.ts +14 -0
  83. package/templates/variants/bun-hono/src/temporal/worker.ts +38 -0
  84. package/templates/variants/bun-hono/src/temporal/workflows.ts +10 -0
  85. package/templates/variants/bun-hono/src/waitlist/service.ts +166 -0
  86. package/templates/variants/bun-hono/src/waitlist/types.ts +50 -0
  87. package/templates/variants/bun-hono/test/app.test.ts +90 -5
  88. package/templates/variants/bun-hono/test/waitlist.integration.test.ts +102 -0
  89. package/templates/variants/bun-hono/tsconfig.json +1 -0
  90. package/templates/variants/go-chi/Makefile +30 -10
  91. package/templates/variants/go-chi/atlas.hcl +8 -0
  92. package/templates/variants/go-chi/cmd/server/main.go +25 -13
  93. package/templates/variants/go-chi/go.mod +3 -2
  94. package/templates/variants/go-chi/internal/app/service.go +279 -70
  95. package/templates/variants/go-chi/internal/auth/middleware.go +289 -0
  96. package/templates/variants/go-chi/internal/auth/middleware_test.go +38 -0
  97. package/templates/variants/go-chi/internal/config/config.go +38 -7
  98. package/templates/variants/go-chi/internal/httpapi/routes.go +170 -47
  99. package/templates/variants/go-chi/internal/httpapi/waitlist_integration_test.go +199 -0
  100. package/templates/variants/go-chi/internal/temporal/activities.go +27 -0
  101. package/templates/variants/go-chi/internal/temporal/worker.go +42 -0
  102. package/templates/variants/go-chi/internal/temporal/workflows.go +18 -0
  103. package/templates/variants/go-chi/migrations/0000_init.sql +20 -0
  104. package/templates/variants/go-chi/migrations/atlas.sum +2 -0
  105. package/templates/variants/go-chi/package.json +7 -1
  106. package/templates/variants/go-chi/test/go.test.ts +4 -1
  107. package/templates/variants/go-connectrpc/Makefile +29 -8
  108. package/templates/variants/go-connectrpc/atlas.hcl +8 -0
  109. package/templates/variants/go-connectrpc/buf.gen.yaml +2 -0
  110. package/templates/variants/go-connectrpc/cmd/server/main.go +44 -9
  111. package/templates/variants/go-connectrpc/gen/waitlist/v1/waitlist.pb.go +960 -0
  112. package/templates/variants/go-connectrpc/gen/waitlist/v1/waitlistv1connect/waitlist.connect.go +283 -0
  113. package/templates/variants/go-connectrpc/go.mod +4 -0
  114. package/templates/variants/go-connectrpc/internal/app/service.go +279 -70
  115. package/templates/variants/go-connectrpc/internal/auth/middleware.go +289 -0
  116. package/templates/variants/go-connectrpc/internal/auth/middleware_test.go +38 -0
  117. package/templates/variants/go-connectrpc/internal/config/config.go +38 -7
  118. package/templates/variants/go-connectrpc/internal/connectapi/handler.go +129 -40
  119. package/templates/variants/go-connectrpc/internal/connectapi/waitlist_integration_test.go +122 -0
  120. package/templates/variants/go-connectrpc/internal/httpapi/routes.go +170 -47
  121. package/templates/variants/go-connectrpc/internal/temporal/activities.go +27 -0
  122. package/templates/variants/go-connectrpc/internal/temporal/worker.go +42 -0
  123. package/templates/variants/go-connectrpc/internal/temporal/workflows.go +18 -0
  124. package/templates/variants/go-connectrpc/migrations/0000_init.sql +20 -0
  125. package/templates/variants/go-connectrpc/migrations/atlas.sum +2 -0
  126. package/templates/variants/go-connectrpc/package.json +7 -1
  127. package/templates/variants/go-connectrpc/protos/waitlist/v1/waitlist.proto +93 -0
  128. package/templates/root/.github/workflows/buf-publish.yml +0 -19
  129. package/templates/root/.github/workflows/ci.yml +0 -26
  130. package/templates/root/.github/workflows/deploy.yml +0 -22
  131. package/templates/root/Dockerfile +0 -23
  132. package/templates/root/README.md +0 -69
  133. package/templates/root/buf.gen.yaml +0 -10
  134. package/templates/root/buf.yaml +0 -9
  135. package/templates/root/cmd/server/main.go +0 -44
  136. package/templates/root/gen/dns/v1/dns.pb.go +0 -623
  137. package/templates/root/gen/dns/v1/dnsv1connect/dns.connect.go +0 -192
  138. package/templates/root/go.mod +0 -10
  139. package/templates/root/internal/app/service.go +0 -152
  140. package/templates/root/internal/app/token_source.go +0 -50
  141. package/templates/root/internal/cloudflare/client.go +0 -160
  142. package/templates/root/internal/config/config.go +0 -55
  143. package/templates/root/internal/connectapi/handler.go +0 -79
  144. package/templates/root/internal/httpapi/routes.go +0 -93
  145. package/templates/root/internal/vault/client.go +0 -148
  146. package/templates/root/package.json +0 -12
  147. package/templates/root/protos/dns/v1/dns.proto +0 -58
  148. package/templates/root/scripts/cloudrun/bootstrap.ts +0 -65
  149. package/templates/root/scripts/cloudrun/config.ts +0 -50
  150. package/templates/root/scripts/cloudrun/deploy.ts +0 -41
  151. package/templates/root/scripts/cloudrun/lib.ts +0 -244
  152. package/templates/root/service.yaml +0 -50
  153. package/templates/root/test/go.test.ts +0 -19
  154. package/templates/shared/.env.example +0 -10
  155. package/templates/variants/go-chi/buf.gen.yaml +0 -10
  156. package/templates/variants/go-chi/buf.yaml +0 -9
  157. package/templates/variants/go-chi/gen/dns/v1/dns.pb.go +0 -623
  158. package/templates/variants/go-chi/gen/dns/v1/dnsv1connect/dns.connect.go +0 -192
  159. package/templates/variants/go-chi/internal/connectapi/handler.go +0 -79
  160. package/templates/variants/go-chi/protos/dns/v1/dns.proto +0 -58
  161. package/templates/variants/go-connectrpc/gen/dns/v1/dns.pb.go +0 -623
  162. package/templates/variants/go-connectrpc/gen/dns/v1/dnsv1connect/dns.connect.go +0 -192
  163. package/templates/variants/go-connectrpc/protos/dns/v1/dns.proto +0 -58
@@ -1,244 +0,0 @@
1
- import { config, manifestEnv } from "./config";
2
-
3
- type CommandOptions = {
4
- allowFailure?: boolean;
5
- capture?: boolean;
6
- input?: string;
7
- };
8
-
9
- const decoder = new TextDecoder();
10
-
11
- export function requireCommand(name: string) {
12
- if (!Bun.which(name)) {
13
- throw new Error(`missing required command: ${name}`);
14
- }
15
- }
16
-
17
- export function run(command: string, args: string[], options: CommandOptions = {}) {
18
- const result = Bun.spawnSync([command, ...args], {
19
- cwd: process.cwd(),
20
- env: process.env,
21
- stdin: options.input,
22
- stdout: options.capture || options.allowFailure ? "pipe" : "inherit",
23
- stderr: options.capture || options.allowFailure ? "pipe" : "inherit",
24
- });
25
-
26
- const stdout = result.stdout ? decoder.decode(result.stdout).trim() : "";
27
- const stderr = result.stderr ? decoder.decode(result.stderr).trim() : "";
28
-
29
- if (!result.success && !options.allowFailure) {
30
- throw new Error([`command failed: ${command} ${args.join(" ")}`, stdout, stderr].filter(Boolean).join("\n"));
31
- }
32
-
33
- return {
34
- success: result.success,
35
- stdout,
36
- stderr,
37
- exitCode: result.exitCode,
38
- };
39
- }
40
-
41
- export function gcloud(args: string[], options: CommandOptions = {}) {
42
- return run("gcloud", args, options);
43
- }
44
-
45
- export function gh(args: string[], options: CommandOptions = {}) {
46
- return run("gh", args, options);
47
- }
48
-
49
- export function ensureServiceAccount(email: string) {
50
- if (gcloud(["iam", "service-accounts", "describe", email, "--project", config.projectId], { allowFailure: true }).success) {
51
- return;
52
- }
53
-
54
- const accountId = email.split("@")[0] ?? email;
55
- gcloud(["iam", "service-accounts", "create", accountId, "--project", config.projectId, "--display-name", accountId]);
56
- }
57
-
58
- export function ensureProjectRole(member: string, role: string) {
59
- gcloud(["projects", "add-iam-policy-binding", config.projectId, "--member", member, "--role", role]);
60
- }
61
-
62
- export function ensureServiceAccountRole(serviceAccount: string, member: string, role: string) {
63
- gcloud([
64
- "iam",
65
- "service-accounts",
66
- "add-iam-policy-binding",
67
- serviceAccount,
68
- "--project",
69
- config.projectId,
70
- "--member",
71
- member,
72
- "--role",
73
- role,
74
- ]);
75
- }
76
-
77
- export function ensureSecret(secretName: string, bootstrapEnv: string) {
78
- if (gcloud(["secrets", "describe", secretName, "--project", config.projectId], { allowFailure: true }).success) {
79
- return;
80
- }
81
-
82
- const value = process.env[bootstrapEnv]?.trim() ?? "";
83
- if (!value) {
84
- throw new Error(`missing bootstrap value for secret ${secretName}; set ${bootstrapEnv}`);
85
- }
86
-
87
- gcloud(["secrets", "create", secretName, "--project", config.projectId, "--replication-policy", "automatic"]);
88
- gcloud(["secrets", "versions", "add", secretName, "--project", config.projectId, "--data-file=-"], { input: value });
89
- }
90
-
91
- export function ensureSecretAccessor(secretName: string, member: string) {
92
- gcloud(["secrets", "add-iam-policy-binding", secretName, "--project", config.projectId, "--member", member, "--role", "roles/secretmanager.secretAccessor"]);
93
- }
94
-
95
- export function projectNumber() {
96
- return gcloud(["projects", "describe", config.projectId, "--format=value(projectNumber)"], { capture: true }).stdout;
97
- }
98
-
99
- export function workloadIdentityPoolResource() {
100
- return `projects/${projectNumber()}/locations/global/workloadIdentityPools/${config.workloadIdentityPoolId}`;
101
- }
102
-
103
- export function workloadIdentityProviderResource() {
104
- return `${workloadIdentityPoolResource()}/providers/${config.workloadIdentityProviderId}`;
105
- }
106
-
107
- export function ensureWorkloadIdentityPool() {
108
- if (
109
- gcloud(["iam", "workload-identity-pools", "describe", config.workloadIdentityPoolId, "--project", config.projectId, "--location", "global"], {
110
- allowFailure: true,
111
- }).success
112
- ) {
113
- return;
114
- }
115
-
116
- gcloud([
117
- "iam",
118
- "workload-identity-pools",
119
- "create",
120
- config.workloadIdentityPoolId,
121
- "--project",
122
- config.projectId,
123
- "--location",
124
- "global",
125
- "--display-name",
126
- "GitHub Actions",
127
- ]);
128
- }
129
-
130
- export function ensureWorkloadIdentityProvider() {
131
- if (
132
- gcloud(
133
- [
134
- "iam",
135
- "workload-identity-pools",
136
- "providers",
137
- "describe",
138
- config.workloadIdentityProviderId,
139
- "--project",
140
- config.projectId,
141
- "--location",
142
- "global",
143
- "--workload-identity-pool",
144
- config.workloadIdentityPoolId,
145
- ],
146
- { allowFailure: true }
147
- ).success
148
- ) {
149
- return;
150
- }
151
-
152
- gcloud([
153
- "iam",
154
- "workload-identity-pools",
155
- "providers",
156
- "create-oidc",
157
- config.workloadIdentityProviderId,
158
- "--project",
159
- config.projectId,
160
- "--location",
161
- "global",
162
- "--workload-identity-pool",
163
- config.workloadIdentityPoolId,
164
- "--display-name",
165
- `${config.serviceName} GitHub`,
166
- "--issuer-uri",
167
- "https://token.actions.githubusercontent.com",
168
- "--attribute-mapping",
169
- "google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository,attribute.repository_owner=assertion.repository_owner",
170
- "--attribute-condition",
171
- `assertion.repository=='${config.githubRepo}'`,
172
- ]);
173
- }
174
-
175
- export function setGithubVariable(name: string, value: string) {
176
- gh(["variable", "set", name, "--repo", config.githubRepo, "--body", value]);
177
- }
178
-
179
- export function setGithubSecret(name: string, value: string) {
180
- gh(["secret", "set", name, "--repo", config.githubRepo], { input: value });
181
- }
182
-
183
- export function ensureArtifactRepository() {
184
- if (
185
- gcloud(
186
- ["artifacts", "repositories", "describe", config.artifactRepository, "--project", config.projectId, "--location", config.region],
187
- { allowFailure: true }
188
- ).success
189
- ) {
190
- return;
191
- }
192
-
193
- gcloud([
194
- "artifacts",
195
- "repositories",
196
- "create",
197
- config.artifactRepository,
198
- "--project",
199
- config.projectId,
200
- "--location",
201
- config.region,
202
- "--repository-format",
203
- "docker",
204
- ]);
205
- }
206
-
207
- export function imageTag() {
208
- const gitSha = run("git", ["rev-parse", "--short", "HEAD"], { allowFailure: true, capture: true }).stdout;
209
- return gitSha || `${Date.now()}`;
210
- }
211
-
212
- export function imageUrl(tag = imageTag()) {
213
- return `${config.region}-docker.pkg.dev/${config.projectId}/${config.artifactRepository}/${config.serviceName}:${tag}`;
214
- }
215
-
216
- export async function renderManifest(image: string) {
217
- const template = await Bun.file(new URL("../../service.yaml", import.meta.url)).text();
218
- const values = {
219
- ...manifestEnv,
220
- IMAGE_URL: image,
221
- };
222
-
223
- return template.replace(/\$\{([A-Z0-9_]+)\}/g, (_, key: string) => {
224
- const value = values[key as keyof typeof values];
225
- if (!value) {
226
- throw new Error(`missing manifest value for ${key}`);
227
- }
228
- return value;
229
- });
230
- }
231
-
232
- export async function writeRenderedManifest(image: string) {
233
- const rendered = await renderManifest(image);
234
- const path = new URL("../../.cloudrun.rendered.yaml", import.meta.url);
235
- await Bun.write(path, rendered);
236
- return path;
237
- }
238
-
239
- export function serviceUrl() {
240
- return gcloud(
241
- ["run", "services", "describe", config.serviceName, "--project", config.projectId, "--region", config.region, "--format=value(status.url)"],
242
- { capture: true }
243
- ).stdout;
244
- }
@@ -1,50 +0,0 @@
1
- apiVersion: serving.knative.dev/v1
2
- kind: Service
3
- metadata:
4
- name: ${SERVICE_NAME}
5
- annotations:
6
- run.googleapis.com/ingress: all
7
- spec:
8
- template:
9
- spec:
10
- serviceAccountName: ${RUNTIME_SERVICE_ACCOUNT}
11
- containerConcurrency: 80
12
- containers:
13
- - image: ${IMAGE_URL}
14
- ports:
15
- - name: h2c
16
- containerPort: 8080
17
- env:
18
- - name: VAULT_ADDR
19
- value: ${VAULT_ADDR}
20
- - name: VAULT_SECRET_PATH
21
- value: ${VAULT_SECRET_PATH}
22
- - name: VAULT_SECRET_KEY
23
- value: ${VAULT_SECRET_KEY}
24
- - name: CLOUDFLARE_ZONE_ID
25
- value: ${CLOUDFLARE_ZONE_ID}
26
- - name: VAULT_ROLE_ID_FILE
27
- value: /var/run/secrets/vault-role-id/value
28
- - name: VAULT_SECRET_ID_FILE
29
- value: /var/run/secrets/vault-secret-id/value
30
- volumeMounts:
31
- - name: vault-role-id
32
- mountPath: /var/run/secrets/vault-role-id
33
- - name: vault-secret-id
34
- mountPath: /var/run/secrets/vault-secret-id
35
- volumes:
36
- - name: vault-role-id
37
- secret:
38
- secretName: ${VAULT_ROLE_ID_SECRET}
39
- items:
40
- - key: latest
41
- path: value
42
- - name: vault-secret-id
43
- secret:
44
- secretName: ${VAULT_SECRET_ID_SECRET}
45
- items:
46
- - key: latest
47
- path: value
48
- traffic:
49
- - latestRevision: true
50
- percent: 100
@@ -1,19 +0,0 @@
1
- import { expect, test } from "bun:test";
2
-
3
- const decoder = new TextDecoder();
4
-
5
- test(
6
- "go test ./...",
7
- { timeout: 60_000 },
8
- () => {
9
- const result = Bun.spawnSync(["go", "test", "./..."], {
10
- cwd: process.cwd(),
11
- stdout: "pipe",
12
- stderr: "pipe",
13
- env: process.env,
14
- });
15
-
16
- const output = [decoder.decode(result.stdout), decoder.decode(result.stderr)].join("").trim();
17
- expect(result.exitCode, output || "go test ./... failed").toBe(0);
18
- }
19
- );
@@ -1,10 +0,0 @@
1
- # Stable repo-local settings for Neon admin key lookup via Vault.
2
- # Copy to .env.local and adjust as needed.
3
-
4
- VAULT_ADDR=https://vault.example.com
5
- VAULT_SECRET_MOUNT=secret
6
- VAULT_NEON_API_KEY_PATH=provider/neon-api-key
7
- VAULT_NEON_API_KEY_FIELD=value
8
-
9
- # Do not commit VAULT_TOKEN. Prefer `vault login`; the CLI will also use
10
- # VAULT_TOKEN_FILE or ~/.vault-token when available.
@@ -1,10 +0,0 @@
1
- version: v2
2
- plugins:
3
- - local: protoc-gen-go
4
- out: gen
5
- opt:
6
- - paths=source_relative
7
- - local: protoc-gen-connect-go
8
- out: gen
9
- opt:
10
- - paths=source_relative
@@ -1,9 +0,0 @@
1
- version: v2
2
- modules:
3
- - path: protos
4
- lint:
5
- use:
6
- - STANDARD
7
- breaking:
8
- use:
9
- - FILE