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 +1 -1
- package/src/scaffold.test.ts +27 -5
- package/src/scaffold.ts +5 -2
- package/templates/shared/.github/workflows/personal.yml +5 -2
- package/templates/shared/.github/workflows/preview-cleanup.yml +4 -1
- package/templates/shared/.github/workflows/preview.yml +16 -3
- package/templates/shared/README.md +3 -1
- package/templates/targets/workers/.github/workflows/preview-cleanup.yml +20 -0
- package/templates/targets/workers/.github/workflows/preview.yml +32 -0
package/package.json
CHANGED
package/src/scaffold.test.ts
CHANGED
|
@@ -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("
|
|
143
|
-
expect(previewWorkflow).toContain("
|
|
144
|
-
expect(previewWorkflow).toContain("-
|
|
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("
|
|
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 ${{
|
|
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 <
|
|
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
|
-
|
|
38
|
+
service deploy --ci --destroy --environment personal --name "${{ inputs.slug }}"
|
|
37
39
|
else
|
|
38
|
-
|
|
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
|
|
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
|
-
|
|
5
|
-
|
|
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
|
|
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 }}
|