Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/agents/pr-fix.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Do not edit anything when returning this block.

- Edit sources: `lib/*.js` (and keep `lib/index.d.ts` in sync when you change an option, a signature, or an export); tests in `test/`; config files at the repo root (`package.json`, `.eslintrc.js`, `.mega-linter.yml`, etc.); workflows in `.github/workflows/`.
- Keep the existing code style (ESLint `eslint:recommended` + Prettier). Use the existing `debug("java-caller")` logging pattern.
- Run local validation that needs no network where possible: `npm run lint:fix`, then `npm run test` (set `DEBUG=java-caller` to mirror CI; the mocha timeout is 5 min because real JRE installs run). Coverage runs under `npm run test:coverage`.
- Run local validation that needs no network where possible: `npm run lint:fix`, then `npm run test` (set `NODE_DEBUG=java-caller` to mirror CI; the mocha timeout is 5 min because real JRE installs run). Coverage runs under `npm run test:coverage`.
- Do NOT introduce defensive hacks (skip-on-fail, retries, `|| true`, weakened assertions, broad eslint ignores) to force green - fix the root cause.
- **npm only**, never `yarn` (it would desync `package-lock.json`).

Expand Down
2 changes: 2 additions & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"Gowans",
"Inclusivity",
"KICS",
"PATHEXT",
"PROSELINT",
"Perso",
"SIGINT",
Expand All @@ -34,6 +35,7 @@
"customarg",
"cvfm",
"danunafig",
"debuglog",
"desync",
"distrib",
"djukxe",
Expand Down
22 changes: 20 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,24 @@ concurrency:
cancel-in-progress: true

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout Code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Install node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "24"
- name: Install dependencies
run: npm ci
- name: Run lint
run: npm run lint

test:
if: github.event_name != 'push' || github.ref_name == github.event.repository.default_branch
strategy:
Expand Down Expand Up @@ -50,7 +68,7 @@ jobs:
run: npm ci
- name: Run tests
env:
DEBUG: java-caller,njre
NODE_DEBUG: java-caller
run: npm run test

test-no-java:
Expand All @@ -73,5 +91,5 @@ jobs:
run: npm ci
- name: Run tests
env:
DEBUG: "java-caller"
NODE_DEBUG: "java-caller"
run: npm run test
2 changes: 2 additions & 0 deletions .mega-linter.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Flavor & version used by mega-linter-runner and the MegaLinter agent skills
MEGALINTER_FLAVOR: javascript
MEGALINTER_VERSION: v10
# The javascript flavor is a deliberate choice: skip the custom-flavor suggestions
FLAVOR_SUGGESTIONS: false
DISABLE_LINTERS:
- TYPESCRIPT_STANDARD
- TYPESCRIPT_PRETTIER
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

## Unreleased

## [6.0.0] 2026-08-09

- **Breaking**: Debug traces are now activated with `NODE_DEBUG=java-caller` instead of `DEBUG=java-caller`. The `debug` package has been replaced by Node's built-in `util.debuglog`, which only reads `NODE_DEBUG` from the environment the process is launched with (setting `process.env.NODE_DEBUG` at runtime no longer enables traces). The output format changes accordingly, from `java-caller <message> +1ms` to `JAVA-CALLER <pid>: <message>`.
- **Breaking**: Minimum supported Node.js version is now 18 (`engines` previously declared `>=12`, while `njre` v3 already required `>=18`)
- Reduce runtime dependencies from 17 packages to 10: remove `fs-extra` (replaced by `node:fs`) and `debug` (replaced by `node:util`), and upgrade `yauzl` so it no longer pulls `buffer-crc32`. Only `njre` and `semver` remain as direct dependencies.
- Remove the `which` dev dependency, whose `engines` excluded the Node versions tested in CI
- Fix `npm run lint`, broken since the ESLint v10 upgrade: `@eslint/js` and `globals` are required by `eslint.config.js` but were no longer installed transitively. They are now explicit dev dependencies, and CI runs `npm run lint` so the script cannot silently break again.

## [5.0.0] 2026-06-30

- **Breaking**: Upgrade `njre` to v2.0.0: auto-installed JDK/JRE now comes from Eclipse Temurin (`api.adoptium.net`) instead of the deprecated AdoptOpenJDK endpoint (which hung under Node 24)
Expand Down
8 changes: 5 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Commands

```shell
npm run test # Run all mocha tests (timeout 300000ms; CI sets DEBUG=java-caller,njre)
npm run test:debug # Tests with DEBUG=java-caller enabled
npm run test # Run all mocha tests (timeout 300000ms; CI sets NODE_DEBUG=java-caller)
npm run test:debug # Tests with NODE_DEBUG=java-caller enabled
npm run lint:fix # eslint --fix on **/*.js, then prettier on lib (tab-width 4, print-width 150)

# Run a single test by name (mocha grep on the it()/describe() title):
Expand Down Expand Up @@ -50,10 +50,12 @@ Three modules in `lib/`, re-exported from `index.js`:
- **Java-version resolution is cached on `globalThis.JAVA_CALLER_VERSIONS_CACHE`** to avoid repeated lookups across instances in one process. Tests reset this in `test/helpers/init.js`; if you add caching state, reset it there too.
- **Platform branching** lives in `getPlatformBinPath()` (darwin = `Contents/Home/bin`) and several `os.platform() === "win32"` checks. Windows also handles arg quoting (`windowsVerbatimArguments`), `javaw` for windowless, and `windowsHide`. Any new behavior must be validated on win32/darwin/linux.
- **`classPath`** accepts a string (split on `:`, converted to the OS delimiter) or a string array; resolved against `rootPath` unless `useAbsoluteClassPaths` is set.
- **Runtime dependencies are deliberately minimal** (`njre` + `semver` only): prefer `node:` built-ins over adding a package.
- **Debug traces use `util.debuglog`**, activated with `NODE_DEBUG=java-caller` (not `DEBUG=`, which was the pre-v6 `debug` package contract). `debuglog` reads `NODE_DEBUG` only from the environment the process was launched with, so tests and helpers cannot turn traces on at runtime — `npm run test:debug` sets it on the command line.

## Testing notes

- Tests are mocha + `node:assert`, with shared helpers in `test/helpers/common.js` (`checkStatus`, `checkStdOutIncludes`, etc.) and per-run init in `test/helpers/init.js` (loaded via the `mocha.require` config in `package.json`).
- `test/java-install.test.js` exercises the real `njre` download/install path, which is why the mocha timeout is 5 minutes.
- CI (`.github/workflows/test.yml`) runs the matrix Node 18/20/24 × Java 8/11/17/21/25 × ubuntu/macos/windows, plus a no-Java job (`Test - No Java`) that runs the suite in a container without a system JDK.
- CI (`.github/workflows/test.yml`) runs the matrix Node 18/20/24 × Java 8/11/17/21/25 × ubuntu/macos/windows, plus a no-Java job (`Test - No Java`) that runs the suite in a container without a system JDK, plus a `Lint` job running `npm run lint`.
- macOS defaults `minimumJavaVersion` to 11 (no Java 8 there); keep that branch intact.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,13 +170,15 @@ You can see **more examples in** [**test methods**](https://github.com/nvuillam/

## TROUBLESHOOTING

Set environment variable `DEBUG=java-caller` before calling your code using java-caller module, and you will see the java commands executed.
Set environment variable `NODE_DEBUG=java-caller` before calling your code using java-caller module, and you will see the java commands executed.

> Since v6.0.0 traces use Node's built-in [`util.debuglog`](https://nodejs.org/api/util.html#utildebuglogsection-callback) instead of the `debug` package, so the variable is `NODE_DEBUG` and not `DEBUG` anymore. It must be set in the environment the process is launched with: setting `process.env.NODE_DEBUG` at runtime has no effect.

Example debug log:

```shell
java-caller Found Java version 1.80131 +1s
java-caller Java command: java -Xms256m -Xmx2048m -cp C:\Work\gitPerso\node-java-caller\test\java\dist com.nvuillam.javacaller.JavaCallerTester -customarg nico +1ms
JAVA-CALLER 12345: Found Java version 1.80131
JAVA-CALLER 12345: Java command: java -Xms256m -Xmx2048m -cp C:\Work\gitPerso\node-java-caller\test\java\dist com.nvuillam.javacaller.JavaCallerTester -customarg nico
```

## CONTRIBUTE
Expand Down
4 changes: 2 additions & 2 deletions examples/cli_app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
"scripts": {
"install-local-cli": "npm install && npm link --force",
"run:source": "node lib/index.js -a list --of arguments",
"run:source:verbose": "env DEBUG=java-caller node lib/index.js -a list --of arguments",
"run:source:verbose": "env NODE_DEBUG=java-caller node lib/index.js -a list --of arguments",
"run:cli": "java-caller-example-cli -a list --of arguments",
"run:verbose": "env DEBUG=java-caller java-caller-example-cli -a list --of arguments"
"run:verbose": "env NODE_DEBUG=java-caller java-caller-example-cli -a list --of arguments"
},
"keywords": [
"java-caller",
Expand Down
4 changes: 2 additions & 2 deletions examples/module_app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
"scripts": {
"install-local-cli": "npm install && npm link --force",
"run:source": "node lib/index.js",
"run:source:verbose": "env DEBUG=java-caller node lib/index.js",
"run:source:verbose": "env NODE_DEBUG=java-caller node lib/index.js",
"run:cli": "java-caller-example-module",
"run:cli:verbose": "env DEBUG=java-caller java-caller-example-module",
"run:cli:verbose": "env NODE_DEBUG=java-caller java-caller-example-module",
"test": "echo \"I strongly encourage you to implement test cases and code coverage, with mocha and nyc for example :)\""
},
"keywords": [
Expand Down
4 changes: 2 additions & 2 deletions lib/cli.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#! /usr/bin/env node
const { JavaCaller } = require("./java-caller");
const fse = require("fs-extra");
const fs = require("fs");
const path = require("path");

class JavaCallerCli {
Expand All @@ -11,7 +11,7 @@ class JavaCallerCli {
constructor(baseDir) {
// Use user-defined JSON file to read configuration
const configFile = path.resolve(`${baseDir}/java-caller-config.json`);
const options = fse.readJSONSync(configFile);
const options = JSON.parse(fs.readFileSync(configFile, "utf8"));
// Default output is console with CLI
if (options.output == null) {
options.output = "console";
Expand Down
22 changes: 13 additions & 9 deletions lib/java-caller.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
#! /usr/bin/env node
const debug = require("debug")("java-caller");
const fse = require("fs-extra");
const fs = require("fs");
const os = require("os");
const path = require("path");
const { debuglog } = require("util");
const { spawn } = require("child_process");
const semver = require("semver");

// NODE_DEBUG must be set in the environment the process is launched with:
// debuglog never re-reads it, so setting process.env.NODE_DEBUG at runtime has no effect.
const debug = debuglog("java-caller");

class JavaCaller {
"use strict";
minimumJavaVersion = os.platform() === "darwin" ? 11 : 8; // Mac starts at 11
Expand Down Expand Up @@ -334,15 +338,15 @@ class JavaCaller {
console.log(`Installing Java ${javaTypeToInstall} ${javaVersionToInstall} in ${this.javaCallerSupportDir}...`);

// Create a directory for installing Java and ensure it contains a dummy package.json
await fse.ensureDir(this.javaCallerSupportDir, { mode: "0777" });
await fs.promises.mkdir(this.javaCallerSupportDir, { recursive: true, mode: "0777" });
const packageJson = `${this.javaCallerSupportDir + path.sep}package.json`;
if (!fse.existsSync(packageJson)) {
if (!fs.existsSync(packageJson)) {
const packageJsonContent = {
name: "java-caller-support",
version: "1.0.0",
description: "Java installations by java-caller (https://github.com/nvuillam/node-java-caller)",
};
await fse.writeFile(packageJson, JSON.stringify(packageJsonContent), "utf8");
await fs.promises.writeFile(packageJson, JSON.stringify(packageJsonContent), "utf8");
}

// Install appropriate java version using njre
Expand Down Expand Up @@ -464,15 +468,15 @@ class JavaCaller {
// check if one matches with javaType , minimumJavaVersion and maximumJavaVersion
async findJavaVersionHome() {
const javaInstallsTopDir = path.join(this.javaCallerSupportDir, "jre");
if (!fse.existsSync(javaInstallsTopDir)) {
if (!fs.existsSync(javaInstallsTopDir)) {
return {};
}

return await fse
return await fs.promises
.readdir(javaInstallsTopDir)
.then((items) =>
items
.filter((item) => fse.statSync(path.join(javaInstallsTopDir, item)).isDirectory())
.filter((item) => fs.statSync(path.join(javaInstallsTopDir, item)).isDirectory())
.map((folder) => {
const version = semver.coerce(folder);
return { version, folder };
Expand All @@ -483,7 +487,7 @@ class JavaCaller {
const bin = path.join(home, this.getPlatformBinPath());
return { version, folder, home, bin };
})
.find(({ bin }) => fse.existsSync(bin)),
.find(({ bin }) => fs.existsSync(bin)),
)
.then((match) => {
if (!match) return {};
Expand Down
Loading
Loading