Skip to content

fix(scripts): make mvnw invocation cross-platform for Windows support#950

Merged
FelixTJDietrich merged 1 commit intomainfrom
windows-mvnw-path-fix
Mar 26, 2026
Merged

fix(scripts): make mvnw invocation cross-platform for Windows support#950
FelixTJDietrich merged 1 commit intomainfrom
windows-mvnw-path-fix

Conversation

@FelixTJDietrich
Copy link
Copy Markdown
Collaborator

@FelixTJDietrich FelixTJDietrich commented Mar 26, 2026

Description

On Windows, npm run lint:java fails with '.' is not recognized as an internal or external command because the npm scripts invoke ./mvnw — a Unix-only path syntax. Windows cmd.exe (npm's default script shell) does not understand ./ as a path prefix.

This PR introduces scripts/run-mvnw.ts, a cross-platform Maven wrapper runner that resolves ./mvnw (Unix) or mvnw.cmd (Windows) based on process.platform, and updates the lint:java and lint:java:report npm scripts to use it.

What changed

File Change
scripts/run-mvnw.ts New. Cross-platform mvnw spawner using spawnSync. Handles result.error (ENOENT with actionable message), result.signal (re-raises), and result.status (propagates exit code). Uses shell: true only on Windows per CVE-2024-27980. Path anchored via __dirname, not process.cwd().
package.json Modified. lint:java and lint:java:report now use node --import tsx scripts/run-mvnw.ts instead of cd server/application-server && ./mvnw.

Design decisions

  • Follows run-quiet.ts pattern — same invocation style (node --import tsx scripts/...), same imports (node: prefix, namespace import path), same __dirname resolution via fileURLToPath, same process.exitCode (not process.exit()).
  • shell: true scoped to Windows only — required for .cmd files post-CVE-2024-27980, but unnecessary (and slightly less safe) on Unix. Args passed as array, never string-concatenated.
  • Synchronous main()spawnSync is inherently sync; wrapping in async would be misleading.

Out of scope

generate:api:application-server:specs (package.json line 11) also calls ./mvnw but is wrapped in sh -c with Docker detection logic that's inherently Unix-only. Fixing that requires a larger refactor and is tracked separately.

How to test

  1. npm run lint:java — runs PMD check, exits 0 on success
  2. npm run lint:java:report — generates PMD report, prints report path
  3. npm run check — full check pipeline including lint:java
  4. On Windows: the '.' is not recognized error is gone

Summary by CodeRabbit

  • Chores
    • Improved build process infrastructure for Maven wrapper execution across Windows and Unix platforms.

Replace Unix-only `./mvnw` calls in npm scripts with a cross-platform
TypeScript runner that resolves `./mvnw` (Unix) or `mvnw.cmd` (Windows)
based on `process.platform`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@FelixTJDietrich FelixTJDietrich requested a review from a team as a code owner March 26, 2026 21:48
Copilot AI review requested due to automatic review settings March 26, 2026 21:48
@github-actions github-actions bot added bug Something isn't working dependencies Package updates, version bumps, lock file changes labels Mar 26, 2026
@github-actions github-actions bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Mar 26, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 26, 2026

📝 Walkthrough

Walkthrough

Two npm linting scripts (lint:java and lint:java:report) were updated to invoke Maven through a new Node.js wrapper script instead of directly calling the Maven Wrapper. A new TypeScript wrapper script was added to handle cross-platform Maven Wrapper invocation with error handling and signal management.

Changes

Cohort / File(s) Summary
NPM Script Updates
package.json
Modified lint:java and lint:java:report scripts to invoke Maven through new Node wrapper (scripts/run-mvnw.ts) instead of direct ./mvnw calls.
Maven Wrapper Script
scripts/run-mvnw.ts
New TypeScript script that wraps Maven Wrapper invocation with cross-platform support (Unix/Windows), inherits stdio, passes CLI arguments, and handles errors including missing binary (ENOENT), general failures, and signal termination.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 hops with joy
Maven wrapped in Node's embrace,
Cross-platform magic, every case!
Windows, Unix, running free,
Scripts unified in harmony! 🚀
The rabbit cheers: "Build tools aligned—
What teamwork beautiful we find!" ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: introducing cross-platform support for Maven wrapper invocation to fix Windows compatibility issues.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch windows-mvnw-path-fix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a cross-platform wrapper for invoking the Maven Wrapper from npm scripts so Java linting works on Windows (where ./mvnw path syntax fails under cmd.exe).

Changes:

  • Introduce scripts/run-mvnw.ts to resolve and execute mvnw/mvnw.cmd depending on platform.
  • Update lint:java and lint:java:report npm scripts to use the new runner instead of cd ... && ./mvnw.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
scripts/run-mvnw.ts New Node/TS script that spawns Maven Wrapper in server/application-server, aiming to propagate exit codes and handle failures.
package.json Switch Java lint scripts to use run-mvnw.ts for Windows compatibility.

Comment on lines +22 to +30
const result = spawnSync(
isWindows ? "mvnw.cmd" : "./mvnw",
process.argv.slice(2),
{
stdio: "inherit",
cwd: mvnwDir,
shell: isWindows,
},
);
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On Windows shell: true means the spawned process is cmd.exe, so a missing mvnw.cmd typically won’t surface as result.error/ENOENT; instead you’ll get a non-zero status (often 9009) and a generic shell message. If you want the actionable “wrapper not found” error cross-platform, check for the wrapper file’s existence before spawning (or explicitly detect the Windows command-not-found exit code and print the same message).

Copilot uses AI. Check for mistakes.
Comment on lines +18 to +28

function main(): void {
// shell: true is required on Windows for .cmd files (CVE-2024-27980).
// Safe here because args come from hardcoded npm scripts, not user input.
const result = spawnSync(
isWindows ? "mvnw.cmd" : "./mvnw",
process.argv.slice(2),
{
stdio: "inherit",
cwd: mvnwDir,
shell: isWindows,
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using shell: true on Windows while forwarding process.argv.slice(2) allows shell metacharacters (e.g. &, |, >) to be interpreted by cmd.exe when someone runs npm run lint:java -- <args...> or invokes this script directly. To avoid this, consider spawning cmd.exe explicitly with a safely-quoted command line (or using a library like cross-spawn), and/or validating/escaping args so they’re never interpreted by the shell.

Suggested change
function main(): void {
// shell: true is required on Windows for .cmd files (CVE-2024-27980).
// Safe here because args come from hardcoded npm scripts, not user input.
const result = spawnSync(
isWindows ? "mvnw.cmd" : "./mvnw",
process.argv.slice(2),
{
stdio: "inherit",
cwd: mvnwDir,
shell: isWindows,
const mvnwPath = path.join(mvnwDir, isWindows ? "mvnw.cmd" : "mvnw");
function main(): void {
// Run the Maven wrapper directly to avoid shell interpretation of arguments.
const result = spawnSync(
mvnwPath,
process.argv.slice(2),
{
stdio: "inherit",
cwd: mvnwDir,

Copilot uses AI. Check for mistakes.
"lint:java": "cd server/application-server && ./mvnw pmd:check -q",
"lint:java:report": "cd server/application-server && ./mvnw pmd:pmd && echo 'Report: server/application-server/target/site/pmd.html'",
"lint:java": "node --import tsx scripts/run-mvnw.ts pmd:check -q",
"lint:java:report": "node --import tsx scripts/run-mvnw.ts pmd:pmd && echo 'Report: server/application-server/target/site/pmd.html'",
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

echo 'Report: ...' is Bash-friendly, but on Windows cmd.exe it prints the single quotes literally. Since this script is being made Windows-compatible, consider switching to echo Report: ... (no quotes) or printing via Node so the output is consistent across shells.

Suggested change
"lint:java:report": "node --import tsx scripts/run-mvnw.ts pmd:pmd && echo 'Report: server/application-server/target/site/pmd.html'",
"lint:java:report": "node --import tsx scripts/run-mvnw.ts pmd:pmd && echo Report: server/application-server/target/site/pmd.html",

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
scripts/run-mvnw.ts (1)

16-18: Use ALL_CAPS for file-level constants.

mvnwDir and isWindows are constants; please rename to MVNW_DIR and IS_WINDOWS (and update references) to match the repository naming rule.

♻️ Proposed rename
-const mvnwDir = path.resolve(__dirname, "..", "server", "application-server");
-const isWindows = process.platform === "win32";
+const MVNW_DIR = path.resolve(__dirname, "..", "server", "application-server");
+const IS_WINDOWS = process.platform === "win32";

-		isWindows ? "mvnw.cmd" : "./mvnw",
+		IS_WINDOWS ? "mvnw.cmd" : "./mvnw",
 		process.argv.slice(2),
 		{
 			stdio: "inherit",
-			cwd: mvnwDir,
-			shell: isWindows,
+			cwd: MVNW_DIR,
+			shell: IS_WINDOWS,
 		},
As per coding guidelines, "Use ALL_CAPS for constants."

Also applies to: 23-24, 27-28

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/run-mvnw.ts` around lines 16 - 18, Rename the file-level constants to
ALL_CAPS: change mvnwDir -> MVNW_DIR and isWindows -> IS_WINDOWS (and update
every usage) to follow repository naming rules; also update the other file-level
consts on the same file referenced in the comment (the ones declared around
lines referenced) to ALL_CAPS as well, ensuring any destructuring or references
in functions (e.g., usages inside runMvnw or scripts invoking these variables)
are updated to the new names.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@scripts/run-mvnw.ts`:
- Around line 16-18: Rename the file-level constants to ALL_CAPS: change mvnwDir
-> MVNW_DIR and isWindows -> IS_WINDOWS (and update every usage) to follow
repository naming rules; also update the other file-level consts on the same
file referenced in the comment (the ones declared around lines referenced) to
ALL_CAPS as well, ensuring any destructuring or references in functions (e.g.,
usages inside runMvnw or scripts invoking these variables) are updated to the
new names.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 67407cdb-09b4-4dc8-bba2-005c8f6388a7

📥 Commits

Reviewing files that changed from the base of the PR and between ecfb078 and b55c26c.

📒 Files selected for processing (2)
  • package.json
  • scripts/run-mvnw.ts

@FelixTJDietrich FelixTJDietrich merged commit 90aae67 into main Mar 26, 2026
47 checks passed
@FelixTJDietrich FelixTJDietrich deleted the windows-mvnw-path-fix branch March 26, 2026 21:56
@github-actions
Copy link
Copy Markdown
Contributor

📚 Documentation Preview

Preview has been removed (PR closed)

@FelixTJDietrich
Copy link
Copy Markdown
Collaborator Author

🎉 This PR is included in version 0.52.1 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@FelixTJDietrich FelixTJDietrich added the released Included in a published release label Mar 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working dependencies Package updates, version bumps, lock file changes released Included in a published release size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants