Files
JMR-devandClaude Opus 4.8 985fb684c5 #4 GitHub Actions CD: deploy via workflow_dispatch
Add a manual, workflow_dispatch-only CD workflow and close the infra
binding-wiring gap (#9) so the deployed Worker is actually functional.

Deliverable 1 - .github/workflows/deploy.yml:
- workflow_dispatch only, with a `stack` choice input (default prod).
- Gated to the `production` GitHub Actions environment and to the main
  branch (guard step fails otherwise); no push/PR trigger.
- Reuses ci.yml's Go 1.26 + TinyGo + pnpm setup and the TinyGo net/http
  patch, builds the Wasm Worker (pnpm run build), then runs pulumi up over
  infra/ via pulumi/actions, injecting the built ../build/worker.mjs as the
  workerScriptPath config. Provider/backend creds come from environment
  secrets (nothing committed). All actions pinned by commit SHA; actionlint
  clean.

Deliverable 2 - infra/deploy.go binding wiring (#9):
- WorkersScript now carries the R2 bucket binding (REPORTS_BUCKET), the four
  Secrets Store bindings (BUGREPORT_ENC_KEYRING, ADMIN_TOKEN, GITHUB_TOKEN,
  OTEL_EXPORTER_OTLP_HEADERS), and the plain vars (GITHUB_REPO, OTEL_*),
  matching wrangler.jsonc and the Worker runtime contract.
- New WorkersCronTrigger resource registers the two Friday UTC crons (#13),
  bound to the Worker.
- Real built artifact wired via ContentFile + computed ContentSha256 when
  the workerScriptPath config is set; documented placeholder otherwise
  (keeps the program testable without the artifact, as #2 did).
- secretsStoreId is a new required config; git rate-limit ruleset insertion
  point (#6/#7) kept reserved.
- Extended the WithMocks tests to assert the R2 + Secrets Store + var
  bindings, the crons, and the artifact ContentFile/ContentSha256 path.
- Updated Pulumi.<stack>.yaml and infra/README.md with the full
  secret/config list and how a maintainer triggers the deploy.

Verified in infra/: go build, go vet, go test all green; actionlint clean
on deploy.yml. No deploy/preview/provision was run.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 17:40:08 -05:00

43 lines
2.7 KiB
YAML

# Dev stack configuration.
#
# NON-SECRET values only. Secrets (Cloudflare API token, GCP credentials) are set
# with `pulumi config set --secret ...` (they land here ENCRYPTED) or supplied via
# provider environment variables. NEVER commit a plaintext secret. See README.md.
#
# Replace every REPLACE_ME_* value below with your real dev account/zone/domain
# before running `pulumi up`.
config:
# --- GCP provider (see also GOOGLE_CREDENTIALS / gcp:credentials) ---
gcp:project: REPLACE_ME_GCP_PROJECT_ID
# --- Program config (namespace = project name from Pulumi.yaml) ---
libremail-bug-report-ingest-infra:cloudflareAccountId: REPLACE_ME_CLOUDFLARE_ACCOUNT_ID
# Cloudflare Secrets Store id holding the Worker's secrets (encryption keyring,
# admin token, GitHub token, OTLP headers). Account-specific; not itself secret.
libremail-bug-report-ingest-infra:secretsStoreId: REPLACE_ME_CLOUDFLARE_SECRETS_STORE_ID
libremail-bug-report-ingest-infra:workerName: libremail-bug-report-ingest
libremail-bug-report-ingest-infra:workerCompatibilityDate: "2025-06-01"
# Built Worker artifact (../build/worker.mjs from `pnpm run build`). Leave UNSET
# here: the CD workflow (.github/workflows/deploy.yml) injects it at deploy time
# via config-map so `pulumi preview` without a build still uses the placeholder.
# libremail-bug-report-ingest-infra:workerScriptPath: ../build/worker.mjs
# Plain (non-secret) Worker vars — mirror wrangler.jsonc "vars".
libremail-bug-report-ingest-infra:githubRepo: JMR-dev/LibreMail
# OTLP endpoint EMPTY disables telemetry until a collector is chosen (#17).
libremail-bug-report-ingest-infra:otelExporterOtlpEndpoint: ""
libremail-bug-report-ingest-infra:otelServiceName: libremail-bug-report-ingest
libremail-bug-report-ingest-infra:r2BucketName: libremail-bug-reports-dev
# R2 location hint (optional). One of: apac, eeur, enam, weur, wnam, oc.
libremail-bug-report-ingest-infra:r2BucketLocation: enam
# Google Cloud DNS managed-zone NAME (the zone already exists; it is referenced,
# not created, by this stack).
libremail-bug-report-ingest-infra:dnsManagedZone: REPLACE_ME_GCLOUD_DNS_MANAGED_ZONE_NAME
# Ingest hostname (FQDN, trailing dot) and the Worker route/custom-domain it
# points at (CNAME target, FQDN, trailing dot).
libremail-bug-report-ingest-infra:dnsRecordName: bugreport.dev.libremail.example.
libremail-bug-report-ingest-infra:dnsRecordType: CNAME
libremail-bug-report-ingest-infra:dnsRecordTarget: libremail-bug-report-ingest.REPLACE_ME_SUBDOMAIN.workers.dev.
libremail-bug-report-ingest-infra:dnsTtlSeconds: "300"
# Optional, reserved for #7 (rate-limit ruleset) and Worker routes/custom domain:
# libremail-bug-report-ingest-infra:cloudflareZoneId: REPLACE_ME_CLOUDFLARE_ZONE_ID