Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8710544
Add CI job to install-test the Windows MSI
vharseko Jun 26, 2026
830629c
Require Java via MSI launch condition; fix test-msi (--doNotStart, JR…
vharseko Jun 28, 2026
2b2d25d
remove MSI JAVA_HOME launch condition
vharseko Jun 28, 2026
3edfa16
document MSI install/upgrade/uninstall
vharseko Jun 28, 2026
f3b7a15
Modernize MSI: WiX v5, x64, register service via WiX, drop wine
vharseko Jun 29, 2026
f925656
Fix MSI CI: DOTNET_ROLL_FORWARD=Major so the net6.0 wix tool runs
vharseko Jun 29, 2026
60364d5
Fix MSI build: WiX v5 Files child Exclude; .NET runtime for wix on macOS
vharseko Jun 29, 2026
8571e3d
Fix MSI build: do Files exclusions in Ant staging (WiX 5 Files takes …
vharseko Jun 29, 2026
7ebd48b
Use forward slashes in WiX Files Include (test non-Windows wix build)
vharseko Jul 1, 2026
8e297c0
Build the MSI on Windows only (WiX cannot author MSIs on Linux/macOS)
vharseko Jul 1, 2026
a3e3d00
Publish the Windows MSI in release/deploy without rebuilding the server
vharseko Jul 2, 2026
431b693
MSI: require a JRE at install, upgrade over existing installs, replac…
vharseko Jul 2, 2026
50247e1
Document the unsigned-MSI SmartScreen warning; drop unused wix.versio…
vharseko Jul 2, 2026
20fbcca
Fix unquoted java.io.tmpdir (paths with spaces); harden MSI tests
vharseko Jul 2, 2026
23b355a
Fix unquoted java.io.tmpdir in Windows scripts (install paths with sp…
vharseko Jul 2, 2026
a80fcdb
Fix classpath building for install paths with spaces/parentheses (set…
vharseko Jul 3, 2026
dc97ea4
Use "if defined" for JAVA_ARGS checks: the quoted java.io.tmpdir brok…
vharseko Jul 3, 2026
74c74bc
Merge branch 'issues/windows-tmpdir-quoting' into features/windows-di…
vharseko Jul 3, 2026
a6c9fb0
MSI: ship the empty instance directories and fix the service ImagePat…
vharseko Jul 4, 2026
1a4f66e
MSI: explicit GUIDs for the CreateFolder components (WIX0230)
vharseko Jul 4, 2026
a5f8748
start-ds.bat: quote the tmp-cleanup paths (parentheses break the block)
vharseko Jul 4, 2026
e3e2813
Merge branch 'issues/windows-tmpdir-quoting' into features/windows-di…
vharseko Jul 4, 2026
262f98f
Windows service: tolerant ImagePath matching in opendj_service.exe
vharseko Jul 6, 2026
7961cb1
Merge branch 'master' into features/windows-distribution-new
vharseko Jul 6, 2026
b23db32
Rebuild Windows native binaries with the ImagePath fix
vharseko Jul 7, 2026
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
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# WiX sources must stay LF so the WiX toolset parses them consistently across runners.
*.wxs text eol=lf
*.wxi text eol=lf
190 changes: 181 additions & 9 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,12 @@ jobs:
- { os: 'windows-latest', java: '26' }
fail-fast: false
steps:
- name: Install wine+rpm for distribution
- name: Install rpm for distribution
if: runner.os == 'Linux'
shell: bash
run: |
sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list
sudo dpkg --add-architecture i386
sudo mkdir -pm755 /etc/apt/keyrings && sudo wget -O /etc/apt/keyrings/winehq-archive.key https://dl.winehq.org/wine-builds/winehq.key
sudo wget -NP /etc/apt/sources.list.d/ https://dl.winehq.org/wine-builds/ubuntu/dists/$(lsb_release -c -s)/winehq-$(lsb_release -c -s).sources
sudo apt-get update
sudo apt install --install-recommends winehq-stable || sudo apt install --install-recommends winehq-staging
wine --version
version="9.4.0"; sudo wget "https://dl.winehq.org/wine/wine-mono/$version/wine-mono-$version-x86.msi" -O /tmp/wine-mono.msi
wine msiexec /i /tmp/wine-mono.msi
sudo apt-get install -y rpm
- uses: actions/checkout@v6
with:
fetch-depth: 0
Expand Down Expand Up @@ -86,6 +79,19 @@ jobs:
run: |
echo "MAVEN_PROFILE_FLAG=-P precommit" >> $GITHUB_OUTPUT

- name: Setup WiX (.NET tool) for MSI
if: runner.os == 'Windows'
shell: bash
run: |
# The MSI builds on Windows only (WiX cannot author MSIs on Linux/macOS). WiX 5 ships as a
# net6.0 tool; allow it to run on the newer .NET runtime present on the runner.
echo "DOTNET_ROLL_FORWARD=Major" >> "$GITHUB_ENV"
export DOTNET_ROLL_FORWARD=Major
dotnet tool install --global wix --version 5.0.2 || dotnet tool update --global wix --version 5.0.2
echo "$HOME/.dotnet/tools" >> "$GITHUB_PATH"
export PATH="$HOME/.dotnet/tools:$PATH"
wix --version
wix extension add -g WixToolset.UI.wixext/5.0.2 || true
- name: Build with Maven
timeout-minutes: 180
env:
Expand Down Expand Up @@ -506,3 +512,169 @@ jobs:
*.jmeter.out
if-no-files-found: warn
retention-days: 90

test-msi:
needs: build-maven
runs-on: 'windows-latest'
steps:
- name: Download artifacts
uses: actions/download-artifact@v8
with:
name: windows-latest-11
- name: Install must fail without a JRE
shell: pwsh
run: |
# Simulate a machine with no Java. The launch condition is evaluated by the Windows
# Installer service, which sees the MACHINE environment - so hide Java there too and
# restart msiserver to make it pick the change up; restore everything afterwards.
Stop-Service msiserver -Force -ErrorAction SilentlyContinue
$oldJH = [Environment]::GetEnvironmentVariable('JAVA_HOME','Machine')
$oldPM = [Environment]::GetEnvironmentVariable('Path','Machine')
[Environment]::SetEnvironmentVariable('JAVA_HOME',$null,'Machine')
[Environment]::SetEnvironmentVariable('Path',((($oldPM -split ';') | Where-Object { $_ -notmatch 'java|jdk|jre|zulu|javapath' }) -join ';'),'Machine')
$env:JAVA_HOME = ""
$env:PATH = (($env:PATH -split ';') | Where-Object { $_ -notmatch 'java|jdk|jre|zulu|javapath' }) -join ';'
try {
$msi = (Get-ChildItem -Recurse -Filter *.msi -Path opendj-packages/opendj-msi | Select-Object -First 1).FullName
if (-not $msi) { throw "MSI not found in the windows-latest-11 artifact" }
$p = Start-Process msiexec -Wait -PassThru -ArgumentList "/i `"$msi`" /quiet /qn /norestart /l*v install-nojre.log"
if ($p.ExitCode -eq 0) {
Get-Content install-nojre.log -Tail 60
throw "installer succeeded without a JRE, but the Java launch condition must fail it"
}
Write-Host "Installer refused to install without a JRE as expected (exit $($p.ExitCode))"
Select-String -Path install-nojre.log -Pattern "requires Java" | Select-Object -First 1
} finally {
[Environment]::SetEnvironmentVariable('JAVA_HOME',$oldJH,'Machine')
[Environment]::SetEnvironmentVariable('Path',$oldPM,'Machine')
Stop-Service msiserver -Force -ErrorAction SilentlyContinue
}
- name: Set up Java
uses: actions/setup-java@v5
with:
java-version: '25'
distribution: 'zulu'
- name: Install MSI (silent)
shell: pwsh
run: |
$msi = (Get-ChildItem -Recurse -Filter *.msi -Path opendj-packages/opendj-msi | Select-Object -First 1).FullName
if (-not $msi) { throw "MSI not found in the windows-latest-11 artifact" }
Write-Host "MSI: $msi"
# No OPENDJ property: exercise the x64 default C:\Program Files\OpenDJ (a path with
# spaces, which the server scripts must handle).
$p = Start-Process msiexec -Wait -PassThru -ArgumentList "/i `"$msi`" /quiet /qn /norestart /l*v install.log"
if ($p.ExitCode -ne 0) { Get-Content install.log -Tail 80; throw "msiexec /i failed: $($p.ExitCode)" }
$root = "C:\Program Files\OpenDJ"
if (-not (Test-Path "$root\setup.bat")) { Get-Content install.log -Tail 80; throw "OpenDJ not installed into the x64 default $root" }
Write-Host "Installed to $root"
"OPENDJ_ROOT=$root" | Out-File -FilePath $env:GITHUB_ENV -Append
- name: Setup and start/stop the Windows service
shell: pwsh
run: |
$root = $env:OPENDJ_ROOT
$env:OPENDJ_JAVA_ARGS = "-server -Xmx512m"
& "$root\setup.bat" -h localhost -p 1389 --ldapsPort 1636 --adminConnectorPort 4444 --enableStartTLS --generateSelfSignedCertificate --rootUserDN "cn=Directory Manager" --rootUserPassword password --baseDN dc=example,dc=com --addBaseEntry --cli --acceptLicense --no-prompt --doNotStart
if ($LASTEXITCODE -ne 0) { throw "setup.bat failed: $LASTEXITCODE" }
# The service is already registered by the MSI (WiX ServiceInstall); just start it.
net start "OpenDJ Server"
if ($LASTEXITCODE -ne 0) { throw "net start failed: $LASTEXITCODE" }
for ($i=0; $i -lt 12; $i++) { try { $c = New-Object System.Net.Sockets.TcpClient('localhost', 1636); $c.Close(); break } catch { Start-Sleep -Seconds 5 } }
& "$root\bat\ldapsearch.bat" --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "dc=example,dc=com" --searchScope base "(objectClass=*)" 1.1
if ($LASTEXITCODE -ne 0) { throw "ldapsearch failed: $LASTEXITCODE" }
net stop "OpenDJ Server"
if ($LASTEXITCODE -ne 0) { throw "net stop failed: $LASTEXITCODE" }
- name: Uninstall MSI
shell: pwsh
run: |
$msi = (Get-ChildItem -Recurse -Filter *.msi -Path opendj-packages/opendj-msi | Select-Object -First 1).FullName
$p = Start-Process msiexec -Wait -PassThru -ArgumentList "/x `"$msi`" /quiet /qn /norestart /l*v uninstall.log"
if ($p.ExitCode -ne 0) { Get-Content uninstall.log -Tail 80; throw "msiexec /x failed: $($p.ExitCode)" }
Write-Host "Uninstalled OK"

# Upgrade path: released 5.1.1 x86 MSI (wine-built, WiX3) -> this build's x64 MSI.
# Verifies the new installer detects the legacy Program Files (x86) install, keeps the
# instance data in place, replaces the windows-service.bat service with the MSI-managed
# one, and the upgraded server starts with the old data.
test-msi-upgrade:
needs: build-maven
runs-on: 'windows-latest'
steps:
- name: Download artifacts
uses: actions/download-artifact@v8
with:
name: windows-latest-11
- name: Set up Java
uses: actions/setup-java@v5
with:
java-version: '25'
distribution: 'zulu'
- name: Install released 5.1.1 MSI and configure an instance
shell: pwsh
run: |
Invoke-WebRequest -Uri "https://github.com/OpenIdentityPlatform/OpenDJ/releases/download/5.1.1/opendj-5.1.1.msi" -OutFile opendj-5.1.1.msi
# The released 5.1.1 scripts cannot run from a directory with spaces (unquoted
# java.io.tmpdir), so put the old install into C:\opendj.
$p = Start-Process msiexec -Wait -PassThru -ArgumentList "/i opendj-5.1.1.msi /quiet /qn /norestart OPENDJ=C:\opendj /l*v install-old.log"
if ($p.ExitCode -ne 0) { Get-Content install-old.log -Tail 80; throw "msiexec /i (5.1.1) failed: $($p.ExitCode)" }
$root = "C:\opendj"
if (-not (Test-Path "$root\setup.bat")) { Get-Content install-old.log -Tail 80; throw "5.1.1 install root not found at $root" }
$env:OPENDJ_JAVA_ARGS = "-server -Xmx512m"
& "$root\setup.bat" -h localhost -p 1389 --ldapsPort 1636 --adminConnectorPort 4444 --enableStartTLS --generateSelfSignedCertificate --rootUserDN "cn=Directory Manager" --rootUserPassword password --baseDN dc=example,dc=com --addBaseEntry --cli --acceptLicense --no-prompt --doNotStart
if ($LASTEXITCODE -ne 0) { throw "setup.bat (5.1.1) failed: $LASTEXITCODE" }
# Register the LEGACY service the pre-MSI way and prove it works, then stop it.
& "$root\bat\windows-service.bat" --enableService
if ($LASTEXITCODE -ne 0) { throw "windows-service --enableService failed: $LASTEXITCODE" }
net start "OpenDJ Server"
if ($LASTEXITCODE -ne 0) { throw "net start (5.1.1) failed: $LASTEXITCODE" }
net stop "OpenDJ Server"
if ($LASTEXITCODE -ne 0) { throw "net stop (5.1.1) failed: $LASTEXITCODE" }
- name: Upgrade with the newly built MSI (same directory passed explicitly)
shell: pwsh
run: |
$msi = (Get-ChildItem -Recurse -Filter *.msi -Path opendj-packages/opendj-msi | Select-Object -First 1).FullName
if (-not $msi) { throw "MSI not found in the windows-latest-11 artifact" }
$p = Start-Process msiexec -Wait -PassThru -ArgumentList "/i `"$msi`" /quiet /qn /norestart OPENDJ=C:\opendj /l*v upgrade.log"
if ($p.ExitCode -ne 0) { Get-Content upgrade.log -Tail 120; throw "msiexec /i (upgrade) failed: $($p.ExitCode)" }
$root = "C:\opendj"
# New package files landed in the old directory, not the x64 default
if (-not (Test-Path "$root\setup.bat")) { throw "upgrade did not keep the old install dir" }
if (Test-Path "C:\Program Files\OpenDJ") { throw "upgrade unexpectedly installed into the x64 default dir" }
# Instance data survived
if (-not (Test-Path "$root\config\config.ldif")) { throw "instance data (config\config.ldif) lost by the upgrade" }
# Legacy service replaced by the MSI-managed one: display name maps to key 'OpenDJ'
$key = (sc.exe getkeyname "OpenDJ Server" | Select-String -Pattern "Name = (.+)").Matches[0].Groups[1].Value.Trim()
if ($key -ne "OpenDJ") { sc.exe query; throw "expected MSI-managed service key 'OpenDJ', got '$key'" }
sc.exe qc OpenDJ
"OPENDJ_ROOT=$root" | Out-File -FilePath $env:GITHUB_ENV -Append
- name: Run upgrade.bat and start the upgraded server
shell: pwsh
run: |
$root = $env:OPENDJ_ROOT
$env:OPENDJ_JAVA_ARGS = "-server -Xmx512m"
& "$root\upgrade.bat" --no-prompt --acceptLicense --force
if ($LASTEXITCODE -ne 0) { throw "upgrade.bat failed: $LASTEXITCODE" }
net start "OpenDJ Server"
if ($LASTEXITCODE -ne 0) { throw "net start (upgraded) failed: $LASTEXITCODE" }
for ($i=0; $i -lt 12; $i++) { try { $c = New-Object System.Net.Sockets.TcpClient('localhost', 1636); $c.Close(); break } catch { Start-Sleep -Seconds 5 } }
# The pre-upgrade data must still be served
& "$root\bat\ldapsearch.bat" --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "dc=example,dc=com" --searchScope base "(objectClass=*)" 1.1
if ($LASTEXITCODE -ne 0) { throw "ldapsearch after upgrade failed: $LASTEXITCODE" }
net stop "OpenDJ Server"
if ($LASTEXITCODE -ne 0) { throw "net stop (upgraded) failed: $LASTEXITCODE" }
- name: Auto-detect the legacy default directory on a fresh install
shell: pwsh
run: |
$msi = (Get-ChildItem -Recurse -Filter *.msi -Path opendj-packages/opendj-msi | Select-Object -First 1).FullName
# Clean up the previous scenario first.
$p = Start-Process msiexec -Wait -PassThru -ArgumentList "/x `"$msi`" /quiet /qn /norestart /l*v uninstall1.log"
if ($p.ExitCode -ne 0) { Get-Content uninstall1.log -Tail 80; throw "msiexec /x failed: $($p.ExitCode)" }
if (Get-Service OpenDJ -ErrorAction SilentlyContinue) { throw "service not removed by uninstall" }
# An existing legacy default directory must be picked up when OPENDJ is not given.
New-Item -ItemType Directory -Force "C:\Program Files (x86)\OpenDJ" | Out-Null
$p = Start-Process msiexec -Wait -PassThru -ArgumentList "/i `"$msi`" /quiet /qn /norestart /l*v install-autodetect.log"
if ($p.ExitCode -ne 0) { Get-Content install-autodetect.log -Tail 80; throw "msiexec /i (autodetect) failed: $($p.ExitCode)" }
if (-not (Test-Path "C:\Program Files (x86)\OpenDJ\setup.bat")) { Get-Content install-autodetect.log -Tail 80; throw "installer did not auto-detect the legacy default dir" }
if (Test-Path "C:\Program Files\OpenDJ") { throw "installer used the x64 default dir despite an existing legacy dir" }
Write-Host "Legacy default directory auto-detected OK"
$p = Start-Process msiexec -Wait -PassThru -ArgumentList "/x `"$msi`" /quiet /qn /norestart /l*v uninstall2.log"
if ($p.ExitCode -ne 0) { Get-Content uninstall2.log -Tail 80; throw "msiexec /x (cleanup) failed: $($p.ExitCode)" }
25 changes: 15 additions & 10 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,12 @@ jobs:
env:
GITHUB_CONTEXT: ${{ toJSON(github) }}
run: echo "$GITHUB_CONTEXT"
- name: Install wine+rpm for distribution
- name: Install rpm for distribution
if: runner.os == 'Linux'
shell: bash
run: |
sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list
sudo dpkg --add-architecture i386
sudo mkdir -pm755 /etc/apt/keyrings && sudo wget -O /etc/apt/keyrings/winehq-archive.key https://dl.winehq.org/wine-builds/winehq.key
sudo wget -NP /etc/apt/sources.list.d/ https://dl.winehq.org/wine-builds/ubuntu/dists/$(lsb_release -c -s)/winehq-$(lsb_release -c -s).sources
sudo apt-get update
sudo apt install --install-recommends winehq-stable || sudo apt install --install-recommends winehq-staging
wine --version
version="9.4.0"; sudo wget "https://dl.winehq.org/wine/wine-mono/$version/wine-mono-$version-x86.msi" -O /tmp/wine-mono.msi
wine msiexec /i /tmp/wine-mono.msi
sudo apt-get install -y rpm
- uses: actions/checkout@v6
with:
fetch-depth: 0
Expand Down Expand Up @@ -90,11 +83,22 @@ jobs:
with:
name: OpenDJ RPM Package
path: opendj-packages/opendj-rpm/opendj-rpm-standard/target/rpm/opendj/RPMS/noarch/*.rpm
# The MSI can only be built on Windows; reuse the one already built by the triggering
# Build run (windows-latest-11 artifact) instead of rebuilding it here.
- name: Download Windows build artifact (contains the MSI)
continue-on-error: true
uses: actions/download-artifact@v8
with:
name: windows-latest-11
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
path: windows-build
- name: Upload artifacts OpenDJ MSI Package
continue-on-error: true
uses: actions/upload-artifact@v7
with:
name: OpenDJ MSI Package
path: opendj-packages/opendj-msi/opendj-msi-standard/target/*.msi
path: windows-build/opendj-packages/opendj-msi/opendj-msi-standard/target/*.msi
- name: Upload artifacts OpenDJ Docker Packages
uses: actions/upload-artifact@v7
with:
Expand Down Expand Up @@ -169,3 +173,4 @@ jobs:
git commit -a -m "upload ${{github.event.repository.name}} docs after deploy ${{ github.sha }}"
git push --force https://github.com/OpenIdentityPlatform/doc.openidentityplatform.org.git
fi

Loading
Loading