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
Original file line number Diff line number Diff line change
Expand Up @@ -264,82 +264,6 @@ private File doInstall(
}
}

//Try to copy the uninstaller
if (logger != null) {
logger.logSection("Installing Uninstaller");
}
File uninstallerPath = registryInstaller.getUninstallerPath();
boolean uninstallerDirCreated = !uninstallerPath.getParentFile().exists();
uninstallerPath.getParentFile().mkdirs();
if (logger != null && uninstallerDirCreated) {
logger.logDirectoryOperation(InstallationLogger.DirectoryOperation.CREATED, uninstallerPath.getParentFile().getAbsolutePath());
}

// In local install mode (via jdeploy install command), there's no native
// launcher executable to copy as an uninstaller. Users can use 'jdeploy uninstall'
// to uninstall in this case. Only copy the uninstaller exe when we have one.
String launcherPathStr = System.getProperty("client4j.launcher.path");
if (launcherPathStr != null) {
File installerExePath = new File(launcherPathStr);
boolean uninstallerExisted = uninstallerPath.exists();
if (uninstallerExisted) {
if (!uninstallerPath.canWrite()) {
uninstallerPath.setWritable(true, false);
try {
// Give windows time to update its cache
Thread.sleep(1000L);
} catch (InterruptedException interruptedException) {
// Ignore
}
}
if (!uninstallerPath.delete()) {
if (logger != null) {
logger.logFileOperation(InstallationLogger.FileOperation.FAILED, uninstallerPath.getAbsolutePath(), "Failed to delete existing uninstaller");
}
throw new IOException("Failed to delete uninstaller: "+uninstallerPath);
}
if (logger != null) {
logger.logFileOperation(InstallationLogger.FileOperation.DELETED, uninstallerPath.getAbsolutePath(), "Existing uninstaller");
}

try {
// Give Windows time to update its cache
Thread.sleep(1000L);
} catch (InterruptedException interruptedException) {
// Ignore
}
}
FileUtils.copyFile(installerExePath, uninstallerPath);
uninstallerPath.setExecutable(true, false);
if (logger != null) {
logger.logFileOperation(InstallationLogger.FileOperation.CREATED, uninstallerPath.getAbsolutePath(), "Uninstaller executable");
}
} else {
if (logger != null) {
logger.logInfo("Skipping native uninstaller copy (local install mode - use 'jdeploy uninstall' to uninstall)");
}
}
File jdeployFilesDestDir = new File(uninstallerPath.getParentFile(), context.findInstallFilesDir().getName());
FileUtils.copyDirectory(context.findInstallFilesDir(), jdeployFilesDestDir);
if (logger != null) {
logger.logDirectoryOperation(InstallationLogger.DirectoryOperation.CREATED, jdeployFilesDestDir.getAbsolutePath(), "jDeploy files for uninstaller");
}

// Persist winAppDir config so the uninstaller knows the app directory
try {
File winAppDirConfig = new File(uninstallerPath.getParentFile(), "winAppDir.txt");
String configuredDir = installationSettings.getWinAppDir();
FileUtils.writeStringToFile(winAppDirConfig, configuredDir != null ? configuredDir : "", "UTF-8");
if (logger != null) {
logger.logFileOperation(InstallationLogger.FileOperation.CREATED,
winAppDirConfig.getAbsolutePath(), "Windows app directory config for uninstaller");
}
} catch (IOException e) {
if (logger != null) {
logger.logError("Failed to persist winAppDir config", e);
}
}

// Build and write uninstall manifest
try {
UninstallManifestBuilder manifestBuilder = new UninstallManifestBuilder();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1042,9 +1042,15 @@ public void register() throws IOException {
registryOps.setLongValue(getUninstallKey(), 1);
registryOps.setStringValue(getUninstallKey(), "Publisher", appInfo.getVendor());
logRegistryValueSet(getUninstallKey(), "Publisher", appInfo.getVendor());
String uninstallString = "\"" + getUninstallerPath().getAbsolutePath() + "\" uninstall";
// Use the app launcher with --jdeploy:uninstall flag for uninstall via Add/Remove Programs.
// The Go-based launcher handles uninstall natively using the uninstall manifest,
// without requiring a JVM. Use --jdeploy:interactive=false since ARP doesn't provide a console.
String uninstallString = "\"" + exe.getAbsolutePath() + "\" --jdeploy:uninstall --jdeploy:interactive=false";
registryOps.setStringValue(getUninstallKey(), "UninstallString", uninstallString);
logRegistryValueSet(getUninstallKey(), "UninstallString", uninstallString);
// Also register QuietUninstallString for silent uninstall support
registryOps.setStringValue(getUninstallKey(), "QuietUninstallString", uninstallString);
logRegistryValueSet(getUninstallKey(), "QuietUninstallString", uninstallString);

if (!skipWinRegistryOperations) {
registry.notifyFileAssociationsChanged();
Expand Down
78 changes: 29 additions & 49 deletions installer/test_install_single_project.sh
Original file line number Diff line number Diff line change
Expand Up @@ -120,39 +120,8 @@ function smoke_test() {

function smoke_test_windows() {
echo "Running Smoke Test..."
local UNINSTALLERS_PATH="$HOME/.jdeploy/uninstallers"
local PROJECT_UNINSTALLER_PATH="$UNINSTALLERS_PATH/$QUALIFIED_PROJECT_NAME"
if [ ! -d "$PROJECT_UNINSTALLER_PATH" ]; then
echo "Smoke Test Failure: Uninstaller for $QUALIFIED_PROJECT_NAME not found at $PROJECT_UNINSTALLER_PATH"
exit 1
else
echo "Found uninstaller for $QUALIFIED_PROJECT_NAME at $PROJECT_UNINSTALLER_PATH"
fi

local UNINSTALLER_FILES="$PROJECT_UNINSTALLER_PATH/.jdeploy-files"
if [ ! -d "$UNINSTALLER_FILES" ]; then
echo "Smoke Test Failure: Uninstaller files not found at $UNINSTALLER_FILES"
exit 1
else
echo "Found uninstaller files at $UNINSTALLER_FILES"
fi

local expectedFiles=(
"$PROJECT_UNINSTALLER_PATH/.jdeploy-files/app.xml"
"$PROJECT_UNINSTALLER_PATH/.jdeploy-files/icon.png"
"$PROJECT_UNINSTALLER_PATH/.jdeploy-files/installsplash.png"
"$PROJECT_UNINSTALLER_PATH/$PROJECT_NAME-uninstall.exe"
)

for file in "${expectedFiles[@]}"; do
if [ ! -f "$file" ]; then
echo "Smoke Test Failure: Expected File $file not found"
exit 1
else
echo "Found $file created successfully"
fi
done

# Verify app files were installed (the app launcher is now also the uninstaller)
local expectedAppFiles=(
"$HOME/.jdeploy/apps/$QUALIFIED_PROJECT_NAME/icon.ico"
"$HOME/.jdeploy/apps/$QUALIFIED_PROJECT_NAME/icon.png"
Expand All @@ -178,6 +147,15 @@ function smoke_test_windows() {
fi
done

# Verify uninstall manifest was created
local MANIFEST_PATH="$HOME/.jdeploy/manifests/x64/$QUALIFIED_PROJECT_NAME/uninstall-manifest.xml"
if [ ! -f "$MANIFEST_PATH" ]; then
echo "Smoke Test Failure: Uninstall manifest not found at $MANIFEST_PATH"
exit 1
else
echo "Found uninstall manifest at $MANIFEST_PATH"
fi

echo "Smoke Test Passed"
}

Expand Down Expand Up @@ -209,21 +187,20 @@ function uninstall_project() {

function uninstall_project_windows() {
echo "Uninstalling $QUALIFIED_PROJECT_NAME"
local UNINSTALLERS_PATH="$HOME/.jdeploy/uninstallers"
local PROJECT_UNINSTALLER_PATH="$UNINSTALLERS_PATH/$QUALIFIED_PROJECT_NAME"
if [ ! -d "$PROJECT_UNINSTALLER_PATH" ]; then
echo "Uninstaller for $QUALIFIED_PROJECT_NAME not found at $PROJECT_UNINSTALLER_PATH"
exit 1
local APP_PATH="$HOME/.jdeploy/apps/$QUALIFIED_PROJECT_NAME"
local APP_EXE="$APP_PATH/$PROJECT_TITLE.exe"

if [ "$USE_PRIVATE_JRE" == "true" ]; then
APP_EXE="$APP_PATH/bin/$PROJECT_TITLE.exe"
fi

local UNINSTALLER_FILES="$PROJECT_UNINSTALLER_PATH/.jdeploy-files"
if [ ! -d "$UNINSTALLER_FILES" ]; then
echo "Uninstaller files not found at $UNINSTALLER_FILES"
if [ ! -f "$APP_EXE" ]; then
echo "App launcher for $QUALIFIED_PROJECT_NAME not found at $APP_EXE"
exit 1
fi

echo "Uninstalling $QUALIFIED_PROJECT_NAME"
"$PROJECT_UNINSTALLER_PATH/$PROJECT_NAME-uninstall.exe" uninstall
echo "Uninstalling $QUALIFIED_PROJECT_NAME via app launcher"
"$APP_EXE" --jdeploy:uninstall --jdeploy:interactive=false
}

function uninstall_project_mac() {
Expand All @@ -239,23 +216,26 @@ function uninstall_project_linux() {

function uninstall_smoke_test_windows() {
echo "Running Uninstall Smoke Test..."
local UNINSTALLERS_PATH="$HOME/.jdeploy/uninstallers"
local PROJECT_UNINSTALLER_PATH="$UNINSTALLERS_PATH/$QUALIFIED_PROJECT_NAME"
local APP_PATH="$HOME/.jdeploy/apps/$QUALIFIED_PROJECT_NAME"

# The uninstaller won't have been removed in this test because it runs delayed via the cmd command
# and for some reason this doesn't work in the tests. You need to manually uninstall via Add/Remove Programs
# to test that the uninstaller is removed
# The Go launcher schedules deferred file cleanup via PowerShell with a 2-second delay.
# Wait up to 10 seconds for the cleanup to complete.
local MAX_WAIT=10
local WAITED=0
while [ -d "$APP_PATH" ] && [ $WAITED -lt $MAX_WAIT ]; do
echo "Waiting for deferred cleanup of $APP_PATH... (${WAITED}s/${MAX_WAIT}s)"
sleep 1
WAITED=$((WAITED + 1))
done

# Verify that the app directory was removed
if [ -d "$APP_PATH" ]; then
echo "Uninstall Smoke Test Failure: App directory $APP_PATH still exists"
echo "Uninstall Smoke Test Failure: App directory $APP_PATH still exists after ${MAX_WAIT}s"
exit 1
else
echo "App directory $APP_PATH was removed"
fi


echo "Uninstall Smoke Test Passed"

}
Expand Down
10 changes: 4 additions & 6 deletions installer/tests/test_install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,17 @@ function install_windows_x64() {
cp "$CWD/../mock_launcher/mock_launcher_win_x64.exe" "$CWD/mock_launcher_win_x64.exe"
"$CWD/mock_launcher_win_x64.exe" install

echo "The uninstaller was written to the ~/.jdeploy/uninstallers directory."
echo "You should test out the installer by going to Add/Remove programs and attempting to uninstall the application."
echo "After running the uninstaller, you can check the results in the ~/.jdeploy/log/jdeploy-installer.log file."
echo "Installation complete."
echo "You can test uninstall via Add/Remove Programs or by running the app launcher with --jdeploy:uninstall."
}

function install_windows_arm64() {
# run the mock_launcher_win.exe script in the CWD
cp "$CWD/../mock_launcher/mock_launcher_win_arm64.exe" "$CWD/mock_launcher_win_arm64.exe"
"$CWD/mock_launcher_win_arm64.exe" install

echo "The uninstaller was written to the ~/.jdeploy/uninstallers directory."
echo "You should test out the installer by going to Add/Remove programs and attempting to uninstall the application."
echo "After running the uninstaller, you can check the results in the ~/.jdeploy/log/jdeploy-installer.log file."
echo "Installation complete."
echo "You can test uninstall via Add/Remove Programs or by running the app launcher with --jdeploy:uninstall."
}

function install_non_windows() {
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading