From a6a3084acd6aa683380255f7e6d48ced6663611b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20M=C3=B6rling?= Date: Mon, 1 Jun 2026 12:42:27 +0200 Subject: [PATCH] Parallelize check across Gradle subprojects for faster pre-commit. Run :backend:check, frontend coverage, and :e2e:check as sibling tasks with org.gradle.parallel=true. Move E2E Docker compose into an e2e subproject so Playwright can start while unit tests run. Copy e2e/ in the E2E backend image so settings.gradle resolves inside Docker. Co-authored-by: Cursor --- AGENTS.md | 14 ++++++++++---- build.gradle | 28 +++++++++++++--------------- docker/backend.e2e.Dockerfile | 1 + e2e/build.gradle | 14 ++++++++++++++ gradle.properties | 1 + scripts/pre-commit-check.sh | 2 +- settings.gradle | 2 +- 7 files changed, 41 insertions(+), 21 deletions(-) create mode 100644 e2e/build.gradle create mode 100644 gradle.properties diff --git a/AGENTS.md b/AGENTS.md index e04a4fc..04b53a4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -37,7 +37,7 @@ docker compose up -d # starts postgres, backend, frontend ### All-in-one ```bash -./gradlew check # lint → frontend/backend tests with coverage thresholds → E2E (Docker) +./gradlew check # backend, frontend coverage, E2E (sibling tasks + org.gradle.parallel) ./gradlew coverage # backend + frontend tests with coverage reports ./gradlew up # docker compose up -d ./gradlew down # docker compose down @@ -170,9 +170,15 @@ export STRIPE_SECRET_KEY=sk_test_fake STRIPE_WEBHOOK_SECRET=whsec_fake STRIPE_PR ./gradlew check ``` -This runs frontend lint, frontend unit tests **with Vitest coverage thresholds** -(70% lines, 60% branches, 70% functions), backend tests with **JaCoCo thresholds** -(70% lines, 60% branches), Flyway checks, and **all 90 E2E tests in Docker**. +`check` depends on `:backend:check`, `frontendCoverage` (root), and `:e2e:check` +as **siblings** with `org.gradle.parallel=true`. Gradle parallelizes **across +subprojects**, not multiple tasks in one project — E2E is in `e2e/` so Docker can +start while backend tests and frontend Vitest run. Within root, `frontendLint` → +`frontendCoverage` stays serial. Measured `unitAndCoverage --rerun-tasks`: ~30s +parallel on vs ~36s off. Full `check` wall time ≈ `max(:backend, frontend, :e2e)`; +E2E (~3 min) is usually the limit. +Forgejo CI already runs unit and E2E as separate workflow jobs in parallel. +Quick path without E2E: `./gradlew unitAndCoverage`. **Do not commit or push if this fails.** Optional local guard: `./scripts/install-pre-commit-hook.sh` (runs the same `check` on every `git commit`; fails if line or branch coverage is below threshold). diff --git a/build.gradle b/build.gradle index 0e99948..d34b1c0 100644 --- a/build.gradle +++ b/build.gradle @@ -28,25 +28,23 @@ tasks.register('coverage') { dependsOn(':backend:jacocoTestReport', 'frontendCoverage') } -tasks.register('frontendE2E', Exec) { +// E2E lives in :e2e subproject so Gradle can run it parallel to root + :backend. + +tasks.register('frontendE2E', Task) { group = 'verification' - description = 'Run Playwright E2E tests in Docker (same stack as Forgejo CI)' - dependsOn frontendCoverage - workingDir = rootProject.projectDir - environment 'POSTGRES_DB', 'bilhej' - environment 'POSTGRES_USER', 'bilhej' - environment 'POSTGRES_PASSWORD', 'test_pw_ci_123' - environment 'JWT_SECRET', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' - environment 'STRIPE_SECRET_KEY', 'sk_test_fake' - environment 'STRIPE_WEBHOOK_SECRET', 'whsec_fake' - environment 'STRIPE_PRICE_ID', 'price_fake' - commandLine 'docker', 'compose', '-f', 'docker-compose.e2e.yml', - 'up', '--build', '--abort-on-container-exit', '--exit-code-from', 'playwright' + description = 'Alias for :e2e:check (Playwright in Docker)' + dependsOn ':e2e:check' +} + +tasks.register('unitAndCoverage', Task) { + group = 'verification' + description = 'Backend + frontend unit tests with coverage thresholds (no E2E)' + dependsOn ':backend:check', 'frontendCoverage' } tasks.named('check').configure { - description = 'Full verification: lint, unit tests with line/branch coverage thresholds, E2E' - dependsOn ':backend:check', frontendE2E + description = 'Full verification: :backend, frontend coverage, and :e2e in parallel' + dependsOn ':backend:check', 'frontendCoverage', ':e2e:check' } tasks.register('up', Exec) { diff --git a/docker/backend.e2e.Dockerfile b/docker/backend.e2e.Dockerfile index bd4720f..84f3df4 100644 --- a/docker/backend.e2e.Dockerfile +++ b/docker/backend.e2e.Dockerfile @@ -3,6 +3,7 @@ WORKDIR /app COPY gradlew settings.gradle ./ COPY gradle/wrapper/ gradle/wrapper/ COPY backend/build.gradle backend/ +COPY e2e/build.gradle e2e/ RUN chmod +x gradlew && ./gradlew :backend:dependencies --no-daemon -q COPY backend/src backend/src RUN ./gradlew :backend:bootJar --no-daemon -q diff --git a/e2e/build.gradle b/e2e/build.gradle new file mode 100644 index 0000000..9f27f3d --- /dev/null +++ b/e2e/build.gradle @@ -0,0 +1,14 @@ +tasks.register('check', Exec) { + group = 'verification' + description = 'Playwright E2E stack in Docker (parallel with :backend and root frontend tasks)' + workingDir = rootProject.projectDir + environment 'POSTGRES_DB', 'bilhej' + environment 'POSTGRES_USER', 'bilhej' + environment 'POSTGRES_PASSWORD', 'test_pw_ci_123' + environment 'JWT_SECRET', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + environment 'STRIPE_SECRET_KEY', 'sk_test_fake' + environment 'STRIPE_WEBHOOK_SECRET', 'whsec_fake' + environment 'STRIPE_PRICE_ID', 'price_fake' + commandLine 'docker', 'compose', '-f', 'docker-compose.e2e.yml', + 'up', '--build', '--abort-on-container-exit', '--exit-code-from', 'playwright' +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..f97ebb7 --- /dev/null +++ b/gradle.properties @@ -0,0 +1 @@ +org.gradle.parallel=true diff --git a/scripts/pre-commit-check.sh b/scripts/pre-commit-check.sh index 401eee0..affb29f 100755 --- a/scripts/pre-commit-check.sh +++ b/scripts/pre-commit-check.sh @@ -19,7 +19,7 @@ export STRIPE_SECRET_KEY="${STRIPE_SECRET_KEY:-sk_test_fake}" export STRIPE_WEBHOOK_SECRET="${STRIPE_WEBHOOK_SECRET:-whsec_fake}" export STRIPE_PRICE_ID="${STRIPE_PRICE_ID:-price_fake}" -echo "pre-commit: running ./gradlew check (lint, coverage thresholds, E2E in Docker)..." +echo "pre-commit: running ./gradlew check (lint, coverage thresholds, E2E)..." if ! ./gradlew check --no-daemon; then echo "" echo "pre-commit: FAILED." diff --git a/settings.gradle b/settings.gradle index 2a2d33a..3a3e0e0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,3 @@ rootProject.name = 'bilhej' -include 'backend' +include 'backend', 'e2e'