|
| 1 | +# frozen_string_literal: true |
| 2 | + |
| 3 | +# Builds packages using a Docker container |
| 4 | +# Env var options: |
| 5 | +# NO_TEARDOWN - If set, do not stop the docker container after the build for debugging purposes. |
| 6 | +# EZBAKE_BRANCH - If set, use this ezbake branch for building. If used, must also set EZBAKE_VERSION accordingly. |
| 7 | +# EZBAKE_REPO - If EZBAKE_BRANCH is set, use this repo URL for ezbake (default: https://github.com/openvoxproject/ezbake) |
| 8 | +# EZBAKE_VERSION - If set, use this version string in project.clj for ezbake. Must correspond to the version currently in defproject |
| 9 | +# in EZBAKE_BRANCH. |
| 10 | +# FULL_DEP_REBUILD_BRANCH - If set, rebuild all dependencies, where all dependency repos have this branch present with your |
| 11 | +# desired changes. Overrides any other DEP_ settings. |
| 12 | +# DEP_REBUILD - Comma-separated list of dependencies to rebuild from source. Should be the repo names. |
| 13 | +# DEP_REBUILD_BRANCH - If DEP_REBUILD is set, use this branch for the specified dependencies (default: main) |
| 14 | +# DEP_REBUILD_ORG - If DEP_REBUILD is set, use this GitHub org for the specified dependencies to build the repo URL (default: openvoxproject) |
| 15 | +# DEB_PLATFORMS - Comma-separated list of debian/ubuntu platforms to build for |
| 16 | +# RPM_PLATFORMS - Comma-separated list of rpm platforms to build for |
| 17 | +# FIPS - If set, build specified platforms with the appropriate 'fips' lein profile(s) enabled. |
| 18 | + |
| 19 | +require 'fileutils' |
| 20 | +require 'tmpdir' |
| 21 | +require_relative 'utils/docker_runner' |
| 22 | + |
| 23 | +module Vox |
| 24 | + class Build |
| 25 | + def platform_targets |
| 26 | + # It seems like these are special files/names that, when you want to add a new one, require |
| 27 | + # changes in some other component. But no, it seems to only really look at the parts of |
| 28 | + # the text in the string, as long as it looks like "base-<whatever you want to call the platform>-i386.cow" |
| 29 | + # and "<doesn't matter>-<os>-<osver>-<arch which doesn't matter because it's actually noarch>". |
| 30 | + # I think it just treats all debs like Debian these days. And all rpms are similar. |
| 31 | + # So do whatever you want I guess. We really don't need separate packages for each platform. |
| 32 | + # To be fixed one of these days. Relevant stuff: |
| 33 | + # https://github.com/puppetlabs/ezbake/blob/aeb7735a16d2eecd389a6bd9e5c0cfc7c62e61a5/resources/puppetlabs/lein-ezbake/template/global/tasks/build.rake |
| 34 | + # https://github.com/puppetlabs/ezbake/blob/aeb7735a16d2eecd389a6bd9e5c0cfc7c62e61a5/resources/puppetlabs/lein-ezbake/template/global/ext/fpm.rb |
| 35 | + deb_platforms = ENV['DEB_PLATFORMS'] || 'ubuntu-20.04,ubuntu-22.04,ubuntu-24.04,ubuntu-25.04,debian-11,debian-12,debian-13' |
| 36 | + rpm_platforms = ENV['RPM_PLATFORMS'] || 'el-8,el-9,el-10,sles-15,sles-16,amazon-2,amazon-2023,fedora-42,fedora-43' |
| 37 | + |
| 38 | + debs = deb_platforms.split(',').map { |p| "base-#{p.split("-").join}-i386.cow" }.join(" ") |
| 39 | + rpms = rpm_platforms.split(',').map { |p| "pl-#{p}-x86_64" }.join(" ") |
| 40 | + |
| 41 | + [debs, rpms] |
| 42 | + end |
| 43 | + # The deps must be built in this order due to dependencies between them. |
| 44 | + # There is a circular dependency between clj-http-client and trapperkeeper-webserver-jetty10, |
| 45 | + # but only for tests, so the build *should* work. |
| 46 | + DEP_BUILD_ORDER = [ |
| 47 | + 'clj-parent', |
| 48 | + 'clj-kitchensink', |
| 49 | + 'clj-i18n', |
| 50 | + 'comidi', |
| 51 | + 'jvm-ssl-utils', |
| 52 | + 'trapperkeeper', |
| 53 | + 'trapperkeeper-filesystem-watcher', |
| 54 | + 'trapperkeeper-webserver-jetty10', |
| 55 | + 'trapperkeeper-authorization', |
| 56 | + 'trapperkeeper-metrics', |
| 57 | + 'trapperkeeper-status', |
| 58 | + 'stockpile', |
| 59 | + 'structured-logging', |
| 60 | + ].freeze |
| 61 | + |
| 62 | + def initialize(tag:) |
| 63 | + @tag = tag |
| 64 | + @runner = Vox::DockerRunner.new(container_name: 'openvoxdb-builder', image: 'ezbake-builder') |
| 65 | + @deps_tmp = Dir.mktmpdir('deps') |
| 66 | + end |
| 67 | + |
| 68 | + def build |
| 69 | + checkout_tag_if_requested |
| 70 | + build_image_unless_present |
| 71 | + start_container |
| 72 | + build_and_install_libs(prepare_deps) |
| 73 | + build_project |
| 74 | + postprocess_output |
| 75 | + ensure |
| 76 | + @runner.teardown unless ENV['NO_TEARDOWN'] |
| 77 | + FileUtils.rm_rf(@deps_tmp) |
| 78 | + end |
| 79 | + |
| 80 | + private |
| 81 | + |
| 82 | + def checkout_tag_if_requested |
| 83 | + if @tag.nil? || @tag.empty? |
| 84 | + puts 'Running build with current branch' |
| 85 | + else |
| 86 | + puts "Running build on #{@tag}" |
| 87 | + Vox::Shell.run("git fetch --tags && git checkout #{@tag}") |
| 88 | + end |
| 89 | + end |
| 90 | + |
| 91 | + def build_image_unless_present |
| 92 | + return if @runner.image_exists? |
| 93 | + |
| 94 | + # If the Dockerfile has changed since this was last built, |
| 95 | + # delete all containers and do `docker rmi ezbake-builder` |
| 96 | + puts 'Building ezbake-builder image' |
| 97 | + Vox::Shell.run("docker build -t ezbake-builder .", silent: false, print_command: true) |
| 98 | + end |
| 99 | + |
| 100 | + def prepare_deps |
| 101 | + libs = {} |
| 102 | + |
| 103 | + # Manage ezbake override |
| 104 | + ezbake_branch = ENV.fetch('EZBAKE_BRANCH', '').strip |
| 105 | + unless ezbake_branch.empty? |
| 106 | + libs['ezbake'] = { |
| 107 | + repo: ENV.fetch('EZBAKE_REPO', 'https://github.com/openvoxproject/ezbake'), |
| 108 | + branch: ENV.fetch('EZBAKE_BRANCH', 'main'), |
| 109 | + } |
| 110 | + end |
| 111 | + |
| 112 | + # Decide if we're rebuilding everything or a subset |
| 113 | + full_rebuild_branch = ENV.fetch('FULL_DEP_REBUILD_BRANCH', '').strip |
| 114 | + subset_list = ENV.fetch('DEP_REBUILD', '').split(',').map(&:strip).reject(&:empty?) |
| 115 | + subset_branch = ENV.fetch('DEP_REBUILD_BRANCH', 'main').strip |
| 116 | + rebuild_org = ENV.fetch('DEP_REBUILD_ORG', 'openvoxproject').strip |
| 117 | + |
| 118 | + selected_libs = [] |
| 119 | + selected_branch = nil |
| 120 | + |
| 121 | + if !full_rebuild_branch.empty? |
| 122 | + selected_branch = full_rebuild_branch |
| 123 | + selected_libs = DEP_BUILD_ORDER.dup |
| 124 | + elsif !subset_list.empty? |
| 125 | + selected_branch = subset_branch |
| 126 | + unknown = subset_list - DEP_BUILD_ORDER |
| 127 | + puts "WARNING: Unknown deps in DEP_REBUILD (will be ignored): #{unknown.join(', ')}" unless unknown.empty? |
| 128 | + selected_libs = DEP_BUILD_ORDER & subset_list # Keeps DEP_BUILD_ORDER ordering |
| 129 | + end |
| 130 | + |
| 131 | + selected_libs.each do |lib| |
| 132 | + libs[lib] = { repo: "https://github.com/#{rebuild_org}/#{lib}", branch: selected_branch } |
| 133 | + end |
| 134 | + |
| 135 | + libs.each do |lib, config| |
| 136 | + puts "Checking out #{lib}" |
| 137 | + Vox::Shell.run( |
| 138 | + "git clone --revision #{config[:branch]} #{config[:repo]} #{@deps_tmp}/#{lib}", |
| 139 | + silent: false, |
| 140 | + print_command: true |
| 141 | + ) |
| 142 | + end |
| 143 | + |
| 144 | + libs |
| 145 | + end |
| 146 | + |
| 147 | + def start_container |
| 148 | + @runner.teardown if @runner.container_exists? |
| 149 | + volumes = [[Dir.pwd, '/code'], [@deps_tmp, '/deps']] |
| 150 | + @runner.start(volumes: volumes) |
| 151 | + end |
| 152 | + |
| 153 | + def build_and_install_libs(libs) |
| 154 | + libs.each_key do |lib| |
| 155 | + puts "Building and installing #{lib} from source" |
| 156 | + @runner.exec("cd /deps/#{lib} && lein install") |
| 157 | + end |
| 158 | + end |
| 159 | + |
| 160 | + def build_project |
| 161 | + debs, rpms = platform_targets |
| 162 | + fips = !ENV['FIPS'].nil? |
| 163 | + ezbake_version_var = ENV['EZBAKE_VERSION'] ? "EZBAKE_VERSION=#{ENV['EZBAKE_VERSION']}" : "" |
| 164 | + |
| 165 | + puts 'Building openvoxdb' |
| 166 | + @runner.exec('cd /code && rm -rf ruby output && bundle install --without test && lein install') |
| 167 | + @runner.exec( |
| 168 | + "cd /code && COW=\"#{debs}\" MOCK=\"#{rpms}\" GEM_SOURCE='https://rubygems.org' #{ezbake_version_var} " \ |
| 169 | + "EZBAKE_ALLOW_UNREPRODUCIBLE_BUILDS=true EZBAKE_NODEPLOY=true LEIN_PROFILES=ezbake " \ |
| 170 | + "lein with-profile #{fips ? "fips," : ""}user,ezbake,provided,internal ezbake local-build" |
| 171 | + ) |
| 172 | + end |
| 173 | + |
| 174 | + def postprocess_output |
| 175 | + Vox::Shell.run("sudo chown -R $USER output", print_command: true) |
| 176 | + Dir.glob("output/**/*i386*").each { |f| FileUtils.rm_rf(f) } |
| 177 | + Dir.glob("output/puppetdb-*.tar.gz").each { |f| FileUtils.mv(f, f.sub('puppetdb', 'openvoxdb')) } |
| 178 | + end |
| 179 | + end |
| 180 | +end |
| 181 | + |
| 182 | +namespace :vox do |
| 183 | + desc 'Build openvoxdb packages with Docker' |
| 184 | + task :build, [:tag] do |_, args| |
| 185 | + Vox::Build.new(tag: args[:tag]).build |
| 186 | + end |
| 187 | +end |
0 commit comments