From b146d9994d0c7a22381665165de83e932a5ad9a6 Mon Sep 17 00:00:00 2001 From: ConnorYoh <40631091+ConnorYoh@users.noreply.github.com> Date: Wed, 20 May 2026 13:57:41 +0100 Subject: [PATCH] fix(task): make `task dev` / `task dev:all` work on Windows (#6392) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary `#6145` (port picker) and `#6244` (gradle unification) combined to break `task dev` / `task dev:all` on Windows. Two independent regressions, both addressed here. ### 1. `find-free-port.ps1` panic (`#6145`) ``` $ task --dry dev The argument 'scriptsfind-free-port.ps1' to the -File parameter does not exist. panic: ended up with a non-nil exitStatus.err but a zero exitStatus.code ``` - **Backslash stripped.** `Taskfile.yml` had `scripts\find-free-port.ps1`. go-task pipes the `sh:` block through mvdan/sh, which treats `\` as a POSIX escape and silently drops it, leaving `scriptsfind-free-port.ps1`. PowerShell can't find the file, exits non-zero, mvdan/sh panics on the inconsistent exit status. Switched to a forward slash. - **`-Preferred 8080,5173` is brittle.** Relies on PowerShell parsing the comma-list into `[int[]]`. Dropped the named flag; switched the script's `param` to `[Parameter(ValueFromRemainingArguments = $true)][int[]]$Preferred`; pass each port as its own positional token (matching how the bash variant is called). ### 2. `bash gradlew` can't find Java on Windows (`#6244`) ``` [backend:dev] ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. ``` `#6244` unified all gradle invocations under `bash gradlew` on the assumption that Git-Bash inherits Windows-side env. It doesn't (and neither does WSL bash, which can also shadow `bash` on a developer's PATH). The standard Adoptium/Temurin installer sets `JAVA_HOME` in the Windows env only, so `bash gradlew` fails before Spring Boot even loads. Restored the per-platform branch for every backend task: `cmd /c ".\gradlew.bat ..."` on Windows, `./gradlew ...` on Linux/macOS. Two Windows-specific gotchas to be aware of for future edits: - mvdan/sh strips `\` outside of double quotes, so `cmd /c .\gradlew.bat` ends up as `.gradlew.bat`. The entire payload must be wrapped in double quotes (`cmd /c "..."`). - Modern Windows excludes cwd from cmd.exe's search path, so the leading `.\` is required — bare `cmd /c gradlew.bat` errors with "is not recognized" even from the repo root. ## Test plan - [x] `task --dry dev` / `task --dry dev:all` on Windows resolve ports cleanly and dispatch the inner tasks - [x] `powershell -NoProfile -File scripts/find-free-port.ps1 8080 5173 5001` prints three ports - [x] `task backend:dev PORT=8081` on Windows starts Spring Boot in ~9s; `/api/v1/info/status` returns `{"status":"UP"}` - [x] `task dev:all` on Windows brings up all three services healthy: backend `/api/v1/info/status` 200, engine `/health` 200, frontend `/` 200 - [ ] Linux / macOS path unaffected (only the Windows branches changed in behaviour) --- .taskfiles/backend.yml | 81 +++++++++++++++++++++++++++++++------- Taskfile.yml | 6 +-- scripts/find-free-port.ps1 | 2 +- 3 files changed, 70 insertions(+), 19 deletions(-) diff --git a/.taskfiles/backend.yml b/.taskfiles/backend.yml index 4fdc50a16..56db8eb4a 100644 --- a/.taskfiles/backend.yml +++ b/.taskfiles/backend.yml @@ -1,7 +1,19 @@ version: '3' -# All gradle invocations go through `bash gradlew` (the sh wrapper) so they work -# uniformly on Linux/macOS (native bash) and Windows-with-Git-Bash. +# Gradle invocation strategy: +# - Linux/macOS: `./gradlew` runs the POSIX shell wrapper natively. +# - Windows: `cmd /c ".\gradlew.bat ..."` invokes the .bat wrapper through +# cmd.exe so it inherits the user's Windows-side `JAVA_HOME` +# and PATH. Routing through `bash gradlew` on Windows ends up +# under WSL or Git-Bash, neither of which inherits +# Adoptium/Temurin's default Windows-only Java env - gradlew +# then errors with "JAVA_HOME is not set and no 'java' command +# could be found". +# +# The entire `.\gradlew.bat ...` payload is double-quoted so +# the leading `.\` survives mvdan/sh's POSIX backslash +# stripping; cmd.exe also requires `.\` (not bare `gradlew.bat`) +# because modern Windows excludes cwd from cmd's search path. tasks: dev: @@ -13,43 +25,67 @@ tasks: env: SERVER_PORT: '{{.PORT}}' cmds: - - '{{if .AIENGINE_URL}}AIENGINE_URL={{.AIENGINE_URL}} AIENGINE_ENABLED=true {{end}}bash gradlew :stirling-pdf:bootRun' + - cmd: '{{if .AIENGINE_URL}}AIENGINE_URL={{.AIENGINE_URL}} AIENGINE_ENABLED=true {{end}}cmd /c ".\gradlew.bat :stirling-pdf:bootRun"' + platforms: [windows] + - cmd: '{{if .AIENGINE_URL}}AIENGINE_URL={{.AIENGINE_URL}} AIENGINE_ENABLED=true {{end}}./gradlew :stirling-pdf:bootRun' + platforms: [linux, darwin] dev:bundled: desc: "Clean + bootRun with frontend bundled into the backend (single :8080 server)" ignore_error: true cmds: - - bash gradlew clean bootRun -PbuildWithFrontend=true + - cmd: cmd /c ".\gradlew.bat clean bootRun -PbuildWithFrontend=true" + platforms: [windows] + - cmd: ./gradlew clean bootRun -PbuildWithFrontend=true + platforms: [linux, darwin] build: desc: "Full backend build" cmds: - - bash gradlew clean build + - cmd: cmd /c ".\gradlew.bat clean build" + platforms: [windows] + - cmd: ./gradlew clean build + platforms: [linux, darwin] build:fast: desc: "Build without tests" cmds: - - bash gradlew clean build -x test + - cmd: cmd /c ".\gradlew.bat clean build -x test" + platforms: [windows] + - cmd: ./gradlew clean build -x test + platforms: [linux, darwin] build:ci: desc: "Build for CI (formatting checked separately)" cmds: - - bash gradlew build -PnoSpotless + - cmd: cmd /c ".\gradlew.bat build -PnoSpotless" + platforms: [windows] + - cmd: ./gradlew build -PnoSpotless + platforms: [linux, darwin] test: desc: "Run backend tests" cmds: - - bash gradlew test + - cmd: cmd /c ".\gradlew.bat test" + platforms: [windows] + - cmd: ./gradlew test + platforms: [linux, darwin] format: desc: "Auto-fix code formatting" cmds: - - bash gradlew spotlessApply + - cmd: cmd /c ".\gradlew.bat spotlessApply" + platforms: [windows] + - cmd: ./gradlew spotlessApply + platforms: [linux, darwin] format:check: desc: "Check code formatting" cmds: - - bash gradlew spotlessCheck + - cmd: cmd /c ".\gradlew.bat spotlessCheck" + platforms: [windows] + - cmd: ./gradlew spotlessCheck + platforms: [linux, darwin] fix: desc: "Auto-fix backend" @@ -59,7 +95,10 @@ tasks: swagger: desc: "Generate OpenAPI docs" cmds: - - bash gradlew :stirling-pdf:copySwaggerDoc + - cmd: cmd /c ".\gradlew.bat :stirling-pdf:copySwaggerDoc" + platforms: [windows] + - cmd: ./gradlew :stirling-pdf:copySwaggerDoc + platforms: [linux, darwin] sources: - app/core/src/main/java/**/*.java - app/proprietary/src/main/java/**/*.java @@ -77,19 +116,31 @@ tasks: desc: "Print project version" silent: true cmds: - - bash gradlew printVersion --quiet | tail -1 + - cmd: cmd /c ".\gradlew.bat printVersion --quiet" | tail -1 + platforms: [windows] + - cmd: ./gradlew printVersion --quiet | tail -1 + platforms: [linux, darwin] licenses:check: desc: "Check dependency licenses" cmds: - - bash gradlew checkLicense --no-parallel + - cmd: cmd /c ".\gradlew.bat checkLicense --no-parallel" + platforms: [windows] + - cmd: ./gradlew checkLicense --no-parallel + platforms: [linux, darwin] licenses:generate: desc: "Check and generate dependency license report" cmds: - - bash gradlew checkLicense generateLicenseReport --no-parallel + - cmd: cmd /c ".\gradlew.bat checkLicense generateLicenseReport --no-parallel" + platforms: [windows] + - cmd: ./gradlew checkLicense generateLicenseReport --no-parallel + platforms: [linux, darwin] clean: desc: "Clean build artifacts" cmds: - - bash gradlew clean + - cmd: cmd /c ".\gradlew.bat clean" + platforms: [windows] + - cmd: ./gradlew clean + platforms: [linux, darwin] diff --git a/Taskfile.yml b/Taskfile.yml index a4b71dce3..7edf1e9b4 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -3,7 +3,7 @@ version: '3' output: prefixed vars: - FIND_FREE_PORT_PS: powershell -NoProfile -File scripts\find-free-port.ps1 -Preferred + FIND_FREE_PORT_PS: powershell -NoProfile -File scripts/find-free-port.ps1 FIND_FREE_PORT_SH: bash scripts/find-free-port.sh includes: @@ -45,7 +45,7 @@ tasks: 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}}' + 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: @@ -62,7 +62,7 @@ tasks: desc: "Start backend + frontend + engine concurrently on free ports" vars: PORTS: - sh: '{{if eq OS "windows"}}{{.FIND_FREE_PORT_PS}} 8080,5173,5001{{else}}{{.FIND_FREE_PORT_SH}} 8080 5173 5001{{end}}' + 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}}' diff --git a/scripts/find-free-port.ps1 b/scripts/find-free-port.ps1 index 7b6697456..241117a14 100644 --- a/scripts/find-free-port.ps1 +++ b/scripts/find-free-port.ps1 @@ -4,7 +4,7 @@ # emits a random free port in 20000-49999. Probes by attempting to bind a # TcpListener on loopback. Tracks picks within this run so outputs are # guaranteed distinct from each other. -param([int[]]$Preferred) +param([Parameter(ValueFromRemainingArguments = $true)][int[]]$Preferred) $script:picked = @()