Skip to content

Feature/local install command#419

Merged
shannah merged 56 commits intomasterfrom
feature/local-install-command
Mar 8, 2026
Merged

Feature/local install command#419
shannah merged 56 commits intomasterfrom
feature/local-install-command

Conversation

@shannah
Copy link
Copy Markdown
Owner

@shannah shannah commented Feb 16, 2026

No description provided.

shannah and others added 30 commits February 16, 2026 21:11
Add support for installing apps locally with full native launchers, CLI
commands, and services to enable local development testing without
publishing to npm or GitHub.

- Add --full flag to 'jdeploy install' command
- Create LocalInstallService to orchestrate local installation
- Create LocalJDeployFilesGenerator to generate .jdeploy-files directory
- Add local-package-json and local-bundle attributes to app.xml
- Add runHeadlessInstallProgrammatic() to installer for CLI invocation
- Add NPMPackageVersion.fromLocalPackageJson() for local package.json
- Update AppInfo/AppDescription with local mode support
- Update MacBundler and LauncherWriterHelper for local attributes
…nstall

Add support for configuring AI tools (MCP servers) during local installation
with the --full flag. This enables developers to test MCP server integrations
locally without publishing.

Changes:
- Add --ai-tools CLI option to specify AI tools (e.g., claude-desktop, cursor)
- Update LocalInstallService to pass AI tools to headless installer
- Add runHeadlessInstallProgrammatic overload accepting AI tools set
- Call applyContext() in headless install to load AI integration config
- Fix findInstallFilesDir() to check client4j.appxml.path at top level
- Fix findInstallFilesDir() to recognize when already in .jdeploy-files dir
- Fix duplicate CLI command path in uninstall manifest on macOS

Usage: jdeploy install --full --ai-tools=claude-desktop,cursor
The cli module now depends on jdeploy-installer, so the build order needed
to be updated to install the parent pom first, then build and install the
installer module before building the cli.
Changed jdeploy install to use the headless installer by default instead
of npm link. Added --npm flag to opt into the legacy npm link behavior.
Add support for running locally-installed jDeploy applications:
- jdeploy run: launches the GUI application
- jdeploy run <command> [args...]: runs a CLI command with arguments

New services:
- InstalledAppLocator: locates installed apps per platform
- LocalRunService: handles app/command launching
- NotInstalledException/CommandNotFoundException for error handling
Add debug command that launches installed applications with Java remote
debugging enabled via JDEPLOY_DEBUG_PORT and JDEPLOY_DEBUG_SUSPEND
environment variables.

- jdeploy debug: Debug GUI app (port 5005, suspend=true by default)
- jdeploy debug --port=N: Use custom debug port
- jdeploy debug --no-suspend: Don't wait for debugger to attach
- jdeploy debug <command> [args...]: Debug CLI command

On macOS, uses 'open --env' to pass environment variables to the
launched application bundle.
- Add --install (-I) flag to run install before run/debug commands
- Require -- delimiter before command arguments to avoid ambiguity
- Pre-process args to extract post-delimiter portion before option parsing

Usage:
  jdeploy run --install              # install first, then run GUI
  jdeploy run mycommand -- foo bar   # run command with args
  jdeploy debug --install --port=5006 mycommand -- arg1 arg2
Add support for writing debug-port and debug-suspend attributes to
app.xml when creating bundles. These properties enable Java remote
debugging via JDWP when the application is launched.

- Add debugPort and debugSuspend fields to AppDescription
- Add addDebugAttributesTo() helper method for centralized logic
- Update MacBundler to include debug attributes in app.xml
- Update LauncherWriterHelper for Windows/Linux bundlers

By default, debug attributes are not written. They must be explicitly
set via AppDescription.setDebugPort() and setDebugSuspend().
Add standalone commands to verify jDeploy app installations and uninstallations
for smoke testing. Supports three input methods:
- Local/remote package.json path or URL
- Project code (jDeploy registry lookup)
- Package name with optional GitHub source

Features:
- Platform-specific verification (macOS, Windows, Linux)
- Checks GUI executable, CLI launcher, command wrappers, PATH updates
- Windows registry verification via reg query
- Graceful degradation for legacy installations
- Summary output with verbose mode option
PackageInfoResolver now correctly parses commands when defined as an
object ({"cmd-name": {...}}) in addition to the array format. This
ensures command wrapper verification works for all jDeploy projects.
The --source parameter now works with --package-json to override the
source URL from package.json. This is needed when an app is published
to both GitHub and NPM, as the source affects the FQPN and installation
paths:
- NPM: bin-arm64/{packageName}/
- GitHub: bin-arm64/{MD5(source)}.{packageName}/
The CLI settings configuration (which determines whether to install
CLI commands based on the commands defined in package.json) was only
being called in buildUI() for GUI mode, but not in headless mode.

This fix extracts the CLI settings configuration into a new method
configureCliSettings() and calls it from both:
- buildUI() for GUI installations
- runHeadlessInstall() for headless installations (jdeploy install)

This ensures that CLI commands are properly installed when using
`jdeploy install` (headless local installation mode).
The computeFullyQualifiedPackageName() method was only checking for
null source, but AppInfo.npmSource defaults to empty string "".
This caused local installations (with no source) to incorrectly
use MD5("") as a prefix, resulting in CLI commands being installed
at ~/.jdeploy/bin-arm64/d41d8cd98f00b204e9800998ecf8427e.{package}/
instead of ~/.jdeploy/bin-arm64/{package}/.

Now empty strings are treated the same as null, so packages without
a source use the simple package name as the fully qualified name.
Shell config files typically use $HOME instead of the absolute home
directory path (e.g., $HOME/.jdeploy/bin-arm64/... vs /Users/user/...).
The PATH verifier was only checking for the absolute path, causing
false negatives.

Now containsPathEntry() checks for both formats.
The CLI metadata file (.jdeploy-cli.json) is stored in Contents/MacOS/
alongside the CLI launcher, not in Contents/. Updated getAppDirectory()
to return the correct path.
Add inverse of jdeploy install command to uninstall locally-installed
apps during development. The command supports:
- jdeploy uninstall (reads package.json in current directory)
- jdeploy uninstall --package=<name> [--source=<url>]

Uses the existing UninstallService from installer module which handles:
- File and directory cleanup based on uninstall manifest
- Registry cleanup on Windows
- PATH entry removal from shell configs
- Package and app directory cleanup
Add support for testing jDeploy applications on Windows using Docker
containers (dockur/windows image). This enables macOS/Linux developers
to test Windows installations without a physical Windows machine.

Two modes supported:
- Headless mode (--windows or --windows=headless): Automated install
  and verification with JSON results
- Interactive mode (--windows=rdp): Boot container with RDP access
  for manual testing

New files:
- WindowsDockerConfig: Configuration model with mode, timeout, port
- WindowsDockerTestService: Orchestrates Docker container lifecycle
- WindowsTestResult: Result model with check status parsing
- PowerShell scripts for installation and verification
- Fix Docker image name: dockur/windows -> dockurr/windows
- Add --privileged flag for proper container access
- Add KVM=N on macOS for software emulation (no /dev/kvm)
- Add inheritIO() to show Docker output in real-time
- Add automatic RDP client opening when Windows boots
- Support RDP auto-open on macOS, Windows, and Linux
Add support for testing jDeploy applications on Linux using Docker
containers (dorowu/ubuntu-desktop-lxde-vnc image). This provides
a Linux desktop environment accessible via browser or VNC client.

Two modes supported:
- Headless mode (--linux or --linux=headless): Automated install
  and verification
- Interactive mode (--linux=vnc): Boot container with VNC access
  via browser (http://localhost:6080) or VNC client (port 5900)

New files:
- LinuxDockerConfig: Configuration model with mode, ports, resolution
- LinuxDockerTestService: Orchestrates Docker container lifecycle
- LinuxTestResult: Result model with check status parsing
- Shell scripts for Linux installation and verification
Dev mode (when running from jdeploy source project):
- Detects jdeploy dev environment automatically
- Copies jdeploy project to container local filesystem for faster builds
- Builds jdeploy from source with mvn package -DskipTests
- Runs jdeploy install && jdeploy run

Non-dev mode (when running from npm-installed jdeploy):
- Installs jdeploy via npm install -g jdeploy
- Runs jdeploy install && jdeploy run

Both modes:
- Mount app project to /app in container
- Auto-open browser when web interface is ready
- Autostart script runs on desktop login
- Terminal stays open for debugging
… installs

Separates local installation settings from headless server settings:
- LocalInstallationSettings enables all GUI integrations (dock, desktop,
  start menu, programs menu) for developer testing
- HeadlessInstallationSettings remains unchanged for headless server environments
- LocalInstallService now uses runLocalInstallProgrammatic() which uses
  the new LocalInstallationSettings
- dev-install-and-launch.sh now builds installer module before CLI

This fixes the issue where apps installed via `jdeploy install` wouldn't
appear in the Linux Applications menu because addToPrograms was disabled.
Windows Docker testing:
- Add golden snapshot approach for fast clean starts
- First run installs Windows to golden volume (~20 min)
- Default: reuses working volume (fast, keeps state)
- --windows=rdp,clean: copies golden to working (clean state)
- Use web interface on port 8006 instead of RDP
- Proper Ctrl+C handling with docker stop

Linux Docker testing:
- Add persistent config volume for state preservation
- Default: reuses existing container (fast, keeps installed apps)
- --linux=vnc,clean: removes container/volume (fresh state)
- Named container (jdeploy-linux-test) for easy management
- Proper Ctrl+C handling with docker stop
The installer creates .desktop files using the app title as filename
(e.g., 'Note Manager.desktop'), but the verifier was looking for
the FQPN (e.g., 'com.example.notemanager.desktop').
- Add autostart-install.bat for Windows startup automation
- Mount OEM folder to /oem in container (copied to C:\OEM in Windows)
- Update instructions to show how to enable autostart
- User can copy batch file to Startup folder for automatic runs
Add comprehensive E2E test framework that runs inside a Docker container
to verify headless installation, verification, uninstallation, and
uninstall verification for jDeploy applications.

Key features:
- Builds jdeploy from source inside the container using Maven
- Tests apps from jdeploy.com registry via headless installers
- Uses jdeploy verify-installation and verify-uninstallation commands
- Uses jdeploy uninstall --package for proper cleanup
- Generates JSON summary and per-app logs

Also fixes verification-checks.sh to use correct app directory paths
(~/.jdeploy/apps/ instead of incorrect ~/.local/share/jdeploy-apps/).

Usage: ./tests/e2e/linux/run-docker-tests.sh [--app=NAME] [--verbose]
Remove duplicate verification-checks.sh shell script and use the
jdeploy CLI verify-installation command instead. This ensures both
the E2E tests and jdeploy run --linux use the same verification logic.

Changes:
- LinuxDockerTestService: Copy jdeploy CLI JAR and libs to container
- install-and-verify.sh: Use jdeploy verify-installation command
- Remove verification-checks.sh (functionality now in Java)
Update LinuxVerifier to check ~/.profile first since the Linux
installer now writes PATH entries to ~/.profile instead of ~/.bashrc.
This ensures verify-installation and verify-uninstallation find the
PATH entry in the primary target file.
- Create GitHub Actions workflow for E2E tests on Linux, macOS, and Windows
- Add shared e2e-test.sh script for Linux/macOS
- Add e2e-test.ps1 script for Windows
- Move apps.conf to shared tests/e2e directory
- Update Linux Docker tests to use shared scripts
- Tests verify headless install, jdeploy verify-installation, uninstall, and verify-uninstallation
When no uninstall manifest exists, the uninstall fallback now scans
~/Applications/ for .app bundles containing .jdeploy-cli.json metadata
that matches the package being uninstalled, and removes them.

This fixes E2E test failures on macOS where uninstall wasn't removing
the app bundle from ~/Applications/.
…entification

The app.xml file in Contents/app.xml is the authoritative source for
package metadata in macOS app bundles. It contains the 'package' and
'source' attributes needed to identify which package the bundle belongs to.
Windows headless installer uses the same install.sh script as other
platforms, run via bash (Git Bash). There is no install.ps1 endpoint.
The InstallationVerifier was defaulting to LOCALAPPDATA when winAppDir
was null, but WindowsAppDirResolver defaults to ~/.jdeploy/apps/.
This caused verification to fail because the verifier looked in the
wrong location.
The installer creates uninstall registry keys with "jdeploy." prefix
(e.g., jdeploy.jdeploy-demo-swingset3) via InstallWindowsRegistry.
The verifier was looking for keys without the prefix.
When uninstalling without a manifest, the uninstaller now also removes
the Windows registry uninstall key (HKCU\Software\Microsoft\Windows\
CurrentVersion\Uninstall\jdeploy.<packageName>) that was created during
installation. This ensures clean uninstallation even when the manifest
file is not available.
Add end-to-end tests that verify the complete local development workflow:
1. Generate project from template
2. Build the project
3. Install locally using jdeploy install
4. Verify installation
5. Uninstall
6. Verify uninstallation

Tests run on Linux, macOS, and Windows via GitHub Actions.
Initial templates tested: swing and picocli.
The CLI parser expects camelCase parameter names (e.g., -t for templateName,
-d for parentDirectory, -n for projectName) not kebab-case. Also fixed
exit code checking for the generate command.
The -y flag is not valid for the generate subcommand and was being
misinterpreted by the CLI parser, causing a true.java file to be created
which fails Maven compilation.
The CommandLineParser expects --key=value format for double-dash flags.
Using --key value format causes the value to be treated as a boolean 'true',
resulting in generated files named 'true.java' instead of the expected
class name.
Windows CI runners don't allow registry access which is required for
jdeploy install to create Start menu entries. Added -SkipInstallation
flag to skip install/uninstall verification on Windows CI while still
testing project generation and build.
Reverts the -SkipInstallation workaround for Windows CI and adds
detailed logging to capture the jdeploy install log when installation
fails. This will help diagnose the actual cause of the Windows
registry error.
The Windows installer was throwing a NullPointerException when trying
to copy the native launcher as an uninstaller during local install mode
(jdeploy install command). The client4j.launcher.path system property
is only set when running from a native launcher executable, not when
running programmatically.

This fix checks if the launcher path is available before attempting to
copy the uninstaller. In local install mode, users can use
'jdeploy uninstall' to remove the application instead.
Added quarkus-rest template to E2E tests which includes a service
controller command. The test scripts now verify that service commands
(status, start, stop, install, uninstall) work correctly after
installation.

Service command tests:
1. service status - check initial state
2. service install - register with system service manager
3. service start - start the service
4. service status - verify running state
5. service stop - stop the service
6. service status - verify stopped state
7. service uninstall - unregister from service manager
@shannah shannah merged commit 533764a into master Mar 8, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant