diff --git a/.gitattributes b/.gitattributes index e195b512dd..464d174671 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,6 @@ +# WiX sources must stay LF so the WiX toolset parses them consistently across runners. +*.wxs text eol=lf +*.wxi text eol=lf # Keep HTML checked out with LF on all platforms so javadoc doclint # (JDK 25/26) does not treat CR (from CRLF) as part of a multi-line tag name. *.html text eol=lf diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8691a0e2c6..ae780952da 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,6 +23,11 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +# The build only checks out sources and publishes artifacts through the actions +# API; no job needs to write back to the repository. +permissions: + contents: read + jobs: build-maven: runs-on: ${{ matrix.os }} @@ -39,19 +44,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 @@ -90,6 +88,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: @@ -594,6 +605,34 @@ jobs: 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: @@ -605,12 +644,12 @@ jobs: $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: use the default install directory (a path with spaces), - # which the server scripts must handle. + # 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 (x86)\OpenDJ","C:\Program Files\OpenDJ") | Where-Object { Test-Path "$_\setup.bat" } | Select-Object -First 1 - if (-not $root) { Get-Content install.log -Tail 80; throw "OpenDJ install root with setup.bat not found" } + $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 @@ -620,8 +659,7 @@ jobs: $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" } - & "$root\bat\windows-service.bat" --enableService - if ($LASTEXITCODE -ne 0) { throw "windows-service --enableService 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 } } @@ -629,7 +667,6 @@ jobs: if ($LASTEXITCODE -ne 0) { throw "ldapsearch failed: $LASTEXITCODE" } net stop "OpenDJ Server" if ($LASTEXITCODE -ne 0) { throw "net stop failed: $LASTEXITCODE" } - & "$root\bat\windows-service.bat" --disableService - name: Uninstall MSI shell: pwsh run: | @@ -637,3 +674,91 @@ jobs: $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)" } diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index c06529245b..5b6e055401 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -33,19 +33,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 @@ -109,11 +102,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: @@ -188,3 +192,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 + diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 48d3cfb5b3..62750246f6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -38,18 +38,11 @@ jobs: env: GITHUB_CONTEXT: ${{ toJSON(github) }} run: echo "$GITHUB_CONTEXT" - - name: Install wine+rpm for distribution + - name: Install rpm for distribution 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 @@ -89,8 +82,17 @@ jobs: MAVEN_OPTS: -Dhttps.protocols=TLSv1.2 -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.requestSentEnabled=true -Dmaven.wagon.http.retryHandler.count=10 if: ${{ env.MAVEN_USERNAME!='' && env.MAVEN_PASSWORD!='' }} run: mvn --batch-mode -Darguments="-Dgpg.passphrase=${{ secrets.GPG_PASSPHRASE }}" -DsignTag=true -DtagNameFormat="${{ github.event.inputs.releaseVersion }}" -DreleaseVersion=${{ github.event.inputs.releaseVersion }} -DdevelopmentVersion=${{ github.event.inputs.developmentVersion }} release:prepare release:perform --file pom.xml + # Hand the just-released server zip to the release-msi job (the MSI can only be + # built on Windows), so it does not have to rebuild opendj-server-legacy. + - name: Upload the server zip for the MSI job + continue-on-error: true + uses: actions/upload-artifact@v7 + with: + name: release-server-zip + retention-days: 1 + path: target/checkout/opendj-server-legacy/target/package/*.zip - name: Release on GitHub - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2 with: name: ${{ github.event.inputs.releaseVersion }} tag_name: ${{ github.event.inputs.releaseVersion }} @@ -103,7 +105,6 @@ jobs: target/checkout/opendj-ldap-toolkit/target/*.zip target/checkout/opendj-packages/opendj-deb/opendj-deb-standard/target/*.deb target/checkout/opendj-packages/opendj-rpm/opendj-rpm-standard/target/rpm/opendj/RPMS/noarch/*.rpm - target/checkout/opendj-packages/opendj-msi/opendj-msi-standard/target/*.msi target/checkout/opendj-packages/opendj-docker/target/Dockerfile.zip target/checkout/opendj-packages/opendj-openshift-template/*.yaml target/checkout/opendj-doc-generated-ref/target/*.zip @@ -149,6 +150,65 @@ jobs: git tag -f ${TAG_NAME} git push --quiet --force origin ${TAG_NAME} + # The MSI can only be built on Windows. Reuses the server zip built by release-maven + # (installed into the local repo), so only the opendj-msi-standard module is built here. + # continue-on-error: an MSI failure must not break the release. + release-msi: + name: Windows MSI release + runs-on: 'windows-latest' + continue-on-error: true + needs: + - release-maven + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ github.event.inputs.releaseVersion }} + submodules: recursive + - name: Set up Java + uses: actions/setup-java@v5 + with: + java-version: '11' + distribution: 'temurin' + - name: Cache Maven packages + uses: actions/cache@v5 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-m2-repository-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2-repository + - name: Setup WiX (.NET tool) + shell: bash + run: | + 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: Download the server zip built by release-maven + uses: actions/download-artifact@v8 + with: + name: release-server-zip + path: server-zip + - name: Install the server zip into the local Maven repository + shell: bash + run: | + ZIP=$(ls server-zip/*.zip | head -1) + echo "Installing $ZIP as opendj-server-legacy:${{ github.event.inputs.releaseVersion }}:zip" + mvn --batch-mode install:install-file -Dfile="$ZIP" \ + -DgroupId=org.openidentityplatform.opendj -DartifactId=opendj-server-legacy \ + -Dversion=${{ github.event.inputs.releaseVersion }} -Dpackaging=zip + - name: Build the MSI (packaging only, no rebuild) + env: + MAVEN_OPTS: -Dhttps.protocols=TLSv1.2 -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.requestSentEnabled=true -Dmaven.wagon.http.retryHandler.count=10 + run: mvn --batch-mode --errors -DskipTests package -pl :opendj-msi-standard --file pom.xml + - name: Attach the MSI to the GitHub release + uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2 + with: + tag_name: ${{ github.event.inputs.releaseVersion }} + fail_on_unmatched_files: true + files: opendj-packages/opendj-msi/opendj-msi-standard/target/*.msi + release-docker: name: Docker release runs-on: 'ubuntu-latest' diff --git a/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-install.adoc b/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-install.adoc index f976103528..5770bfc24d 100644 --- a/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-install.adoc +++ b/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-install.adoc @@ -640,14 +640,16 @@ opendj 0:off 1:off 2:on 3:on 4:on 5:on 6:off [#install-msi] .To Install With the Windows Installer (MSI) ==== -On Windows you can install OpenDJ directory server from the `.msi` package. The installer only copies the server files to disk: it does not configure or start a server, it does not register a Windows service, and it does not install a Java runtime. +On Windows you can install OpenDJ directory server from the `.msi` package. The installer copies the server files to disk and registers the `OpenDJ Server` Windows service, but it does not configure or start a server (run `setup` first) and it does not install a Java runtime. . Make sure a supported Java runtime is available, as described in xref:#before-you-install["To Prepare For Installation"]. + -The installer does not check for Java. If your default Java environment is not appropriate, set `OPENDJ_JAVA_HOME` to the correct Java installation (or `OPENDJ_JAVA_BIN` to the absolute path of the `java` command), or make sure `java` is on the `PATH`, before you run `setup` or start the server. +The installer refuses to install when it cannot detect a Java installation: install a JRE (for example link:https://adoptium.net[Eclipse Temurin, window=\_blank]), set `JAVA_HOME` to your Java installation, or make sure the `java` executable is on the `PATH`, before running it. If your default Java environment is not the one OpenDJ should use, additionally set `OPENDJ_JAVA_HOME` to the correct Java installation (or `OPENDJ_JAVA_BIN` to the absolute path of the `java` command) before you run `setup` or start the server. . Install the package, either with the GUI or silently: + +The package is not code-signed, so Windows SmartScreen or User Account Control may warn about an unrecognized publisher; choose to run the installer anyway. ++ * GUI: double-click `opendj-{opendj-version}.msi` and follow the wizard. + * Silent: run the following command (optionally set the installation directory with the `OPENDJ` property): @@ -658,7 +660,7 @@ The installer does not check for Java. If your default Java environment is not a C:\> msiexec /i opendj-{opendj-version}.msi /quiet OPENDJ="C:\opendj" ---- + -By default the package installs under `C:\Program Files\OpenDJ` (the 32-bit installer uses `C:\Program Files (x86)\OpenDJ` on 64-bit Windows). +By default the x64 package installs under `C:\Program Files\OpenDJ`. . Configure OpenDJ directory server by running the `setup` command, described in xref:../reference/admin-tools-ref.adoc#setup-1[setup(1)] in the __Reference__, from the installation directory. Use `setup.bat` for the GUI wizard or `setup.bat --cli` for the command-line: + @@ -668,12 +670,11 @@ By default the package installs under `C:\Program Files\OpenDJ` (the 32-bit inst C:\path\to\opendj> setup.bat --cli ---- -. (Optional) Register OpenDJ as a Windows service and start it. The MSI does not register the service; use the `windows-service` command: +. Start the OpenDJ Windows service. The installer already registered it as `OpenDJ Server`; after configuring with `setup`, start it (it is not started automatically during installation): + [source, console] ---- -C:\path\to\opendj\bat> windows-service.bat --enableService C:\> net start "OpenDJ Server" ---- diff --git a/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-uninstall.adoc b/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-uninstall.adoc index 17e0413635..3adc45d381 100644 --- a/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-uninstall.adoc +++ b/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-uninstall.adoc @@ -162,17 +162,9 @@ Removing the package does not remove your data or configuration. You must remove [#uninstall-msi] .To Uninstall the Windows MSI Package ==== -Remove OpenDJ directory server installed from the `.msi` package like any other Windows program. +Remove OpenDJ directory server installed from the `.msi` package like any other Windows program. The uninstaller stops and removes the `OpenDJ Server` Windows service that the package registered. -. If OpenDJ is registered as a Windows service, remove the service first: -+ - -[source, console] ----- -C:\path\to\opendj\bat> windows-service.bat --disableService ----- - -. Uninstall the package, either through __Settings > Apps__ (or __Control Panel > Programs and Features__) by selecting OpenDJ and choosing Uninstall, or from the command-line: +* Uninstall the package, either through __Settings > Apps__ (or __Control Panel > Programs and Features__) by selecting OpenDJ and choosing Uninstall, or from the command-line: + [source, console, subs="attributes"] diff --git a/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-upgrade.adoc b/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-upgrade.adoc index c514e71c82..d91b763711 100644 --- a/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-upgrade.adoc +++ b/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-upgrade.adoc @@ -250,25 +250,19 @@ $ ==== Before starting this procedure, follow the steps in xref:#before-you-upgrade["Before You Upgrade"]. Installing the newer `.msi` performs a major upgrade that replaces the installed program files, so make a full file-system backup of the current installation first. -. Stop the current OpenDJ server. - -. If OpenDJ is registered as a Windows service, disable the service: -+ - -[source, console] ----- -C:\path\to\opendj\bat> windows-service.bat --disableService ----- +. Stop the current OpenDJ server (if it runs as a Windows service, `net stop "OpenDJ Server"`). . Back up the file-system directory where OpenDJ is installed. -. Install the newer package (GUI or silent), using the same installation directory as the current server. Your configured instance data (`config`, `db`, `logs`) is kept; only the program files are replaced: +. Install the newer package (GUI or silent). The installer detects the existing installation — the location recorded in the registry by a previous x64 package, or the default directory of the older 32-bit package (`C:\Program Files (x86)\OpenDJ`) — and installs into the same directory, so your configured instance data (`config`, `db`, `logs`) is kept and only the program files are replaced. If the older server was installed in a custom directory the installer did not detect, select that directory in the GUI or pass it explicitly: + [source, console, subs="attributes"] ---- C:\> msiexec /i opendj-{opendj-version}.msi /quiet OPENDJ="C:\path\to\opendj" ---- ++ +The installer replaces a service registered by the older `windows-service.bat` command with the MSI-managed `OpenDJ Server` service automatically; no manual `--disableService`/`--enableService` is needed. . Run the `upgrade` command, described in xref:../reference/admin-tools-ref.adoc#upgrade-1[upgrade(1)] in the __Reference__, to bring the configuration and application data up to date with the new binary and script files: + @@ -278,14 +272,12 @@ C:\> msiexec /i opendj-{opendj-version}.msi /quiet OPENDJ="C:\path\to\opendj" C:\path\to\opendj> upgrade.bat --no-prompt --acceptLicense ---- -. Start the upgraded OpenDJ server. - -. If you disabled the Windows service, enable it again: +. Start the upgraded OpenDJ server: + [source, console] ---- -C:\path\to\opendj\bat> windows-service.bat --enableService +C:\> net start "OpenDJ Server" ---- ==== diff --git a/opendj-packages/opendj-msi/opendj-msi-standard/pom.xml b/opendj-packages/opendj-msi/opendj-msi-standard/pom.xml index 807215df25..aa5ea6b003 100644 --- a/opendj-packages/opendj-msi/opendj-msi-standard/pom.xml +++ b/opendj-packages/opendj-msi/opendj-msi-standard/pom.xml @@ -13,7 +13,7 @@ information: "Portions Copyright [year] [name of copyright owner]". Copyright 2015-2016 ForgeRock AS. - Portions Copyright 2018 Open Identity Platform Community + Portions Copyright 2018-2026 3A Systems, LLC --> 4.0.0 @@ -29,265 +29,130 @@ OpenDJ MSI Standard Package - This module generates an OpenDJ MSI package. + This module generates an OpenDJ MSI package using the WiX Toolset v5 .NET tool + (cross-platform; no wine). The `wix` tool must be on the PATH (CI installs it via + `dotnet tool install --global wix`). ${basedir}/resources/msi ${project.build.directory}/${product.name.lowercase} + + ${project.build.directory}/msi-staging + ${project.build.directory}/msi-staging-lib + ${package.dir}/lib/opendj_service.exe + ${project.build.directory}/${product.name.lowercase}-${project.version}.msi - - - + + ${project.groupId}.${project.artifactId} + org.codehaus.mojo build-helper-maven-plugin - - - - org.openidentityplatform.commons - maven-external-dependency-plugin - false - - - ${project.build.directory}/dependencies/ - - false - true - false - - - openidentityplatform.org - wixtoolset - 3.11.1 - zip - - https://github.com/wixtoolset/wix3/releases/download/wix3111rtm/wix311-binaries.zip - - false - - - openidentityplatform.org - winetricks - LAST - sh - - https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks - - false - - - - - - clean-external-dependencies - clean - - clean-external - - - - resolve-install-external-dependencies - process-resources - - resolve-external - install-external - - - - deploy-external-dependencies - deploy - - deploy-external - - - - - - org.apache.maven.plugins - maven-dependency-plugin - unpack-wix + attach-msi-and-bundle package - unpack + attach-artifact - - - openidentityplatform.org - wixtoolset - 3.11.1 - zip - - - - ${project.build.directory}/wix - - - - - unpack-winetricks - package - - copy - - - - - openidentityplatform.org - winetricks - LAST - sh - - - - ${project.build.directory}/winetricks - + + ${msi.file}msi + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + org.apache.maven.plugins maven-antrun-plugin - build-msi-package-prepare + stage-msi-payload package run - - - + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ------------------- ${exec.heat} ${param.heat} ------------------- - - - - - - - - - - - - - - - - ------------------- ${exec.candle} ${param.candle} ------------------- - - - - - - - - - - - - - ------------------- ${exec.light} ${param.light} ------------------- - - - - - - - - - - - - - - - - - + + + + org.codehaus.mojo + exec-maven-plugin + + + wix-build-msi + package + + exec + + + wix + ${project.build.directory} + + build + ${msi.resources}/package.wxs + -archx64 + -extWixToolset.UI.wixext + -bindpath${msi.resources} + -dname=${product.name} + -dmajor=${parsedVersion.majorVersion} + -dminor=${parsedVersion.minorVersion} + -dpoint=${parsedVersion.incrementalVersion} + -dstagingRoot=${staging.root} + -dstagingLib=${staging.lib} + -dserviceExe=${service.exe} + -o${msi.file} + + + + + - \ No newline at end of file + diff --git a/opendj-packages/opendj-msi/opendj-msi-standard/resources/msi/package.wxs b/opendj-packages/opendj-msi/opendj-msi-standard/resources/msi/package.wxs index f77dda7a14..cfb0d85b79 100644 --- a/opendj-packages/opendj-msi/opendj-msi-standard/resources/msi/package.wxs +++ b/opendj-packages/opendj-msi/opendj-msi-standard/resources/msi/package.wxs @@ -15,45 +15,155 @@ ! Copyright 2013-2016 ForgeRock AS. ! Portions Copyright 2018-2026 3A Systems, LLC ! --> - - - - - - - + + + + + + + + - - - + + + + + + + + + + + + + + + + - - + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + - - - - - - - NOT Installed - 1 - - + + + diff --git a/opendj-packages/opendj-msi/pom.xml b/opendj-packages/opendj-msi/pom.xml index 748456f462..1611afa4bb 100644 --- a/opendj-packages/opendj-msi/pom.xml +++ b/opendj-packages/opendj-msi/pom.xml @@ -32,63 +32,10 @@ This module contains configuration and generic plugin call to build OpenDJ MSI packages. - - - /usr/bin/wine - - unix - /usr/bin/wine - - - opendj-msi-standard - - - /usr/bin/wine${project.build.directory}/wix/heat.exe - /usr/bin/wine${project.build.directory}/wix/candle.exe - /usr/bin/wine${project.build.directory}/wix/light.exe - - - - /usr/local/bin/wine - - unix - /usr/local/bin/wine - - - opendj-msi-standard - - - /usr/local/bin/wine${project.build.directory}/wix/heat.exe - /usr/local/bin/wine${project.build.directory}/wix/candle.exe - /usr/local/bin/wine${project.build.directory}/wix/light.exe - - - - /opt/local/bin/wine - - unix - /opt/local/bin/wine - - - opendj-msi-standard - - - /opt/local/bin/wine${project.build.directory}/wix/heat.exe - /opt/local/bin/wine${project.build.directory}/wix/candle.exe - /opt/local/bin/wine${project.build.directory}/wix/light.exe - - - - windows - windows - - opendj-msi-standard - - - ${project.build.directory}\wix\heat.exe - ${project.build.directory}\wix\candle.exe - ${project.build.directory}\wix\light.exe - - - + + + opendj-msi-standard + diff --git a/opendj-packages/pom.xml b/opendj-packages/pom.xml index d2148a7768..d6d05e72d9 100644 --- a/opendj-packages/pom.xml +++ b/opendj-packages/pom.xml @@ -55,7 +55,6 @@ opendj-deb opendj-rpm opendj-svr4 - opendj-msi opendj-docker @@ -69,7 +68,6 @@ opendj-svr4 - opendj-msi opendj-docker @@ -79,6 +77,8 @@ windows + opendj-msi opendj-docker diff --git a/opendj-server-legacy/lib/launcher_administrator.exe b/opendj-server-legacy/lib/launcher_administrator.exe index 50b885811d..bfb8ae4fdf 100644 Binary files a/opendj-server-legacy/lib/launcher_administrator.exe and b/opendj-server-legacy/lib/launcher_administrator.exe differ diff --git a/opendj-server-legacy/lib/opendj_service.exe b/opendj-server-legacy/lib/opendj_service.exe index 0b458d8f72..4e20ac7941 100644 Binary files a/opendj-server-legacy/lib/opendj_service.exe and b/opendj-server-legacy/lib/opendj_service.exe differ diff --git a/opendj-server-legacy/lib/winlauncher.exe b/opendj-server-legacy/lib/winlauncher.exe index 04f210cb64..d1e3eaef75 100644 Binary files a/opendj-server-legacy/lib/winlauncher.exe and b/opendj-server-legacy/lib/winlauncher.exe differ diff --git a/opendj-server-legacy/src/build-tools/windows/service.c b/opendj-server-legacy/src/build-tools/windows/service.c index 1fd7e373d0..29fbb87381 100644 --- a/opendj-server-legacy/src/build-tools/windows/service.c +++ b/opendj-server-legacy/src/build-tools/windows/service.c @@ -826,6 +826,118 @@ ServiceReturnCode createServiceBinPath(char* serviceBinPath) return returnValue; } // createServiceBinPath +// ---------------------------------------------------- +// Reads the next command line token starting at *p into out (at most +// outSize - 1 characters). A token is either a run of non-blank +// characters or a double-quoted string; quotes are treated as plain +// delimiters without escape processing, so a trailing backslash-quote +// sequence (as written by msiexec) leaves trailing backslashes on the +// token, which normalizeInstanceDir strips. Returns the position right +// after the token. +// ---------------------------------------------------- + +static const char* nextCmdToken(const char* p, char* out, int outSize) +{ + int i = 0; + while ((*p == ' ') || (*p == '\t')) + { + p++; + } + if (*p == '"') + { + p++; + while ((*p != '\0') && (*p != '"')) + { + if (i < (outSize - 1)) + { + out[i++] = *p; + } + p++; + } + if (*p == '"') + { + p++; + } + } + else + { + while ((*p != '\0') && (*p != ' ') && (*p != '\t')) + { + if (i < (outSize - 1)) + { + out[i++] = *p; + } + p++; + } + } + out[i] = '\0'; + return p; +} // nextCmdToken + +// ---------------------------------------------------- +// Strips trailing backslashes and "\." path segments from an instance +// dir so that "C:\opendj", "C:\opendj\" and "C:\opendj\." all compare +// equal. +// ---------------------------------------------------- + +static void normalizeInstanceDir(char* dir) +{ + size_t len = strlen(dir); + BOOL changed = TRUE; + while (changed && (len > 0)) + { + changed = FALSE; + if (dir[len - 1] == '\\') + { + dir[--len] = '\0'; + changed = TRUE; + } + else if ((len > 1) && (dir[len - 1] == '.') && (dir[len - 2] == '\\')) + { + dir[--len] = '\0'; + changed = TRUE; + } + } +} // normalizeInstanceDir + +// ---------------------------------------------------- +// Tells whether two service command lines refer to the same server +// instance. The strings cannot be compared verbatim because every +// writer quotes differently: this executable and the java tools write +// '"\lib\opendj_service.exe" start ""' while the MSI +// ServiceInstall writes the executable unquoted (when the path has no +// spaces) and the instance dir with a trailing backslash. Instead the +// executable path, the subcommand and the normalized instance dir are +// compared token by token, case-insensitively. +// ---------------------------------------------------- + +static BOOL serviceCmdsMatch(const char* cmd1, const char* cmd2) +{ + char exe1[COMMAND_SIZE]; + char exe2[COMMAND_SIZE]; + char sub1[COMMAND_SIZE]; + char sub2[COMMAND_SIZE]; + char dir1[COMMAND_SIZE]; + char dir2[COMMAND_SIZE]; + const char* p1 = cmd1; + const char* p2 = cmd2; + + p1 = nextCmdToken(p1, exe1, COMMAND_SIZE); + p1 = nextCmdToken(p1, sub1, COMMAND_SIZE); + nextCmdToken(p1, dir1, COMMAND_SIZE); + + p2 = nextCmdToken(p2, exe2, COMMAND_SIZE); + p2 = nextCmdToken(p2, sub2, COMMAND_SIZE); + nextCmdToken(p2, dir2, COMMAND_SIZE); + + normalizeInstanceDir(dir1); + normalizeInstanceDir(dir2); + + return (_stricmp(exe1, exe2) == 0) + && (_stricmp(sub1, sub2) == 0) + && (_stricmp(dir1, dir2) == 0); +} // serviceCmdsMatch + // ---------------------------------------------------- // Returns the service name that maps the command used to start the // product. All commands are supposed to be unique because they have @@ -866,7 +978,7 @@ ServiceReturnCode getServiceName(char* cmdToRun, char* serviceName) ServiceDescriptor curService = serviceList[i]; if (curService.cmdToRun != NULL) { - if (_stricmp(cmdToRun, curService.cmdToRun) == 0) + if (serviceCmdsMatch(cmdToRun, curService.cmdToRun)) { if (strlen(curService.serviceName) < MAX_SERVICE_NAME) {