Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 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
bc9ef7e
Merge remote-tracking branch 'origin/master' into features/windows-di…
vharseko Jul 17, 2026
72f5ac2
[#701] restrict build workflow permissions and pin action-gh-release
vharseko Jul 20, 2026
55000cc
Merge origin/master into features/windows-distribution-new
vharseko Aug 5, 2026
66a80b0
Merge remote-tracking branch 'origin/master' into features/windows-di…
vharseko Aug 5, 2026
78d6b04
Merge branch 'master' into features/windows-distribution-new
vharseko Aug 6, 2026
597c6c4
Upgrade test: take the released 5.1.2 MSI as the upgrade source
vharseko Aug 6, 2026
e108320
Refresh the checked-in Windows launcher binaries from the CI build
vharseko Aug 6, 2026
3aa24c5
Address review: harden MSI service handling, CI permissions and locat…
vharseko Aug 6, 2026
be03df2
Refresh the Windows launcher binaries; make their build reproducible …
vharseko Aug 6, 2026
ac4eeed
Refresh the launcher binaries from the first /Brepro build
vharseko Aug 6, 2026
c287e5b
Fix the two new MSI CI scenarios
vharseko Aug 7, 2026
af7d80f
Address review round 2: service ownership edge cases
vharseko Aug 7, 2026
f0c9a11
Merge remote-tracking branch 'origin/master' into features/windows-di…
vharseko Aug 7, 2026
4bf7927
Refresh opendj_service.exe for the round-2 guard changes
vharseko Aug 7, 2026
0d88657
Address review round 3: prove legacy-service ownership, ship the full…
vharseko Aug 10, 2026
1417a25
Refresh opendj_service.exe for the round-3 ownership guard
vharseko Aug 10, 2026
461644a
Address review round 4: immediate legacy-service stop, NOT OPENDJ_REG…
vharseko Aug 10, 2026
775322d
Refresh opendj_service.exe for the round-4 fail-closed ownership check
vharseko Aug 10, 2026
0b19faf
Address review round 5: ImagePath ownership proof, refuse upgrade on …
vharseko Aug 10, 2026
252d5ba
Refresh opendj_service.exe for the round-5 SCM-failure and REG_EXPAND…
vharseko Aug 10, 2026
24b1b7e
Commit the rebuilt Windows launchers from the deploy run
vharseko Aug 11, 2026
b2de060
Revert the MSI to the legacy service registration scheme
vharseko Aug 11, 2026
92b30bd
Refuse the upgrades that would destroy an installation, and stop lyin…
vharseko Aug 11, 2026
1cf5a88
Close the gaps the seventh review found in the upgrade guards and the…
vharseko Aug 12, 2026
10c99b9
State the Package/Deploy trust boundary in the job condition
vharseko Aug 12, 2026
90c4271
Close the two silent-failure defects the eighth review found in the u…
vharseko Aug 12, 2026
20a77b6
Keep Restart Manager from restarting the service the upgrade stopped
vharseko Aug 12, 2026
ff54e5f
Stop asking a 32-bit MSI to install into the 64-bit Program Files
vharseko Aug 12, 2026
99a0116
Close the ninth review round: relocation guard, in-use detection, CI …
vharseko Aug 12, 2026
777b773
Make the named-directory search public, as WiX requires, without reop…
vharseko Aug 12, 2026
e6f0a44
Put Restart Manager back to Disable: DisableShutdown still restarts
vharseko Aug 12, 2026
59b7570
Make the upgrade guards able to refuse at all
vharseko Aug 13, 2026
6bcada5
Read the guard authoring instead of the MSI tables in test-msi
vharseko Aug 13, 2026
ad4ba90
Keep the MSI modules in every reactor so releases keep versioning them
vharseko Aug 13, 2026
c22a4da
Refuse an upgrade whose target cannot prove it is the installation
vharseko Aug 13, 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
5 changes: 5 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# WiX sources must stay LF so the WiX toolset parses them consistently across runners.
*.wxs text eol=lf
# The checked-in Windows launcher/service binaries are actively maintained: never diff,
# merge or eol-convert them.
*.exe binary
# 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
42 changes: 42 additions & 0 deletions .github/scripts/wait-server-stopped.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# The contents of this file are subject to the terms of the Common Development and
# Distribution License (the License). You may not use this file except in compliance with the
# License.
#
# You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
# specific language governing permission and limitations under the License.
#
# When distributing Covered Software, include this CDDL Header Notice in each file and include
# the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
# Header, with the fields enclosed by brackets [] replaced by your own identifying
# information: "Portions copyright [year] [name of copyright owner]".
#
# Copyright 2026 3A Systems, LLC.

# Verify a stop took effect before moving on: wait until the server releases the exclusive
# byte-range lock it holds on locks\server.lock. Checking the exit code of stop-ds is not a
# substitute - #768 was exactly the case where winlauncher.exe reported success without
# having stopped the server - and starting the service on a lock the old JVM still holds
# fails in ways that look like flakiness.
#
# The explicit Lock(0, 1) probe is required: a byte-range lock does not prevent opening the
# file, so a bare Open() would always succeed.
#
# Dot-source this file to use it: . .github\scripts\wait-server-stopped.ps1

function Wait-ServerStopped($lockFile) {
# Callers pass either a workspace-relative path (the zip build) or an absolute one (an
# installed tree), so only resolve the relative ones.
if (-not [System.IO.Path]::IsPathRooted($lockFile)) { $lockFile = Join-Path $PWD $lockFile }
for ($i = 0; $i -lt 30; $i++) {
if (-not (Test-Path $lockFile)) { return }
# IOException only - that is what both a held byte-range lock and a sharing
# violation raise. A blanket catch would also swallow UnauthorizedAccessException,
# spin out the full minute on a permissions problem under Program Files and then
# report a lock that was never held; let anything else surface with its own message.
try {
$fs = [System.IO.File]::Open($lockFile, 'Open', 'ReadWrite', 'ReadWrite')
try { $fs.Lock(0, 1); $fs.Unlock(0, 1); return } finally { $fs.Close() }
} catch [System.IO.IOException] { Start-Sleep -Seconds 2 }
}
throw "The server still holds the lock on ${lockFile}: the stop did not take effect"
}
826 changes: 783 additions & 43 deletions .github/workflows/build.yml

Large diffs are not rendered by default.

137 changes: 126 additions & 11 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,36 +26,136 @@ concurrency:

# contents: write is required to push the generated documentation to the project wiki
# with github.token. The doc site push uses a separate PAT, not this token.
# actions: read is required to download the MSI artifact from the triggering Build run
# (a permissions block sets every unlisted scope to none).
permissions:
contents: write
actions: read

jobs:
package-deploy-maven:
if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event=='push'}}
# head_repository states the trust boundary instead of leaving it to be re-derived.
# The checkout below takes its ref from the triggering run, and the branches filter
# above matches that run's head branch NAME - which a fork can also call master. What
# actually keeps the ref trusted is event=='push': a Build run for a pull request
# carries event 'pull_request', and a push to a fork runs the fork's own workflows,
# never ours. The repository check makes that explicit for the next reader, and for
# the next person tempted to relax the event condition.
if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' && github.event.workflow_run.head_repository.full_name == github.repository }}
runs-on: 'ubuntu-latest'
steps:
- name: Print github context
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
submodules: recursive
ref: ${{ github.event.workflow_run.head_branch }}
# The committed opendj-server-legacy/lib/*.exe are what every Linux-built server zip
# ships - the snapshots this job publishes, and later the tagged releases and their
# Maven Central artifacts - while only a Windows job can rebuild them. Nothing used
# to make the two meet, so a native source change that was never re-committed as a
# refreshed binary shipped the old wrapper while CI stayed green (master carried such
# a gap for weeks). The triggering Build run compiled them from source already, so
# take its binaries and commit them here rather than rebuild.
#
# Here rather than in build.yml: this workflow already holds contents: write for the
# wiki push, so build-maven - which runs the whole Maven plugin tree - stays
# read-only, and it only runs at all once the Build succeeded on a push to a release
# branch. The cost is latency: the refresh lands after the full matrix, not minutes
# into it. Committing before the Maven steps below also means the snapshot zip this
# job publishes carries the fresh launchers.
#
# This only works because the Makefile passes /Brepro to both cl and link: the output
# is a function of the sources, not of the build time. Without it every run would
# produce different bytes and this would commit on every push. An MSVC toolchain bump
# on the runner image does change them, and that refresh commit is correct - the
# committed binary then matches what CI verifies. Pushes made with GITHUB_TOKEN do
# not start new workflow runs, so this cannot loop; a PAT would break that.
- name: Download the launchers built by the triggering Build run
continue-on-error: true
uses: actions/download-artifact@v8
with:
name: windows-exe-11
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
path: ${{ runner.temp }}/windows-exe
- name: Commit the rebuilt launchers
shell: bash
env:
# NOT github.ref: on a workflow_run event that is the default branch, not the
# branch the triggering run was for.
BRANCH: ${{ github.event.workflow_run.head_branch }}
BUILT: ${{ runner.temp }}/windows-exe
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
RUN_ID: ${{ github.event.workflow_run.id }}
run: |
set -e
if ! ls "$BUILT"/*.exe >/dev/null 2>&1; then
echo "::warning title=No launcher binaries from the Build run::windows-exe-11 could not be downloaded, leaving opendj-server-legacy/lib/*.exe as committed."
exit 0
fi
cp "$BUILT"/*.exe opendj-server-legacy/lib/
# status --porcelain, not diff: it reports a brand-new launcher that was never
# git-added just as well as a modified one.
if [ -z "$(git status --porcelain -- opendj-server-legacy/lib)" ]; then
echo "Committed launchers already match the sources."
exit 0
fi
git status --porcelain -- opendj-server-legacy/lib
git config user.name "Open Identity Platform Community"
git config user.email "open-identity-platform-opendj@googlegroups.com"
git add -- opendj-server-legacy/lib
git commit --quiet \
-m "Refresh the Windows native launchers" \
-m "Rebuilt from opendj-server-legacy/src/build-tools/windows for ${HEAD_SHA} by the Build workflow (run ${RUN_ID})."
# The checkout is of the branch, which may have moved on since the Build run, and
# it can move again while we push: rebase onto the current tip and retry. An
# identical refresh already there leaves an empty commit that rebase drops, and
# the push then has nothing to send.
#
# This step runs before the Maven deploy, the package uploads and the wiki push,
# so it must not be the thing that costs them: a refresh that cannot be landed
# warns and lets the job carry on. The next push to this branch retries it, and
# nothing downstream depends on the committed binaries being current - the Build
# run that produced them compiled its own.
for attempt in 1 2 3; do
# Guarded like everything else in this block: bare, it is the one command left
# that could still take the job down with it. The step runs under set -e with
# no continue-on-error, so a transient fetch failure would skip the Maven
# deploy, all eight artifact uploads, the MSI attachment and both documentation
# pushes over a refresh that is allowed to fail.
if ! git fetch --quiet origin "$BRANCH"; then
echo "::warning title=Could not refresh the launcher binaries::$BRANCH could not be fetched. Refresh opendj-server-legacy/lib/*.exe from the windows-exe-11 artifact of Build run ${RUN_ID} and commit them."
exit 0
fi
if ! git rebase --quiet FETCH_HEAD; then
git rebase --abort || true
echo "::warning title=Could not refresh the launcher binaries::$BRANCH moved on and the rebuilt launchers conflict with it. Refresh opendj-server-legacy/lib/*.exe from the windows-exe-11 artifact of Build run ${RUN_ID} and commit them."
exit 0
fi
if git push --quiet origin "HEAD:refs/heads/$BRANCH"; then
# A refresh that already landed leaves the rebase with nothing to replay and
# the push with nothing to send, both of them silently successful: report
# what happened rather than claiming a push that was a no-op.
if [ "$(git rev-parse HEAD)" = "$(git rev-parse FETCH_HEAD)" ]; then
echo "The launchers committed on $BRANCH already match the rebuilt ones."
else
echo "Refreshed launchers pushed to $BRANCH."
fi
exit 0
fi
echo "$BRANCH moved while pushing - retrying ($attempt/3)."
done
echo "::warning title=Could not refresh the launcher binaries::$BRANCH kept moving under this job. Refresh opendj-server-legacy/lib/*.exe from the windows-exe-11 artifact of Build run ${RUN_ID} and commit them."
- name: Set up Java for publishing to Maven Central Repository OSS
uses: actions/setup-java@v5
with:
Expand Down Expand Up @@ -114,11 +214,25 @@ 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
# Make a silently-missing MSI visible: the step fails (job continues via
# continue-on-error) instead of warning and publishing nothing.
if-no-files-found: error
- name: Upload artifacts OpenDJ Docker Packages
uses: actions/upload-artifact@v7
with:
Expand Down Expand Up @@ -193,3 +307,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