mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-14 10:34:06 +02:00
## Move editor under `frontend/editor/`
Pure restructure: `frontend/` becomes the workspace, `frontend/editor/`
holds
the PDF editor. 1775 file renames + 40 wiring edits. No logic changes.
### Why
`frontend/` is currently the editor — its `src/`, `public/`,
`src-tauri/`,
config files all sit at the root. Promoting `frontend/` to a
workspace and putting the editor in a sibling folder leaves room for
future
apps to drop in alongside it, sharing one `package.json` /
`node_modules` /
lint config / Storybook.
### What moves
frontend/
├── editor/ ← NEW: everything editor-specific
│ ├── src/ ← was frontend/src/
│ ├── public/ ← was frontend/public/
│ ├── src-tauri/ ← was frontend/src-tauri/
│ ├── index.html, vite.config.ts, vitest.config.ts, playwright.config.ts
│ ├── tsconfig*.json, tailwind.config.js, postcss.config.js
│ ├── scripts/
│ ├── .env, .env.desktop, .env.saas
│ └── DeveloperGuide.md
├── package.json, package-lock.json, node_modules/ ← workspace install
├── eslint.config.mjs, .prettierrc, .prettierignore ← shared tooling
├── .gitignore
└── README.md
### Wiring edits (40 files)
- `.taskfiles/frontend.yml`, `desktop.yml`, `e2e.yml`
- `build.gradle`, `app/core/build.gradle`
- `eslint.config.mjs`, `frontend/package.json`, `.gitignore`,
`.prettierignore`
- `docker/frontend/Dockerfile`
- 8 `.github/workflows/*.yml`, plus `.github/dependabot.yml`,
`.github/config/.files.yaml`, `.github/labeler-config-srvaroa.yml`
- `scripts/translations/**`
- Docs: `AGENTS.md`, `CLAUDE.md`, `ADDING_TOOLS.md`,
`DeveloperGuide.md`,
`WINDOWS_SIGNING.md`, `devGuide/HowToAddNewLanguage.md`,
`frontend/README.md`,
`frontend/editor/DeveloperGuide.md`
Plus 3 renamed + edited: `editor/vite.config.ts` (env path +
node_modules
walk-up), `editor/scripts/setup-env.mts` (renamed from `.ts` for
`import.meta.url`), `editor/scripts/build-provisioner.mjs` (resolve
src-tauri
relative to script).
### Verification
| Check | Result |
|---|---|
| `task frontend:typecheck:all` (6 variants) | exit 0 |
| `task frontend:lint` (eslint + dpdm) | exit 0 |
| `task frontend:format:check` | exit 0 |
| `task frontend:test` | 657 tests pass, 50 files |
| `task frontend:build:{core,proprietary,saas,desktop,prototypes}` | all
green |
| `task desktop:build` | full Tauri pipeline →
`Stirling-PDF_2.11.0_x64_en-US.msi` |
| `playwright test --list --project=stubbed` | 172 tests discovered |
`task desktop:build` exercises the heaviest path — Rust + WiX + MSI
bundle
against the moved `editor/src-tauri/`. If anything in the restructure
was
wrong it wouldn't have built.
### Test plan
- [ ] `frontend-validation.yml` green
- [ ] `e2e-stubbed.yml` green
- [ ] `tauri-build.yml` green on at least one platform
- [ ] `check_toml.yml` runs on a translation-touching PR
---------
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
205 lines
7.5 KiB
YAML
205 lines
7.5 KiB
YAML
version: '3'
|
|
|
|
tasks:
|
|
install:
|
|
desc: "Install Playwright browsers"
|
|
dir: frontend/editor
|
|
deps: [ ':frontend:install' ]
|
|
cmds:
|
|
- npx playwright install {{.CLI_ARGS}} --with-deps
|
|
|
|
stubbed:
|
|
desc: "Run stubbed E2E tests"
|
|
dir: frontend/editor
|
|
deps: [ ':frontend:prepare' ]
|
|
cmds:
|
|
- npx playwright test --project=stubbed {{.CLI_ARGS}}
|
|
|
|
live:
|
|
desc: "Run live E2E tests"
|
|
summary: |
|
|
Auto-spawns a Spring Boot backend with isolated state under
|
|
.test-state/playwright/ (purged on every run) so the suite doesn't
|
|
touch your local DB, settings.yml, backups or customFiles. The
|
|
backend is stopped automatically when the runner exits.
|
|
|
|
Pass extra Playwright flags via -- :
|
|
task e2e:live -- --headed --grep merge
|
|
deps:
|
|
- live:backend
|
|
- live:runner
|
|
|
|
live:backend:
|
|
internal: true
|
|
ignore_error: true
|
|
vars:
|
|
BASE_DIR: '{{.ROOT_DIR}}/.test-state/playwright'
|
|
env:
|
|
STIRLING_BASE_PATH: '{{.BASE_DIR}}'
|
|
# Suppress the analytics opt-in modal that fires on first admin login.
|
|
# It renders a Mantine overlay that intercepts pointer events and blocks
|
|
# FirstLoginSlide from rendering, so the bootstrap spec times out waiting
|
|
# for the password-change prompt.
|
|
SYSTEM_ENABLEANALYTICS: "false"
|
|
# NOTE: SECURITY_INITIALLOGIN_USERNAME/PASSWORD are intentionally NOT set.
|
|
# The live-setup project's bootstrap spec performs the real first-login
|
|
# flow against the backend's default admin/stirling user, exercising the
|
|
# forced-password-change UI and leaving the DB at admin/adminadmin for
|
|
# the rest of the live suite. This is both real coverage of the first-
|
|
# login flow and a stronger seed than env-var-driven user creation.
|
|
cmds:
|
|
# Wrapped in `bash -c` because Task's embedded shell (mvdan/sh) doesn't
|
|
# support `&` + `$!` + `wait` reliably (gradle was never actually
|
|
# backgrounded, so `$!` recorded a stale PID and the runner saw an
|
|
# immediate "backend died"). Real bash backgrounds gradle properly and
|
|
# gives us a stable PID to clean up.
|
|
- |
|
|
bash -c '
|
|
set -e
|
|
rm -rf "{{.BASE_DIR}}"
|
|
mkdir -p "{{.BASE_DIR}}"
|
|
# Background gradle and record its PID so the runner can clean up
|
|
# the exact process tree (wrapper + forked Spring Boot JVM) without
|
|
# resorting to fuzzy `pkill -f` patterns. `wait` keeps this script
|
|
# alive for the lifetime of gradle so Task'"'"'s parallel deps stay
|
|
# synchronised.
|
|
bash gradlew :stirling-pdf:bootRun > "{{.BASE_DIR}}/backend.log" 2>&1 &
|
|
GRADLE_PID=$!
|
|
echo $GRADLE_PID > "{{.BASE_DIR}}/backend.pid"
|
|
wait $GRADLE_PID
|
|
'
|
|
|
|
live:runner:
|
|
internal: true
|
|
deps: [ ':frontend:prepare' ]
|
|
dir: frontend/editor
|
|
vars:
|
|
BASE_DIR: '{{.ROOT_DIR}}/.test-state/playwright'
|
|
cmds:
|
|
# Wrapped in `bash -c` because Task's embedded shell (mvdan/sh) only
|
|
# supports `EXIT`/`ERR` in `trap`, not `INT`/`TERM`. CLI_ARGS are
|
|
# passed positionally so quoted args like --grep "foo bar" survive.
|
|
- |
|
|
bash -c '
|
|
set +e
|
|
# bootRun forks a separate Spring Boot JVM as a child of the gradle
|
|
# wrapper; killing only the wrapper leaves that JVM orphaned holding
|
|
# :8080. Walk the recorded PID'"'"'s process tree via pgrep so we kill
|
|
# every descendant, then lsof as a last-resort backstop.
|
|
kill_tree() {
|
|
local pid=$1
|
|
for child in $(pgrep -P "$pid" 2>/dev/null); do
|
|
kill_tree "$child"
|
|
done
|
|
kill "$pid" 2>/dev/null || true
|
|
}
|
|
cleanup() {
|
|
if [ -f "{{.BASE_DIR}}/backend.pid" ]; then
|
|
echo "Stopping backend..."
|
|
kill_tree "$(cat "{{.BASE_DIR}}/backend.pid")"
|
|
sleep 1
|
|
lsof -ti:8080 2>/dev/null | xargs kill 2>/dev/null || true
|
|
echo "Backend stopped"
|
|
fi
|
|
}
|
|
# Trap so cleanup runs on every exit path: tests pass, tests fail,
|
|
# backend never starts, user Ctrl-Cs. Without this the gradle JVM
|
|
# is left orphaned holding :8080 whenever the wait loop times out.
|
|
trap cleanup EXIT INT TERM
|
|
|
|
echo "Waiting for backend on :8080 (up to 10min; first compile can be slow)..."
|
|
WAITED=0
|
|
while [ $WAITED -lt 600 ]; do
|
|
if curl -sf http://localhost:8080/api/v1/info/status 2>/dev/null | grep -q UP; then
|
|
echo "Backend ready, starting Playwright"
|
|
break
|
|
fi
|
|
# Bail early if the backend already died: no point waiting 10min.
|
|
if [ -f "{{.BASE_DIR}}/backend.pid" ]; then
|
|
BACKEND_PID=$(cat "{{.BASE_DIR}}/backend.pid")
|
|
if ! kill -0 "$BACKEND_PID" 2>/dev/null; then
|
|
echo "Backend process exited before becoming ready; last 100 log lines:"
|
|
tail -100 "{{.BASE_DIR}}/backend.log" 2>/dev/null || true
|
|
exit 1
|
|
fi
|
|
fi
|
|
sleep 2
|
|
WAITED=$((WAITED + 2))
|
|
done
|
|
if [ $WAITED -ge 600 ]; then
|
|
echo "Backend did not become ready within 10min, aborting"
|
|
exit 1
|
|
fi
|
|
|
|
npx playwright test --project=live "$@"
|
|
' bash {{.CLI_ARGS}}
|
|
|
|
enterprise:
|
|
desc: "Run enterprise E2E tests"
|
|
summary: |
|
|
Requires an already-running keycloak compose stack on :8080. Bring one
|
|
up first with:
|
|
task e2e:oauth:up
|
|
task e2e:saml:up
|
|
dir: frontend/editor
|
|
deps: [ ':frontend:prepare' ]
|
|
cmds:
|
|
- npx playwright test --project=enterprise {{.CLI_ARGS}}
|
|
|
|
cross-browser:
|
|
desc: "Run stubbed E2E tests on Chromium, Firefox, and WebKit"
|
|
dir: frontend/editor
|
|
deps: [ ':frontend:prepare' ]
|
|
cmds:
|
|
- npx playwright test --project=stubbed --project=stubbed-firefox --project=stubbed-webkit {{.CLI_ARGS}}
|
|
|
|
check:
|
|
desc: "Run all E2E tests not requiring Docker"
|
|
cmds:
|
|
- task: stubbed
|
|
- task: live
|
|
|
|
check:all:
|
|
desc: "Run all E2E tests"
|
|
summary: |
|
|
Includes the enterprise project, which requires a keycloak compose
|
|
stack on :8080. Bring one up first with:
|
|
task e2e:oauth:up
|
|
task e2e:saml:up
|
|
cmds:
|
|
- task: stubbed
|
|
- task: live
|
|
- task: enterprise
|
|
|
|
oauth:up:
|
|
desc: "Start the OAuth keycloak test environment"
|
|
summary: |
|
|
Set LICENSE_KEY=<KEY> to skip the interactive license prompt:
|
|
task e2e:oauth:up LICENSE_KEY=abc123
|
|
Pass extra flags via -- :
|
|
task e2e:oauth:up -- --auto --nobuild
|
|
ignore_error: true
|
|
cmds:
|
|
- bash testing/compose/start-oauth-test.sh {{if .LICENSE_KEY}}--license-key "{{.LICENSE_KEY}}"{{end}} {{.CLI_ARGS}}
|
|
|
|
oauth:down:
|
|
desc: "Stop the OAuth keycloak test environment"
|
|
cmds:
|
|
- docker compose -f testing/compose/docker-compose-keycloak-oauth.yml down -v
|
|
|
|
saml:up:
|
|
desc: "Start the SAML keycloak test environment"
|
|
summary: |
|
|
Set LICENSE_KEY=<KEY> to skip the interactive license prompt:
|
|
task e2e:saml:up LICENSE_KEY=abc123
|
|
Pass extra flags via -- :
|
|
task e2e:saml:up -- --auto --with-storage --nobuild --language sv-SE
|
|
ignore_error: true
|
|
cmds:
|
|
- bash testing/compose/start-saml-test.sh {{if .LICENSE_KEY}}--license-key "{{.LICENSE_KEY}}"{{end}} {{.CLI_ARGS}}
|
|
|
|
saml:down:
|
|
desc: "Stop the SAML keycloak test environment"
|
|
cmds:
|
|
- docker compose -f testing/compose/docker-compose-keycloak-saml.yml down -v
|