Copy jenkins-ci-rme into the source tree and adapt it to this project

- Jenkinsfile: guez-only release pipeline; the prebuilt build image is
  built from the in-tree Dockerfile (via new_docker_build.sh
  --build-prebuilt-image) instead of a pre-pulled harbor image; the
  builder runs inside the container with the workspace mounted at
  /workspace; the post-publish cleanup stage was dropped so the DEBs
  stay under ${WORKSPACE}/rpms/... (the documented output location)
- jenkins_builder_ubuntu26.sh: uses the in-tree rpmbuild/
  make_rpm_ubuntu26.py instead of the outer-repo copy; same-file guard
  on the packages-out copy (host output dir == make_rpm output dir)
- jenkins_publisher_ubuntu26.sh: uploads rpms/${PROJECT}/${OS_VERSION}/
  directly - make_rpm_ubuntu26.py writes the DEBs there without a TDS
  subfolder
- release-16-10-2026.sh: local driver adapted to the current
  new_docker_build.sh interface (drops the no-longer-existing
  --use-prebuilt-image option, builds the image on demand, outputs to
  ${WORKSPACE}/rpms/${PROJECT}/${OS_VERSION}/)
- make_rpm_ubuntu26.py: stage the new jenkins-ci-rme/ files (Jenkinsfile,
  builder, publisher) into the build-pipeline DEB (release-16-10-2026.sh
  was already listed)

Not copied (superseded): openSUSE builder/publisher, the old Dockerfile
recipes (replaced by the in-tree Dockerfile), Bitbucket notification
(jenkins_notifier.sh is not part of this project)
This commit is contained in:
berrux-dev committed 2026-10-14 16:28:03 +02:00
1 parent bf3fecf5c0
commit 2d1b12b5d8
6 files changed
+426

No files matched your search

@@ -0,0 +1,141 @@
// Release pipeline for the Qt6-migrated legatus validation-scope branch
// (story/release-validation-scope): TetraIF, Z, Lts/Common (+
// Lts/Proxies/TrainingAreaBase) -> 5 release DEB packages.
//
// Workspace layout (checked out by Jenkins, outer pipeline repo):
// <WORKSPACE>/
// rti_license.dat
// external/
// qt-migration-legatus/qt6-after/qt6-migrated-legatus/ <- LEGATUS_ROOT
// Dockerfile (prebuilt build image recipe)
// new_docker_build.sh (local build driver)
// rpmbuild/make_rpm_ubuntu26.py (in-tree packaging)
// jenkins-ci-rme/ (this pipeline)
//
// The build container image is the prebuilt image built from the in-tree
// Dockerfile; it is built on the agent when not present (RebuildImage
// forces a fresh build).
def project = 'guez'
def legatusRoot = 'qt-migration-legatus/qt6-after/qt6-migrated-legatus'
def imageName = 'legatus-qt6-prebuilt:latest'
properties([
disableConcurrentBuilds(),
parameters([
string(name: 'OsVersions', defaultValue: '26.04', description: 'Comma-separated Ubuntu base versions, all built in parallel'),
string(name: 'LtsVersion', defaultValue: '1.0', description: 'LTS version (major.minor) passed to make_rpm_ubuntu26.py --version'),
booleanParam(name: 'ClearWorkspace', defaultValue: false, description: 'Clear entire workspace on all nodes used for build'),
booleanParam(name: 'PublishArtifactRepo', defaultValue: true, description: 'Upload produced DEBs to the internal artifact repository'),
booleanParam(name: 'RebuildImage', defaultValue: false, description: 'Rebuild the prebuilt build image from the in-tree Dockerfile (default: reuse when present)')
])
])
env.GIT_TERMINAL_PROMPT = '0'
env.LTS_VERSION = "${params.LtsVersion}"
env.ARTIFACTREPO = 'https://internal-repo.sl.defence-elec.de'
// OS versions this build covers; every build is isolated under
// rpms/${project}/${osVersion} so concurrent OS builds never collide.
def osVersions = params.OsVersions.split(',').collect { it.trim() }.findAll { it }
def builds = [:]
osVersions.each { osVersion ->
def rpmsDir = "rpms/${project}/${osVersion}"
builds["${project} (${osVersion})"] = {
stage("Prepare ${project} (${osVersion})") {
sh "rm -rf ${rpmsDir}"
sh "mkdir -p ${rpmsDir}"
}
stage("Prepare build image ${project} (${osVersion})") {
if (params.RebuildImage) {
sh "docker rmi ${imageName} || true"
}
// The in-tree driver owns the Dockerfile build args (APT_PACKAGES,
// QT_VERSION, ...) - single source of truth.
sh """
if docker image inspect ${imageName} >/dev/null 2>&1; then
echo "Reusing existing prebuilt image ${imageName}"
else
echo "Building prebuilt image ${imageName} from the in-tree Dockerfile..."
cd ${legatusRoot} && bash ./new_docker_build.sh --build-prebuilt-image --docker-image ${imageName}
fi
"""
}
stage("Build DEB ${project} (${osVersion})") {
def buildContainer = docker.image("${imageName}")
def guezMode = (project == 'guez') ? 'ON' : 'OFF'
def dockerOpts = "--init --shm-size=2g" +
" -v ${WORKSPACE}:/workspace" +
" -e WORKSPACE=/workspace" +
" -e PROJECT_BASE_DIR=/workspace" +
" -e PROJECT=${project}" +
" -e OS_VERSION=${osVersion}" +
" -e LTS_VERSION=${LTS_VERSION}" +
" -e GUEZ_MODE=${guezMode}" +
" -e BUILD_NUMBER=${env.BUILD_NUMBER}" +
" -e BUILD_TAG=${env.BUILD_TAG}"
buildContainer.inside(dockerOpts) {
sh "bash /workspace/${legatusRoot}/jenkins-ci-rme/jenkins_builder_ubuntu26.sh"
}
}
stage("Publish ${project} (${osVersion})") {
if (params.PublishArtifactRepo) {
withCredentials([usernamePassword(credentialsId: 'internal-repo-uploader',
usernameVariable: 'CURL_USER',
passwordVariable: 'CURL_PASS')]) {
sh """
export CURL_CRED="\${CURL_USER}:\${CURL_PASS}"
export PROJECT='${project}'
export OS_VERSION='${osVersion}'
export LTS_VERSION='${LTS_VERSION}'
export BUILD_NUMBER='${env.BUILD_NUMBER}'
export ARTIFACTREPO='${env.ARTIFACTREPO}'
export ARTIFACTFOLDER='${project}'
bash ${legatusRoot}/jenkins-ci-rme/jenkins_publisher_ubuntu26.sh
"""
}
def tdsUrl = readFile("url-${project}-${osVersion}.txt").trim()
def tdsVersion = readFile("tdsversion-${project}-${osVersion}.txt").trim()
def link = """<a href="${tdsUrl}" target="_blank">${project} ${tdsVersion} (OS ${osVersion})</a>"""
currentBuild.description = currentBuild.description ? "${currentBuild.description}<br/>${link}" : link
} else {
echo "Artifact-repo publish skipped for ${project} (OS ${osVersion}, PublishArtifactRepo=false)"
}
}
}
}
try {
stage("Initialization") {
if (params.ClearWorkspace) {
cleanWs()
checkout([
$class: 'GitSCM',
branches: scm.branches,
doGenerateSubmoduleConfigurations: false,
extensions: [[$class: 'SubmoduleOption', disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: '', trackingSubmodules: false]],
userRemoteConfigs: scm.userRemoteConfigs
])
}
echo "Legatus Qt6 release build initialized — branch '${env.BRANCH_NAME}', project: ${project}, LTS ${env.LTS_VERSION}, OS version(s): ${osVersions.join(', ')}"
}
parallel builds
stage("Finalization") {
echo "Legatus Qt6 release build completed — project: ${project} (LTS ${env.LTS_VERSION}), OS version(s): ${osVersions.join(', ')}"
}
currentBuild.result = 'SUCCESS'
} catch (e) {
currentBuild.result = 'FAILURE'
throw e
}
@@ -0,0 +1,54 @@
# jenkins-ci-rme — release pipeline for the Qt6 validation-scope branch
In-tree copy of the Jenkins CI flow, adapted so the release pipeline depends
only on files inside this source tree:
| File | Purpose |
|---|---|
| `Jenkinsfile` | Jenkins pipeline: prepare the prebuilt image (in-tree `Dockerfile`), run the builder inside the container, publish the DEBs to the artifact repo |
| `jenkins_builder_ubuntu26.sh` | Runs **inside** the build container: stages the workspace, runs the in-tree `rpmbuild/make_rpm_ubuntu26.py`, copies the DEBs to the packages-out mount |
| `jenkins_publisher_ubuntu26.sh` | Runs on the Jenkins agent: uploads the DEBs from `${WORKSPACE}/rpms/${PROJECT}/${OS_VERSION}/` to the internal artifact repository |
| `release-16-10-2026.sh` | Local driver: the same flow without Jenkins, through the in-tree `new_docker_build.sh` |
## Not included (superseded)
- `jenkins_builder.sh` / `jenkins_publisher.sh` — openSUSE/RPM flow, not used
by this branch (DEB/Ubuntu 26.04 only)
- `Dockerfile-15.1` / `Dockerfile-ubuntu26.04` / `Dockerfile-ubuntu26-qt6` —
old image recipes; the prebuilt build image is built from the in-tree
`Dockerfile` (one level up)
- `jenkins_notifier.sh` — Bitbucket build-status notification, not part of
this project
## Adaptations vs. the outer-repo copy
- Packaging uses the **in-tree** `rpmbuild/make_rpm_ubuntu26.py`
(was `${WORKSPACE}/rpmbuild` from the outer repo)
- The prebuilt build image is built from the **in-tree** `Dockerfile` via
`new_docker_build.sh --build-prebuilt-image` (was a pre-pulled harbor
image) — build args stay in `new_docker_build.sh` (single source of truth)
- `MIGRATE_PIPELINE_ROOT` is no longer needed: `new_docker_build.sh`
derives it from its own location
- DEB output path: `${WORKSPACE}/rpms/${PROJECT}/${OS_VERSION}/` (Jenkins
convention); the packages-out copy is skipped when that directory is the
make_rpm output dir (same device:inode guard as in `new_docker_build.sh`)
- Publisher uploads `${WORKSPACE}/rpms/${PROJECT}/${OS_VERSION}/` directly —
`make_rpm_ubuntu26.py` writes the DEBs there without a TDS subfolder
- The post-publish Cleanup stage was dropped: the DEBs are kept under
`${WORKSPACE}/rpms/...` (the documented output location)
## Local run
```bash
cd qt6-migrated-legatus
bash ./jenkins-ci-rme/release-16-10-2026.sh
# DEBs: <pipeline-root>/rpms/guez/26.04/
```
## Jenkins setup
The job SCM must be the outer pipeline repository (the Jenkinsfile lives in
the `qt-migration-legatus` submodule; point the job's script path at
`qt-migration-legatus/qt6-after/qt6-migrated-legatus/jenkins-ci-rme/Jenkinsfile`).
Required Jenkins credential: `internal-repo-uploader` (username/password for
the artifact repository).
@@ -0,0 +1,94 @@
#!/usr/bin/env bash
#
# Build the LTS DEB packages for the Qt6-migrated legatus release
# (validation scope: TetraIF, Z, Lts/Common + Lts/Proxies/TrainingAreaBase)
# for Ubuntu 26.04.
#
# Runs inside the prebuilt build container (built from the in-tree
# Dockerfile) with the pipeline workspace mounted at /workspace.
# Packaging is done by the IN-TREE rpmbuild copy
# (qt-migration-legatus/qt6-after/qt6-migrated-legatus/rpmbuild/), so the
# release flow needs no files outside the source tree.
#
# Required environment (set by the Jenkinsfile or `docker run -e ...`):
# PROJECT LTS flavor: guez
# OS_VERSION Ubuntu version, e.g. 26.04
# LTS_VERSION Major.minor LTS version, e.g. 1.0
# GUEZ_MODE ON | OFF — cmake -DGUEZ_MODE
# WORKSPACE Container workspace path (/workspace)
# PROJECT_BASE_DIR Staging root inside the container (/workspace)
# BUILD_NUMBER Build number (used as release in the DEB version)
# BUILD_TAG Build tag
#
# IMPORTANT: Source files under git management must NEVER be deleted.
# Only the temporary staging directory (deb_stage) is removed.
set -eo pipefail
: "${PROJECT:?PROJECT must be set}"
: "${OS_VERSION:?OS_VERSION must be set}"
: "${LTS_VERSION:?LTS_VERSION must be set}"
: "${GUEZ_MODE:?GUEZ_MODE must be set}"
: "${WORKSPACE:?WORKSPACE must be set}"
: "${PROJECT_BASE_DIR:?PROJECT_BASE_DIR must be set}"
echo "Building LTS ${LTS_VERSION}-${BUILD_NUMBER:-?} DEB for project '${PROJECT}' (Ubuntu ${OS_VERSION})."
echo "PROJECT_BASE_DIR=${PROJECT_BASE_DIR}"
export RTI_LICENSE_FILE="${WORKSPACE}/rti_license.dat"
# Stage workspace as a clean copy under PROJECT_BASE_DIR/deb_stage.
# Only the staging subdirectory is removed — never the workspace itself,
# which contains git-managed source files that must not be deleted.
STAGE_DIR="${PROJECT_BASE_DIR}/deb_stage"
rm -rf "${STAGE_DIR}"
mkdir -p "${STAGE_DIR}"
tar --exclude='./rpms' --exclude='./.git' --exclude='./build' \
--exclude='./deb_build_tmp' --exclude='./deb_stage' --exclude='./packages-out' \
-cf - -C "${WORKSPACE}" . | tar -xf - -C "${STAGE_DIR}"
export VERSION_DATE
VERSION_DATE=$(date +%Y-%m-%d)
# Real CMake project root inside the staged tree (the in-tree source)
SOURCE_DIR="${STAGE_DIR}/qt-migration-legatus/qt6-after/qt6-migrated-legatus"
if [ ! -f "${SOURCE_DIR}/CMakeLists.txt" ] || [ ! -f "${SOURCE_DIR}/rpmbuild/make_rpm_ubuntu26.py" ]; then
echo "ERROR: staged source incomplete under ${SOURCE_DIR} (CMakeLists.txt or rpmbuild/make_rpm_ubuntu26.py missing)" >&2
exit 1
fi
# Output is isolated per OS version so parallel OS builds of the same
# project never collide
cd "${SOURCE_DIR}/rpmbuild"
python3 make_rpm_ubuntu26.py -c LTS \
--project_name "${PROJECT}" \
--version "${LTS_VERSION}" \
--output_dir "${WORKSPACE}/rpms/${PROJECT}/${OS_VERSION}/" \
--source_dir "${SOURCE_DIR}" \
--cmake_build_flags "-DUSE_IPO=ON -DUSE_UNITY_BUILD=ON -DGUEZ_MODE=${GUEZ_MODE} -DBUILD_WLS=ON -DPostgreSQL_TYPE_INCLUDE_DIR=/usr/include/postgresql/ -DRELEASE_BUILD=ON" \
--build_number "${BUILD_NUMBER:-1}" \
--release_scope
# Copy produced DEB packages to the packages-out mount (host:
# ${PACKAGE_OUTPUT_DIR}, container: /workspace/packages-out) so they are
# available on the host after the container exits.
# make_rpm already writes to ${WORKSPACE}/rpms/... (the host workspace
# mount), so when the host's PACKAGE_OUTPUT_DIR is that same directory the
# packages are already in place - skip copying onto themselves.
PACKAGE_OUT_DIR=/workspace/packages-out
mkdir -p "${PACKAGE_OUT_DIR}"
shopt -s nullglob
for deb in "${WORKSPACE}/rpms/${PROJECT}/${OS_VERSION}/"*.deb; do
base="$(basename "${deb}")"
if [[ -e "${PACKAGE_OUT_DIR}/${base}" ]] &&
[[ "$(stat -c '%d:%i' "${deb}")" == "$(stat -c '%d:%i' "${PACKAGE_OUT_DIR}/${base}")" ]]; then
continue
fi
cp -f "${deb}" "${PACKAGE_OUT_DIR}/"
done
shopt -u nullglob
echo "DEB packages produced for '${PROJECT}' (Ubuntu ${OS_VERSION}):"
ls -lhR "${WORKSPACE}/rpms/${PROJECT}/${OS_VERSION}/"
echo "DEB packages copied to ${PACKAGE_OUT_DIR}:"
ls -lh "${PACKAGE_OUT_DIR}/"
@@ -0,0 +1,71 @@
#!/usr/bin/env bash
#
# Upload the produced DEBs to the internal artifact repository and emit two
# helper files for the Jenkinsfile to consume (named per project + OS
# version):
# ${WORKSPACE}/url-${PROJECT}-${OS_VERSION}.txt — browseable URL of the uploaded folder
# ${WORKSPACE}/tdsversion-${PROJECT}-${OS_VERSION}.txt — full version string (LTS-build)
#
# Adapted for the in-tree flow: make_rpm_ubuntu26.py writes the DEBs
# directly under ${WORKSPACE}/rpms/${PROJECT}/${OS_VERSION}/ (no TDS
# subfolder), so that directory is uploaded as-is.
#
# Required environment (set by the Jenkinsfile / withCredentials):
# PROJECT LTS flavor: guez
# OS_VERSION Ubuntu base version, e.g. 26.04
# LTS_VERSION Major.minor LTS version, e.g. 1.0
# BUILD_NUMBER Build number (release component of the artifact name)
# WORKSPACE Jenkins workspace path
# ARTIFACTREPO Base URL of the internal repo (no trailing slash)
# ARTIFACTFOLDER Subfolder under cts-rpms/ (project name, e.g. guez)
# CURL_CRED user:password for the upload (Jenkins credential)
#
set -eo pipefail
: "${PROJECT:?PROJECT must be set}"
: "${OS_VERSION:?OS_VERSION must be set}"
: "${LTS_VERSION:?LTS_VERSION must be set}"
: "${BUILD_NUMBER:?BUILD_NUMBER must be set}"
: "${WORKSPACE:?WORKSPACE must be set}"
: "${ARTIFACTREPO:?ARTIFACTREPO must be set}"
: "${ARTIFACTFOLDER:?ARTIFACTFOLDER must be set}"
: "${CURL_CRED:?CURL_CRED must be set}"
tds_folder_name="${PROJECT}-TDS-${LTS_VERSION}-${BUILD_NUMBER}"
tds_folder_path="${WORKSPACE}/rpms/${PROJECT}/${OS_VERSION}"
tds_artifact_store="${ARTIFACTREPO}/repository/cts-rpms/${ARTIFACTFOLDER}/${OS_VERSION}/${tds_folder_name}"
if [ ! -d "${tds_folder_path}" ]; then
echo "ERROR: expected package folder not found: ${tds_folder_path}" >&2
exit 1
fi
shopt -s nullglob
uploaded=0
for file in "${tds_folder_path}"/*; do
[ -f "${file}" ] || continue
# Upload .deb, .dsc, .changes, .buildinfo files
if [[ "${file}" == *.deb ]] || [[ "${file}" == *.dsc ]] || [[ "${file}" == *.changes ]] || [[ "${file}" == *.buildinfo ]]; then
name=$(basename "${file}")
echo "Uploading: ${name}"
curl --fail --silent --show-error \
--user "${CURL_CRED}" \
--upload-file "${file}" \
"${tds_artifact_store}/${name}"
uploaded=$((uploaded + 1))
fi
done
shopt -u nullglob
if [ "${uploaded}" -eq 0 ]; then
echo "ERROR: no files uploaded from ${tds_folder_path}" >&2
exit 1
fi
echo "Uploaded ${uploaded} file(s) to ${tds_artifact_store}/"
# Per-(project, OS) filenames so concurrent publish stages don't clobber each
# other's helper files; the Jenkinsfile reads back the matching names.
echo "${ARTIFACTREPO}/service/rest/repository/browse/cts-rpms/${ARTIFACTFOLDER}/${OS_VERSION}/${tds_folder_name}/" > "${WORKSPACE}/url-${PROJECT}-${OS_VERSION}.txt"
echo "${LTS_VERSION}-${BUILD_NUMBER}" > "${WORKSPACE}/tdsversion-${PROJECT}-${OS_VERSION}.txt"
@@ -0,0 +1,63 @@
#!/usr/bin/env bash
#
# Local release pipeline for the Qt6-migrated legatus validation scope
# (TetraIF, Z, Lts/Common + Lts/Proxies/TrainingAreaBase).
#
# Runs the in-tree new_docker_build.sh:
# - builds the prebuilt Docker image from the in-tree Dockerfile when the
# image is not present yet
# - builds the release scope, runs ctest and produces the release DEB
# packages with the in-tree rpmbuild script
#
# The DEB packages land in ${WORKSPACE}/rpms/${PROJECT}/${OS_VERSION}/
# (Jenkins-style layout; locally WORKSPACE is the pipeline root).
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LEGATUS_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
PIPELINE_ROOT="$(cd "${LEGATUS_ROOT}/../../.." && pwd)"
# Release build configuration (Jenkins-style variables)
export WORKSPACE="${PIPELINE_ROOT}"
export PROJECT="${PROJECT:-guez}"
export OS_VERSION="${OS_VERSION:-26.04}"
export LTS_VERSION="${LTS_VERSION:-1.0}"
export GUEZ_MODE="${GUEZ_MODE:-ON}"
export BUILD_NUMBER="${BUILD_NUMBER:-$(date +%Y%m%d-%H%M%S)}"
export BUILD_TAG="${BUILD_TAG:-release-qt6-first}"
DOCKER_IMAGE="${DOCKER_IMAGE:-legatus-qt6-prebuilt:latest}"
log_info() {
printf 'INFO: %s\n' "$*"
}
log_info "=== Qt6 Release Pipeline ==="
log_info "Pipeline root: ${PIPELINE_ROOT}"
log_info "Legatus root: ${LEGATUS_ROOT}"
log_info "Docker image: ${DOCKER_IMAGE}"
log_info "Build number: ${BUILD_NUMBER}"
log_info "Build tag: ${BUILD_TAG}"
log_info "Package output: ${WORKSPACE}/rpms/${PROJECT}/${OS_VERSION}/"
cd "${LEGATUS_ROOT}"
# Build the prebuilt image from the in-tree Dockerfile when not present yet
# (--build-prebuilt-image builds the image and exits)
if ! docker image inspect "${DOCKER_IMAGE}" >/dev/null 2>&1; then
log_info "Prebuilt image not found - building it from the in-tree Dockerfile..."
bash ./new_docker_build.sh --build-prebuilt-image --docker-image "${DOCKER_IMAGE}"
fi
# Release scope build + ctest + DEB production (in-tree rpmbuild)
bash ./new_docker_build.sh \
--docker-image "${DOCKER_IMAGE}" \
--run-jenkins-builder \
--package-output-dir "${WORKSPACE}/rpms/${PROJECT}/${OS_VERSION}/" \
--project "${PROJECT}" \
--lts-version "${LTS_VERSION}" \
--build-number "${BUILD_NUMBER}" \
--build-tag "${BUILD_TAG}"
log_info "=== Release Pipeline Complete ==="
@@ -839,6 +839,9 @@ def build_all_debs(source_dir, output_dir, project_name, version, build_number,
"new_docker_build.sh",
"Dockerfile",
"Dockerfile.ubuntu26",
"jenkins-ci-rme/Jenkinsfile",
"jenkins-ci-rme/jenkins_builder_ubuntu26.sh",
"jenkins-ci-rme/jenkins_publisher_ubuntu26.sh",
"jenkins-ci-rme/release-16-10-2026.sh",
"rpmbuild/make_rpm_ubuntu26.py",
"CMakeLists.txt",