#!/usr/bin/env bash set -euo pipefail # ----------------------------------------------------------------------------- # Build helper for qt-migrated-legatus inside Docker - RELEASE 2 VERSION # # Release 2 Scope: # - AP3: Shared libraries (LtsUtils, Widgets, Session, Krypto, PluginManager) # - AP4: DDS middleware (Core, DDSConnector, Routing, QosProfile, RTI layer) # - AP9: Tools and translations (DdsView, LogViewer, SqlBrowser, generators, lupdate) # # This script is derived from new_docker_build.sh but focused on Release 2 targets only. # ----------------------------------------------------------------------------- usage() { cat <<'EOF' Usage: ./new_docker_build_release2.sh [options] Build groups (Release 2 scope): --group1 / --no-group1 Enable/disable DDS core libraries build (AP4) --group3 / --no-group3 Enable/disable Lts services build (AP3) --group5 / --no-group5 Enable/disable Lts/Viewer module targets and tests (AP9) --group6 / --no-group6 Enable/disable LTS Tools - generators and lupdate (AP9) Test execution: --no-ctest Skip ctest execution --tests-only Run only ctest, skip all make build groups Prebuilt Docker image usage: --use-prebuilt-image Use a Docker image with dependencies already installed --no-prebuilt-image Use base image and run apt-get install inside the container --docker-image IMAGE Docker image name to use/build/delete --cmake_build_flags FLAGS Extra CMake flags passed to the in-container configure (space-separated -D flags) Package production options (DEB): --run-jenkins-builder Build the release DEB packages inside the container using the in-tree rpmbuild/make_rpm_ubuntu26.py --package-output-dir DIR Directory to mount for package output (default: ./packages-out) --lts-version VERSION LTS version for package (default: 1.0) --project NAME Project name for package (default: guez) --build-number NUM Build number for package (default: 1) --build-tag TAG Build tag for package (default: manual-build) Coverage options (lcov): --run-coverage Build with -O0 --coverage, run ctest and collect lcov --coverage-output-dir DIR Directory to mount for coverage output (default: ./coverage-out) Docker mount configuration: --docker-mount-root PATH Container-side mount root path (default: /repo) Changes where host directories are mounted in container Environment variable overrides: BUILD_GROUP1=ON|OFF DDS core libraries (AP4) BUILD_GROUP3=ON|OFF Lts services (AP3) BUILD_GROUP5=ON|OFF Viewer module (AP9) BUILD_GROUP6=ON|OFF Tools/generators (AP9) RUN_CTEST=ON|OFF TESTS_ONLY=ON|OFF USE_PREBUILT_IMAGE=ON|OFF RUN_JENKINS_BUILDER=ON|OFF PACKAGE_OUTPUT_DIR= LTS_VERSION= PROJECT= BUILD_NUMBER= BUILD_TAG= RUN_COVERAGE=ON|OFF COVERAGE_OUTPUT_DIR= MIGRATE_PIPELINE_ROOT resolution (highest precedence first): 1. exported MIGRATE_PIPELINE_ROOT (e.g. from a Jenkinsfile) 2. MIGRATE_PIPELINE_ROOT set by .envrc in this directory 3. derived from the script location (the DLBO Qt6 repo root) Examples: ./new_docker_build_release2.sh --group1 --group3 --group5 --group6 BUILD_GROUP1=ON BUILD_GROUP3=ON ./new_docker_build_release2.sh ./new_docker_build_release2.sh --use-prebuilt-image --no-ctest ./new_docker_build_release2.sh --group3 --run-jenkins-builder \ --package-output-dir "${WORKSPACE}/rpms/guez/release2/" ./new_docker_build_release2.sh --group3 --run-coverage \ --coverage-output-dir "${WORKSPACE}/coverage/guez/release2/" EOF exit 1 } log_info() { echo "[INFO] $*" } log_error() { printf "ERROR: %s\n" "$*" >&2 } die() { echo "[ERROR] $*" >&2 exit 1 } # Default values BUILD_GROUP1="${BUILD_GROUP1:-ON}" # AP4: DDS BUILD_GROUP3="${BUILD_GROUP3:-ON}" # AP3: Lts services BUILD_GROUP5="${BUILD_GROUP5:-ON}" # AP9: Viewer BUILD_GROUP6="${BUILD_GROUP6:-ON}" # AP9: Tools/generators RUN_CTEST="${RUN_CTEST:-ON}" TESTS_ONLY="${TESTS_ONLY:-OFF}" USE_PREBUILT_IMAGE="${USE_PREBUILT_IMAGE:-OFF}" DOCKER_IMAGE="${DOCKER_IMAGE:-qt6-migrated-legatus-release2}" CMAKE_BUILD_FLAGS="${CMAKE_BUILD_FLAGS:-}" DOCKER_MOUNT_ROOT="${DOCKER_MOUNT_ROOT:-/repo}" export DOCKER_MOUNT_ROOT # Jenkins builder options for DEB package production RUN_JENKINS_BUILDER="${RUN_JENKINS_BUILDER:-OFF}" PACKAGE_OUTPUT_DIR="${PACKAGE_OUTPUT_DIR:-./packages-out}" LTS_VERSION="${LTS_VERSION:-1.0}" PROJECT="${PROJECT:-guez}" BUILD_NUMBER="${BUILD_NUMBER:-1}" BUILD_TAG="${BUILD_TAG:-manual-build}" # Code coverage (lcov) RUN_COVERAGE="${RUN_COVERAGE:-OFF}" COVERAGE_OUTPUT_DIR="${COVERAGE_OUTPUT_DIR:-./coverage-out}" # Export variables for Docker container export BUILD_GROUP1 BUILD_GROUP3 BUILD_GROUP5 BUILD_GROUP6 RUN_CTEST TESTS_ONLY USE_PREBUILT_IMAGE export RUN_JENKINS_BUILDER PACKAGE_OUTPUT_DIR LTS_VERSION PROJECT BUILD_NUMBER BUILD_TAG export RUN_COVERAGE COVERAGE_OUTPUT_DIR # MIGRATE_PIPELINE_ROOT resolution (highest precedence first): # 1. exported MIGRATE_PIPELINE_ROOT (e.g. from a Jenkinsfile) # 2. MIGRATE_PIPELINE_ROOT set by .envrc in this directory # 3. derived from the script location (the DLBO Qt6 repo root) SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" MIGRATE_PIPELINE_ROOT_DEFAULT="${SCRIPT_DIR}" PIPELINE_ROOT_EXPORTED="${MIGRATE_PIPELINE_ROOT:-}" if [[ -f ".envrc" ]]; then set -a source ".envrc" set +a fi # An explicitly exported MIGRATE_PIPELINE_ROOT (Jenkinsfile) wins over .envrc if [[ -n "${PIPELINE_ROOT_EXPORTED}" ]]; then MIGRATE_PIPELINE_ROOT="${PIPELINE_ROOT_EXPORTED}" else MIGRATE_PIPELINE_ROOT="${MIGRATE_PIPELINE_ROOT:-${MIGRATE_PIPELINE_ROOT_DEFAULT}}" fi export MIGRATE_PIPELINE_ROOT # Parse arguments while [[ $# -gt 0 ]]; do case "$1" in --group1) BUILD_GROUP1="ON" shift ;; --no-group1) BUILD_GROUP1="OFF" shift ;; --group3) BUILD_GROUP3="ON" shift ;; --no-group3) BUILD_GROUP3="OFF" shift ;; --group5) BUILD_GROUP5="ON" shift ;; --no-group5) BUILD_GROUP5="OFF" shift ;; --group6) BUILD_GROUP6="ON" shift ;; --no-group6) BUILD_GROUP6="OFF" shift ;; --no-ctest) RUN_CTEST="OFF" shift ;; --tests-only) TESTS_ONLY="ON" shift ;; --use-prebuilt-image) USE_PREBUILT_IMAGE="ON" shift ;; --no-prebuilt-image) USE_PREBUILT_IMAGE="OFF" shift ;; --docker-image) DOCKER_IMAGE="$2" shift 2 ;; --cmake_build_flags) CMAKE_BUILD_FLAGS="$2" shift 2 ;; --docker-mount-root) DOCKER_MOUNT_ROOT="$2" shift 2 ;; --run-jenkins-builder) RUN_JENKINS_BUILDER="ON" shift ;; --package-output-dir) PACKAGE_OUTPUT_DIR="$2" shift 2 ;; --lts-version) LTS_VERSION="$2" shift 2 ;; --project) PROJECT="$2" shift 2 ;; --build-number) BUILD_NUMBER="$2" shift 2 ;; --build-tag) BUILD_TAG="$2" shift 2 ;; --run-coverage) RUN_COVERAGE="ON" shift ;; --coverage-output-dir) COVERAGE_OUTPUT_DIR="$2" shift 2 ;; -h|--help) usage ;; *) die "Unknown option: $1" ;; esac done # Validate mutually exclusive options if [[ "${RUN_JENKINS_BUILDER}" == "ON" ]] && [[ "${RUN_COVERAGE}" == "ON" ]]; then die "Error: --run-jenkins-builder and --run-coverage are mutually exclusive" fi require_command() { if ! command -v "$1" &> /dev/null; then die "Required command not found: $1" fi } require_command docker # Validate environment if [[ ! -f ".envrc" ]]; then die ".envrc not found in current directory. Please create .envrc with required environment variables (e.g., MIGRATE_PIPELINE_ROOT)" fi set -a # shellcheck disable=SC1091 source ".envrc" set +a if [[ -z "${MIGRATE_PIPELINE_ROOT:-}" ]]; then die "MIGRATE_PIPELINE_ROOT is not set in .envrc" fi if [[ ! -d "${MIGRATE_PIPELINE_ROOT}" ]]; then die "MIGRATE_PIPELINE_ROOT directory does not exist: ${MIGRATE_PIPELINE_ROOT}" fi # Validate rti_license.dat exists (required for DDS builds) if [[ "${BUILD_GROUP1}" == "ON" ]] && [[ ! -f "${MIGRATE_PIPELINE_ROOT}/rti_license.dat" ]]; then die "rti_license.dat not found in MIGRATE_PIPELINE_ROOT. Required for DDS builds." fi log_info "=== Release 2 Build Configuration ===" log_info "BUILD_GROUP1 (DDS/AP4): ${BUILD_GROUP1}" log_info "BUILD_GROUP3 (Lts/AP3): ${BUILD_GROUP3}" log_info "BUILD_GROUP5 (Viewer/AP9): ${BUILD_GROUP5}" log_info "BUILD_GROUP6 (Tools/AP9): ${BUILD_GROUP6}" log_info "RUN_CTEST: ${RUN_CTEST}" log_info "TESTS_ONLY: ${TESTS_ONLY}" log_info "USE_PREBUILT_IMAGE: ${USE_PREBUILT_IMAGE}" log_info "DOCKER_IMAGE: ${DOCKER_IMAGE}" log_info "DOCKER_MOUNT_ROOT: ${DOCKER_MOUNT_ROOT}" log_info "CMAKE_BUILD_FLAGS: ${CMAKE_BUILD_FLAGS:-none}" log_info "======================================" # Build prebuilt image if requested if [[ "${USE_PREBUILT_IMAGE}" == "ON" ]]; then if ! docker image inspect "${DOCKER_IMAGE}" &> /dev/null; then log_info "Prebuilt image ${DOCKER_IMAGE} not found. Building it now..." # Create temporary directory for Dockerfile tmp_dir="$(mktemp -d)" trap 'rm -rf "${tmp_dir}"' EXIT cat > "${tmp_dir}/Dockerfile" <<'DOCKERFILE' FROM ubuntu:26.04 ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update -qq && \ apt-get install -y -qq \ build-essential \ cmake \ ninja-build \ git \ python3-pip \ libboost-dev \ libkf6texttemplate-dev \ libgtest-dev \ libgmock-dev \ libpq-dev \ postgresql-server-dev-all \ libvlc-dev \ vlc-plugin-base \ libssl-dev \ libxml2-dev \ libxerces-c-dev \ && \ rm -rf /var/lib/apt/lists/* # Install Qt6 packages via aqtinstall RUN pip3 install --break-system-packages --quiet aqtinstall && \ aqt install-qt linux desktop 6.10.2 linux_gcc_64 \ -m qtmultimedia qtserialport qtimageformats qtwebsockets qtwebengine \ qtpdf qtwebview qt5compat qtwebchannel qtpositioning qtlocation qtsensors \ --outputdir /opt/Qt && \ rm -rf /root/.cache/Aqt # Install KDReports from source RUN git clone --depth 1 https://github.com/KDAB/KDReports.git /tmp/KDReports && \ cd /tmp/KDReports && \ mkdir build && cd build && \ cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_PREFIX_PATH=/opt/Qt .. && \ make -j$(nproc) && \ make install && \ cd / && rm -rf /tmp/KDReports # Create symlink for KDReports headers RUN ln -sfn KDReports-qt6 /usr/local/include/KDReports CMD ["/bin/bash"] DOCKERFILE docker build -t "${DOCKER_IMAGE}" "${tmp_dir}" log_info "Prebuilt image created: ${DOCKER_IMAGE}" else log_info "Prebuilt image already exists: ${DOCKER_IMAGE}" fi fi # Prepare Docker run command DOCKER_RUN_ARGS=( --rm -e MIGRATE_PIPELINE_ROOT -e DOCKER_MOUNT_ROOT="${DOCKER_MOUNT_ROOT}" -e BUILD_GROUP1="${BUILD_GROUP1}" -e BUILD_GROUP3="${BUILD_GROUP3}" -e BUILD_GROUP5="${BUILD_GROUP5}" -e BUILD_GROUP6="${BUILD_GROUP6}" -e RUN_CTEST="${RUN_CTEST}" -e TESTS_ONLY="${TESTS_ONLY}" -e RUN_JENKINS_BUILDER="${RUN_JENKINS_BUILDER}" -e PACKAGE_OUTPUT_DIR="${PACKAGE_OUTPUT_DIR}" -e LTS_VERSION="${LTS_VERSION}" -e PROJECT="${PROJECT}" -e BUILD_NUMBER="${BUILD_NUMBER}" -e BUILD_TAG="${BUILD_TAG}" -e RUN_COVERAGE="${RUN_COVERAGE}" -e COVERAGE_OUTPUT_DIR="${COVERAGE_OUTPUT_DIR}" -v "${MIGRATE_PIPELINE_ROOT}:${DOCKER_MOUNT_ROOT}" -w "${DOCKER_MOUNT_ROOT}/qt-migration-legatus/qt6-after/qt6-migrated-legatus" ) if [[ "${USE_PREBUILT_IMAGE}" == "ON" ]]; then DOCKER_RUN_ARGS+=("${DOCKER_IMAGE}") else DOCKER_RUN_ARGS+=("ubuntu:26.04") fi # When not using prebuilt image, we need to install dependencies first if [[ "${USE_PREBUILT_IMAGE}" == "OFF" ]]; then # Prepend dependency installation to the build command INSTALL_DEPS=' set -e export DEBIAN_FRONTEND=noninteractive apt-get update -qq && \ apt-get install -y -qq \ build-essential \ cmake \ ninja-build \ git \ python3-pip \ libboost-dev \ libkf6texttemplate-dev \ libqt6core6 \ libqt6gui6 \ libqt6widgets6 \ libqt6sql6 \ libqt6sql6-psql \ libqt6test6 \ qt6-base-dev \ qt6-l10n-tools \ qt6-serialport-dev \ qt6-svg-dev \ qt6-tools-dev-tools \ qt6-tools-dev \ qt6-websockets-dev \ libgtest-dev \ libgmock-dev \ libpq-dev \ postgresql-server-dev-all \ libvlc-dev \ vlc-plugin-base \ libssl-dev \ libxml2-dev \ libxerces-c-dev \ && \ rm -rf /var/lib/apt/lists/* # Install Qt6 packages via aqtinstall pip3 install --break-system-packages --quiet aqtinstall && \ aqt install-qt linux desktop 6.10.2 linux_gcc_64 \ -m qtmultimedia qtserialport qtimageformats qtwebsockets qtwebengine \ qtpdf qtwebview qt5compat qtwebchannel qtpositioning qtlocation qtsensors \ --outputdir /opt/Qt && \ rm -rf /root/.cache/Aqt # Install KDReports from source git clone --depth 1 https://github.com/KDAB/KDReports.git /tmp/KDReports && \ cd /tmp/KDReports && \ mkdir build && cd build && \ cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_PREFIX_PATH=/opt/Qt .. && \ make -j$(nproc) && \ make install && \ cd / && rm -rf /tmp/KDReports # Create symlink for KDReports headers ln -sfn KDReports-qt6 /usr/local/include/KDReports export PATH="/opt/Qt/6.10.2/gcc_64/bin:$PATH" export CMAKE_PREFIX_PATH="/opt/Qt/6.10.2/gcc_64" log_info() { echo "[INFO] $*" } die() { echo "[ERROR] $*" >&2 exit 1 } log_info "Dependencies installed successfully" # Return to project directory cd "${DOCKER_MOUNT_ROOT}/qt-migration-legatus/qt6-after/qt6-migrated-legatus" ' else INSTALL_DEPS='' fi # Build command DOCKER_RUN_ARGS+=( /bin/bash -c ' set -e log_info() { echo "[INFO] $*" } log_error() { printf "ERROR: %s\n" "$*" >&2 } die() { echo "[ERROR] $*" >&2 exit 1 } '"${INSTALL_DEPS}"' # Configure CMake log_info "Configuring CMake..." # Use unique build directory to avoid Docker mount caching issues BUILD_DIR="build4_$(date +%Y%m%d_%H%M%S)_$$" log_info "Using build directory: ${BUILD_DIR}" rm -rf "${BUILD_DIR}" mkdir -p "${BUILD_DIR}" cmake -B "${BUILD_DIR}" \ -G Ninja \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_PREFIX_PATH="/opt/Qt/6.10.2/gcc_64" \ -DCMAKE_CXX_FLAGS="-DRTI_LINUX -DRTI_UNIX" \ -DCMAKE_C_FLAGS="-DRTI_LINUX -DRTI_UNIX" \ -DBUILD_RTI=ON \ -DBUILD_GROUP1="${BUILD_GROUP1}" \ -DBUILD_GROUP3="${BUILD_GROUP3}" \ -DBUILD_GROUP5="${BUILD_GROUP5}" \ -DBUILD_GROUP6="${BUILD_GROUP6}" \ -DRUN_JENKINS_BUILDER="${RUN_JENKINS_BUILDER}" \ -DPACKAGE_OUTPUT_DIR="${PACKAGE_OUTPUT_DIR}" \ -DLTS_VERSION="${LTS_VERSION}" \ -DPROJECT="${PROJECT}" \ -DBUILD_NUMBER="${BUILD_NUMBER}" \ -DBUILD_TAG="${BUILD_TAG}" \ -DRUN_COVERAGE="${RUN_COVERAGE}" \ -DCOVERAGE_OUTPUT_DIR="${COVERAGE_OUTPUT_DIR}" \ '"${CMAKE_BUILD_FLAGS}"' \ . # Build targets log_info "Building Release 2 targets..." # AP4: DDS middleware (Release 1 + Release 2 scope) GROUP1_TARGETS=(ddscore ddsftccore ddsfts ddsgen ddsQoS ddsconnector) # AP3: Shared libraries + TetraIF + Lts/Common + Tests (Release 1 + Release 2 scope) GROUP3_TARGETS=(NGLLogger nglapplication nglcompression nglcoreapplication ngldb nglguiapplication nglsettings nglsingleapplication nglsinglecoreapplication ngltools ngltranslator nglwidgets qtsingleapplication qtsinglecoreapplication wms_json_ds tcpserver LtsCoordinate LtsCoordTrans LtsSession LtsMultiMonitor DataPluginInterface GuiPluginInterface CoPrtCnv LogViewLogger LogView PrtCnvLib PrtCnv LtsLicence MockCoordTrans aes_crypt_test ngltools_qt_smoke lts_coord_trans_unit_test test_nglpdfwidget coprtcnv_unit_test logview_unit_test prtcnv_unit_test) GROUP5_TARGETS=(ddsview LogViewer sqlbrowser) GROUP6_TARGETS=(ngl_codegen) build_group() { local group_name="$1" local build_flag="$2" shift 2 local targets=("$@") if [[ "${build_flag}" != "ON" ]]; then log_info "Skipping ${group_name} (disabled)" return fi log_info "Building ${group_name}..." for target in "${targets[@]}"; do log_info " Building: ${target}" cmake --build "${BUILD_DIR}" --target "${target}" -j$(nproc) || die "Failed to build ${target}" done } build_group "GROUP1 (DDS/AP4)" "${BUILD_GROUP1}" "${GROUP1_TARGETS[@]}" build_group "GROUP3 (Lts/AP3)" "${BUILD_GROUP3}" "${GROUP3_TARGETS[@]}" build_group "GROUP5 (Viewer/AP9)" "${BUILD_GROUP5}" "${GROUP5_TARGETS[@]}" build_group "GROUP6 (Tools/AP9)" "${BUILD_GROUP6}" "${GROUP6_TARGETS[@]}" # Run tests if enabled if [[ "${RUN_CTEST}" == "ON" ]] && [[ "${TESTS_ONLY}" != "ON" ]]; then log_info "Running ctest..." cd "${BUILD_DIR}" ctest --output-on-failure -j$(nproc) || log_info "Some tests failed (continuing)" cd .. fi # DEB Package Production (in-tree rpmbuild) if [[ "${RUN_JENKINS_BUILDER}" == "ON" ]]; then log_info "=== DEB Package Production (in-tree rpmbuild) ===" # Extract OS version for package naming OS_VERSION=$(eval $(grep "^VERSION_ID=" /etc/os-release) && echo "$VERSION_ID") STAGE_DIR="${DOCKER_MOUNT_ROOT}/deb_stage" STAGED_SOURCE_DIR="${STAGE_DIR}" WORKSPACE_DIR="${DOCKER_MOUNT_ROOT}/qt-migration-legatus/qt6-after/qt6-migrated-legatus" log_info "Staging workspace to ${STAGE_DIR}" 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' \ --exclude='./legatus-qt6_1.0.tar.gz' \ -cf - -C "${WORKSPACE_DIR}" . | tar -xf - -C "${STAGE_DIR}" if [[ ! -f "${STAGED_SOURCE_DIR}/CMakeLists.txt" || ! -f "${STAGED_SOURCE_DIR}/rpmbuild/make_rpm_ubuntu26_release2.py" ]]; then log_error "Staged source incomplete under ${STAGED_SOURCE_DIR} (CMakeLists.txt or rpmbuild/make_rpm_ubuntu26_release2.py missing)" exit 1 fi log_info "Running ${STAGED_SOURCE_DIR}/rpmbuild/make_rpm_ubuntu26_release2.py with:" log_info " PROJECT=${PROJECT}" log_info " OS_VERSION=${OS_VERSION}" log_info " LTS_VERSION=${LTS_VERSION}" log_info " GUEZ_MODE=${GUEZ_MODE}" log_info " BUILD_NUMBER=${BUILD_NUMBER}" log_info " BUILD_TAG=${BUILD_TAG}" cd "${STAGED_SOURCE_DIR}/rpmbuild" if python3 make_rpm_ubuntu26_release2.py -c LTS \ --project_name "${PROJECT}" \ --version "${LTS_VERSION}" \ --output_dir "${DOCKER_MOUNT_ROOT}/rpms/${PROJECT}/${OS_VERSION}/" \ --source_dir "${STAGED_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}" \ --release_scope; then log_info "Package build completed successfully" else log_error "Package build failed (exit code: $?)" exit 1 fi # Copy produced DEB packages to the packages-out DEST_DIR="${DOCKER_MOUNT_ROOT}/packages-out" mkdir -p "${DEST_DIR}" shopt -s nullglob for deb in "${DOCKER_MOUNT_ROOT}/rpms/${PROJECT}/${OS_VERSION}/"*.deb; do base="$(basename "${deb}")" if [[ -e "${DEST_DIR}/${base}" ]] && [[ "$(stat -c '%d:%i' "${deb}")" == "$(stat -c '%d:%i' "${DEST_DIR}/${base}")" ]]; then continue fi cp -f "${deb}" "${DEST_DIR}/" done shopt -u nullglob log_info "DEB packages available in ${DEST_DIR} (host: ${PACKAGE_OUTPUT_DIR:-unknown}):" ls -lh "${DEST_DIR}/" else log_info "Skipping DEB package production" fi log_info "Release 2 build completed successfully!" ' ) log_info "Starting Docker container..." docker run "${DOCKER_RUN_ARGS[@]}" log_info "Build script completed."