version: '3' output: prefixed vars: FIND_FREE_PORT_PS: powershell -NoProfile -File scripts/find-free-port.ps1 FIND_FREE_PORT_SH: bash scripts/find-free-port.sh includes: backend: taskfile: .taskfiles/backend.yml dir: . frontend: taskfile: .taskfiles/frontend.yml dir: frontend engine: taskfile: .taskfiles/engine.yml dir: engine docker: taskfile: .taskfiles/docker.yml dir: . desktop: taskfile: .taskfiles/desktop.yml dir: frontend e2e: taskfile: .taskfiles/e2e.yml dir: . cucumber: taskfile: .taskfiles/cucumber.yml dir: testing/cucumber pre-commit: taskfile: .taskfiles/pre-commit.yml dir: . tasks: # ============================================================ # Help (shown when you run `task` with no arguments) # ============================================================ default: desc: "List the most common commands" silent: true cmds: - | echo "Common commands (run 'task --list' to see all):" echo "" echo " task dev Start backend & frontend on free ports" echo " task backend:dev Start backend on default port" echo " task frontend:dev Start frontend on default port" echo " task desktop:dev Start desktop app" echo " task check Quality gate (lint, typecheck, test, etc.)" # ============================================================ # Setup & Prerequisites # ============================================================ install: desc: "Install all project dependencies" cmds: - task: frontend:install - task: engine:install # ============================================================ # Development # ============================================================ dev: desc: "Start backend + frontend concurrently on free ports" vars: PORTS: sh: '{{if eq OS "windows"}}{{.FIND_FREE_PORT_PS}} 8080 5173{{else}}{{.FIND_FREE_PORT_SH}} 8080 5173{{end}}' BACKEND_PORT: '{{index (splitList "\n" .PORTS) 0}}' FRONTEND_PORT: '{{index (splitList "\n" .PORTS) 1}}' deps: - task: backend:dev vars: PORT: '{{.BACKEND_PORT}}' - task: frontend:dev vars: PORT: '{{.FRONTEND_PORT}}' BACKEND_URL: 'http://localhost:{{.BACKEND_PORT}}' OPEN: "true" dev:portal: desc: "Start backend + editor; the portal is an admin route at /portal" vars: PORTS: sh: '{{if eq OS "windows"}}{{.FIND_FREE_PORT_PS}} 8080 5173{{else}}{{.FIND_FREE_PORT_SH}} 8080 5173{{end}}' BACKEND_PORT: '{{index (splitList "\n" .PORTS) 0}}' EDITOR_PORT: '{{index (splitList "\n" .PORTS) 1}}' deps: - task: backend:dev vars: PORT: '{{.BACKEND_PORT}}' SECURITY_ENABLELOGIN: "true" - task: frontend:dev:proprietary vars: PORT: '{{.EDITOR_PORT}}' BACKEND_URL: 'http://localhost:{{.BACKEND_PORT}}' OPEN: "true" # Set SAAS_DEV_PROJECT_REF in app/.env.saas.local to pick the PR. dev:saas: desc: "Start SaaS backend + frontend + engine against the current PR's preview branch" cmds: - task: dev:_all vars: { FRONTEND: saas, BACKEND: saas, SAAS_ENV: dev } staging:saas: desc: "Start SaaS backend + frontend + engine against the shared v3 staging project" cmds: - task: dev:_all vars: FRONTEND: saas BACKEND: saas BACKEND_TASK: backend:staging:saas SAAS_ENV: staging dev:all: desc: "Start backend + frontend + engine concurrently on free ports" cmds: - task: dev:_all # No engine: linking never calls it. linked:staging: desc: "SaaS on the shared v3 project + a self-hosted instance linked to it" cmds: - task: linked:_all vars: { SAAS_ENV: staging } linked:dev: desc: "SaaS on the current PR's preview branch + a self-hosted instance linked to it" cmds: - task: linked:_all vars: { SAAS_ENV: dev } linked:_all: internal: true vars: SAAS_ENV: '{{.SAAS_ENV | default "staging"}}' PORTS: sh: '{{if eq OS "windows"}}{{.FIND_FREE_PORT_PS}} 8081 5174 8080 5173{{else}}{{.FIND_FREE_PORT_SH}} 8081 5174 8080 5173{{end}}' SAAS_BACKEND_PORT: '{{index (splitList "\n" .PORTS) 0}}' SAAS_FRONTEND_PORT: '{{index (splitList "\n" .PORTS) 1}}' APP_BACKEND_PORT: '{{index (splitList "\n" .PORTS) 2}}' APP_FRONTEND_PORT: '{{index (splitList "\n" .PORTS) 3}}' deps: # APP_BASE_URL is the SaaS *frontend*: the approval page is served by vite, not # by the API. BASE_PATH moves this backend's configs/pipeline aside so it does not # race the self-hosted one, which keeps ./configs and its existing database. - task: 'backend:{{.SAAS_ENV}}:saas' vars: PORT: '{{.SAAS_BACKEND_PORT}}' APP_BASE_URL: 'http://localhost:{{.SAAS_FRONTEND_PORT}}' BASE_PATH: 'tmp/linked-saas' - task: frontend:dev:saas vars: PORT: '{{.SAAS_FRONTEND_PORT}}' BACKEND_URL: 'http://localhost:{{.SAAS_BACKEND_PORT}}' SAAS_ENV: '{{.SAAS_ENV}}' - task: backend:dev:linked vars: PORT: '{{.APP_BACKEND_PORT}}' SAAS_BASE_URL: 'http://localhost:{{.SAAS_BACKEND_PORT}}' - task: frontend:dev:proprietary vars: PORT: '{{.APP_FRONTEND_PORT}}' BACKEND_URL: 'http://localhost:{{.APP_BACKEND_PORT}}' OPEN: "true" - task: linked:_ready vars: SAAS_BACKEND_PORT: '{{.SAAS_BACKEND_PORT}}' SAAS_FRONTEND_PORT: '{{.SAAS_FRONTEND_PORT}}' APP_BACKEND_PORT: '{{.APP_BACKEND_PORT}}' APP_FRONTEND_PORT: '{{.APP_FRONTEND_PORT}}' # Waits for all four to answer, then prints where they landed. linked:_ready: internal: true cmds: - cmd: | n=0 ok=0 while [ "$n" -lt 150 ]; do ok=1 for u in "http://localhost:{{.SAAS_BACKEND_PORT}}" \ "http://localhost:{{.SAAS_FRONTEND_PORT}}" \ "http://localhost:{{.APP_BACKEND_PORT}}" \ "http://localhost:{{.APP_FRONTEND_PORT}}"; do # Not -o /dev/null: Windows curl.exe treats it as a real path and exits 23. curl -s -m 2 "$u" >/dev/null 2>&1 || ok=0 done if [ "$ok" = 1 ]; then break; fi n=$((n + 1)) # `sleep` is a binary, not a builtin, and Windows has none. {{if eq OS "windows"}}powershell -NoProfile -Command "Start-Sleep -Seconds 2"{{else}}sleep 2{{end}} done echo "" if [ "$ok" = 1 ]; then echo ">> all four answering" else echo ">> still waiting on one or more after 5 minutes; addresses below anyway" fi echo ">> self-hosted UI http://localhost:{{.APP_FRONTEND_PORT}}/processor" echo ">> self-hosted api http://localhost:{{.APP_BACKEND_PORT}}" echo ">> saas UI http://localhost:{{.SAAS_FRONTEND_PORT}}" echo ">> saas api http://localhost:{{.SAAS_BACKEND_PORT}}" echo "" dev:_all: internal: true vars: FRONTEND: '{{.FRONTEND | default "proprietary"}}' BACKEND: '{{.BACKEND | default "proprietary"}}' BACKEND_TASK: '{{.BACKEND_TASK | default (printf "backend:dev:%s" .BACKEND)}}' # Only meaningful to the saas frontend; every other flavor ignores it. SAAS_ENV: '{{.SAAS_ENV | default ""}}' PORTS: sh: '{{if eq OS "windows"}}{{.FIND_FREE_PORT_PS}} 8080 5173 5001{{else}}{{.FIND_FREE_PORT_SH}} 8080 5173 5001{{end}}' BACKEND_PORT: '{{index (splitList "\n" .PORTS) 0}}' FRONTEND_PORT: '{{index (splitList "\n" .PORTS) 1}}' ENGINE_PORT: '{{index (splitList "\n" .PORTS) 2}}' deps: - task: engine:dev vars: PORT: '{{.ENGINE_PORT}}' - task: '{{.BACKEND_TASK}}' vars: PORT: '{{.BACKEND_PORT}}' AIENGINE_URL: 'http://localhost:{{.ENGINE_PORT}}' AIENGINE_ENABLED: "true" - task: 'frontend:dev:{{.FRONTEND}}' vars: PORT: '{{.FRONTEND_PORT}}' BACKEND_URL: 'http://localhost:{{.BACKEND_PORT}}' OPEN: "true" SAAS_ENV: '{{.SAAS_ENV}}' # ============================================================ # Build # ============================================================ build: desc: "Build all components" cmds: - task: backend:build - task: frontend:build # ============================================================ # Test # ============================================================ test: desc: "Run ALL tests (backend + frontend + engine)" cmds: - task: backend:test - task: frontend:test - task: engine:test # ============================================================ # Lint & Format # ============================================================ lint: desc: "Run all linters" cmds: - task: frontend:lint - task: engine:lint fix: desc: "Auto-fix all components" cmds: - task: backend:fix - task: frontend:fix - task: engine:fix format: desc: "Auto-fix formatting across all components" cmds: - task: backend:format - task: frontend:format - task: engine:format format:check: desc: "Check formatting across all components" cmds: - task: backend:format:check - task: frontend:format:check - task: engine:format:check # ============================================================ # Code generation # ============================================================ tool-models: desc: "Generate all API models from the Java OpenAPI spec" cmds: - task: frontend:tool-models - task: engine:tool-models tool-models:check: desc: "Fail if any committed API model is out of date" cmds: - task: frontend:tool-models:check - task: engine:tool-models:check # ============================================================ # Quality Gate # ============================================================ check: desc: "Quick quality gate for local development" cmds: - task: backend:check - task: frontend:check - task: engine:check check:all: desc: "Full CI quality gate" cmds: - task: backend:check - task: frontend:check:all - task: engine:check # ============================================================ # Clean # ============================================================ clean: desc: "Clean all build artifacts" cmds: - task: backend:clean - task: frontend:clean - task: engine:clean - task: pre-commit:clean