Modernize the Windows MSI: WiX v5, x64, service registration, upgrade from 5.1.1#701
Open
vharseko wants to merge 25 commits into
Open
Modernize the Windows MSI: WiX v5, x64, service registration, upgrade from 5.1.1#701vharseko wants to merge 25 commits into
vharseko wants to merge 25 commits into
Conversation
The opendj-msi package was built and uploaded but never installed or exercised in CI. Add a test-msi job (needs: build-maven) that, on a windows-latest runner, installs the built .msi silently (msiexec /i), runs setup, registers and starts/stops the OpenDJ Windows service with an ldapsearch liveness check, then uninstalls (msiexec /x).
…E 25) The MSI ships no JRE, so add a WiX launch condition that fails the install early with a clear message when Java is not detected (it does not install Java). JAVA_HOME is captured from the environment before LaunchConditions; `Installed` keeps uninstall/repair working regardless of Java. Fix the test-msi CI job (it failed with `net start` exit 2 "service already started"): - setup.bat is now invoked with --doNotStart, so the server is started only by `net start "OpenDJ Server"` (setup.bat no longer starts a standalone instance first). - Bump actions/setup-java from 21 to 25 (latest LTS; smoke-tests the MSI under a fresh JRE). The runner has JAVA_HOME from setup-java, so the new launch condition is satisfied and the install proceeds.
The launch condition (Installed OR JAVA_HOME_ENV) false-blocked valid installs: a JRE does not always set JAVA_HOME (it may be only on PATH). Drop it - the MSI again only copies files and Java availability stays the admin's responsibility. The test-msi fix (--doNotStart, JRE 25) is unaffected.
The install guide covered only the .zip and native .deb/.rpm. Add Windows MSI sections to chap-install/chap-upgrade/chap-uninstall: GUI and silent msiexec install, Java as a runtime prerequisite the installer does not enforce, configure via setup.bat, optional Windows service registration via windows-service.bat, MSI upgrade (disable service, back up, install newer .msi, upgrade.bat, re-enable), and uninstall via Apps & features / msiexec /x.
Migrate the Windows MSI off the EOL WiX 3.11.1 (heat/candle/light run under wine + winetricks dotnet40) to the cross-platform WiX v5 .NET tool, and address the MSI validation findings: - package.wxs rewritten to the WiX v4+ schema: single <Package>, x64 (ProgramFiles64Folder), MediaTemplate CompressionLevel="high", InstallerVersion=500, <Files> directory harvest (replaces heat), and a <ServiceInstall>/<ServiceControl> on opendj_service.exe so the MSI itself registers the "OpenDJ Server" Windows service (Start=auto, not started during install - setup must configure the instance first). UpgradeCode preserved. - opendj-msi-standard/pom.xml builds the MSI with `wix build -arch x64`; removed the external-dependency-plugin (wix3111 + winetricks), heat/candle/light and the wine/winetricks antrun steps. - opendj-msi/pom.xml: wine-path profiles replaced by an unconditional module so the MSI builds natively on every OS where opendj-msi is included. - build.yml: drop wine (keep rpm); add a "Setup WiX (.NET tool)" step; test-msi no longer calls windows-service.bat --enableService (the MSI registers it). - install guide: x64 install path; the MSI registers the service. Signing (OpenIdentityPlatform#4) deferred (no certificate). The Burn bundle that auto-installs a JRE is a follow-up, to be added after this MSI build is validated in CI.
WiX 5 ships as a net6.0 .NET tool, but the runners only have a newer .NET runtime (no .NET 6), so 'wix' failed to launch (exit 131, missing_runtime) on arm64 macOS. Set DOTNET_ROLL_FORWARD=Major in the Setup WiX step (exported and written to GITHUB_ENV) so the tool rolls forward to the installed runtime, both for that step and the Maven build that invokes 'wix build'.
wix build failed with WIX0004 ('Files element contains an unexpected attribute Exclude'): in WiX 5.0.2 exclusion is a child <Exclude Path=.../> element (with <Include Path=.../>), not an inline attribute. Convert both <Files> harvests to the child-element form.
macOS (arm64) runners have no .NET runtime for the net6.0 wix apphost (DOTNET_ROOT unset, exit 131); add actions/setup-dotnet (8.0.x) on macOS so the tool finds a runtime and rolls forward.
…only Include) WiX 5.0.2 requires Include as an attribute on <Files> and rejects an Exclude attribute as well as <Include>/<Exclude Path> child elements (WIX0004/0005/0010). Move all exclusions to the Ant staging step: stagingRoot = payload minus lib (and macOS/Unix bits), stagingLib = lib minus opendj_service.exe (registered via an explicit component sourced from the package dir). Each <Files> now uses only the Include attribute, and there is no duplicate lib directory.
On Linux/macOS the wix tool treats backslash as a literal, not a path separator, which may cascade into the WIX0389 'not a relative path' errors. Use '/**' (accepted on Windows too) to test whether the cross-platform wix build then succeeds.
The WiX Toolset can create MSI databases on Windows only: it warns "only supports Windows" and, on Linux/macOS, fails with WIX0389 on every Directory name and (with no name) on a missing msi.dll (the Windows Installer library). Verified locally on macOS with WiX 5.0.2, 6.0.1 and 7.0.0 - none can build. - opendj-packages/pom.xml: build opendj-msi only in the distribution-windows profile (removed from distribution-unix and distribution-mac). - build.yml: run the WiX setup step only on Windows; drop macOS setup-dotnet. - package.wxs: keep the Windows-proven backslash glob in <Files Include>. - .gitattributes: force LF for *.wxs/*.wxi.
The MSI can only be built on Windows. Instead of rebuilding opendj-server-legacy on a Windows runner: - deploy.yml: reuse the MSI already built by the triggering Build run (download the windows-latest-11 artifact, re-upload as "OpenDJ MSI Package"); both steps continue-on-error; drop wine (rpm only). - release.yml: release-maven uploads the released server zip as an artifact; new release-msi job (windows, continue-on-error so an MSI failure does not break the release) installs the zip into the local Maven repository and only packages :opendj-msi-standard (no -am), then attaches the MSI to the GitHub release; drop the .msi from the ubuntu release file list; drop wine.
…e legacy service - package.wxs: refuse to install when no Java is detectable (JAVA_HOME\bin\java.exe or java-looking PATH entries; MSI cannot scan PATH, so substring heuristic) with a message pointing at https://adoptium.net; detect an existing install (InstallDir registry value written by this package, else the legacy x86 default directory) and apply it only when OPENDJ is not set explicitly; drop the legacy windows-service.bat "OpenDJ Server" service before InstallServices (its display name collides with the MSI ServiceInstall). - build.yml: test-msi asserts the installer fails without a JRE before one is set up; new test-win-upgrade installs the released 5.1.1 x86 MSI, configures an instance with the legacy service, upgrades with the newly built x64 MSI without OPENDJ and asserts same directory, intact data, MSI-managed service, then runs upgrade.bat, starts the service and searches the pre-upgrade data. - docs: Java is required by the installer (adoptium.net link); MSI upgrade autodetects the directory and replaces the service; uninstall removes the service automatically.
- _script-util.bat: quote -Djava.io.tmpdir="%OPENDJ_TMP_DIR%" - any install directory containing spaces (including the x64 default C:\Program Files\OpenDJ and the legacy Program Files (x86)) broke the CheckJVMVersion probe and every script with "The detected Java version could not be used". - build.yml test-msi: the no-JRE negative test now hides Java at MACHINE scope (the Windows Installer service evaluates launch conditions with the machine environment) and restarts msiserver, restoring everything afterwards; the positive install now goes to the spaced x64 default directory end-to-end. - build.yml test-msi-upgrade (renamed from test-win-upgrade): the released 5.1.1 scripts cannot run from a spaced directory, so the old install lives in C:\opendj and the upgrade passes OPENDJ explicitly; added a fresh-install scenario asserting the legacy default directory is auto-detected when OPENDJ is not given, plus uninstall/service cleanup checks. - ADNotificationRequestControl: escape && in the javadoc code sample (javadoc "invalid input: '&'" warnings).
…aces) _script-util.bat appends -Djava.io.tmpdir=%OPENDJ_TMP_DIR% to OPENDJ_JAVA_ARGS without quotes, so in any install directory containing spaces (for example C:\Program Files (x86)\OpenDJ) the CheckJVMVersion probe - and with it setup and every other command-line tool - fails with "The detected Java version could not be used with the set of Java arguments". Quote the value: -Djava.io.tmpdir="%OPENDJ_TMP_DIR%".
…cp.bat) _script-util.bat passed unquoted paths to setcp.bat, and setcp.bat compared arguments with if ""%1""=="""". The argument-joining hack survived spaces, but a parenthesis in the path (C:\Program Files (x86)\OpenDJ - the default MSI directory) breaks the cmd parser with "... was unexpected at this time", so setup.bat and every tool exit with 255. Quote the setcp.bat arguments at the three call sites and switch setcp.bat to %~1 with quoted comparisons.
…e "%VAR%" == "" comparisons
After -Djava.io.tmpdir="%OPENDJ_TMP_DIR%" is appended, OPENDJ_JAVA_ARGS
contains embedded quotes, and the subsequent if "%OPENDJ_JAVA_ARGS%" == ""
checks blow up the cmd parser ('...\tmp"" was unexpected at this time', every
tool exits 255) regardless of whether the install path has spaces. Compare
with "if defined", which does not expand the value.
…h quoting
- The <Files> harvest ships files only (heat had -ke), so the empty instance
directories (bak, changelogDb, classes, db, import-tmp, ldif, locks, logs,
tmp and the template ones) were missing from the installed tree and the
server could not create its lock/pid files - setup aborted with "error
stopping server". Create them with explicit CreateFolder components.
- [OPENDJ] ends with a backslash which escaped the closing quote in the
service ImagePath ('start "C:\opendj\"' -> broken argv), so the MSI-managed
service failed to start (NET HELPMSG 2186). Append an extra backslash so
\\" parses as backslash + closing quote.
Components whose KeyPath is a directory cannot use auto-generated GUIDs, so the empty-instance-directory components broke wix build with WIX0230. Assign fixed GUIDs.
The tmp-cleanup block expands %OPENDJ_TMP_DIR% unquoted inside a ( ) compound statement, so a parenthesis in the install path - C:\Program Files (x86)\OpenDJ - terminates the block at parse time and start-ds fails; the Windows service then dies with a service-specific error -1 while setup (which never runs this block) succeeds. Quote the three path expansions.
opendj_service.exe located its own service entry by comparing the raw SCM ImagePath with the exact string it builds itself, so a service registered by the MSI (exe unquoted, instance dir with a trailing backslash) was never found and "net start" failed with error 2186. getServiceName now compares the executable path, the subcommand and the normalized instance dir token by token instead. The MSI ServiceInstall argument becomes 'start "[OPENDJ]."' and the sc-config workaround is not needed.
Resolve build.yml conflict: keep master's build-docker-alpine benchmark steps and append the test-msi and test-msi-upgrade CI jobs. The build-maven matrix and WiX setup step merged automatically.
Replace opendj_service.exe, winlauncher.exe and launcher_administrator.exe with the CI-built artifacts from the fixed service.c (tolerant ImagePath matching, commit 262f98f). Verified by green test-msi and test-msi-upgrade — the Windows service now starts (no more error 2186).
maximthomas
approved these changes
Jul 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Modernizes the Windows MSI end to end. Builds on #664 (branched from it) and includes the Windows script quoting fixes from #671 (they merge cleanly once #671 lands).
Toolchain: WiX 3.11 + wine → WiX v5, Windows-native
wixtool): heat/candle/light + wine + winetricks + dotnet40 collapse into a singlewix build -arch x64. WiX v5 is the newest version without the Open Source Maintenance Fee (v7 refuses to run without accepting the OSMF EULA).WIX0389for anyDirectory/@Nameand, with no names at all, on the missingmsi.dll— the MSI database is written by the Windows Installer library, so no cross-platform build exists. wine is removed frombuild.yml,deploy.ymlandrelease.yml.package.wxsrewritten to the v4+ schema: single<Package>,MediaTemplate CompressionLevel="high",InstallerVersion=500,<Files>harvest from Ant-staged payloads, empty instance directories shipped viaCreateFoldercomponents (the harvest ships files only).Installer behavior
C:\Program Files\OpenDJ(was x86 /Program Files (x86)).OpenDJ ServerWindows service (ServiceInstallonopendj_service.exe, not started during install — runsetupfirst). A service registered by the legacywindows-service.batis replaced automatically (its display name collides with the new one).config,db,logs) is preserved; an explicitOPENDJ=...always wins. Verified in CI by upgrading from the released 5.1.1 MSI: "OpenDJ was successfully upgraded from 5.1.1 to 5.1.2".%JAVA_HOME%\bin\java.exeor java-looking PATH entries, with a message pointing at https://adoptium.net. The MSI is not code-signed (no certificate) — SmartScreen note added to the docs.Release / deploy
deploy.ymlreuses the MSI already built by the triggering Build run (no rebuild).release.yml: newrelease-msijob (windows,continue-on-error) installs the released server zip into the local repository and packages only:opendj-msi-standard, then attaches the MSI to the GitHub release.CI
test-msi: asserts the installer fails without a JRE, then installs into the spaced x64 default directory, runssetup, starts/stops the MSI-registered service,ldapsearch, uninstalls.test-msi-upgrade: released 5.1.1 x86 MSI → configure → legacy service → upgrade with the new x64 MSI: same directory kept, data intact, service replaced (sc getkeyname "OpenDJ Server"→OpenDJ),upgrade.bat, start, search; plus a fresh-install scenario asserting the legacy default directory is auto-detected.Windows script fixes surfaced by these tests (in #671)
Install paths with spaces/parentheses (
C:\Program Files (x86)\OpenDJ— the old default!) broke the batch scripts in four places: unquotedjava.io.tmpdir,setcp.batargument parsing,"%VAR%" == ""checks with the now-quoted tmpdir, and thestart-ds.battmp-cleanup block. All fixed and exercised by the MSI tests in this PR.Deferred
CI on the branch is still validating the two latest fixes (empty directories, service ImagePath).