Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
816b5e3
fix(audit): wave A backend hygiene — clock skew unification, EF SQL c…
claude May 27, 2026
8742891
fix(audit): wave B CI/CD/infra hygiene — pin flyctl, /health/ready co…
claude May 27, 2026
25c2186
fix(audit): wave C frontend P0/P1 — hydration year, rehydrate race, C…
claude May 27, 2026
a6678b0
fix(audit): wave D security/integrity — RT reuse detection, telemetry…
claude May 27, 2026
a7efecc
docs(audit): codify wave A-D invariants — clock skew, refresh-reuse, …
claude May 27, 2026
64f6682
feat(audit): wave F frontend AbortController + opt-in pre-commit hook…
claude May 27, 2026
20207b3
refactor(audit): T2.3 — consolidate BaseRepository + OutboxRepository…
claude May 27, 2026
09971e0
feat(audit): wave G frontend — loading.tsx + error.tsx per segment, d…
claude May 27, 2026
b97fb1a
feat(security): wave H — header hardening, gateway forwarded-headers …
claude May 27, 2026
16b0278
feat(observability): T4.3 — cache hit-ratio metric
claude May 27, 2026
11900a1
feat(perf): T4.1 — EF N+1 sentinel interceptor (Phase 4)
claude May 27, 2026
7f47914
feat(audit): wave I — RabbitMQ publisher confirms, pool sizing, CodeQ…
claude May 27, 2026
c29d7fe
feat(perf): wave J — Redis maxmemory + next/image optimizer for avata…
claude May 27, 2026
c1c4ab2
feat(security): T3.7 — Sigstore keyless SBOM attestation for frontend…
claude May 28, 2026
6aa35a4
feat(realtime): T2.5 scaffold — durable Notification + NotificationDe…
claude May 28, 2026
01bb504
feat(security): T3.5 — forward-looking security-stamp rotation policy
claude May 28, 2026
661c7dd
feat(perf): T4.10 — MotionConfig + hardware-adaptive WebGL background
claude May 28, 2026
5a3f4c2
feat(perf): T4.5 — Postgres idle_in_transaction_session_timeout = 30s
claude May 28, 2026
7423d08
feat(e2e): T2.6 start — Playwright UI project + browser-rendered logi…
claude May 28, 2026
02aac07
test(e2e): T2.6 — add register flow UI spec
claude May 28, 2026
080f7a4
test(e2e): T2.6 — add forgot-password and tasks-page UI specs
claude May 28, 2026
79e8d93
feat(perf): T4.2 first pass — outbox partial index + missing Messagin…
claude May 28, 2026
076ec04
docs: T2.7 — ADR-0006 force-dynamic + CSP nonce trade-off
claude May 28, 2026
3eab114
feat(security): T3.6 baseline + T2.6 reset-password / profile UI specs
claude May 28, 2026
a213313
fix(audit): self-audit pass — defects found in this branch's prior co…
claude May 28, 2026
ec7fbf1
test(e2e): T2.6 — verify-email UI spec + honest INV-DATA-5
claude May 28, 2026
3cc51da
fix(perf): T4.10 — Toaster was outside MotionConfig, animations skipp…
claude May 28, 2026
941693e
docs: T2.5 — clarify NoOpDomainEventDispatcher TryAddScoped semantics
claude May 28, 2026
3c5aa55
docs(security): T3.6 — fix wrong routes + add missing endpoints in ID…
claude May 28, 2026
2a5264e
docs: audit-2026-05 close-out summary
claude May 28, 2026
a63f231
fix(ci): make backend build/test/security scan green on this branch
claude May 28, 2026
9e32cd2
fix(ci): close Trivy IaC HIGH findings + polish closeout doc
claude May 28, 2026
bab7454
fix(ci): replace fictitious SHA pins with real ones across workflows
claude May 28, 2026
6452623
docs: prune audit artefacts, normalise prose, restructure index
claude May 29, 2026
0011b14
ci: add develop to push and pull-request triggers across all workflows
claude May 29, 2026
7ca3965
perf(frontend): lazy-load heavy modals + preconnect to API origin
claude May 29, 2026
839537e
perf(frontend): opt-in Avatar priority for above-the-fold LCP candidate
claude May 29, 2026
37815ba
fix(frontend): silence canceled-request errors in API client
4Keyy May 29, 2026
7e394d0
perf(scripts): harden and speed up Start-Planora-Local launcher
4Keyy May 29, 2026
dc914e0
test(frontend): assert network errors log via warn outside production
4Keyy May 29, 2026
a793276
perf(frontend): memoize TodoCard, trim motion, window the /tasks feed
4Keyy May 29, 2026
4af1b2b
feat: extract task comment timeline into the Collaboration microservi…
4Keyy May 30, 2026
f108dd4
fix(ci): add missing IUnitOfWork using in WorkerLifecycleEventTests
claude May 30, 2026
0e3a0b7
fix(docs): restore MD041 disable for HTML-first README
claude May 30, 2026
748377e
fix(ci): repair build, markdownlint, and Trivy after Collaboration split
4Keyy May 31, 2026
1835e8c
docs(readme): correct license, fix React version, slim styling
4Keyy May 31, 2026
2fb9edc
feat(scripts): launch Collaboration service + add -Stop/-Help to laun…
4Keyy May 31, 2026
7f991b2
ci: bump pinned actions to Node 24 runtimes
4Keyy May 31, 2026
08ceb14
feat(scripts): bring Docker launcher to parity with the local launcher
4Keyy May 31, 2026
ca2f516
fix(collaboration): allow genesis comment edits up to 5000 chars
4Keyy May 31, 2026
b1fd91a
docs(features): reconcile Todo description limit to 2000
4Keyy May 31, 2026
e345bb4
build: migrate backend from .NET 9 to .NET 10 (LTS)
4Keyy May 31, 2026
2d1dd67
chore(deps): bump npm-patch-minor group (8 packages)
4Keyy May 31, 2026
c7a9806
test(auth): pin password hash on-disk format with a golden vector
4Keyy May 31, 2026
e94ff58
build: pin .NET 10 SDK and auto-resolve it in the local launcher
4Keyy May 31, 2026
aa8a9bd
fix: show task timeline promptly + center navbar avatar
4Keyy May 31, 2026
923efa4
refactor(collaboration): task description as single source of truth +…
4Keyy May 31, 2026
fb2e270
fix(security): stop CategoryApi leaking raw exception messages
4Keyy Jun 1, 2026
ed0a536
fix: make event consumers idempotent + AsNoTracking on Category reads
4Keyy Jun 1, 2026
9773429
perf(auth): AsNoTracking on append-only history reads
4Keyy Jun 1, 2026
145ab87
fix(a11y): label auth form fields + name password-visibility toggles
4Keyy Jun 1, 2026
a84cca8
fix(a11y): associate category Name/Description labels with their inputs
4Keyy Jun 1, 2026
4744dea
feat(frontend): mirror /tasks category filter on Completed page
4Keyy Jun 1, 2026
121db4e
fix(frontend): gate owner-only fields for viewers + fixed-size branch…
4Keyy Jun 1, 2026
ed40a07
feat(frontend): sticky Author's Note + task actions in branch compose…
4Keyy Jun 1, 2026
9e771ac
fix: branch comment 409 + live branch updates, open-at-bottom, comple…
4Keyy Jun 1, 2026
d180e8f
fix(frontend): round all corners of the condensed Author's Note bar
4Keyy Jun 1, 2026
3fd5528
perf(outbox): signal-driven dispatch so branch system comments appear…
4Keyy Jun 1, 2026
6e95653
feat(frontend): unified Quick Filter bar + hide quick-picks for non-o…
4Keyy Jun 1, 2026
4046e51
fix(frontend): keep branch modal open when leaving work + faster stat…
4Keyy Jun 1, 2026
ce480c5
feat(devx): one-command Wi-Fi/LAN sharing for the local launcher (VPN…
4Keyy Jun 1, 2026
bbe10ca
docs(launcher): rewrite Start-Planora-Local help + sync all documenta…
4Keyy Jun 1, 2026
79dbce2
feat(frontend): continuous branch rail with centered, event-typed sys…
4Keyy Jun 1, 2026
07aefbb
fix(frontend): keep modal open on leave (dashboard), wrap long branch…
4Keyy Jun 1, 2026
eafe282
docs: comprehensive, marketing-grade README with full package links
4Keyy Jun 1, 2026
6bcef8d
build: remove redundant framework packages (NU1510) for a clean -warn…
4Keyy Jun 1, 2026
bb14cce
fix(ci): green markdownlint + restore 85% branch-coverage gate
4Keyy Jun 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
18 changes: 13 additions & 5 deletions .env.production.example
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,19 @@ JwtSettings__Secret=<same-value-as-JWT_SECRET>
JwtSettings__Issuer=Planora.Auth
JwtSettings__Audience=Planora.Clients

# Direct connection strings for non-Compose production deployments
ConnectionStrings__AuthDatabase=Host=<postgres-host>;Port=5432;Database=planora_auth_db;Username=<user>;Password=<password>;SSL Mode=Require;Trust Server Certificate=false;
ConnectionStrings__TodoDatabase=Host=<postgres-host>;Port=5432;Database=planora_todo;Username=<user>;Password=<password>;SSL Mode=Require;Trust Server Certificate=false;
ConnectionStrings__CategoryDatabase=Host=<postgres-host>;Port=5432;Database=planora_category;Username=<user>;Password=<password>;SSL Mode=Require;Trust Server Certificate=false;
ConnectionStrings__MessagingDatabase=Host=<postgres-host>;Port=5432;Database=planora_messaging;Username=<user>;Password=<password>;SSL Mode=Require;Trust Server Certificate=false;
# Direct connection strings for non-Compose production deployments.
#
# Pool sizing (T4.4):
# Maximum Pool Size=10 per service × 6 services × N replicas = total connections
# against your managed Postgres. Tune up if you scale replicas, but stay
# well below the provider's max_connections (Neon free = 100, Fly Postgres
# shared = 64) to leave headroom for the migrator and autovacuum.
# Connection Idle Lifetime=60 evicts idle connections after a minute so a
# restarted Postgres does not hand back stale sockets on the next request.
ConnectionStrings__AuthDatabase=Host=<postgres-host>;Port=5432;Database=planora_auth_db;Username=<user>;Password=<password>;SSL Mode=Require;Trust Server Certificate=false;Maximum Pool Size=10;Connection Idle Lifetime=60;
ConnectionStrings__TodoDatabase=Host=<postgres-host>;Port=5432;Database=planora_todo;Username=<user>;Password=<password>;SSL Mode=Require;Trust Server Certificate=false;Maximum Pool Size=10;Connection Idle Lifetime=60;
ConnectionStrings__CategoryDatabase=Host=<postgres-host>;Port=5432;Database=planora_category;Username=<user>;Password=<password>;SSL Mode=Require;Trust Server Certificate=false;Maximum Pool Size=10;Connection Idle Lifetime=60;
ConnectionStrings__MessagingDatabase=Host=<postgres-host>;Port=5432;Database=planora_messaging;Username=<user>;Password=<password>;SSL Mode=Require;Trust Server Certificate=false;Maximum Pool Size=10;Connection Idle Lifetime=60;
ConnectionStrings__Redis=<redis-host>:6379,password=<password>,ssl=True,abortConnect=false

# RabbitMQ service credentials for non-Compose deployments
Expand Down
72 changes: 72 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/usr/bin/env bash
# Planora pre-commit hook. Runs only on the files staged for the current commit.
# Activate per-clone with `scripts/install-hooks.sh` (sets core.hooksPath = .githooks).
#
# Two cheap, locally-runnable gates:
# 1. Frontend: ESLint on staged .ts/.tsx/.js/.jsx (errors only, no auto-fix).
# 2. Backend: dotnet format --verify-no-changes on the touched solution.
#
# Both gates pass quickly on a no-op commit. Skip the hook for emergency commits
# with `git commit --no-verify`.

set -euo pipefail

repo_root="$(git rev-parse --show-toplevel)"
cd "$repo_root"

# Files staged for commit (added, copied, modified, renamed — not deleted).
mapfile -t staged < <(git diff --cached --name-only --diff-filter=ACMR)
if [[ ${#staged[@]} -eq 0 ]]; then
exit 0
fi

# --- Frontend ----------------------------------------------------------------

frontend_files=()
for f in "${staged[@]}"; do
case "$f" in
frontend/*.ts|frontend/*.tsx|frontend/*.js|frontend/*.jsx|\
frontend/**/*.ts|frontend/**/*.tsx|frontend/**/*.js|frontend/**/*.jsx)
frontend_files+=("$f")
;;
esac
done

if [[ ${#frontend_files[@]} -gt 0 ]]; then
if [[ -x frontend/node_modules/.bin/eslint ]]; then
# ESLint accepts paths relative to its CWD; strip the "frontend/" prefix.
eslint_targets=()
for f in "${frontend_files[@]}"; do
eslint_targets+=("${f#frontend/}")
done
echo "[pre-commit] eslint ${#eslint_targets[@]} staged frontend file(s)…"
( cd frontend && ./node_modules/.bin/eslint --max-warnings=0 "${eslint_targets[@]}" )
else
echo "[pre-commit] frontend/node_modules/.bin/eslint missing — run 'npm --prefix frontend install' to enable the gate. Skipping for now."
fi
fi

# --- Backend -----------------------------------------------------------------

backend_files=()
for f in "${staged[@]}"; do
case "$f" in
*.cs|*.csproj|*.props|*.sln)
backend_files+=("$f")
;;
esac
done

if [[ ${#backend_files[@]} -gt 0 ]]; then
if command -v dotnet >/dev/null 2>&1; then
echo "[pre-commit] dotnet format --verify-no-changes on Planora.sln…"
dotnet format Planora.sln --verify-no-changes --severity warn --no-restore || {
echo "[pre-commit] dotnet format reported issues. Run 'dotnet format Planora.sln' to apply, then re-stage."
exit 1
}
else
echo "[pre-commit] dotnet CLI not on PATH — skipping the backend format check."
fi
fi

exit 0
49 changes: 49 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Code-ownership policy for Planora. Until additional contributors land,
# the maintainer reviews every PR; the entries below codify which surfaces are
# *especially* sensitive (security primitives, observability pipeline, schema
# migrations, deployment manifests) so a future multi-reviewer setup keeps the
# right eyes on the right diffs.

# Default owner for everything not matched below.
* @4Keyy

# Security primitives — auth flows, JWT, CSRF, gRPC interceptors, security stamp.
BuildingBlocks/Planora.BuildingBlocks.Infrastructure/Security/ @4Keyy
BuildingBlocks/Planora.BuildingBlocks.Infrastructure/Middleware/ @4Keyy
BuildingBlocks/Planora.BuildingBlocks.Infrastructure/Grpc/ @4Keyy
BuildingBlocks/Planora.BuildingBlocks.Infrastructure/Extensions/JwtAuthenticationExtensions.cs @4Keyy
Services/AuthApi/Planora.Auth.Infrastructure/Services/Security/ @4Keyy
Services/AuthApi/Planora.Auth.Api/Filters/ @4Keyy
SECURITY.md @4Keyy
docs/auth-security.md @4Keyy
docs/secrets-management.md @4Keyy

# Observability pipeline — INV-OBS-* invariants enforced here.
BuildingBlocks/Planora.BuildingBlocks.Infrastructure/Logging/ @4Keyy
BuildingBlocks/Planora.BuildingBlocks.Infrastructure/Observability/ @4Keyy

# Outbox / inbox state machines — at-least-once delivery and idempotency.
BuildingBlocks/Planora.BuildingBlocks.Infrastructure/Outbox/ @4Keyy
BuildingBlocks/Planora.BuildingBlocks.Infrastructure/Inbox/ @4Keyy
BuildingBlocks/Planora.BuildingBlocks.Application/Outbox/ @4Keyy
BuildingBlocks/Planora.BuildingBlocks.Infrastructure/IdempotentConsumer/ @4Keyy

# Migrator + EF schema — wrong move here corrupts production __EFMigrationsHistory.
tools/Planora.Migrator/ @4Keyy
**/Migrations/ @4Keyy

# CI/CD + deployment manifests + Dockerfiles — supply-chain blast radius.
.github/workflows/ @4Keyy
.github/dependabot.yml @4Keyy
deploy/ @4Keyy
**/Dockerfile @4Keyy
docker-compose.yml @4Keyy

# Architectural truth.
docs/INVARIANTS.md @4Keyy
docs/DECISIONS/ @4Keyy
docs/ROADMAP.md @4Keyy
ARCHITECTURE.md @4Keyy

# Gateway — the only public HTTP edge.
Planora.ApiGateway/ @4Keyy
41 changes: 29 additions & 12 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ inputs.ref || github.ref }}

Expand All @@ -83,11 +83,7 @@ jobs:
fi

- name: Install flyctl
# TODO(security): pin this action to a SHA before relying on this
# workflow in production. Run:
# gh api /repos/superfly/flyctl-actions/git/refs/heads/master --jq .object.sha
# and replace `@master` with `@<sha> # master at <date>`.
uses: superfly/flyctl-actions/setup-flyctl@master
uses: superfly/flyctl-actions/setup-flyctl@ed8efb33836e8b2096c7fd3ba1c8afe303ebbff1 # 1.6

- name: Validate every fly.toml parses
shell: bash
Expand All @@ -106,12 +102,12 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ inputs.ref || github.ref }}

- name: Install flyctl
uses: superfly/flyctl-actions/setup-flyctl@master
uses: superfly/flyctl-actions/setup-flyctl@ed8efb33836e8b2096c7fd3ba1c8afe303ebbff1 # 1.6

- name: Run Planora.Migrator one-shot
shell: bash
Expand Down Expand Up @@ -159,12 +155,12 @@ jobs:
config: deploy/fly/realtime.fly.toml
dockerfile: Services/RealtimeApi/Planora.Realtime.Api/Dockerfile
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ inputs.ref || github.ref }}

- name: Install flyctl
uses: superfly/flyctl-actions/setup-flyctl@master
uses: superfly/flyctl-actions/setup-flyctl@ed8efb33836e8b2096c7fd3ba1c8afe303ebbff1 # 1.6

- name: Deploy ${{ matrix.app }}
shell: bash
Expand All @@ -184,12 +180,12 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ inputs.ref || github.ref }}

- name: Install flyctl
uses: superfly/flyctl-actions/setup-flyctl@master
uses: superfly/flyctl-actions/setup-flyctl@ed8efb33836e8b2096c7fd3ba1c8afe303ebbff1 # 1.6

- name: Deploy planora-gateway
shell: bash
Expand Down Expand Up @@ -217,6 +213,27 @@ jobs:
run: |
echo "url=https://planora-gateway.fly.dev" >> "$GITHUB_OUTPUT"

- name: Probe /health/live (liveness)
shell: bash
# Liveness must succeed within a tight window. If the gateway process is
# alive but readiness is delayed (e.g. a slow Postgres warm-up), readiness
# polling below handles that — but if /health/live itself does not respond,
# the deploy is unhealthy and we surface immediately rather than burning
# two minutes on readiness retries.
run: |
set -e
live="${{ steps.gateway.outputs.url }}/health/live"
for attempt in {1..15}; do
if curl --fail --silent --show-error --max-time 3 "${live}" > /dev/null; then
echo "::notice::Gateway /health/live is OK"
exit 0
fi
echo "Liveness attempt ${attempt}/15 — gateway process not responding"
sleep 2
done
echo "::error::Gateway /health/live did not return 200 within 30 s — deploy is broken"
exit 1

- name: Wait for /health/ready
shell: bash
run: |
Expand Down
26 changes: 17 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
name: CI
on:
push:
branches: [main, 'audit/**', 'fix/**', 'claude/**']
branches: [main, develop, 'audit/**', 'fix/**']
pull_request:
branches: [main]
branches: [main, develop]

# Minimum permissions. Each job only receives what it needs.
permissions:
Expand All @@ -19,9 +19,9 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Lint Markdown
uses: DavidAnson/markdownlint-cli2-action@992badcdf24e3b8eb7e87ff9287fe931bcb00c6e # v20
uses: DavidAnson/markdownlint-cli2-action@ded1f9488f68a970bc66ea5619e13e9b52e601cd # v23.2.0
- name: Check Markdown links
uses: lycheeverse/lychee-action@8646ba30535128ac92d33dfc9133794bfdd9b411 # v2
with:
Expand All @@ -32,14 +32,22 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5
with:
dotnet-version: '9.0.x'
dotnet-version: '10.0.x'
# CPM: hash every input that can change the restore graph (csproj graph
# + central package versions + build props). No packages.lock.json yet,
# so the action hashes these files to derive the cache key.
cache: true
cache-dependency-path: |
**/*.csproj
Directory.Packages.props
Directory.Build.props
- run: dotnet restore Planora.sln
- run: dotnet build Planora.sln --no-restore --configuration Release -warnaserror
- run: dotnet test Planora.sln --no-build --configuration Release --collect:"XPlat Code Coverage" --settings coverage.runsettings --results-directory ./coverage/backend
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: backend-coverage-report
path: ./coverage/backend
Expand All @@ -51,7 +59,7 @@ jobs:
run:
working-directory: frontend
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: '20'
Expand All @@ -62,7 +70,7 @@ jobs:
- run: npm run type-check
- run: npm run test:coverage
- run: npm run build
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: frontend-coverage-report
path: frontend/coverage
53 changes: 49 additions & 4 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: E2E
on:
workflow_dispatch:
pull_request:
branches: [main]
branches: [main, develop]
paths:
- '.github/workflows/e2e.yml'
- 'BuildingBlocks/**'
Expand All @@ -26,7 +26,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
Expand Down Expand Up @@ -83,16 +83,61 @@ jobs:
run: npm ci
working-directory: frontend

- name: Run Playwright e2e
# T2.6 — install Chromium for browser-rendered UI specs. The chromium
# binary is cached by setup-node along with the npm cache; first run
# downloads it (~150 MB), subsequent runs hit the cache.
- name: Install Playwright browsers (chromium)
run: npx playwright install --with-deps chromium
working-directory: frontend

# T2.6 — build + start the Next.js frontend so the `ui` project can
# drive a real browser against it. The dev server is intentionally not
# used: dev mode has different hydration timing and Webpack overlays
# that interfere with Playwright.
- name: Build frontend
run: npm run build
working-directory: frontend
env:
NEXT_PUBLIC_API_URL: http://127.0.0.1:5132

- name: Start frontend (background) and wait for readiness
shell: bash
working-directory: frontend
run: |
NEXT_PUBLIC_API_URL=http://127.0.0.1:5132 \
nohup npm run start -- -p 3000 > ../frontend-server.log 2>&1 &
echo $! > ../frontend.pid
for attempt in {1..60}; do
if curl --fail --silent --show-error "http://127.0.0.1:3000" > /dev/null; then
echo "Frontend ready after ${attempt} attempts"
exit 0
fi
sleep 2
done
echo "Frontend failed to start; tailing log:"
tail -200 ../frontend-server.log
exit 1

- name: Run Playwright e2e (api + ui projects)
run: npm run e2e
working-directory: frontend
env:
E2E_API_URL: http://127.0.0.1:5132
E2E_FRONTEND_URL: http://127.0.0.1:3000
E2E_AUTH_LOG_CONTAINER: planora-auth-api

- name: Stop frontend
if: always()
shell: bash
run: |
if [ -f frontend.pid ]; then
pid=$(cat frontend.pid)
kill "$pid" 2>/dev/null || true
fi

- name: Upload Playwright report
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: playwright-report
path: |
Expand Down
Loading
Loading