create-svc 0.1.57 → 0.1.58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-svc",
3
- "version": "0.1.57",
3
+ "version": "0.1.58",
4
4
  "description": "Local microservice bootstrap CLI for Cloud Run and Workers services with Neon-backed data.",
5
5
  "module": "index.ts",
6
6
  "type": "module",
@@ -139,12 +139,18 @@ test("scaffolds all runtime/framework variants with shared cloudrun config", asy
139
139
  expect(await Bun.file(join(generatedRoot, "grafana", "alerts.yaml")).exists()).toBeTrue();
140
140
 
141
141
  const previewWorkflow = await Bun.file(join(generatedRoot, ".github", "workflows", "preview.yml")).text();
142
- expect(previewWorkflow).toContain("push:");
143
- expect(previewWorkflow).toContain("branches-ignore:");
144
- expect(previewWorkflow).toContain("- main");
145
- expect(previewWorkflow).toContain("github.ref_name");
142
+ expect(previewWorkflow).toContain("issue_comment:");
143
+ expect(previewWorkflow).toContain("/deploy preview");
144
+ expect(previewWorkflow).not.toContain("branches-ignore:");
146
145
  expect(previewWorkflow).toContain("service deploy --ci --environment preview --name");
146
+ expect(previewWorkflow).toContain("steps.pr.outputs.number");
147
147
  expect(previewWorkflow).toContain("NEON_API_KEY");
148
+ expect(previewWorkflow).toContain("CLOUDFLARE_API_TOKEN");
149
+
150
+ const previewCleanupWorkflow = await Bun.file(join(generatedRoot, ".github", "workflows", "preview-cleanup.yml")).text();
151
+ expect(previewCleanupWorkflow).toContain("pull_request:");
152
+ expect(previewCleanupWorkflow).toContain("types: [closed]");
153
+ expect(previewCleanupWorkflow).toContain("github.event.pull_request.number");
148
154
 
149
155
  const deployWorkflow = await Bun.file(join(generatedRoot, ".github", "workflows", "deploy.yml")).text();
150
156
  expect(deployWorkflow).toContain("branches:");
@@ -156,6 +162,11 @@ test("scaffolds all runtime/framework variants with shared cloudrun config", asy
156
162
  expect(deployWorkflow).toContain("bun run dashboards");
157
163
  expect(deployWorkflow).toContain("GCX_ENABLED");
158
164
 
165
+ const personalWorkflow = await Bun.file(join(generatedRoot, ".github", "workflows", "personal.yml")).text();
166
+ expect(personalWorkflow).toContain("workflow_dispatch:");
167
+ expect(personalWorkflow).toContain("service deploy --ci --environment personal --name");
168
+ expect(personalWorkflow).toContain("service deploy --ci --destroy --environment personal --name");
169
+
159
170
  if (variant.runtime === "go") {
160
171
  const goMod = await Bun.file(join(generatedRoot, "go.mod")).text();
161
172
  const goSumExists = await Bun.file(join(generatedRoot, "go.sum")).exists();
@@ -309,10 +320,11 @@ test("scaffolds a backend package cleanly into a nested monorepo-style directory
309
320
  expect(readme).toContain("GitHub Actions deployment");
310
321
  expect(readme).toContain(".github/workflows/preview.yml");
311
322
  expect(readme).toContain(".github/workflows/deploy.yml");
312
- expect(readme).toContain("pushes to non-main branches");
323
+ expect(readme).toContain("/deploy preview");
313
324
  expect(readme).toContain("GCP_WIF_PROVIDER");
314
325
  expect(readme).toContain("GCP_DEPLOYER_SERVICE_ACCOUNT");
315
326
  expect(readme).toContain("NEON_API_KEY");
327
+ expect(readme).toContain("CLOUDFLARE_API_TOKEN");
316
328
 
317
329
  const packageJson = await Bun.file(join(generatedRoot, "package.json")).text();
318
330
  expect(packageJson).toContain('"hono"');
@@ -392,6 +404,16 @@ test("scaffolds the workers target with wrangler lifecycle commands", async () =
392
404
  expect(await Bun.file(join(generatedRoot, "src", "db", "repository.ts")).exists()).toBeFalse();
393
405
  expect(await Bun.file(join(generatedRoot, "src", "temporal", "worker.ts")).exists()).toBeFalse();
394
406
  expect(await Bun.file(join(generatedRoot, "scripts", "codegen.ts")).exists()).toBeFalse();
407
+
408
+ const previewWorkflow = await Bun.file(join(generatedRoot, ".github", "workflows", "preview.yml")).text();
409
+ expect(previewWorkflow).toContain("issue_comment:");
410
+ expect(previewWorkflow).toContain("/deploy preview");
411
+ expect(previewWorkflow).toContain("service deploy --name dns-api-pr-");
412
+ expect(previewWorkflow).not.toContain("google-github-actions/auth");
413
+
414
+ const previewCleanupWorkflow = await Bun.file(join(generatedRoot, ".github", "workflows", "preview-cleanup.yml")).text();
415
+ expect(previewCleanupWorkflow).toContain("pull_request:");
416
+ expect(previewCleanupWorkflow).toContain("wrangler delete --name dns-api-pr-");
395
417
  });
396
418
 
397
419
  test("microservice profile does not generate a website package", async () => {
package/src/scaffold.ts CHANGED
@@ -13,7 +13,10 @@ import { exampleForProfile, type Profile } from "./profiles";
13
13
  import type { GitBootstrapConfig } from "./git-bootstrap";
14
14
 
15
15
  const GENERATED_GITHUB_ACTION_WORKFLOWS = new Set([
16
+ ".github/workflows/ci.yml",
16
17
  ".github/workflows/preview.yml",
18
+ ".github/workflows/preview-cleanup.yml",
19
+ ".github/workflows/personal.yml",
17
20
  ".github/workflows/deploy.yml",
18
21
  ]);
19
22
 
@@ -243,9 +246,9 @@ function buildReplacements(config: ScaffoldConfig) {
243
246
  config.runtime === "bun" ? "bun run observability-bootstrap" : "make observability-bootstrap",
244
247
  WORKFLOW_DEPLOY_MAIN_COMMAND: "service deploy --ci",
245
248
  WORKFLOW_DEPLOY_PREVIEW_COMMAND:
246
- "service deploy --ci --environment preview --name ${{ github.ref_name }}",
249
+ "service deploy --ci --environment preview --name ${{ steps.pr.outputs.number }}",
247
250
  WORKFLOW_DEPLOY_MAIN_DOC_COMMAND: "service deploy --ci",
248
- WORKFLOW_DEPLOY_PREVIEW_DOC_COMMAND: "service deploy --ci --environment preview --name <branch-name>",
251
+ WORKFLOW_DEPLOY_PREVIEW_DOC_COMMAND: "service deploy --ci --environment preview --name <pull-request-number>",
249
252
  COMMAND_AUTH_RESOURCE: "service auth resource-server",
250
253
  COMMAND_AUTH_CLIENT: "service auth client create",
251
254
  COMMAND_AUTH_TOKEN: "service auth token",
@@ -30,12 +30,15 @@ jobs:
30
30
  workload_identity_provider: ${{ vars.GCP_WIF_PROVIDER }}
31
31
  service_account: ${{ vars.GCP_DEPLOYER_SERVICE_ACCOUNT }}
32
32
  - uses: google-github-actions/setup-gcloud@v2
33
+ - run: gcloud components install beta --quiet
33
34
  - run: bun install
35
+ - run: bun install -g create-svc@latest
34
36
  - run: |
35
37
  if [ "${{ inputs.destroy }}" = "true" ]; then
36
- bun run deploy -- --ci --destroy --environment personal --name "${{ inputs.slug }}"
38
+ service deploy --ci --destroy --environment personal --name "${{ inputs.slug }}"
37
39
  else
38
- bun run deploy -- --ci --environment personal --name "${{ inputs.slug }}"
40
+ service deploy --ci --environment personal --name "${{ inputs.slug }}"
39
41
  fi
40
42
  env:
41
43
  NEON_API_KEY: ${{ secrets.NEON_API_KEY }}
44
+ CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
@@ -19,7 +19,10 @@ jobs:
19
19
  workload_identity_provider: ${{ vars.GCP_WIF_PROVIDER }}
20
20
  service_account: ${{ vars.GCP_DEPLOYER_SERVICE_ACCOUNT }}
21
21
  - uses: google-github-actions/setup-gcloud@v2
22
+ - run: gcloud components install beta --quiet
22
23
  - run: bun install
23
- - run: bun run deploy -- --ci --destroy --environment preview --name ${{ github.event.pull_request.number }}
24
+ - run: bun install -g create-svc@latest
25
+ - run: service deploy --ci --destroy --environment preview --name ${{ github.event.pull_request.number }}
24
26
  env:
25
27
  NEON_API_KEY: ${{ secrets.NEON_API_KEY }}
28
+ CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
@@ -1,19 +1,30 @@
1
1
  name: Preview
2
2
 
3
3
  on:
4
- push:
5
- branches-ignore:
6
- - main
4
+ issue_comment:
5
+ types: [created]
7
6
 
8
7
  permissions:
9
8
  contents: read
9
+ issues: read
10
10
  id-token: write
11
+ pull-requests: read
11
12
 
12
13
  jobs:
13
14
  deploy:
15
+ if: ${{ github.event.issue.pull_request && contains(github.event.comment.body, '/deploy preview') }}
14
16
  runs-on: ubuntu-latest
15
17
  steps:
18
+ - id: pr
19
+ env:
20
+ GH_TOKEN: ${{ github.token }}
21
+ run: |
22
+ pr_json="$(gh api "repos/${{ github.repository }}/pulls/${{ github.event.issue.number }}")"
23
+ echo "number=$(jq -r '.number' <<< "$pr_json")" >> "$GITHUB_OUTPUT"
24
+ echo "head_sha=$(jq -r '.head.sha' <<< "$pr_json")" >> "$GITHUB_OUTPUT"
16
25
  - uses: actions/checkout@v4
26
+ with:
27
+ ref: ${{ steps.pr.outputs.head_sha }}
17
28
  - uses: oven-sh/setup-bun@v2
18
29
  - if: ${{ '{{RUNTIME}}' == 'go' }}
19
30
  uses: actions/setup-go@v5
@@ -24,8 +35,10 @@ jobs:
24
35
  workload_identity_provider: ${{ vars.GCP_WIF_PROVIDER }}
25
36
  service_account: ${{ vars.GCP_DEPLOYER_SERVICE_ACCOUNT }}
26
37
  - uses: google-github-actions/setup-gcloud@v2
38
+ - run: gcloud components install beta --quiet
27
39
  - run: bun install
28
40
  - run: bun install -g create-svc@latest
29
41
  - run: {{WORKFLOW_DEPLOY_PREVIEW_COMMAND}}
30
42
  env:
31
43
  NEON_API_KEY: ${{ secrets.NEON_API_KEY }}
44
+ CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
@@ -230,7 +230,8 @@ secrets only when you add a provider adapter. A generic adapter can honor:
230
230
 
231
231
  The scaffold emits a minimal deployment workflow slice for Cloud Run:
232
232
 
233
- - [.github/workflows/preview.yml](.github/workflows/preview.yml) deploys a preview environment on pushes to non-main branches. It runs `{{WORKFLOW_DEPLOY_PREVIEW_DOC_COMMAND}}`.
233
+ - [.github/workflows/preview.yml](.github/workflows/preview.yml) deploys a preview environment only when a pull request comment contains `/deploy preview`. It runs `{{WORKFLOW_DEPLOY_PREVIEW_DOC_COMMAND}}`.
234
+ - [.github/workflows/preview-cleanup.yml](.github/workflows/preview-cleanup.yml) destroys the preview service and Neon preview branch when the pull request is closed or merged.
234
235
  - [.github/workflows/deploy.yml](.github/workflows/deploy.yml) deploys the main production environment on pushes to `main`. It runs `{{WORKFLOW_DEPLOY_MAIN_DOC_COMMAND}}`.
235
236
 
236
237
  These workflows intentionally assume only GitHub OIDC, Google Cloud Workload Identity Federation, the generated `service` CLI, and Neon.
@@ -244,6 +245,7 @@ Before enabling the workflows, set these GitHub repository variables:
244
245
  Set this GitHub repository secret:
245
246
 
246
247
  - `NEON_API_KEY`: Neon admin API key used to create preview branches and resolve the main database
248
+ - `CLOUDFLARE_API_TOKEN`: Cloudflare API token used to publish Cloud Run custom-domain DNS records
247
249
 
248
250
  The deployer service account needs enough access in the generated GCP project to run `service deploy`, including Cloud Run, Artifact Registry, Secret Manager, IAM service account usage, and storage operations for this service.
249
251
  Run `{{COMMAND_BOOTSTRAP}}` once before relying on the production workflow for a fresh project.
@@ -0,0 +1,20 @@
1
+ name: Preview Cleanup
2
+
3
+ on:
4
+ pull_request:
5
+ types: [closed]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ cleanup:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: oven-sh/setup-bun@v2
16
+ - run: bun install
17
+ - run: bun install -g create-svc@latest
18
+ - run: wrangler delete --name {{SERVICE_ID}}-pr-${{ github.event.pull_request.number }} --force
19
+ env:
20
+ CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
@@ -0,0 +1,32 @@
1
+ name: Preview
2
+
3
+ on:
4
+ issue_comment:
5
+ types: [created]
6
+
7
+ permissions:
8
+ contents: read
9
+ issues: read
10
+ pull-requests: read
11
+
12
+ jobs:
13
+ deploy:
14
+ if: ${{ github.event.issue.pull_request && contains(github.event.comment.body, '/deploy preview') }}
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - id: pr
18
+ env:
19
+ GH_TOKEN: ${{ github.token }}
20
+ run: |
21
+ pr_json="$(gh api "repos/${{ github.repository }}/pulls/${{ github.event.issue.number }}")"
22
+ echo "number=$(jq -r '.number' <<< "$pr_json")" >> "$GITHUB_OUTPUT"
23
+ echo "head_sha=$(jq -r '.head.sha' <<< "$pr_json")" >> "$GITHUB_OUTPUT"
24
+ - uses: actions/checkout@v4
25
+ with:
26
+ ref: ${{ steps.pr.outputs.head_sha }}
27
+ - uses: oven-sh/setup-bun@v2
28
+ - run: bun install
29
+ - run: bun install -g create-svc@latest
30
+ - run: service deploy --name {{SERVICE_ID}}-pr-${{ steps.pr.outputs.number }}
31
+ env:
32
+ CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}