diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ffa074..c75862f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ All notable changes to this project will be documented in this file. ## Unreleased +- **Fix: `server start --dry-run` Writes Nothing and Shows `--name`:** In a folder without `lucee.json`, the dry run wrote a default `lucee.json` into the project, and its preview showed the folder name even when `--name` was given. A dry run now loads the configuration without persisting the default (`LuceeServerConfig.loadConfig(projectDir, file, persistDefault)`), and the preview applies `--name` as a real start does. Upstream: cybersonic/LuCLI#139. Adds two `ServerCommandHandlerTest` cases. + - **Fix: REPL History and Settings Follow `LUCLI_HOME`:** `lucli repl` history, the interactive terminal's history and `Settings` (`settings.json`, used e.g. by `secrets provider use`) were written under `user.home` (`~/.lucli` or `~/`) even when `LUCLI_HOME` / `-Dlucli.home` pointed elsewhere, so an isolated LuCLI home still wrote into the real home, and `settings.json` was not the file `system paths` reports. They now use the active LuCLI home (`LucliPaths`). Settings saved under the old location (`~/.lucli/settings.json`) are read once and copied to the active home when it has none; the old file is left as it is. Upstream: cybersonic/LuCLI#140. Adds `SettingsTest` cases and `ReplCommandHistoryTest`. - **Fix: `--env` / `--envfile` Reach Modules Run Through the Module Shortcut:** `lucli … --env=x`, `lucli --env=x …` and an aliased binary (`-Dlucli.binary.name=`, e.g. `wheels start --env=prod`) re-execute `modules run …` on the root command, which reset the root's options, so the module saw `LuCLI.getCurrentEnvironment() == null` (only `LUCLI_ENV` worked). The shortcut now re-injects `--env` / `--envfile` at the root position, as it already did for `--verbose` / `--debug` (after the module args). Upstream: cybersonic/LuCLI#136. Adds `ModuleShortcutArgsTest`. diff --git a/FORK.md b/FORK.md index 109060b..03568f9 100644 --- a/FORK.md +++ b/FORK.md @@ -85,6 +85,7 @@ No release is tagged for this queue yet; tagging needs the Wheels release owner' | 8 | wheels-dev/LuCLI [#19](https://github.com/wheels-dev/LuCLI/pull/19) (issue [#17](https://github.com/wheels-dev/LuCLI/issues/17)) | A module function run by an MCP `tools/call` receives the runtime-owned `__lucliMcpCall=true` argument; any caller-supplied argument of that name is dropped, and a terminal call never carries it | | 9 | wheels-dev/LuCLI [#21](https://github.com/wheels-dev/LuCLI/pull/21) (issue: upstream [cybersonic/LuCLI#136](https://github.com/cybersonic/LuCLI/issues/136)) | `--env` / `--envfile` reach a module run through the module shortcut or an aliased binary (re-injected at the root position) | | 10 | wheels-dev/LuCLI [#22](https://github.com/wheels-dev/LuCLI/pull/22) (issue: upstream [cybersonic/LuCLI#140](https://github.com/cybersonic/LuCLI/issues/140)) | REPL / terminal history and `settings.json` follow the active LuCLI home instead of `user.home`; legacy `~/.lucli/settings.json` read once and copied | +| 11 | wheels-dev/LuCLI [#23](https://github.com/wheels-dev/LuCLI/pull/23) (issue: upstream [cybersonic/LuCLI#139](https://github.com/cybersonic/LuCLI/issues/139)) | `server start --dry-run` no longer writes a default `lucee.json`; the preview shows `--name` | Upstream (row 8): issue [cybersonic/LuCLI#133](https://github.com/cybersonic/LuCLI/issues/133), PR [cybersonic/LuCLI#134](https://github.com/cybersonic/LuCLI/pull/134) (a `cherry-pick -x` of diff --git a/src/main/java/org/lucee/lucli/server/LuceeServerConfig.java b/src/main/java/org/lucee/lucli/server/LuceeServerConfig.java index e1f5ab1..764d4a9 100644 --- a/src/main/java/org/lucee/lucli/server/LuceeServerConfig.java +++ b/src/main/java/org/lucee/lucli/server/LuceeServerConfig.java @@ -338,6 +338,16 @@ public static ServerConfig loadConfig(Path projectDir) throws IOException { * @param configFileName The configuration file name (e.g., "lucee.json", "lucee-simple.json") */ public static ServerConfig loadConfig(Path projectDir, String configFileName) throws IOException { + return loadConfig(projectDir, configFileName, true); + } + + /** + * Load configuration from a specified file in the directory. + * @param persistDefault when the file doesn't exist, whether to write the default + * configuration to it. A preview ({@code server start --dry-run}) passes false so + * it never writes to the project (upstream cybersonic/LuCLI#139). + */ + public static ServerConfig loadConfig(Path projectDir, String configFileName, boolean persistDefault) throws IOException { // Reset any previously loaded .env variables for this new project/config load clearLoadedEnvFileVariables(); clearRealizedEnvVariables(); @@ -347,7 +357,9 @@ public static ServerConfig loadConfig(Path projectDir, String configFileName) th if (!Files.exists(configFile)) { // Create default configuration ServerConfig defaultConfig = createDefaultConfig(projectDir); - saveConfig(defaultConfig, configFile); + if (persistDefault) { + saveConfig(defaultConfig, configFile); + } return defaultConfig; } diff --git a/src/main/java/org/lucee/lucli/server/ServerCommandHandler.java b/src/main/java/org/lucee/lucli/server/ServerCommandHandler.java index a29f764..39eac2a 100644 --- a/src/main/java/org/lucee/lucli/server/ServerCommandHandler.java +++ b/src/main/java/org/lucee/lucli/server/ServerCommandHandler.java @@ -332,7 +332,8 @@ private String handleServerStart(LuceeServerManager serverManager, String[] args String cfgFile = configFileName != null ? configFileName : "lucee.json"; Path cfgPath = projectDir.resolve(cfgFile); boolean configFileExisted = Files.exists(cfgPath); - LuceeServerConfig.ServerConfig finalConfig = LuceeServerConfig.loadConfig(projectDir, cfgFile); + // A dry run is a preview: it must not write a default lucee.json into the project. + LuceeServerConfig.ServerConfig finalConfig = LuceeServerConfig.loadConfig(projectDir, cfgFile, !dryRun); // Apply environment overrides if --env flag was provided if (environment != null && !environment.trim().isEmpty()) { @@ -413,6 +414,11 @@ private String handleServerStart(LuceeServerManager serverManager, String[] args // If dry-run, show what would happen and exit if (dryRun) { + // --name applies to the started server (LuceeServerManager.startServerInternal); + // show it in the preview too. + if (customName != null && !customName.trim().isEmpty()) { + finalConfig.name = customName.trim(); + } StringBuilder result = new StringBuilder(); result.append("📋 DRY RUN: Server configuration that would be used:\n\n"); if (includeSelection.includeConfig) { diff --git a/src/test/java/org/lucee/lucli/server/ServerCommandHandlerTest.java b/src/test/java/org/lucee/lucli/server/ServerCommandHandlerTest.java index 9f0176e..52356ef 100644 --- a/src/test/java/org/lucee/lucli/server/ServerCommandHandlerTest.java +++ b/src/test/java/org/lucee/lucli/server/ServerCommandHandlerTest.java @@ -23,6 +23,43 @@ class ServerCommandHandlerTest { private static final ObjectMapper MAPPER = new ObjectMapper(); + // upstream cybersonic/LuCLI#139: a dry run is a preview and must not write to the project + @Test + void serverStartDryRun_withoutLuceeJson_writesNothing() throws Exception { + Files.writeString(tempDir.resolve("index.cfm"), "hi"); + + ServerCommandHandler handler = new ServerCommandHandler(true, tempDir); + String output = handler.executeCommand("server", new String[] { + "start", "--dry-run", "--port", "63301" + }); + + assertNotNull(output); + assertTrue(output.contains("DRY RUN"), "a preview is still printed"); + assertFalse(Files.exists(tempDir.resolve("lucee.json")), "start --dry-run must not create lucee.json"); + } + + // upstream cybersonic/LuCLI#139: --name applies to the started server, so the preview shows it + @Test + void serverStartDryRun_previewShowsTheNameOverride() throws Exception { + Files.writeString(tempDir.resolve("lucee.json"), """ + { + "name": "from-config", + "port": 8080 + } + """); + + ServerCommandHandler handler = new ServerCommandHandler(true, tempDir); + String output = handler.executeCommand("server", new String[] { + "start", "--dry-run", "--name", "foo" + }); + + assertNotNull(output); + assertTrue(output.contains("\"name\" : \"foo\"") || output.contains("\"name\": \"foo\""), + "Dry-run output should show the --name override"); + assertTrue(Files.readString(tempDir.resolve("lucee.json")).contains("from-config"), + "lucee.json is not modified"); + } + @Test void serverStartDryRun_portOverrideDoesNotMutateLuceeJson() throws Exception { Path configFile = tempDir.resolve("lucee.json");