From 09698aa2810bf036802eb664af685fea83bc934f Mon Sep 17 00:00:00 2001 From: Craig Comstock Date: Thu, 27 Aug 2026 14:13:15 -0500 Subject: [PATCH 1/4] Added simple README-build-in-container.md explaining how to work inside the container Ticket: ENT-12599 Changelog: none --- README-build-in-container.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 README-build-in-container.md diff --git a/README-build-in-container.md b/README-build-in-container.md new file mode 100644 index 000000000..d8e0b3fdd --- /dev/null +++ b/README-build-in-container.md @@ -0,0 +1,8 @@ +Build with ./build-in-container.py + +Using ./build-in-container.py --shell you can run the build interactively and debug issues. +Start in the container by running /srv/source/buildscripts/build-in-container-inner.sh + +This will copy repository sources that are needed from the read-only /srv location to read-write work area in /home/builder/build. + +Continue debugging by running steps in /home/builder/buildscripts/build-scripts/0*.sh From 2ce790c8ac749f454d33ac33a849819020fcdb12 Mon Sep 17 00:00:00 2001 From: Craig Comstock Date: Wed, 2 Sep 2026 11:00:51 -0500 Subject: [PATCH 2/4] Skip sshd hardening for build hosts that are containers Ticket: ENT-12599 Changelog: None --- build-in-container-inner.sh | 3 +++ ci/cfengine-build-host-setup.cf | 27 ++++++++++++++++----------- container/Dockerfile.debian | 3 +++ container/Dockerfile.mingw | 3 +++ container/Dockerfile.rhel | 3 +++ container/Dockerfile.rhel7 | 3 +++ container/Dockerfile.tarballs | 4 ++++ 7 files changed, 35 insertions(+), 11 deletions(-) diff --git a/build-in-container-inner.sh b/build-in-container-inner.sh index a77d0d2af..155bb3792 100755 --- a/build-in-container-inner.sh +++ b/build-in-container-inner.sh @@ -4,6 +4,9 @@ set -e # Configuration via environment variables: # PROJECT, BUILD_TYPE, EXPLICIT_ROLE, BUILD_NUMBER, EXPLICIT_VERSION +# let setup-cfengine-build-host.sh know we are in a container +sudo touch /etc/cfengine-in-container.flag + BASEDIR=/home/builder/build export BASEDIR export AUTOBUILD_PATH="$BASEDIR/buildscripts" diff --git a/ci/cfengine-build-host-setup.cf b/ci/cfengine-build-host-setup.cf index ceb93eb10..a54f1c0af 100644 --- a/ci/cfengine-build-host-setup.cf +++ b/ci/cfengine-build-host-setup.cf @@ -36,7 +36,10 @@ bundle agent cfengine_build_host_setup "ntp"; debian|ubuntu:: - "fail2ban" comment => "Ban IPs with repeated failed SSH auth attempts"; + "fail2ban" + comment => "Ban IPs with repeated failed SSH auth attempts", + if => "not_in_container"; + "libltdl7" package_policy => "delete"; "libltdl-dev" package_policy => "delete"; "binutils"; @@ -176,10 +179,11 @@ bundle agent cfengine_build_host_setup !(redhat_7|centos_7).(redhat|centos).(yum_dnf_conf_ok).epel_release_ok:: "fail2ban-server" - comment => "Ban IPs with repeated failed SSH auth attempts. On centos/rhel 8+ we must specify individual packages instead of just fail2ban as package method will append -*.* which would include conflicting shorewall and shorewall-lite packages."; + comment => "Ban IPs with repeated failed SSH auth attempts. On centos/rhel 8+ we must specify individual packages instead of just fail2ban as package method will append -*.* which would include conflicting shorewall and shorewall-lite packages.", + if => "not_in_container"; - "fail2ban-sendmail"; - "fail2ban-firewalld"; + "fail2ban-sendmail" if => "not_in_container"; + "fail2ban-firewalld" if => "not_in_container"; "ccache"; "fakeroot"; "perl-JSON-PP"; @@ -277,7 +281,6 @@ bundle agent cfengine_build_host_setup "not_in_container" expression => not(fileexists("/etc/cfengine-in-container.flag")), comment => "We use an explicit flag file that we control to avoid ambiguity about whether we are in a container or not."; - @if minimum_version(3.23) # Rust is build dependency for leech2 (gate on ubuntu>=20, debian>=12, redhat>=7) ubuntu:: @@ -292,7 +295,6 @@ bundle agent cfengine_build_host_setup "leech2_build_toolchain_host" expression => version_compare("$(sys.os_version_major)", ">=", "7"); @endif - any:: "have_rust" expression => fileexists("/opt/rust/bin/rustc"); "have_protoc" expression => fileexists("/usr/local/bin/protoc"); @@ -381,11 +383,12 @@ bundle agent cfengine_build_host_setup comment => "note: centos-7 has installed instead of --installed argument, and that works on rhel-8 and rhel-9 so go with the sub-command instead of option"; commands: - sshd_hardened:: + sshd_hardened.not_in_container:: 'kill -1 $(pgrep -f "sshd -D")' handle => "sshd_restarted", contain => in_shell, comment => "Reload sshd config with SIGHUP(1) to apply hardened configuration"; + have_tmp_mount:: "mount -o remount,size=5G /tmp" comment => "We could check if /tmp was size 5G but not worth the trouble since this remount call just sets the maximum size of the tmpfs in virtual memory.", @@ -513,6 +516,7 @@ root - core unlimited ); "/etc/fail2ban/jail.local" + if => "not_in_container", create => "true", content => "[sshd] enabled = true @@ -524,6 +528,7 @@ findtime = 600", comment => "Configure fail2ban to ban IPs after 5 failed SSH attempts within 10 minutes"; "$(sshd_config_files)" + if => "not_in_container", edit_line => comment_lines_matching( "^$(sshd_hardening_directives)\s+(?!no\s*$).*", "#" ), @@ -532,7 +537,7 @@ findtime = 600", "/etc/ssh/sshd_config" edit_line => prepend_if_no_line("$(sshd_hardening_directives) no"), - if => fileexists("/etc/ssh/sshd_config"), + if => and("not_in_container", fileexists("/etc/ssh/sshd_config")), classes => if_repaired("sshd_hardened"), comment => "Ensure SSH hardening directives are at the top of sshd_config, before any Include"; @@ -642,7 +647,7 @@ jenkins ALL=NOPASSWD: /usr/bin/podman !have_sys_user.(suse|sles|opensuse):: "useradd -u 3 sys -g sys" contain => in_shell; - linux:: + linux.not_in_container:: "sshd -T 2>/dev/null | grep -qiE '^PermitRootLogin no'" depends_on => { "sshd_restarted" }, contain => in_shell, @@ -659,12 +664,12 @@ jenkins ALL=NOPASSWD: /usr/bin/podman comment => "Verify KbdInteractiveAuthentication (OpenSSH 8.7+) or ChallengeResponseAuthentication (older) is disabled"; services: - any:: + not_in_container:: "fail2ban" service_policy => "start", comment => "Ensure fail2ban is running"; - fail2ban_config_changed:: + fail2ban_config_changed.not_in_container:: "fail2ban" service_policy => "restart", comment => "Restart fail2ban to apply jail configuration"; diff --git a/container/Dockerfile.debian b/container/Dockerfile.debian index bc139e0f9..ee9be9321 100644 --- a/container/Dockerfile.debian +++ b/container/Dockerfile.debian @@ -44,6 +44,9 @@ RUN /tmp/linux-install-rust.sh RUN useradd -m -s /bin/bash builder \ && echo "builder ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/builder +# let setup-cfengine-build-host.sh know that we are in a container +RUN touch /etc/cfengine-in-container.flag + USER builder WORKDIR /home/builder diff --git a/container/Dockerfile.mingw b/container/Dockerfile.mingw index a8b9faec9..7982b5fd7 100644 --- a/container/Dockerfile.mingw +++ b/container/Dockerfile.mingw @@ -38,6 +38,9 @@ RUN /tmp/linux-install-rust.sh x86_64-pc-windows-gnu RUN useradd -m -s /bin/bash builder \ && echo "builder ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/builder +# Let setup-cfengine-build-host.sh know we are in a container +RUN touch /etc/cfengine-in-container.flag + USER builder WORKDIR /home/builder diff --git a/container/Dockerfile.rhel b/container/Dockerfile.rhel index 12d5a2179..4d82a419d 100644 --- a/container/Dockerfile.rhel +++ b/container/Dockerfile.rhel @@ -59,6 +59,9 @@ RUN if [ -n "${EXTRA_PKGS}" ]; then dnf install -y ${EXTRA_PKGS} && dnf clean al RUN useradd -m -s /bin/bash builder \ && echo "builder ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/builder +# let setup-cfengine-build-host.sh know we are in a container +RUN touch /etc/cfengine-in-container.flag + USER builder WORKDIR /home/builder diff --git a/container/Dockerfile.rhel7 b/container/Dockerfile.rhel7 index eb61542af..cadf71d8d 100644 --- a/container/Dockerfile.rhel7 +++ b/container/Dockerfile.rhel7 @@ -51,6 +51,9 @@ RUN /tmp/linux-install-rust.sh RUN useradd -m -s /bin/bash builder \ && echo "builder ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/builder +# let setup-cfengine-build-host.sh know we are in a container +RUN touch /etc/cfengine-in-container.flag + USER builder WORKDIR /home/builder diff --git a/container/Dockerfile.tarballs b/container/Dockerfile.tarballs index 0a06d2121..c040039e2 100644 --- a/container/Dockerfile.tarballs +++ b/container/Dockerfile.tarballs @@ -28,6 +28,10 @@ RUN apt-get -qy update && apt-get -y install \ RUN useradd -m -s /bin/bash builder \ && echo "builder ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/builder +# let setup-cfengine-build-host.sh know we are in a container and bootstrapping +RUN touch /etc/cfengine-in-container.flag +RUN touch /etc/cfengine-bootstrap-pr-host.flag + USER builder WORKDIR /home/builder From 0b655bbc1ddb265e3e304d56cd76c7e344babb96 Mon Sep 17 00:00:00 2001 From: Craig Comstock Date: Wed, 2 Sep 2026 11:04:10 -0500 Subject: [PATCH 3/4] Refactored build-scripts scripts to support running as numbered steps The numbered steps are symlinks to existing scripts. The artifacts output directory had to be refactored slightly to work in both containers and regular build hosts. Some existing scripts were split up or refactored to migrate steps previously in build-in-container-inner.sh to build-script scripts. Ticket: ENT-12599 Changelog: None --- Jenkinsfile | 91 ++------- build-in-container-inner.sh | 123 ++---------- build-scripts/0000-system-dependencies | 5 + build-scripts/0005-repositories | 1 + build-scripts/0010-autogen | 1 + build-scripts/0020-clean-buildmachine | 1 + build-scripts/0030-bootstrap-mission-portal | 1 + build-scripts/0035-generate-pull-request-file | 1 + build-scripts/0040-build-tarballs | 1 + build-scripts/0050-unpack-tarballs | 1 + build-scripts/0060-install-dependencies | 1 + build-scripts/0070-configure | 1 + build-scripts/0080-generate-source-tarballs | 1 + build-scripts/0090-compile | 1 + build-scripts/0100-produce-debug-symbols | 1 + build-scripts/0110-package | 1 + build-scripts/0120-prepare-results | 1 + build-scripts/0130-test | 1 + build-scripts/0140-prepare-results | 1 + build-scripts/autogen | 14 +- build-scripts/bootstrap-mission-portal | 55 +++++ build-scripts/bootstrap-tarballs | 189 +----------------- build-scripts/build-tarballs | 108 ++++++++++ build-scripts/compare-versions | 4 + build-scripts/create-empty-test | 1 + build-scripts/functions | 15 +- build-scripts/generate-pull-request-file | 70 +++++++ build-scripts/generate-source-tarballs | 12 +- build-scripts/prepare-results | 36 ++-- build-scripts/repositories | 19 ++ build-scripts/revision-file | 4 +- build-scripts/test | 8 +- build-scripts/unpack-tarballs | 15 +- build-scripts/version | 4 +- ci/setup-cfengine-build-host.sh | 7 +- ci/setup-ci-host.sh | 14 +- 36 files changed, 403 insertions(+), 407 deletions(-) create mode 100755 build-scripts/0000-system-dependencies create mode 120000 build-scripts/0005-repositories create mode 120000 build-scripts/0010-autogen create mode 120000 build-scripts/0020-clean-buildmachine create mode 120000 build-scripts/0030-bootstrap-mission-portal create mode 120000 build-scripts/0035-generate-pull-request-file create mode 120000 build-scripts/0040-build-tarballs create mode 120000 build-scripts/0050-unpack-tarballs create mode 120000 build-scripts/0060-install-dependencies create mode 120000 build-scripts/0070-configure create mode 120000 build-scripts/0080-generate-source-tarballs create mode 120000 build-scripts/0090-compile create mode 120000 build-scripts/0100-produce-debug-symbols create mode 120000 build-scripts/0110-package create mode 120000 build-scripts/0120-prepare-results create mode 120000 build-scripts/0130-test create mode 120000 build-scripts/0140-prepare-results create mode 100755 build-scripts/bootstrap-mission-portal create mode 100755 build-scripts/build-tarballs create mode 100755 build-scripts/generate-pull-request-file create mode 100755 build-scripts/repositories diff --git a/Jenkinsfile b/Jenkinsfile index 181827d8e..a44556e66 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -59,56 +59,21 @@ def revFor(String repo) { return (rev && rev.trim()) ? normalizeRev(rev.trim()) : defaultRev(repo) } -// Returns the refspec to fetch a repo with. Branch heads are always fetched. -// Pull requests live outside refs/heads. Building one means fetching its ref -// too, or its commit is not in the clone. -def refspecFor(String rev) { - def heads = '+refs/heads/*:refs/remotes/origin/*' - if (!(rev ==~ /^(?:refs\/)?pull\/\d+\/(merge|head)$/)) { return heads } - // pull/1234/merge -> [pull, 1234, merge] - def parts = rev.replaceAll(/^refs\//, '').split('/') - return "${heads} +refs/pull/${parts[1]}/${parts[2]}:refs/remotes/origin/pr/${parts[1]}" -} - -// Resolves rev to the commit to build. -// -// url is the repo to ask. -// rev is what revFor returned: a branch, tag, pull//merge ref, -// refs/... ref, or commit id. -// -// Returns '' when rev names no ref, as a commit id does. -// -// A bare name is an ls-remote pattern matched against the tail of every ref, -// not a ref name: 'master' also matched core's CFE-159/master, which sorts -// first and so won. Ask for the full ref, refs/heads before refs/tags before -// refs/, and take the first that exists. -def resolveRev(String url, String rev) { - def candidates = rev.startsWith('refs/') ? [rev] - : ["refs/heads/${rev}", "refs/tags/${rev}", "refs/${rev}"] - for (ref in candidates) { - // An annotated tag's own ref names the tag object, its ^{} the commit. - def peeled = "${ref}^{}" - def out = sh(returnStdout: true, - script: "git ls-remote '${url}' '${ref}' '${peeled}'").trim() - def sha = '' - for (line in out.readLines()) { - def parts = line.split() - if (parts[1] == peeled) { return parts[0] } - if (parts[1] == ref) { sha = parts[0] } - } - if (sha) { return sha } - } - return '' +// The refspec the other jobs fetch with. Branch heads are always fetched. Pull +// requests live outside refs/heads, so building one means fetching its ref too, +// or its commit is not in the clone. +def refspec() { + return '+refs/heads/*:refs/remotes/origin/* +refs/pull/*:refs/remotes/origin/pull/*' } // Runs one build in the workspace of the node the caller allocated. // -// Cleans up after the previous build. Checks out each repo at its commit from -// shas. Builds, then archives the packages. +// Cleans up after the previous build. Checks out each repo at its revision from +// revs. Builds, then archives the packages. // // opts holds the build-in-container.py flags that vary per build. The flags // every build shares are added below. -def containerBuild(String opts, List repos, Map shas, Map revs) { +def containerBuild(String opts, List repos, Map revs) { // The container hands the directories it writes back to us as it exits, so // this only covers a build that never got to exit (e.g. killed). sh 'sudo chown -R "$(id -u):$(id -g)" "$WORKSPACE" 2>/dev/null || true' @@ -117,10 +82,10 @@ def containerBuild(String opts, List repos, Map shas, Map revs) { repos.each { repo -> dir("src/${repo}") { checkout([$class: 'GitSCM', - branches: [[name: shas[repo]]], + branches: [[name: revs[repo]]], userRemoteConfigs: [[url: "git@github.com:cfengine/${repo}.git", credentialsId: 'jenkins-github', - refspec: refspecFor(revs[repo])]], + refspec: refspec()]], // Full history on purpose: the build reads SOURCE_DATE_EPOCH and // every dependency's revision out of git log, so a shallow clone // would change the timestamps it pins. @@ -151,15 +116,10 @@ def containerBuild(String opts, List repos, Map shas, Map revs) { } // All filled in by Resolve refs and read by the build stages, which run on other -// nodes. labels holds the build labels asked for. revs and shas hold what was -// asked for, and what it resolved to: +// nodes. labels holds the build labels asked for. revs holds the revision each +// repo builds at: // // revs['core'] = pull/1234/merge -// shas['core'] = 5dca070a98f9be... -// -// Nodes check out the sha, so a push mid-run cannot change what is built. The -// rev is kept too: a sha does not say whether a pull ref has to be fetched. -def shas = [:] def labels = [] def revs = [:] @@ -218,27 +178,12 @@ pipeline { def repos = reposFor(params.PROJECT) echo "Building ${labels.size()} labels:\n ${labels.join('\n ')}" - repos.each { repo -> revs[repo] = revFor(repo) } - - // Each platform checks out on its own node, so a push while the job - // runs would otherwise leave them building different sources. Resolve - // to commits once, here, and hand those to every build. - sshagent(['jenkins-github']) { - repos.each { repo -> - def rev = revs[repo] - def sha = resolveRev("git@github.com:cfengine/${repo}.git", rev) - if (!sha) { - // A commit id matches no ref, which is the one case where an - // empty answer is fine. - if (!(rev ==~ /[0-9a-f]{7,40}/)) { error "${repo}: cannot resolve '${rev}'" } - sha = rev - } - shas[repo] = sha - echo "${repo}: ${rev} -> ${sha}" - } + repos.each { repo -> + revs[repo] = revFor(repo) + echo "${repo}: ${revs[repo]}" } - currentBuild.description = "${params.PROJECT} @ ${shas['core'].take(7)}: ${labels.size()} labels" + currentBuild.description = "${params.PROJECT} @ ${revs['core']}: ${labels.size()} labels" } } } @@ -251,7 +196,7 @@ pipeline { // --tarballs builds core and masterfiles alone, in an image of its own, // and forces project and platform itself. Only the build type is ours // to pass: it decides the version string. - containerBuild('--tarballs', ['buildscripts', 'core', 'masterfiles'], shas, revs) + containerBuild('--tarballs', ['buildscripts', 'core', 'masterfiles'], revs) } } } @@ -267,7 +212,7 @@ pipeline { // The label decides the platform, the role and the container // architecture, so --arch would only contradict it. containerBuild("--label '${label}' --project '${params.PROJECT}'", - repos, shas, revs) + repos, revs) } }] } diff --git a/build-in-container-inner.sh b/build-in-container-inner.sh index 155bb3792..98a1bd0d9 100755 --- a/build-in-container-inner.sh +++ b/build-in-container-inner.sh @@ -10,12 +10,14 @@ sudo touch /etc/cfengine-in-container.flag BASEDIR=/home/builder/build export BASEDIR export AUTOBUILD_PATH="$BASEDIR/buildscripts" +OUTPUT=/output +export OUTPUT mkdir -p "$BASEDIR" # Bind-mounted directories may be owned by the host user's UID. # Fix ownership so builder can write to them. -sudo chown -R "$(id -u):$(id -g)" "$HOME/.cache" /output +sudo chown -R "$(id -u):$(id -g)" "$HOME/.cache" "$OUTPUT" # And hand ownership back to the host user on the way out. if [ -n "$HOST_UID" ] && [ -n "$HOST_GID" ]; then @@ -88,49 +90,10 @@ fi export SOURCE_DATE_EPOCH echo "SOURCE_DATE_EPOCH=$SOURCE_DATE_EPOCH" -install_mission_portal_deps() ( - set -e - - if [ -f "$BASEDIR/mission-portal/public/scripts/package.json" ]; then - echo "Installing npm dependencies..." - npm ci --prefix "$BASEDIR/mission-portal/public/scripts/" - echo "Building react components..." - npm run build --prefix "$BASEDIR/mission-portal/public/scripts/" - rm -rf "$BASEDIR/mission-portal/public/scripts/node_modules" - fi - - if [ -f "$BASEDIR/mission-portal/composer.json" ]; then - echo "Installing Mission Portal PHP dependencies..." - (cd "$BASEDIR/mission-portal" && composer install --no-dev --ignore-platform-reqs --prefer-dist) - fi - - if [ -f "$BASEDIR/nova/api/http/composer.json" ]; then - echo "Installing Nova API PHP dependencies..." - (cd "$BASEDIR/nova/api/http" && composer install --no-dev --ignore-platform-reqs --prefer-dist) - fi - - if [ -f "$BASEDIR/mission-portal/public/themes/default/bootstrap/cfengine_theme.less" ]; then - echo "Compiling Mission Portal styles..." - mkdir -p "$BASEDIR/mission-portal/public/themes/default/bootstrap/compiled/css" - (cd "$BASEDIR/mission-portal/public/themes/default/bootstrap" && - lessc --compress ./cfengine_theme.less ./compiled/css/cfengine.less.css) - fi - - if [ -f "$BASEDIR/mission-portal/ldap/composer.json" ]; then - echo "Installing LDAP API PHP dependencies..." - (cd "$BASEDIR/mission-portal/ldap" && composer install --no-dev --ignore-platform-reqs --prefer-dist) - fi - - # Composer falls back to git clone when GitHub's anonymous zipball - # rate limit is hit, leaving non-reproducible .git directories in the - # vendor tree. Strip them. - find "$BASEDIR/mission-portal" "$BASEDIR/nova/api/http" -type d -name .git -path '*/vendor/*' -exec rm -rf {} + -) - # Lets whoever consumes the output check that it arrived intact. Sorted in the C # locale so that the list itself comes out the same every time. write_sha256sums() ( - cd /output + cd "$OUTPUT" # shellcheck disable=SC2094 # > Make sure not to read and write the same file in the same pipeline. # find leaves it out by name, so the list never covers itself. @@ -138,55 +101,11 @@ write_sha256sums() ( | LC_ALL=C sort | xargs -r sha256sum > sha256sums.txt ) -# Build the source tarballs. They are the same whichever platform builds them, -# so only this image builds them, and nothing else here does. /output is -# /tarballs on the host, as the packages' /output is per label. -# -# Each tarball's timestamps follow its own repository: Makefile.am in core and in -# masterfiles clamps every mtime in the tarball to SOURCE_DATE_EPOCH, so taking -# it from the last commit keeps a tarball identical until its own sources change. -build_tarballs() ( - set -e - - ( - cd "$BASEDIR/core" - SOURCE_DATE_EPOCH=$(git log -1 --format=%ct) - export SOURCE_DATE_EPOCH - echo "core SOURCE_DATE_EPOCH=$SOURCE_DATE_EPOCH" - - rm -f cfengine-3.*.tar.gz - # Configure so the dist target exists, undone again below. - ./configure -C - make dist - mv cfengine-3.*.tar.gz /output/ - make distclean - ) - - ( - cd "$BASEDIR/masterfiles" - SOURCE_DATE_EPOCH=$(git log -1 --format=%ct) - export SOURCE_DATE_EPOCH - echo "masterfiles SOURCE_DATE_EPOCH=$SOURCE_DATE_EPOCH" - - rm -f cfengine-masterfiles*.tar.gz - ./configure - make dist # source tarball: cfengine-masterfiles-.tar.gz - make tar-package # package tarball: cfengine-masterfiles-.pkg.tar.gz - mv cfengine-masterfiles*.tar.gz /output/ - make distclean - ) - - write_sha256sums -) - -# === Step runner with failure reporting === -# Disable set -e so we can capture exit codes and report which step failed. -set +e run_step() { local name="$1" shift echo "=== Running $name ===" - "$@" + "$BASEDIR/buildscripts/build-scripts/$name" "$@" local rc=$? if [ $rc -ne 0 ]; then echo "" @@ -196,36 +115,26 @@ run_step() { } # === Build steps === -run_step "01-autogen" "$BASEDIR/buildscripts/build-scripts/autogen" if [ "$TARBALLS" = yes ]; then - run_step "02-tarballs" build_tarballs + run_step autogen + run_step build-tarballs echo "" echo "=== Build complete ===" - ls -lh /output/ + ls -lh "$OUTPUT" exit 0 fi -run_step "02-install-dependencies" "$BASEDIR/buildscripts/build-scripts/install-dependencies" -# Mission Portal is an Enterprise/nova-only component; its sources are only -# synced when PROJECT=nova. Skip this step for community hubs. -if [ "$PROJECT" = "nova" ] && [ "$EXPLICIT_ROLE" = "hub" ]; then - run_step "03-mission-portal-deps" install_mission_portal_deps -fi -run_step "04-configure" "$BASEDIR/buildscripts/build-scripts/configure" -run_step "05-compile" "$BASEDIR/buildscripts/build-scripts/compile" -run_step "06-package" "$BASEDIR/buildscripts/build-scripts/package" - -# === Copy output packages === -# Packages are created under $BASEDIR// by dpkg-buildpackage / rpmbuild. -# Exclude deps-packaging to avoid copying dependency packages. -find "$BASEDIR" -maxdepth 4 \ - -path "$BASEDIR/buildscripts/deps-packaging" -prune -o \ - \( -name '*.deb' -o -name '*.rpm' -o -name '*.msi' -o -name '*.pkg.tar.gz' \) -print \ - -exec cp {} /output/ \; +NO_TESTS=true +export NO_TESTS + +for script in "$BASEDIR/buildscripts/build-scripts"/0*; do + name="$(basename "$script")" + run_step "$name" +done write_sha256sums echo "" echo "=== Build complete ===" -ls -lh /output/ +ls -lh "$OUTPUT"/ diff --git a/build-scripts/0000-system-dependencies b/build-scripts/0000-system-dependencies new file mode 100755 index 000000000..8a6db48de --- /dev/null +++ b/build-scripts/0000-system-dependencies @@ -0,0 +1,5 @@ +#!/bin/sh + +set -ex +thisdir="$(dirname "$0")" +sudo bash "$thisdir"/../ci/setup-cfengine-build-host.sh diff --git a/build-scripts/0005-repositories b/build-scripts/0005-repositories new file mode 120000 index 000000000..e69f3dc94 --- /dev/null +++ b/build-scripts/0005-repositories @@ -0,0 +1 @@ +repositories \ No newline at end of file diff --git a/build-scripts/0010-autogen b/build-scripts/0010-autogen new file mode 120000 index 000000000..563ea1500 --- /dev/null +++ b/build-scripts/0010-autogen @@ -0,0 +1 @@ +autogen \ No newline at end of file diff --git a/build-scripts/0020-clean-buildmachine b/build-scripts/0020-clean-buildmachine new file mode 120000 index 000000000..3e44a57b8 --- /dev/null +++ b/build-scripts/0020-clean-buildmachine @@ -0,0 +1 @@ +clean-buildmachine \ No newline at end of file diff --git a/build-scripts/0030-bootstrap-mission-portal b/build-scripts/0030-bootstrap-mission-portal new file mode 120000 index 000000000..da139a1d1 --- /dev/null +++ b/build-scripts/0030-bootstrap-mission-portal @@ -0,0 +1 @@ +bootstrap-mission-portal \ No newline at end of file diff --git a/build-scripts/0035-generate-pull-request-file b/build-scripts/0035-generate-pull-request-file new file mode 120000 index 000000000..019f7567e --- /dev/null +++ b/build-scripts/0035-generate-pull-request-file @@ -0,0 +1 @@ +generate-pull-request-file \ No newline at end of file diff --git a/build-scripts/0040-build-tarballs b/build-scripts/0040-build-tarballs new file mode 120000 index 000000000..0d1b19ec2 --- /dev/null +++ b/build-scripts/0040-build-tarballs @@ -0,0 +1 @@ +build-tarballs \ No newline at end of file diff --git a/build-scripts/0050-unpack-tarballs b/build-scripts/0050-unpack-tarballs new file mode 120000 index 000000000..f4e734e68 --- /dev/null +++ b/build-scripts/0050-unpack-tarballs @@ -0,0 +1 @@ +unpack-tarballs \ No newline at end of file diff --git a/build-scripts/0060-install-dependencies b/build-scripts/0060-install-dependencies new file mode 120000 index 000000000..365df31b2 --- /dev/null +++ b/build-scripts/0060-install-dependencies @@ -0,0 +1 @@ +install-dependencies \ No newline at end of file diff --git a/build-scripts/0070-configure b/build-scripts/0070-configure new file mode 120000 index 000000000..cabc7046d --- /dev/null +++ b/build-scripts/0070-configure @@ -0,0 +1 @@ +configure \ No newline at end of file diff --git a/build-scripts/0080-generate-source-tarballs b/build-scripts/0080-generate-source-tarballs new file mode 120000 index 000000000..ae7ba0935 --- /dev/null +++ b/build-scripts/0080-generate-source-tarballs @@ -0,0 +1 @@ +generate-source-tarballs \ No newline at end of file diff --git a/build-scripts/0090-compile b/build-scripts/0090-compile new file mode 120000 index 000000000..f8808babb --- /dev/null +++ b/build-scripts/0090-compile @@ -0,0 +1 @@ +compile \ No newline at end of file diff --git a/build-scripts/0100-produce-debug-symbols b/build-scripts/0100-produce-debug-symbols new file mode 120000 index 000000000..017abf496 --- /dev/null +++ b/build-scripts/0100-produce-debug-symbols @@ -0,0 +1 @@ +produce-debug-symbols \ No newline at end of file diff --git a/build-scripts/0110-package b/build-scripts/0110-package new file mode 120000 index 000000000..597acc768 --- /dev/null +++ b/build-scripts/0110-package @@ -0,0 +1 @@ +package \ No newline at end of file diff --git a/build-scripts/0120-prepare-results b/build-scripts/0120-prepare-results new file mode 120000 index 000000000..b1b48550e --- /dev/null +++ b/build-scripts/0120-prepare-results @@ -0,0 +1 @@ +prepare-results \ No newline at end of file diff --git a/build-scripts/0130-test b/build-scripts/0130-test new file mode 120000 index 000000000..30d74d258 --- /dev/null +++ b/build-scripts/0130-test @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/build-scripts/0140-prepare-results b/build-scripts/0140-prepare-results new file mode 120000 index 000000000..b1b48550e --- /dev/null +++ b/build-scripts/0140-prepare-results @@ -0,0 +1 @@ +prepare-results \ No newline at end of file diff --git a/build-scripts/autogen b/build-scripts/autogen index 236f30b25..7ba0dbef3 100755 --- a/build-scripts/autogen +++ b/build-scripts/autogen @@ -18,6 +18,10 @@ # ``` # +# note that this script should be a no-op if an artifacts.tgz is present +thisdir="$(dirname "$0")" +bash "$thisdir"/../ci/setup-ci-host.sh --bootstrap + # Get the BASEDIR variable holding the path to where our repos are checked out . "$(dirname "$0")/functions" @@ -61,11 +65,15 @@ done # Run autogen.sh on each repository for proj in $projects; do # autogen.sh is quite verbose, so only print the output in case of failure - log_debug "Running autogen.sh for project $proj..." ( cd "$BASEDIR/$proj" - export NO_CONFIGURE=1 - run_and_print_on_failure ./autogen.sh + if [ ! -f configure ] || [ ! -f CFVERSION ]; then + log_debug "Running autogen.sh for project $proj..." + export NO_CONFIGURE=1 + run_and_print_on_failure ./autogen.sh + else + log_debug "Skipping autogen.sh in $proj as configure and/or CFVERSION files already exist" + fi ) done diff --git a/build-scripts/bootstrap-mission-portal b/build-scripts/bootstrap-mission-portal new file mode 100755 index 000000000..c59b7063d --- /dev/null +++ b/build-scripts/bootstrap-mission-portal @@ -0,0 +1,55 @@ +. "$(dirname "$0")"/functions +. detect-environment +. compile-options +. version + set -e + +if [ "$ROLE" != "hub" ]; then + echo "ROLE is not hub so skipping $0" + exit 0 +fi + +if command -v composer >/dev/null; then + COMPOSER=$(command -v composer) +elif [ -f /usr/local/bin/composer.phar ]; then + COMPOSER=/usr/local/bin/composer.phar +else + echo "Error: could not find composer command in PATH or at /usr/local/bin/composer.phar" + exit 1 +fi + + +if [ -f "$BASEDIR/mission-portal/public/scripts/package.json" ]; then + echo "Installing npm dependencies..." + npm ci --prefix "$BASEDIR/mission-portal/public/scripts/" + echo "Building react components..." + npm run build --prefix "$BASEDIR/mission-portal/public/scripts/" + rm -rf "$BASEDIR/mission-portal/public/scripts/node_modules" +fi + +if [ -f "$BASEDIR/mission-portal/composer.json" ]; then + echo "Installing Mission Portal PHP dependencies..." + (cd "$BASEDIR/mission-portal" && php "$COMPOSER" install --no-dev --ignore-platform-reqs --prefer-dist) +fi + +if [ -f "$BASEDIR/nova/api/http/composer.json" ]; then + echo "Installing Nova API PHP dependencies..." + (cd "$BASEDIR/nova/api/http" && php "$COMPOSER" install --no-dev --ignore-platform-reqs --prefer-dist) +fi + +if [ -f "$BASEDIR/mission-portal/public/themes/default/bootstrap/cfengine_theme.less" ]; then + echo "Compiling Mission Portal styles..." + mkdir -p "$BASEDIR/mission-portal/public/themes/default/bootstrap/compiled/css" + (cd "$BASEDIR/mission-portal/public/themes/default/bootstrap" && + lessc --compress ./cfengine_theme.less ./compiled/css/cfengine.less.css) +fi + +if [ -f "$BASEDIR/mission-portal/ldap/composer.json" ]; then + echo "Installing LDAP API PHP dependencies..." + (cd "$BASEDIR/mission-portal/ldap" && php "$COMPOSER" install --no-dev --ignore-platform-reqs --prefer-dist) +fi + +# Composer falls back to git clone when GitHub's anonymous zipball +# rate limit is hit, leaving non-reproducible .git directories in the +# vendor tree. Strip them. +find "$BASEDIR/mission-portal" "$BASEDIR/nova/api/http" -type d -name .git -path '*/vendor/*' -exec rm -rf {} + diff --git a/build-scripts/bootstrap-tarballs b/build-scripts/bootstrap-tarballs index 4a27d3009..72b2a28a1 100755 --- a/build-scripts/bootstrap-tarballs +++ b/build-scripts/bootstrap-tarballs @@ -1,184 +1,7 @@ -#!/bin/bash - -# This script is supposed generate tarballs from the core & masterfiles -# repositories. We also use these tarballs later in the build process to make -# sure that they actually work. -# -# Currently this script does a lot more than bootstrap tarballs. -# It also fetches pull request info and installs PHP and javascript dependencies. -# The history behind this is that this script is run early in the bootstrap-pr job, -# and these extra steps effectively saves compute time for subsequent hub builds. -# Maybe we should rename it to only bootstrap (see ENT-13064). -# -# You will first need to run the autogen script. E.g.: -# PROJECT=community ./buildscripts/build-scripts/autogen -# Then you can run it like this: -# BUILD_TYPE=DEBUG ./buildscripts/build-scripts/bootstrap-tarballs -# -# The script expects the following repositories to be side by side: -# . -# ├── buildscripts -# ├── core -# ├── enterprise -# ├── nova -# ├── mission-portal -# ├── libntech -# └── masterfiles -# -# ^ When building community you won't need enterprise, nova, mission-portal -# - -_dir=$(readlink -e "$(dirname "$0")") -# refactored a few functions into single file scripts for easier development/debugging, see ENT-12741 and ENT-12595 -# Easier to add a path to a script than source a file of functions. -export PATH="$_dir"/bin:$PATH -. "$(dirname "$0")"/functions -. detect-environment -. compile-options -. version - -mkdir -p "$BASEDIR"/output/tarballs - -# the first part of the script is not really critical -set +e - -# Get information about PRs among the used revisions. -# These PRs will have to be notified of build progress. -# -# Variables such as MISSION_PORTAL_REV may be set by the CI (Jenkins) to build and test multiple PR's together. -# The variables typically hold a branch name or a pull request ID. -# PR IDs can also be preceded by 'pull' or 'origin/pull'. -# E.g.: -# - MISSION_PORTAL_REV=pull/1755 -# - MISSION_PORTAL_REV=origin/pull/1755 -# where the trailing number is the pull request ID. -# -# Furthermore, they can be suffixed by anything after a subsequent slash (/). -# We usually use: -# - ID alone -# - pull/ID/head -# - pull/ID/merge -# -# This loop fetches information about the PRs if the respective variable is set. -# -for repo_spec in cfengine/buildscripts cfengine/core cfengine/masterfiles cfengine/enterprise cfengine/nova cfengine/mission-portal NorthernTechHQ/libntech; do - # remove organization/ from start of repo_spec - # E.g. 'cfengine/mission-portal' -> 'mission-portal' - repo="${repo_spec#*/}" - - # Convert to uppercase, swap hyphens with underscore and append '_REV' - # E.g. 'mission-portal' -> 'MISSION_PORTAL_REV' - rev_param_name="$(echo "$repo" | tr '[:lower:]-' '[:upper:]_')_REV" - - # Try to dereference the result from above and skip the rest of the loop - # unless the variable is defined. - revision="${!rev_param_name}" || continue - - # remove "origin/" (if any) - revision="${revision##origin/}" - - # Check to see if the resolved variable starts with 'pull/' - if expr "$revision" : "pull/" >/dev/null; then - # Extract the revision number. E.g. 'pull/1755' -> '1755' - pr_nr="$(echo "$revision" | cut -d/ -f2)" - - get-github-pull-request-info "$repo_spec" "$pr_nr" >>"$BASEDIR"/output/PRs - fi -done - -# now script failures should fail the script +#!/usr/bin/env bash +# legacy bootstrap-tarballs was split into build-tarballs and bootstrap-mission-portal +# todo: remove this script when it is no longer called from anywhere set -e - -# Build tarball from core repository -cd "$BASEDIR"/core -rm -f cfengine-3.*.tar.gz -git rev-parse HEAD >"$BASEDIR"/output/core-commitID -# Configure in order to run "make dist", deleted later. -log_debug "Running configure on core repository..." -run_and_print_on_failure ./configure -C -# Normalize source timestamps to avoid errors like: -# configure: error: newly created file is older than distributed files! -find . -exec touch -t 202501010000.00 {} + -log_debug "Running make dist on core repository..." -run_and_print_on_failure make dist -mv cfengine-3.*.tar.gz "$BASEDIR"/output/tarballs/ -log_debug "Running make distclean on core repository..." -run_and_print_on_failure make distclean - -# Build tarballs from masterfiles repository -cd "$BASEDIR"/masterfiles -rm -f cfengine-masterfiles*.tar.gz -git rev-parse HEAD >"$BASEDIR"/output/masterfiles-commitID -# Configure in order to run "make dist", deleted later. -log_debug "Running configure on masterfiles repository..." -run_and_print_on_failure ./configure -# Normalize source timestamps to avoid errors like: -# configure: error: newly created file is older than distributed files! -find . -exec touch -t 202501010000.00 {} + -log_debug "Running make dist on masterfiles repository..." -run_and_print_on_failure make dist # source tarball -log_debug "Running make tar-package on masterfiles repository..." -run_and_print_on_failure make tar-package # package tarball (containing all files as if they were installed under "prefix".) -mv cfengine-masterfiles*.tar.gz "$BASEDIR"/output/tarballs/ -log_debug "Running make distclean on masterfiles repository..." -run_and_print_on_failure make distclean - -# Compute a checksum list that can be used to verify the integrity of the -# tarballs -cd "$BASEDIR"/output/tarballs -sha256sum -- *.tar.gz >sha256sums.txt -# Add the BSD (16-bit) checksum of the checksum list to it's filename. This way -# you can verify the integrity of the checksum list itself. -CKSUM=$(sum sha256sums.txt | cut -d ' ' -f 1) -mv sha256sums.txt sha256sums."$CKSUM".txt - -log_debug "Installing javascript npm dependencies..." -( - if test -f "$BASEDIR"/mission-portal/public/scripts/package.json; then - cd "$BASEDIR"/mission-portal/public/scripts - # display node & npm versions - npm --version - node --version - # install dependencies from npmjs - run_and_print_on_failure npm ci --prefix "$BASEDIR"/mission-portal/public/scripts/ - # build react components - run_and_print_on_failure npm run build --prefix "$BASEDIR"/mission-portal/public/scripts/ - # remove node_modules since the bundles are already built - run_and_print_on_failure rm -rf "$BASEDIR"/mission-portal/public/scripts/node_modules - fi -) - -log_debug "Installing PHP composer dependencies from mission-portal repository..." -( - if test -f "$BASEDIR"/mission-portal/composer.json; then - cd "$BASEDIR"/mission-portal - # install PHP dependencies from composer - run_and_print_on_failure php /usr/bin/composer install --no-dev - fi -) - -log_debug "Installing PHP composer dependencies from nova repository..." -( - if test -f "$BASEDIR"/nova/api/http/composer.json; then - cd "$BASEDIR"/nova/api/http - # install PHP dependencies from composer - run_and_print_on_failure php /usr/bin/composer install --no-dev --ignore-platform-reqs - fi -) - -log_debug "Compiling Mission Portal styles..." -( - if test -f "$BASEDIR"/mission-portal/public/themes/default/bootstrap/cfengine_theme.less; then - cd "$BASEDIR"/mission-portal/public/themes/default/bootstrap - run_and_print_on_failure npx -p less lessc --compress ./cfengine_theme.less ./compiled/css/cfengine.less.css - fi -) - -log_debug "Installing LDAP API PHP composer dependencies..." -( - if test -f "$BASEDIR"/mission-portal/ldap/composer.json; then - cd "$BASEDIR"/mission-portal/ldap - # install PHP dependencies from composer - run_and_print_on_failure php /usr/bin/composer install --no-dev - fi -) +thisdir="$(dirname "$0")" +bash "$thisdir"/build-tarballs +bash "$thisdir"/bootstrap-mission-portal diff --git a/build-scripts/build-tarballs b/build-scripts/build-tarballs new file mode 100755 index 000000000..541837c1e --- /dev/null +++ b/build-scripts/build-tarballs @@ -0,0 +1,108 @@ +#!/bin/sh + +# +# This script builds tarballs for core and masterfiles for later use. +# +# Build the source tarballs. They are the same whichever platform builds them, +# so only this image builds them, and nothing else here does. +# +# This script looks for two optional env vars: +# - TARBALLS - if yes, then run this script, otherwise do not. The default is to not run this script. +# this variable is provided by build-in-container.py +# - OUTPUT - optional, for container builds this should be /output. The default is /output +# +# Additionally, if we are running in a Jenkins job, JOB_NAME will be set and if bootstrap-pr then the script will run fully. +# +# Each tarball's timestamps follow its own repository: Makefile.am in core and in +# masterfiles clamps every mtime in the tarball to SOURCE_DATE_EPOCH, so taking +# it from the last commit keeps a tarball identical until its own sources change. + +# The script expects the following repositories to exist side by side: +# . +# ├── buildscripts +# ├── core +# └── masterfiles +# The script can be run as described in the usage message below: + +usage() { + echo "Usage: [TARBALLS=yes] $0" +} + +. "$(dirname "$0")"/functions +. detect-environment +. compile-options +. version +set -e + +if [ -z "$TARBALLS" ] || [ "$TARBALLS" != "yes" ]; then + if [ -z "$JOB_NAME" ] || [ "$JOB_NAME" != "bootstrap-pr" ]; then + log_debug "Neither TARBALLS=yes or JOB_NAME=bootstrap-pr was in environment variables so skipping $0" + exit 0 + fi +fi + +echo "CRAIG given env" +env + +if [ -z "$OUTPUT" ]; then + OUTPUT="$BASEDIR"/output/tarballs/ +fi + +if [ ! -d "$OUTPUT" ]; then + log_debug "OUTPUT dir: $OUTPUT does not exist. Creating it now." + mkdir -p "$OUTPUT" +fi + +log_debug "OUTPUT dir is $OUTPUT" +set -x + +( +set -x + cd "$BASEDIR/core" + SOURCE_DATE_EPOCH=$(git log -1 --format=%ct) + export SOURCE_DATE_EPOCH + log_debug "core SOURCE_DATE_EPOCH=$SOURCE_DATE_EPOCH" + + rm -f cfengine-3.*.tar.gz + + log_debug "Writing flag file used by vagrant-pr and testing-pr: $OUTPUT/core-commitID" + git rev-parse HEAD > "$OUTPUT"/core-commitID + + # Configure so the dist target exists, undone again below. + log_debug "Running ./configure -C in core..." + run_and_print_on_failure ./configure -C + + log_debug "Running make dist in core..." + run_and_print_on_failure make dist + + mv cfengine-3.*.tar.gz "$OUTPUT" + + log_debug "Running make distclean in core..." + run_and_print_on_failure make distclean +) + +( +set -x + cd "$BASEDIR/masterfiles" + SOURCE_DATE_EPOCH=$(git log -1 --format=%ct) + export SOURCE_DATE_EPOCH + log_debug "masterfiles SOURCE_DATE_EPOCH=$SOURCE_DATE_EPOCH" + + rm -f cfengine-masterfiles*.tar.gz + + log_debug "Writing flag file used by vagrant-pr and testing-pr: $OUTPUT/masterfiles-commitID" + git rev-parse HEAD >"$OUTPUT"/masterfiles-commitID + + log_debug "Running ./configure in masterfiles..." + run_and_print_on_failure ./configure + + log_debug "Running make dist in masterfiles..." + run_and_print_on_failure make dist # source tarball: cfengine-masterfiles-.tar.gz + + log_debug "Running make tar-package in masterfiles..." + run_and_print_on_failure make tar-package # package tarball: cfengine-masterfiles-.pkg.tar.gz + + mv cfengine-masterfiles*.tar.gz "$OUTPUT" + log_debug "Running make distclean in masterfiles..." + run_and_print_on_failure make distclean +) diff --git a/build-scripts/compare-versions b/build-scripts/compare-versions index c17ba4e6c..a0c0d5d75 100755 --- a/build-scripts/compare-versions +++ b/build-scripts/compare-versions @@ -54,6 +54,10 @@ fi # themselves, however, in my opinion it reads better this way) for proj_i in $projects; do for proj_j in $projects; do + if [ ! -f "$BASEDIR/$proj_i/CFVERSION" ]; then + echo "Missing CFVERSION file in $BASEDIR/$proj_i" + exit 1 + fi # The CFVERSION file is read into the tr command which removes # whitespace and the sed command extracts the major, minor & patch # version number from the string. diff --git a/build-scripts/create-empty-test b/build-scripts/create-empty-test index e7905f59c..8e5f52dbf 100755 --- a/build-scripts/create-empty-test +++ b/build-scripts/create-empty-test @@ -5,6 +5,7 @@ # test report saying that there were no tests to report. for repo in core nova; do + mkdir -p "$BASEDIR/$repo/tests/unit" cat <"$BASEDIR/$repo/tests/unit/no_tests.xml" diff --git a/build-scripts/functions b/build-scripts/functions index 769b8f55c..4d79a2289 100644 --- a/build-scripts/functions +++ b/build-scripts/functions @@ -50,7 +50,7 @@ fi # export_variables: Set up and export all environment variables needed for builds # This function configures: # - System information (UNAME_S, UNAME_R, UNAME_M) -# - Build directories (BASEDIR, BUILDPREFIX, PREFIX, OUTDIR) +# - Build directories (BASEDIR, BUILDPREFIX, PREFIX, OUTPUT) # - PATH with platform-specific additions # Called automatically when this file is sourced export_variables() { @@ -75,8 +75,10 @@ export_variables() { esac AUTOBUILD_PATH=$(dirname "$SCRIPTDIR") - + else + SCRIPTDIR="$AUTOBUILD_PATH"/build-scripts fi + export SCRIPTDIR BASEDIR=$(dirname "$AUTOBUILD_PATH") export BASEDIR @@ -122,10 +124,11 @@ export_variables() { ACCEPT_INFERIOR_RM_PROGRAM=yes export ACCEPT_INFERIOR_RM_PROGRAM fi - - # Final destination of generated buildscripts output - OUTDIR="$BASEDIR/output/${OS}-${OS_VERSION}-${ARCH}" - export OUTDIR + if [ -z "$OUTPUT" ]; then + echo "No OUTPUT env var defined so setting to default of $BASEDIR/output" + OUTPUT="$BASEDIR/output" + fi + export OUTPUT } export_variables diff --git a/build-scripts/generate-pull-request-file b/build-scripts/generate-pull-request-file new file mode 100755 index 000000000..23680a9fa --- /dev/null +++ b/build-scripts/generate-pull-request-file @@ -0,0 +1,70 @@ +#!/bin/bash + +# Get information about PRs among the used revisions. +# These PRs will have to be notified of build progress. +# +# Variables such as MISSION_PORTAL_REV may be set by the CI (Jenkins) to build and test multiple PR's together. +# The variables typically hold a branch name or a pull request ID. +# PR IDs can also be preceded by 'pull' or 'origin/pull'. +# E.g.: +# - MISSION_PORTAL_REV=pull/1755 +# - MISSION_PORTAL_REV=origin/pull/1755 +# where the trailing number is the pull request ID. +# +# Furthermore, they can be suffixed by anything after a subsequent slash (/). +# We usually use: +# - ID alone +# - pull/ID/head +# - pull/ID/merge +# +# This loop fetches information about the PRs if the respective variable is set. +# +# The script expects the following repositories to be side by side: +# . +# ├── buildscripts +# ├── core +# ├── enterprise +# ├── nova +# ├── mission-portal +# ├── libntech +# └── masterfiles +# +# ^ When building community you won't need enterprise, nova, mission-portal +# + +_dir=$(readlink -e "$(dirname "$0")") +# refactored a few functions into single file scripts for easier development/debugging, see ENT-12741 and ENT-12595 +# Easier to add a path to a script than source a file of functions. +export PATH="$_dir"/bin:$PATH +. "$(dirname "$0")"/functions +. detect-environment +. compile-options +. version + +# errors are not critical +set +e + +for repo_spec in cfengine/buildscripts cfengine/core cfengine/masterfiles cfengine/enterprise cfengine/nova cfengine/mission-portal NorthernTechHQ/libntech; do + # remove organization/ from start of repo_spec + # E.g. 'cfengine/mission-portal' -> 'mission-portal' + repo="${repo_spec#*/}" + + # Convert to uppercase, swap hyphens with underscore and append '_REV' + # E.g. 'mission-portal' -> 'MISSION_PORTAL_REV' + rev_param_name="$(echo "$repo" | tr '[:lower:]-' '[:upper:]_')_REV" + + # Try to dereference the result from above and skip the rest of the loop + # unless the variable is defined. + revision="${!rev_param_name}" || continue + + # remove "origin/" (if any) + revision="${revision##origin/}" + + # Check to see if the resolved variable starts with 'pull/' + if expr "$revision" : "pull/" >/dev/null; then + # Extract the revision number. E.g. 'pull/1755' -> '1755' + pr_nr="$(echo "$revision" | cut -d/ -f2)" + + get-github-pull-request-info "$repo_spec" "$pr_nr" >>"$BASEDIR"/output/PRs + fi +done diff --git a/build-scripts/generate-source-tarballs b/build-scripts/generate-source-tarballs index e99f6cc67..a195cd7df 100755 --- a/build-scripts/generate-source-tarballs +++ b/build-scripts/generate-source-tarballs @@ -8,7 +8,7 @@ # This script, on the other hand, is meant to be run on all hosts to make sure that make dist works everywhere. # However, it's currently only used in our GitHub workflows and is not a part of the Jenkins PR pipeline (I think). # -# The source tarballs a built into $OUTDIR (defined in the functions script). +# The source tarballs a built into $OUTPUT (defined in the functions script). # # The script expects the following repositories to exist side by side: # . @@ -26,15 +26,17 @@ . detect-environment . compile-options +cd "$BASEDIR" + for repo in core masterfiles; do log_debug "Running target make dist in $repo repository..." - (cd $repo && run_and_print_on_failure "$MAKE" V=1 dist) + (cd "$repo" && run_and_print_on_failure "$MAKE" V=1 dist) done # Copy to the directory that's being uploaded to buildcache -mkdir -p "$OUTDIR" -cp core/*.tar.gz masterfiles/*.tar.gz "$OUTDIR" +mkdir -p "$OUTPUT" +cp core/*.tar.gz masterfiles/*.tar.gz "$OUTPUT" rm core/*.tar.gz masterfiles/*.tar.gz echo ' @@ -45,4 +47,4 @@ sure that "make dist" works everywhere. The release tarball is generated from the "bootstrap-*-community" jenkins jobs, running only on one buildslave. It should be located under the "output/tarballs" directory of that job. -' >"$OUTDIR"/TARBALLS.txt +' >"$OUTPUT"/TARBALLS.txt diff --git a/build-scripts/prepare-results b/build-scripts/prepare-results index 3903a6fe9..392a1f6ff 100755 --- a/build-scripts/prepare-results +++ b/build-scripts/prepare-results @@ -2,20 +2,20 @@ # Prepare build artifacts and test results for collection/archival. # This script collects built packages and test results from various locations -# and copies them to a single output directory ($OUTDIR) for easy retrieval. +# and copies them to a single output directory ($OUTPUT) for easy retrieval. # Source required configuration and utility files -. "$(dirname "$0")"/functions # $OUTDIR, $BASEDIR +. "$(dirname "$0")"/functions # $OUTPUT, $BASEDIR . detect-environment # $PACKAGING . compile-options . version log_debug "--- Preparing build results and test artifacts ---" -log_debug "Output directory: $OUTDIR" +log_debug "Output directory: $OUTPUT" log_debug "Packaging format: $PACKAGING" # Create output directory if it doesn't exist -mkdir -p "$OUTDIR" +mkdir -p "$OUTPUT" # Collect built packages from each CFEngine edition directory # Iterates through community, nova (enterprise agent), and nova-hub directories @@ -25,35 +25,35 @@ for dir in cfengine-community cfengine-nova cfengine-nova-hub; do # Copy packages based on platform-specific packaging format case "$PACKAGING" in rpm) - cp "$BASEDIR"/$dir/RPMS/*/*.rpm "$OUTDIR" - cp "$BASEDIR"/$dir/RPMS/*.pkg.tar.gz "$OUTDIR" + cp "$BASEDIR"/$dir/RPMS/*/*.rpm "$OUTPUT" + cp "$BASEDIR"/$dir/RPMS/*.pkg.tar.gz "$OUTPUT" ;; deb) - cp "$BASEDIR"/$dir/*.deb "$OUTDIR" - cp "$BASEDIR"/$dir/pkg/*.pkg.tar.gz "$OUTDIR" + cp "$BASEDIR"/$dir/*.deb "$OUTPUT" + cp "$BASEDIR"/$dir/pkg/*.pkg.tar.gz "$OUTPUT" ;; msi) - cp "$BASEDIR"/$dir/*.msi "$OUTDIR" + cp "$BASEDIR"/$dir/*.msi "$OUTPUT" ;; solaris) - cp "$BASEDIR"/$dir/*.pkg "$OUTDIR" + cp "$BASEDIR"/$dir/*.pkg "$OUTPUT" ;; freebsd) - cp build/$dir/pkg/*.tbz "$OUTDIR" + cp build/$dir/pkg/*.tbz "$OUTPUT" ;; hpux) - cp "$BASEDIR"/$dir/pkg/*.depot "$OUTDIR" + cp "$BASEDIR"/$dir/pkg/*.depot "$OUTPUT" ;; lpp) - cp "$BASEDIR"/$dir/RPMS/*/*.rpm "$OUTDIR" - cp "$HOME"/lppdir/out/*.bff "$OUTDIR" + cp "$BASEDIR"/$dir/RPMS/*/*.rpm "$OUTPUT" + cp "$HOME"/lppdir/out/*.bff "$OUTPUT" ;; *) log_error "Unknown packaging system: $PACKAGING" exit 1 ;; esac - #cp $BASEDIR/$dir/*.generic-pkg.tar.gz "$OUTDIR" + #cp $BASEDIR/$dir/*.generic-pkg.tar.gz "$OUTPUT" fi done @@ -62,9 +62,9 @@ done for project in $(projects_to_test); do if [ -f "$BASEDIR"/"$project"/tests/acceptance/summary.log ]; then log_debug "Collecting test results from $project" - cp "$BASEDIR"/"$project"/tests/acceptance/summary.log "$OUTDIR"/"$project"-summary.log - cp "$BASEDIR"/"$project"/tests/acceptance/test.log "$OUTDIR"/"$project"-test.log - cp "$BASEDIR"/"$project"/tests/acceptance/test.xml "$OUTDIR"/"$project"-test.xml + cp "$BASEDIR"/"$project"/tests/acceptance/summary.log "$OUTPUT"/"$project"-summary.log + cp "$BASEDIR"/"$project"/tests/acceptance/test.log "$OUTPUT"/"$project"-test.log + cp "$BASEDIR"/"$project"/tests/acceptance/test.xml "$OUTPUT"/"$project"-test.xml fi done diff --git a/build-scripts/repositories b/build-scripts/repositories new file mode 100755 index 000000000..2ee171071 --- /dev/null +++ b/build-scripts/repositories @@ -0,0 +1,19 @@ +set -ex +. "$(dirname "$0")"/functions + +REPOS="core masterfiles" +if test "$PROJECT" = "nova"; then + REPOS="$REPOS enterprise" +fi +if [ "$PROJECT" = "nova" ] && [ "$EXPLICIT_ROLE" = "hub" ]; then + REPOS="$REPOS nova mission-portal" +fi + +cd "$BASEDIR" + +for repo in $REPOS; do + if [ ! -d "$BASEDIR/$repo" ]; then + echo "cloning $repo at pwd $(pwd)" + git clone git@github.com:cfengine/$repo --recursive + fi +done diff --git a/build-scripts/revision-file b/build-scripts/revision-file index c3445f0af..6d35edec5 100755 --- a/build-scripts/revision-file +++ b/build-scripts/revision-file @@ -79,8 +79,8 @@ for _dir in $_dirs; do echo "$(basename "$0"): Debug: Revision file already exists in $_dir" fi else - echo "$(basename "$0"): Error: Expected to find the '$_dir' directory in '$BASEDIR', but it's not there" - exit 1 + # just a warning below, to enable just building with buildscripts for dependencies only without any other repositories like core, etc. + echo "$(basename "$0"): Warning: Expected to find the '$_dir' directory in '$BASEDIR', but it's not there" fi done diff --git a/build-scripts/test b/build-scripts/test index 524b67074..114e4c1f1 100755 --- a/build-scripts/test +++ b/build-scripts/test @@ -4,14 +4,18 @@ . detect-environment . compile-options -# $NO_TEST and $NO_ACCEPTANCE_TESTS are usually set by Jenkins +# NO_TESTS and NO_ACCEPTANCE_TESTS are usually set by Jenkins if [ "true" = "$NO_TESTS" ] || [ "true" = "$NO_ACCEPTANCE_TESTS" ]; then # In order to keep Jenkins happy, we create an empty test report saying that there were no tests to report. - log_debug "Creating empty test (\$NO_TESTS=$NO_TEST, \$NO_ACCEPTANCE_TESTS=$NO_ACCEPTANCE_TESTS)" + log_debug "Creating empty test (\$NO_TESTS=$NO_TESTS, \$NO_ACCEPTANCE_TESTS=$NO_ACCEPTANCE_TESTS)" create-empty-test exit 0 fi +# pre-reqs during a build, clean all packages and re-install from packages built in $HOME/.cache probably +"$BASEDIR"/buildscripts/build-scripts/clean-buildmachine +"$BASEDIR"/buildscripts/build-scripts/install-dependencies + if [ -n "$TEST_MACHINE" ]; then # Prepares the test machine by creating a chroot environment. log_debug "Preparing test machine" diff --git a/build-scripts/unpack-tarballs b/build-scripts/unpack-tarballs index bb67aa9af..87d2d7083 100755 --- a/build-scripts/unpack-tarballs +++ b/build-scripts/unpack-tarballs @@ -28,8 +28,21 @@ . detect-environment . compile-options -SOURCE_TARBALL="$BASEDIR/output/tarballs/cfengine-3.*.tar.gz" +if [ ! -d "$BASEDIR"/output/tarballs ]; then + echo "No $BASEDIR/output/tarballs so skipping $0" + exit 0 +fi + +SOURCE_TARBALL="$(ls $BASEDIR/output/tarballs/cfengine-3.*.tar.gz)" +log_debug "found SOURCE_TARBALL=$SOURCE_TARBALL" + MASTERFILES_TARBALL=$(find "$BASEDIR"/output/tarballs/ -name 'cfengine-masterfiles*.tar.gz' -a ! \( -name '*pkg.tar.gz' \)) +log_debug "found MASTERFILES_TARBALL=$MASTERFILES_TARBALL" + +if [ ! -f "$SOURCE_TARBALL" ] || [ ! -f "$MASTERFILES_TARBALL" ]; then + echo "Did not find SOURCE_TARBALL: $SOURCE_TARBALL or MASTERFILES_TARBALL: $MASTERFILES_TARBALL so skipping $0 script" + exit 0 +fi # DELETE the git-checked-out directories, they are tainted with # ./configure artifacts anyway. The tarballs are unpacked and symlinked diff --git a/build-scripts/version b/build-scripts/version index d1f392e1f..9026569bf 100644 --- a/build-scripts/version +++ b/build-scripts/version @@ -19,11 +19,11 @@ # repository. The script is executed when you run the autogen.sh. CORE_VERSION=$(cat "$BASEDIR"/core/CFVERSION) VERSION="${EXPLICIT_VERSION:-$CORE_VERSION}" -echo "$(basename $0): Debug: Detected CFEngine version number $VERSION" +log_debug "Detected CFEngine version number $VERSION" # The RELEASE number is the actual release of the package. It starts as 1 and # would incremented each time you create a new release of the CFEngine package # of the same version. RELEASE="${EXPLICIT_RELEASE:-1}" export RELEASE -echo "$(basename $0): Debug: Detected CFEngine package release number $RELEASE" +log_debug "Detected CFEngine package release number $RELEASE" diff --git a/ci/setup-cfengine-build-host.sh b/ci/setup-cfengine-build-host.sh index 9bf06059c..bd259e1d4 100755 --- a/ci/setup-cfengine-build-host.sh +++ b/ci/setup-cfengine-build-host.sh @@ -8,6 +8,11 @@ if [ -n "$cfengine_role" ]; then touch /etc/cfengine-"$cfengine_role".flag fi +if uname | grep -i darwin >/dev/null; then + echo "Error: MacOS(Darwin) not supported yet." + exit 1 +fi + # install needed packages and software for a build host set -e if [ "$(id -u)" != "0" ]; then @@ -62,7 +67,7 @@ function cleanup() { set -e set -x [ -f /var/log/messages ] && tail /var/log/messages - command -v journalctl >/dev/null && journalctl | grep -P '(error|fail)' + command -v journalctl >/dev/null && journalctl | grep -P '(error|fail)' || true if command -v apt >/dev/null 2>&1; then # workaround for CFE-4544, remove scriptlets call systemctl even when systemctl is-system-running returns false # Replace systemctl with a no-op stub that always succeeds. We can't diff --git a/ci/setup-ci-host.sh b/ci/setup-ci-host.sh index b646fb9d5..d0a71aa89 100755 --- a/ci/setup-ci-host.sh +++ b/ci/setup-ci-host.sh @@ -27,14 +27,20 @@ function file-line() function github-known-hosts() { - echo "ensuring github hostkeys are added to /home/jenkins/.ssh/known_hosts" + mkdir ~/.ssh + echo "ensuring github hostkeys are added to ~/.ssh/known_hosts" grep '^github.com' "$thisdir"/known_hosts | while read -r key; do - file-line /home/jenkins/.ssh/known_hosts "$key" + file-line ~/.ssh/known_hosts "$key" done - chown jenkins /home/jenkins/.ssh/known_hosts - chmod 0600 /home/jenkins/.ssh/known_hosts + chown $(id -un) ~/.ssh/known_hosts + chmod 0600 ~/.ssh/known_hosts } +if [ "$1" = "--bootstrap" ]; then + # todo, setup needed dependencies for a bootstrap host + exit 0 +fi + echo "ensuring that github.com hostkeys are in ~/.ssh/known_hosts" github-known-hosts From 9795a2ba6634624322c132d10863ae11f1b4343a Mon Sep 17 00:00:00 2001 From: Craig Comstock Date: Wed, 2 Sep 2026 11:06:12 -0500 Subject: [PATCH 4/4] Added build-scripts/canaries.txt for use in the future with Jenkinsfile Ticket: None Changelog: None --- build-scripts/canaries.txt | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 build-scripts/canaries.txt diff --git a/build-scripts/canaries.txt b/build-scripts/canaries.txt new file mode 100644 index 000000000..94d367bb2 --- /dev/null +++ b/build-scripts/canaries.txt @@ -0,0 +1,9 @@ +# canary labels + +PACKAGES_HUB_x86_64_linux_redhat_8 + +PACKAGES_HUB_x86_64_linux_ubuntu_22 + +PACKAGES_x86_64_linux_redhat_7 + +PACKAGES_x86_64_mingw