Commit Graph

67 Commits

Author SHA1 Message Date
saltbo 53076d7873 feat(downloads): add remote download workers 2026-06-03 02:21:50 -04:00
saltbo 44fe744711 ci: use separate cloud e2e accounts 2026-06-01 12:13:40 -04:00
saltbo 4707e9703d ci: serialize cloud e2e jobs 2026-06-01 11:39:57 -04:00
saltbo d8feabeaee ci: use system chrome for e2e 2026-06-01 11:21:36 -04:00
saltbo f569368dd4 ci: skip playwright system deps in e2e 2026-06-01 11:12:28 -04:00
saltbo 5a64cbe4e3 ci: run e2e in playwright image 2026-06-01 11:05:27 -04:00
saltbo 30bc6e1a12 build: migrate project to pnpm 2026-06-01 10:44:33 -04:00
saltbo a3b2146400 ci(e2e): run cloud runtimes in parallel 2026-05-10 12:36:10 -04:00
saltbo a8aa2e8784 ci(e2e): isolate cloud account binding 2026-05-10 02:54:34 -04:00
saltbo f341b969a9 ci(test): exclude cloud e2e from default suite 2026-05-10 02:38:26 -04:00
saltbo 4ecccbba95 ci(release): provide cloud e2e credentials 2026-05-10 02:35:00 -04:00
saltbo ddeb49ded6 test(cloud): verify staging license unbind 2026-05-10 01:18:03 -04:00
saltbo 14a0723120 fix(cloud): keep staging e2e licenses isolated 2026-05-09 22:33:50 -04:00
saltbo 46008a139d ci(cloud): serialize tunnel e2e jobs 2026-05-09 22:29:12 -04:00
saltbo 800cda9f9c test(cloud): add tunnel-backed store e2e 2026-05-09 22:17:52 -04:00
Jasper Van 0b65e2dc15 [v2.6] Integrate zpan with new cloud order flow and complete migration cleanup
fix(store): guard cloud order actions by org
2026-05-08 15:42:05 -04:00
saltbo 0adaac8dbe fix(cf-deploy): run wrangler from repo root, not dist/zpan
wrangler 4.x errors when it finds both a local wrangler.json and a
parent .wrangler/deploy/config.json redirect file — "base paths do not
match" — which is exactly what happens if you cd into dist/zpan after
the @cloudflare/vite-plugin build.

The old `cd dist/zpan` workaround predates the vite-plugin generating
.wrangler/deploy/config.json on build. Now that the redirect file
exists, running wrangler from the repo root is the correct path:
wrangler picks up the redirect, uses dist/zpan/wrangler.json, and
resolves ../client / index.js / etc. relative to *that* config's
location — all paths correct.

Empirically: `wrangler deploy --dry-run` from root reports
"Read 333 files from the assets directory .../dist/client" and all
three bindings (D1 + R2 + ASSETS) are recognized. No 404.

Fixes the cloudflare deploy job in bonaysoft/zpan's first run of the
new dispatcher.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 23:31:06 -04:00
saltbo 07973b6520 ci(deploy): fan-out dispatcher — one push, only configured platforms run
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>
2026-04-22 23:22:44 -04:00
saltbo 95a6872f94 fix(cf-deploy): use R2 REST API for bucket ensure (wrangler lacks --json)
\`wrangler r2 bucket list\` has no \`--json\` flag (confirmed via
\`--help\`), so piping its text output to jq would fail silently. Switch
the \"Ensure R2 public-images bucket exists\" step to the CF REST API
which returns proper JSON and also lets us check-then-create in two
clean HTTP calls. Both endpoints use the same \`CLOUDFLARE_API_TOKEN\`
with R2 Storage: Edit scope.

Idempotency unchanged: check-exists-before-create.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:56:35 -04:00
saltbo 64774b9039 ci(cf-deploy): auto-provision R2 public-images bucket + URL secret
Three new workflow steps turn the CF deploy into fully zero-touch for
the avatar/logo feature:

1. Ensure R2 bucket exists — `wrangler r2 bucket list --json` → create if
   missing. Idempotent; prior deploys skip creation.
2. Enable managed public URL — `PUT /r2/buckets/.../domains/managed`
   with {enabled:true}. CF returns the same pub-<hash>.r2.dev on every
   call once enabled, so this is idempotent too. Then GET the domain
   from the same endpoint and capture into step output.
3. Upsert `PUBLIC_IMAGES_URL` as a Worker secret via
   `wrangler secret put`. Always overwritten to keep in sync with the
   managed domain (which is stable but this is defensive).

Order: bucket/URL steps run BEFORE `wrangler deploy` so the binding
declared in wrangler.toml (new `[[r2_buckets]] binding = "PUBLIC_IMAGES"`)
references an existing bucket. The secret step runs AFTER deploy —
wrangler secret put requires the Worker to exist.

`wrangler.toml`: new `[[r2_buckets]]` for production and
`[[env.staging.r2_buckets]]` for the staging environment. Staging uses
a distinct bucket (`zpan-public-images-staging`) so preview deploys
don't mix objects into production.

README updated: `CLOUDFLARE_API_TOKEN` now requires
**R2 Storage: Edit** in addition to Workers Scripts + D1. The workflow
surfaces an actionable error message when this scope is missing.

User experience on a fresh fork: add the 2 GitHub secrets (ACCOUNT_ID
+ API_TOKEN with the 3 scopes), push. Workflow creates D1, creates R2,
enables public URL, migrates, builds, deploys, sets both BETTER_AUTH_SECRET
(random) and PUBLIC_IMAGES_URL (from R2). Avatars + org logos work
immediately — no trip to Admin → Storages required.

Non-CF deployments continue to require a user-added mode='public'
storage (no change to those paths).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:56:35 -04:00
saltbo 2656970ecd fix(ci): deploy from vite-plugin output instead of repo root
Running `wrangler deploy --assets dist` from the repo root used the
source wrangler.toml (no assets.directory) and treated the whole dist/
tree as static assets, so the built worker under dist/zpan/ was served
as a file and SPA routes 404'd. It also forced wrangler to re-bundle
the TS entry, diverging from what @cloudflare/vite-plugin produced.

The vite plugin emits dist/zpan/wrangler.json with the right asset dir
(../client), the prebuilt worker, and all bindings. Deploy from there
so the GitHub Actions workflow matches Cloudflare Workers Builds.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 21:04:18 -04:00
saltbo 0883051f0f fix(ci): drop unsupported --json from wrangler d1 create
wrangler d1 create has no --json flag, so the first-time deploy path
errored with "Unknown argument: json" on fresh forks. d1 list --json
masked the bug for anyone whose CF account already had zpan-db.

Parse the TOML snippet wrangler prints instead, and fail fast if the
UUID can't be extracted so downstream sed/wrangler steps don't run with
an empty database_id.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 20:43:02 -04:00
Jasper Van d33800f23e feat: Azure Functions deployment target (v4, Node 22) (#330)
* feat: add Azure Functions deployment target (v4, Node 22)

- server/entry-azure.ts: Azure Functions v4 handler wrapping the Hono
  app via app.http(); uses createLibsqlPlatform for Turso and serves
  the SPA from ./dist via @hono/node-server/serve-static
- server/azure-host.json: runtime manifest (extensionBundle v4)
- deploy/azure-functions/main.bicep: idempotent Bicep template
  provisioning Storage Account, Consumption plan and Function App;
  BETTER_AUTH_SECRET handled separately by the workflow
- .github/workflows/deploy-azure.yml: 8-step workflow (secret check,
  checkout, Node setup, az login, Bicep deploy, build, db:migrate,
  func publish) with BETTER_AUTH_SECRET generate-if-missing logic
- package.json: build:azure script + @azure/functions dependency
- docs/deploy/azure-functions.md: setup guide covering SP JSON format,
  required secrets, and local emulation with func start

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

* fix: address review issues in Azure Functions deploy

- Move BETTER_AUTH_SECRET and APP_URL setup to before func publish
  (bootstrap.ts throws on missing secret; any request between publish
  and the old secret-set step would have returned 500)
- Remove placeholder appUrl Bicep param; workflow sets APP_URL and
  BETTER_AUTH_URL via appsettings after Bicep, before publish
- Fix HttpRequest→Request body handling: construct a proper Web API
  Request with body cast and duplex option instead of double-casting
  HttpRequest, ensuring POST/PUT/PATCH body-reading routes work
- Add push: branches: [master] trigger + upstream guard to match other
  deploy workflow conventions; document the auto-deploy behaviour
- Update docs/deploy/azure-functions.md to reflect the push trigger

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

* ci: re-trigger CI for review fixes

---------

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
2026-04-22 02:08:54 -04:00
Jasper Van 8b72a7dba9 feat: v2.5.0 T6 — Google Cloud Run deployment (service.yaml + workflow + docs) (#332)
* 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>
2026-04-22 01:43:34 -04:00
Jasper Van 0f403f8081 feat: v2.5.0 T4 — Netlify deployment target (#329)
* feat: v2.5.0 T4 — Netlify deployment target

- server/entry-netlify.ts: Netlify Functions v2 (ESM) handler using hono/netlify
  adapter; connects to Turso via @libsql/client; skips in-process migrations
  (workflow applies them before deploy via drizzle-kit)
- deploy/netlify/netlify.toml: build command, functions directory, SPA fallback redirect
- .github/workflows/deploy-netlify.yml: 8-step workflow — secret guard, tag resolve,
  Turso migrations, build, netlify deploy --prod, BETTER_AUTH_SECRET first-deploy, summary
- package.json: add build:netlify script (tsup ESM → netlify/functions)
- docs/deploy/netlify.md: 5-section setup guide covering Turso, site creation,
  secrets, deploy trigger, first-boot storage setup, and cost breakdown

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

* fix: address Netlify deploy review blockers

BLOCKER 1 — move BETTER_AUTH_SECRET step before Deploy in workflow so
the function always has the secret set before its first cold start.

BLOCKER 2 — replace inline platform construction in entry-netlify.ts
with createLibsqlPlatform(); removes duplicated db/schema wiring and
re-unifies with the shared factory. migrate() runs at cold start and
is idempotent (~50–100ms) per the workflow's prior drizzle-kit migrate.

BLOCKER 3 — add --external @libsql/client to build:netlify so tsup
leaves the native-binding package for Netlify to resolve; switch
netlify.toml to node_bundler=esbuild so Netlify bundles @libsql/client
from node_modules. Add included_files=["migrations/**"] so the
migrations folder is available in the function zip for migrate().

Minor — replace 2>/dev/null with 2>&1 in deploy step so netlify-cli
errors surface in CI logs instead of being silently swallowed.

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>
2026-04-22 01:28:08 -04:00
Jasper Van b5be4c5ebd feat: v2.5.0 T2 — AWS Lambda deployment (entry + SAM + workflow + docs) (#331)
- server/entry-lambda.ts: Hono app via hono/aws-lambda handle(); lazy init
  pattern for CJS compatibility and warm-start reuse; serves SPA static
  files from dist/ with MIME detection and index.html fallback
- deploy/aws-lambda/template.yaml: SAM template with Function URL (no API
  Gateway), Node 22, TURSO_* / BETTER_AUTH_SECRET / APP_URL env vars,
  minimal IAM (AWSLambdaBasicExecutionRole)
- .github/workflows/deploy-aws-lambda.yml: 8-step contract (guard upstream,
  check secrets with exact names, resolve tag, checkout, ensure SAM artifact
  bucket, apply Turso migrations, build + sam deploy, post-deploy auto-gen
  BETTER_AUTH_SECRET + patch BETTER_AUTH_URL + write URL to summary)
- package.json: build:lambda script (tsup CJS, external @libsql/client)
- docs/deploy/aws-lambda.md: Prerequisites / Secrets / Trigger /
  First-boot storage / Cost sections
- README.md, V2_ROADMAP.md: link new doc

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
2026-04-22 01:28:05 -04:00
Jasper Van 5593eec3ce feat: v2.5.0 T3 — Vercel deployment (entry + vercel.json + workflow + docs) (#328)
* feat: add Vercel deployment target (Node runtime + Turso)

Adds first-class Vercel support: server/entry-vercel.ts using hono/vercel
handler, deploy/vercel/vercel.json with nodejs22.x function config and SPA
rewrites, build:vercel npm script producing api/entry-vercel.js + dist/,
deploy-vercel GitHub Actions workflow (8-step: secrets check, tag resolve,
checkout, install, migrate, build, link, deploy), and docs/deploy/vercel.md
documenting secrets, quick-start, local dev, and pricing notes.

Edge runtime is explicitly not used — @aws-sdk/client-s3 requires Node APIs.

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

* fix: auto-generate BETTER_AUTH_SECRET on first Vercel deploy

Remove BETTER_AUTH_SECRET from the required secrets check. Add a
dedicated step that detects whether the secret already exists in the
Vercel project env via `vercel env ls production`, then either upserts
the user-supplied GitHub secret, auto-generates one with openssl on
first deploy, or skips if already present. Auto-generation case appends
a backup warning to GITHUB_STEP_SUMMARY. Docs move BETTER_AUTH_SECRET
to Optional Secrets with a note about the auto-gen behaviour.

Matches the existing CF Workers deploy.yml pattern (step 8 contract).

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

---------

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
2026-04-22 01:14:54 -04:00
saltbo 93c5e7739b fix(ci): use --format json for wrangler secret list
d1 list uses --json, but secret list uses --format json.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-21 20:18:21 -04:00
saltbo ed1bebe3a4 fix(ci): add build step before wrangler deploy
wrangler requires built assets in dist/ directory.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-21 20:13:59 -04:00
saltbo 71bded20f4 ci(deploy): auto-disable CI and Release workflows on fork
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-21 20:12:45 -04:00
saltbo e5555b20fa ci: simplify fork guards to root jobs only
Downstream jobs auto-skip when their dependency is skipped.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-21 20:11:49 -04:00
saltbo cdc7a72983 ci: skip CI and release workflows on fork repositories
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-21 20:10:47 -04:00
saltbo 2aa6ab40fd fix(ci): use --json flag for wrangler d1/secret list commands
wrangler v4 uses --json, not --format json.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-21 20:08:42 -04:00
saltbo 3d8f51f175 ci: add fork-based deploy workflow for Cloudflare Workers
- Add deploy.yml: auto-deploys latest upstream release tag on fork sync
- Support manual trigger with optional version override
- Auto-create D1 database, apply migrations, and set BETTER_AUTH_SECRET
- Only runs on forks (skipped on saltbo/zpan)
- Validate required secrets with clear error message
- Update README with fork + GitHub Actions deploy instructions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-21 19:57:04 -04:00
saltbo f2f148298f ci: drop linux/arm/v7 platform from Docker build
node:24-slim no longer provides an arm/v7 manifest, causing the
release Docker Image job to fail with 'no match for platform'.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-20 22:32:59 -04:00
saltbo b4545c50d6 ci: add multi-arch Docker build support (amd64, arm64, arm/v7)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-20 22:03:59 -04:00
saltbo df32f35ba9 refactor(test): separate unit and integration tests
Rename 19 integration test files from *.test.ts to *.integration.test.ts.
Configure vitest projects to run them independently with separate coverage
thresholds. CI now reports unit and integration coverage as separate flags
to Codecov.

Unit tests: pure function calls, mocked dependencies, no DB
Integration tests: createTestApp() with in-memory DB + HTTP requests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 21:01:37 -04:00
saltbo c70b684acb fix(ci): enable coverage generation and fix Codecov upload path
Add --coverage flag to npm test in CI so coverage files are generated.
Fix upload path to coverage-final.json (v8 provider default). Simplify
vitest reporter config.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 15:13:06 -04:00
saltbo c376f2eb1b feat(ci): add Codecov coverage reporting and badge
Add json/json-summary reporters to vitest config, upload coverage
to Codecov after test step in CI, and add coverage badge to README.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 15:07:29 -04:00
saltbo 388b200497 fix(deploy): use preview_database_id for preview D1 binding
Workers Builds preview versions share the same Worker's bindings.
[env.staging] creates a separate Worker which is not how preview URLs
work. Use preview_database_id instead — preview versions automatically
use the staging D1 database while production uses the main one.

Also cache node_modules in CI to speed up npm ci across jobs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 12:39:59 -04:00
saltbo a8b8f5719f fix(ci): apply D1 migrations before CF Workers E2E tests
The cloudflare vite plugin uses a local Miniflare D1 database which
starts empty. Migrations must be applied before tests can run.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 12:33:43 -04:00
saltbo a8f1154959 feat(ci): run E2E tests on both Node and CF Workers runtimes
Split E2E into two parallel jobs:
- e2e-node: Node backend + Vite (--mode node), uses process.env
- e2e-cf: CF Workers via cloudflare vite plugin, uses .dev.vars

Playwright config switches webServer based on E2E_RUNTIME env var.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 12:27:48 -04:00
saltbo a082a51dca fix(ci): pass tag input to changelog action
requarks/changelog-action requires explicit tag when it cannot
auto-detect from context.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 10:18:29 -04:00
saltbo d110d6b38e chore(ci): upgrade actions to latest versions (Node.js 24 runtime)
- actions/checkout v4 → v6
- actions/setup-node v4 → v6
- docker/login-action v3 → v4
- docker/setup-buildx-action v3 → v4
- docker/build-push-action v6 → v7

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 10:14:53 -04:00
saltbo e673fc6252 ci: add release workflow for tag-triggered Docker + GitHub Release
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 09:56:16 -04:00
saltbo fa99b31b07 fix(ci): remove drizzle-kit push and add BETTER_AUTH_SECRET for E2E
Since b73d7a0 added auto-migrate on Node platform boot, the explicit
drizzle-kit push step conflicts — push creates tables without recording
a migration journal, so the subsequent migrate() call fails with
"table already exists". Also add BETTER_AUTH_SECRET env var required
since ff1ad78 added fail-fast on missing secret.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 00:37:04 -04:00
saltbo 7c77731e96 chore: update CI for npm, upgrade Node to 24 with volta pin
- Replace pnpm with npm in GitHub Actions workflow
- Upgrade Node.js from 20 to 24 in CI, Dockerfile, and engines
- Pin Node 24.14.1 via volta

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 00:31:08 -04:00
saltbo 906799a517 fix: create dist/ before CF tests in CI
wrangler.toml references assets.directory=./dist which doesn't
exist until frontend is built. CF tests don't need static assets.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 11:23:27 -04:00
saltbo c682995cbf fix: run drizzle-kit push before E2E tests in CI
The Node dev server needs a zpan.db with tables created.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 10:25:22 -04:00
saltbo 6b937948df feat: add project infrastructure — testing, CI, linting, logging
- Vitest unit/integration tests for server (22 tests) and shared (14 tests)
- Cloudflare Workers integration tests via @cloudflare/vitest-pool-workers (9 tests)
- Playwright E2E config with auto webServer startup
- GitHub Actions CI: lint → typecheck → test (Node + CF) → E2E
- Biome for linting + formatting (replaces ESLint + Prettier)
- Unified access log middleware for all API routes
- Lefthook pre-commit hooks: lint, typecheck, test
- Coverage threshold enforced at 90% (currently 96.9%)
- Drizzle D1 migrations generated from schema
- Fix pre-existing typecheck errors across all packages

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 10:11:03 -04:00