From b6db4d342bdd1845c7ff5740ef69e6bf5afaf091 Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Fri, 04 Sep 2026 08:12:29 +0000
Subject: [PATCH] Modernize Debian and RPM packaging (systemd, dedicated user, CI tests) (#663)
---
.github/workflows/build.yml | 237 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 236 insertions(+), 1 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 90ecc71..efb811e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -24,7 +24,8 @@
cancel-in-progress: true
# Nothing in this workflow writes back to the repository: the docker jobs push to
-# the local registry service, not to a remote one. The docker jobs additionally get
+# the local registry service, not to a remote one, and the other jobs only
+# publish artifacts through the actions API. The docker jobs additionally get
# security-events: write to upload Trivy scan results to code scanning.
permissions:
contents: read
@@ -680,6 +681,240 @@
if-no-files-found: warn
retention-days: 90
+ test-deb:
+ needs: build-maven
+ runs-on: 'ubuntu-latest'
+ steps:
+ - uses: actions/checkout@v6
+ - name: Download artifacts
+ uses: actions/download-artifact@v8
+ with:
+ name: ubuntu-latest-11
+ - name: Clean-room install + SysV start/stop (debian:12 container)
+ shell: bash
+ run: |
+ docker run --rm -v "$PWD:/work" -w /work debian:12 bash -c '
+ set -e
+ export DEBIAN_FRONTEND=noninteractive
+ DEB=$(ls opendj-packages/opendj-deb/opendj-deb-standard/target/*.deb | head -1)
+ echo "Found $DEB"
+ apt-get update
+ apt-get install -y lintian
+ lintian --info --no-tag-display-limit "$DEB" || true
+ dpkg-deb -I "$DEB"
+ # Both service files must be present (two greps: a single alternation
+ # would pass with either one missing).
+ dpkg-deb -c "$DEB" | grep "lib/systemd/system/opendj.service"
+ dpkg-deb -c "$DEB" | grep "etc/init.d/opendj"
+ # /etc/default/opendj must be a real conffile, so admin edits survive
+ # upgrades and the file survives "apt remove".
+ dpkg-deb -e "$DEB" /tmp/ctrl
+ grep -qx "/etc/default/opendj" /tmp/ctrl/conffiles
+ apt-get install -y "./$DEB"
+ id opendj
+ test "$(stat -c %U /opt/opendj)" = opendj
+ # No JAVA_HOME in this clean container: Java resolves from the PATH
+ # java that the package JRE dependency pulled in.
+ runuser -u opendj -- /opt/opendj/setup --cli --no-prompt --acceptLicense --doNotStart \
+ --rootUserDN "cn=Directory Manager" --rootUserPassword password \
+ --hostname localhost --ldapPort 1389 --adminConnectorPort 4444 \
+ --baseDN dc=example,dc=com --addBaseEntry
+ /etc/init.d/opendj start
+ bash .github/scripts/wait-for-ldap.sh 1389
+ /etc/init.d/opendj status
+ /etc/init.d/opendj stop
+ apt-get purge -y opendj
+ '
+ - name: Live systemd install + start/stop (runner)
+ shell: bash
+ run: |
+ DEB=$(ls opendj-packages/opendj-deb/opendj-deb-standard/target/*.deb | head -1)
+ sudo apt-get update
+ sudo apt-get install -y "$PWD/$DEB"
+ test "$(stat -c '%U' /opt/opendj)" = opendj
+ # postinst must have enabled the unit; do not enable by hand here or
+ # the postinst registration would go untested.
+ sudo systemctl is-enabled --quiet opendj
+ # Before setup the unit's start condition must keep it from
+ # start-bursting: "start" succeeds but no process may appear.
+ sudo systemctl start opendj
+ sleep 2
+ if sudo systemctl is-failed --quiet opendj; then echo "unit failed before setup"; exit 1; fi
+ # sudo/runuser/systemd strip JAVA_HOME -> the service uses the PATH java.
+ # Privileged port 389: only AmbientCapabilities=CAP_NET_BIND_SERVICE lets
+ # the non-root service bind it - the very reason it can drop root. setup
+ # itself runs via runuser WITHOUT that capability, so it must skip its
+ # own bind probe; the systemd start below performs the real 389 bind.
+ sudo runuser -u opendj -- /opt/opendj/setup --cli --no-prompt --acceptLicense --doNotStart \
+ --rootUserDN "cn=Directory Manager" --rootUserPassword password \
+ --hostname localhost --ldapPort 389 --adminConnectorPort 4444 \
+ --baseDN dc=example,dc=com --addBaseEntry --skipPortCheck
+ sudo systemctl start opendj
+ bash .github/scripts/wait-for-ldap.sh 389
+ sudo systemctl is-active --quiet opendj
+ echo "OpenDJ is active under systemd"
+ sudo systemctl stop opendj
+ sleep 3
+ if sudo systemctl is-active --quiet opendj; then echo "still active"; exit 1; fi
+ sudo apt-get purge -y opendj
+
+ # Known coverage limit: the container has no booted systemd (/run/systemd/system
+ # is absent), so only the SysV/chkconfig path of the scriptlets is exercised
+ # here; start/stop through systemd is covered by the deb live-systemd jobs on
+ # the runner. The enable registration IS asserted below - systemctl enable is
+ # a pure symlink operation and works without a booted systemd.
+ test-rpm:
+ needs: build-maven
+ runs-on: 'ubuntu-latest'
+ steps:
+ - uses: actions/checkout@v6
+ - name: Download artifacts
+ uses: actions/download-artifact@v8
+ with:
+ name: ubuntu-latest-11
+ - name: Install and start/stop in Rocky Linux 9
+ shell: bash
+ run: |
+ docker run --rm -v "$PWD:/work" -w /work rockylinux:9 bash -c '
+ set -e
+ RPM=$(ls opendj-packages/opendj-rpm/opendj-rpm-standard/target/rpm/opendj/RPMS/noarch/*.rpm | head -1)
+ echo "Found $RPM"
+ # Both service files must be shipped.
+ rpm -qlp "$RPM" | grep "^/usr/lib/systemd/system/opendj.service$"
+ rpm -qlp "$RPM" | grep "^/etc/init.d/opendj$"
+ # The %pre user-creation dependency must be declared for minimal images.
+ rpm -qp --requires "$RPM" | grep "shadow-utils"
+ # A real EL9 host always has systemd, but the container image ships no
+ # systemctl at all - %post then (correctly) skips the enable in silence.
+ # Preinstall it as host infrastructure, deliberately NOT a package
+ # Requires, so the is-enabled assertion below tests the enable path.
+ dnf install -y systemd
+ # No further manual dependencies: the package must pull everything else
+ # itself (Requires).
+ dnf install -y "$RPM"
+ id opendj
+ test "$(stat -c %U /opt/opendj)" = opendj
+ # %post must have enabled the unit on initial install.
+ systemctl is-enabled opendj.service
+ # No JAVA_HOME here: Java resolves from the PATH java pulled in by
+ # the java-headless dependency.
+ runuser -u opendj -- /opt/opendj/setup --cli --no-prompt --acceptLicense --doNotStart \
+ --rootUserDN "cn=Directory Manager" --rootUserPassword password \
+ --hostname localhost --ldapPort 1389 --adminConnectorPort 4444 \
+ --baseDN dc=example,dc=com --addBaseEntry
+ /etc/init.d/opendj start
+ bash .github/scripts/wait-for-ldap.sh 1389
+ /etc/init.d/opendj status
+ /etc/init.d/opendj stop
+ rpm -e opendj
+ '
+
+ # Upgrade path: released 5.1.2 deb (root-owned, SysV) -> this build's deb, on the
+ # runner's LIVE systemd. The new package must stop the running server, create the
+ # opendj user, migrate ownership, run the upgrade tool and restart the server with
+ # the old data - through deb-systemd-invoke and the native unit, the path a real
+ # systemd host takes (docs: chap-upgrade).
+ test-deb-upgrade:
+ needs: build-maven
+ runs-on: 'ubuntu-latest'
+ steps:
+ - uses: actions/checkout@v6
+ - name: Download artifacts
+ uses: actions/download-artifact@v8
+ with:
+ name: ubuntu-latest-11
+ - name: Download released 5.1.2 deb
+ shell: bash
+ run: |
+ curl -fsSL -o opendj-5.1.2.deb https://github.com/OpenIdentityPlatform/OpenDJ/releases/download/5.1.2/opendj_5.1.2-1_all.deb
+ # Pin the upgrade source: a retagged release asset must fail loudly here,
+ # not surface as an unrelated upgrade-job failure.
+ echo "8f7f8bdd526b2d63eaef0621545be6aa55749cb60200d3bc2a2849d9ab69eb59 opendj-5.1.2.deb" | sha256sum -c -
+ - name: Upgrade 5.1.2 -> new deb (live systemd on the runner)
+ shell: bash
+ run: |
+ NEW=$(ls opendj-packages/opendj-deb/opendj-deb-standard/target/*.deb | head -1)
+ echo "New deb: $NEW"
+ sudo apt-get update
+ # No manual dependencies: even the released 5.1.2 deb declares a JRE dependency.
+ sudo apt-get install -y "$PWD/opendj-5.1.2.deb"
+ # 5.1.2 model: no dedicated user, root-owned tree, SysV only - on this
+ # booted host its unit comes from the systemd-sysv-generator.
+ sudo /opt/opendj/setup --cli --no-prompt --acceptLicense --doNotStart \
+ --rootUserDN "cn=Directory Manager" --rootUserPassword password \
+ --hostname localhost --ldapPort 1389 --adminConnectorPort 4444 \
+ --baseDN dc=example,dc=com --addBaseEntry
+ sudo systemctl daemon-reload
+ sudo /etc/init.d/opendj start
+ bash .github/scripts/wait-for-ldap.sh 1389
+ # Leave the server RUNNING: the new package must stop it, upgrade and
+ # restart it - through deb-systemd-invoke and the native unit this time.
+ sudo apt-get install -y "$PWD/$NEW"
+ id opendj
+ test "$(stat -c %U /opt/opendj)" = opendj
+ test -f /opt/opendj/config/config.ldif
+ # The systemd-hosted upgrade must leave the unit enabled and active.
+ sudo systemctl is-enabled --quiet opendj
+ sudo systemctl is-active --quiet opendj
+ # The pre-upgrade data must be served again, now by the dedicated user.
+ bash .github/scripts/wait-for-ldap.sh 1389
+ test "$(stat -c %U /proc/$(cat /opt/opendj/logs/server.pid))" = opendj
+ sudo systemctl stop opendj
+ sleep 3
+ if sudo systemctl is-active --quiet opendj; then echo "still active"; exit 1; fi
+ sudo apt-get purge -y opendj
+
+ test-rpm-upgrade:
+ needs: build-maven
+ runs-on: 'ubuntu-latest'
+ steps:
+ - uses: actions/checkout@v6
+ - name: Download artifacts
+ uses: actions/download-artifact@v8
+ with:
+ name: ubuntu-latest-11
+ - name: Download released 5.1.2 rpm
+ shell: bash
+ run: |
+ curl -fsSL -o opendj-5.1.2.rpm https://github.com/OpenIdentityPlatform/OpenDJ/releases/download/5.1.2/opendj-5.1.2-1.noarch.rpm
+ # Pin the upgrade source: a retagged release asset must fail loudly here,
+ # not surface as an unrelated upgrade-job failure.
+ echo "9622ef7c3292f0c156c96029a647ca704064aa93b567e5608734fe07d224ec58 opendj-5.1.2.rpm" | sha256sum -c -
+ - name: Upgrade 5.1.2 -> new rpm (Rocky Linux 9 container)
+ shell: bash
+ run: |
+ docker run --rm -v "$PWD:/work" -w /work rockylinux:9 bash -c '
+ set -e
+ NEW=$(ls opendj-packages/opendj-rpm/opendj-rpm-standard/target/rpm/opendj/RPMS/noarch/*.rpm | head -1)
+ echo "New rpm: $NEW"
+ # No manual dependencies: the released 5.1.2 rpm already declares its runtime
+ # requirements (#677), and everything the NEW package needs must come from its
+ # own Requires.
+ dnf install -y ./opendj-5.1.2.rpm
+ # 5.1.2 model: no dedicated user, root-owned tree, SysV only
+ /opt/opendj/setup --cli --no-prompt --acceptLicense --doNotStart \
+ --rootUserDN "cn=Directory Manager" --rootUserPassword password \
+ --hostname localhost --ldapPort 1389 --adminConnectorPort 4444 \
+ --baseDN dc=example,dc=com --addBaseEntry
+ /etc/init.d/opendj start
+ bash .github/scripts/wait-for-ldap.sh 1389
+ # Leave the server RUNNING: the new package must stop it, upgrade and restart it.
+ dnf install -y "./$NEW"
+ id opendj
+ test "$(stat -c %U /opt/opendj)" = opendj
+ test -f /opt/opendj/config/config.ldif
+ # 5.1.2 was chkconfig-enabled and the native unit now shadows the
+ # sysv-generator unit: %post must have migrated that enable state
+ # (a pure symlink operation, asserted fine without booted systemd).
+ systemctl is-enabled opendj.service
+ # The package restarted the server; the pre-upgrade data must be served again,
+ # now by the dedicated user.
+ bash .github/scripts/wait-for-ldap.sh 1389
+ test "$(stat -c %U /proc/$(cat /opt/opendj/logs/server.pid))" = opendj
+ /etc/init.d/opendj stop
+ rpm -e opendj
+ '
+
test-msi:
needs: build-maven
runs-on: 'windows-latest'
--
Gitblit v1.10.0