Clears all 23 open CodeQL alerts:
- actions/missing-workflow-permissions (19, medium): add a top-level
least-privilege `permissions: contents: read` to ci.yml and the 7
deploy workflows. The one CI job that needs `packages: write` already
declares its own block; all deploys authenticate via static secrets
(no OIDC / id-token, no repo writes), so read is sufficient.
- js/insecure-randomness (1, high): `genPassword()` built share
passwords with Math.random(); switch to crypto.getRandomValues() over
the same unambiguous alphabet (length/charset/uniqueness preserved).
- js/incomplete-url-substring-sanitization (2, high): two test fetch
stubs routed on `String(url).includes('api.github.com')`; tighten to
`new URL(url).hostname === 'api.github.com'` — precise and no longer
flagged.
- js/stack-trace-exposure (1, medium): the E2E S3 mock echoed
error.message in 500 responses; log server-side and return a generic
body instead.
Verified: typecheck green; share-dialog/changelog/system.integration
tests pass.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Previously, push to master triggered 6 separate deploy workflows (CF +
5 new v2.5.0 targets). If the fork hadn't configured, say, AWS or Azure
secrets, those workflows would run just to fail on "Check required
secrets" — producing 5 red X's in Actions tab per push, 5 failure
notifications, 5 wasted runner allocations.
Collapse to one top-level `deploy.yml` dispatcher that:
1. Runs a lightweight `detect` job (~5s) probing which platform secret
bundles are fully present — without invoking secrets.* in job-level
`if:` (which GH disallows).
2. Invokes the corresponding reusable child workflow via `uses:` +
`secrets: inherit` only when that platform's flag is true.
Each child workflow (`deploy-<target>.yml`) is now a reusable workflow:
- `push: [master]` trigger → removed (dispatcher owns push)
- `workflow_call:` trigger → added (invoked by dispatcher)
- `workflow_dispatch:` trigger → kept (manual runs via Actions UI)
- `if: github.repository != 'saltbo/zpan'` job guard → removed
(dispatcher enforces this once)
The old `deploy.yml` (CF Workers flow) is renamed to
`deploy-cloudflare.yml` for consistency with the other 5. Content of
the CF flow is unchanged.
For a fork with only CF configured: 1 dispatcher run + 1 cloudflare run.
For a fork with nothing configured: 1 dispatcher run with all 6 child
jobs shown as "Skipped" (not failed), and a ::notice:: pointing at the
README secrets table.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: v2.5.0 T6 — Google Cloud Run deployment (service.yaml + workflow + docs)
Add Cloud Run as a first-class deploy target. Reuses the existing root
Dockerfile via gcloud run deploy --source (Cloud Build). Turso for DB,
external S3-compatible storage. Follows the standard 8-step workflow
contract: secret check, release resolution, GCloud auth, Turso migration,
Secret Manager upsert, deploy. min-instances=0 for free-tier eligibility
with cold-start callout in docs.
Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f
* fix: resolve first-deploy failure and drive deploy from service.yaml
BLOCKER: BETTER_AUTH_URL and TURSO_AUTH_TOKEN were passed to --set-secrets
even when the corresponding Secret Manager entries didn't exist yet.
BETTER_AUTH_URL isn't known until after the first deploy (it IS the Cloud
Run service URL). TURSO_AUTH_TOKEN is optional. Both caused 'secret not
found' aborts.
Fix: separate deploy into two phases.
Phase 1 — gcloud run services replace with service.yaml, which only
references the guaranteed secrets (turso-database-url, better-auth-secret).
Phase 2 — post-deploy step captures the service URL, upserts better-auth-url
and (if provided) turso-auth-token in Secret Manager, then wires them into
the running service via gcloud run services update --update-secrets.
MINOR: service.yaml was orphaned — the workflow used gcloud run deploy
--source . with inline flags instead. Rebuilt workflow to use gcloud builds
submit to build the image, then gcloud run services replace to drive the
deploy from the manifest. PROJECT_ID is substituted at deploy time.
Also demote BETTER_AUTH_URL from required to optional GitHub secret
(auto-derived from Cloud Run service URL on first deploy) and update docs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>