diff --git a/.pipelines/templates/stages/trident_rpms/build-rpms-parallel.yml b/.pipelines/templates/stages/trident_rpms/build-rpms-parallel.yml new file mode 100644 index 000000000..1dc80eb65 --- /dev/null +++ b/.pipelines/templates/stages/trident_rpms/build-rpms-parallel.yml @@ -0,0 +1,70 @@ +parameters: + - name: targetArchitecture + type: string + default: "amd64" + values: + - arm64 + - amd64 + + - name: tridentRepoDirectory + type: string + + # Base artifact directory. azl3 RPMs/binaries land here; azl4 lands under + # the azl4/ subdirectory to preserve the single-artifact dual-distro layout. + - name: artifactDirectory + type: string + +steps: + # --- Shared setup (run once for both distros) --- + - script: | + set -eux + TRIDENT_VERSION=$(python3 ./scripts/get-version.py "$(Build.BuildNumber)" --commit) + echo "##vso[task.setvariable variable=trident_version]$TRIDENT_VERSION" + displayName: "Setting Trident version" + workingDirectory: ${{ parameters.tridentRepoDirectory }} + + - task: onebranch.pipeline.version@1 + displayName: "Set build number" + inputs: + system: "Custom" + customVersion: $(trident_version) + + - script: sudo tdnf install -y moby-buildx p7zip p7zip-plugins zstd + displayName: Install native dependencies + retryCountOnTaskFailure: 3 + + - script: | + set -eux + sudo systemctl start docker + workingDirectory: ${{ parameters.tridentRepoDirectory }} + displayName: Start Docker + + - template: ../common_tasks/preview-container.yml + parameters: + dockerfilePath: ${{ parameters.tridentRepoDirectory }}/packaging/docker/Dockerfile.full + tridentSourceDirectory: ${{ parameters.tridentRepoDirectory }} + targetArchitecture: ${{ parameters.targetArchitecture }} + + # --- Build azl3 and azl4 RPMs concurrently in a single task --- + # See scripts/build-rpms-parallel.sh: both docker builds run against the + # same BuildKit daemon as background jobs, each writing to its own dest dir + # and log file with explicit exit-code checks. This avoids a second ADO job + # (and its OneBranch/checkout/setup overhead) while overlapping the + # I/O-bound parts of the two builds. + - script: | + ./scripts/build-rpms-parallel.sh \ + "$(trident_version)" \ + "${{ parameters.tridentRepoDirectory }}/packaging/docker/Dockerfile.full" \ + "${{ parameters.artifactDirectory }}" \ + "$(Agent.TempDirectory)/_rpm_parallel" + workingDirectory: ${{ parameters.tridentRepoDirectory }} + displayName: Build azl3 + azl4 RPMs (parallel) + + # --- Extract Trident binaries for each distro --- + - script: ./scripts/extract-binary.sh ${{ parameters.artifactDirectory }} ${{ parameters.artifactDirectory }} ${{ parameters.targetArchitecture }} azl3 + workingDirectory: ${{ parameters.tridentRepoDirectory }} + displayName: Extract Trident binary (azl3) + + - script: ./scripts/extract-binary.sh ${{ parameters.artifactDirectory }}/azl4 ${{ parameters.artifactDirectory }}/azl4 ${{ parameters.targetArchitecture }} azl4 + workingDirectory: ${{ parameters.tridentRepoDirectory }} + displayName: Extract Trident binary (azl4) diff --git a/.pipelines/templates/stages/trident_rpms/build-source.yml b/.pipelines/templates/stages/trident_rpms/build-source.yml index 2b12e9756..78232ba13 100644 --- a/.pipelines/templates/stages/trident_rpms/build-source.yml +++ b/.pipelines/templates/stages/trident_rpms/build-source.yml @@ -89,17 +89,11 @@ stages: - template: ../common_tasks/cargo-auth.yml parameters: cargoConfigPath: $(TRIDENT_SOURCE_DIR)/.cargo/config.toml - - template: release.yml + - template: build-rpms-parallel.yml parameters: targetArchitecture: ${{ parameters.targetArchitecture }} tridentRepoDirectory: $(TRIDENT_SOURCE_DIR) artifactDirectory: "$(ob_outputDirectory)" - - template: release.yml - parameters: - targetArchitecture: ${{ parameters.targetArchitecture }} - tridentRepoDirectory: $(TRIDENT_SOURCE_DIR) - artifactDirectory: "$(ob_outputDirectory)/azl4" - distro: azl4 - ${{ elseif eq( parameters.targetArchitecture, 'arm64' ) }}: - job: BuildTrident_${{ parameters.targetArchitecture }} @@ -128,18 +122,8 @@ stages: - template: ../common_tasks/cargo-auth.yml parameters: cargoConfigPath: $(TRIDENT_SOURCE_DIR)/.cargo/config.toml - - script: | - set -eux - sudo systemctl start docker - displayName: Start Docker - - template: release.yml + - template: build-rpms-parallel.yml parameters: targetArchitecture: ${{ parameters.targetArchitecture }} tridentRepoDirectory: $(TRIDENT_SOURCE_DIR) artifactDirectory: "$(ob_outputDirectory)" - - template: release.yml - parameters: - targetArchitecture: ${{ parameters.targetArchitecture }} - tridentRepoDirectory: $(TRIDENT_SOURCE_DIR) - artifactDirectory: "$(ob_outputDirectory)/azl4" - distro: azl4 diff --git a/.pipelines/templates/stages/trident_rpms/release.yml b/.pipelines/templates/stages/trident_rpms/release.yml deleted file mode 100644 index 0cd2cd86a..000000000 --- a/.pipelines/templates/stages/trident_rpms/release.yml +++ /dev/null @@ -1,96 +0,0 @@ -parameters: - - name: targetArchitecture - type: string - default: "amd64" - values: - - arm64 - - amd64 - - - name: distro - type: string - default: "azl3" - values: - - azl3 - - azl4 - - - name: tridentRepoDirectory - type: string - - - name: artifactDirectory - type: string - -steps: - - script: | - set -eux - TRIDENT_VERSION=$(python3 ./scripts/get-version.py "$(Build.BuildNumber)" --commit) - echo "##vso[task.setvariable variable=trident_version]$TRIDENT_VERSION" - displayName: "Setting Trident version" - workingDirectory: ${{ parameters.tridentRepoDirectory }} - - - task: onebranch.pipeline.version@1 - displayName: "Set build number" - inputs: - system: "Custom" - customVersion: $(trident_version) - - - script: sudo tdnf install -y moby-buildx p7zip p7zip-plugins zstd - displayName: Install native dependencies - retryCountOnTaskFailure: 3 - - - script: | - set -eux - sudo systemctl start docker - workingDirectory: ${{ parameters.tridentRepoDirectory }} - displayName: Start Docker - - - template: ../common_tasks/preview-container.yml - parameters: - dockerfilePath: ${{ parameters.tridentRepoDirectory }}/packaging/docker/Dockerfile.full - tridentSourceDirectory: ${{ parameters.tridentRepoDirectory }} - targetArchitecture: ${{ parameters.targetArchitecture }} - - - script: | - set -eux - full_version=$(trident_version) - - # Separate into version and prerelease identifier - # for the RPM build. - version=$(echo $full_version | cut -d'-' -f1) - prerelease=$(echo $full_version | cut -d'-' -f2-) - - # Build RPMs and export only the artifact tarball (no image load/unpack). - # CARGO_REGISTRIES_BMP_PUBLICPACKAGES_TOKEN is populated by the CargoAuthenticate task. - outdir="/tmp/_rpm_artifacts" - rm -rf "$outdir" - mkdir -p "$outdir" - - AZL_IMAGE="$(DISTRO=${{ parameters.distro }} make azl-version-vars | grep AZL_IMAGE | cut -d '=' -f2)" - DISTRO_PACKAGES="$(DISTRO=${{ parameters.distro }} make azl-version-vars | grep DISTRO_PACKAGES | cut -d '=' -f2)" - RPM_PACKAGES="$(DISTRO=${{ parameters.distro }} make azl-version-vars | grep RPM_PACKAGES | cut -d '=' -f2)" - RPM_DEST="$(DISTRO=${{ parameters.distro }} make azl-version-vars | grep RPM_DEST | cut -d '=' -f2)" - RUST_PACKAGE="$(DISTRO=${{ parameters.distro }} make azl-version-vars | grep RUST_PACKAGE | cut -d '=' -f2)" - - docker build -f ${{ parameters.tridentRepoDirectory }}/packaging/docker/Dockerfile.full -t trident/trident-build:latest \ - --secret id=registry_token,env=CARGO_REGISTRIES_BMP_PUBLICPACKAGES_TOKEN \ - --build-arg TRIDENT_VERSION="$full_version" \ - --build-arg RPM_VER="$version"\ - --build-arg RPM_REL="$prerelease.${{ parameters.distro }}"\ - --build-arg AZL_IMAGE="$AZL_IMAGE"\ - --build-arg DISTRO_PACKAGES="$DISTRO_PACKAGES" \ - --build-arg RPM_PACKAGES="$RPM_PACKAGES" \ - --build-arg RUST_PACKAGE="$RUST_PACKAGE" \ - --build-arg RPM_DEST="$RPM_DEST" \ - --target artifact \ - --output type=local,dest="$outdir" \ - . - - cp -f "$outdir/trident-rpms.tar.gz" ./trident-rpms.tar.gz - - mkdir -p ${{ parameters.artifactDirectory }} - tar -xzf trident-rpms.tar.gz -C ${{ parameters.artifactDirectory }} --strip-components=3 - workingDirectory: ${{ parameters.tridentRepoDirectory }} - displayName: Build RPMs - - - script: ./scripts/extract-binary.sh ${{ parameters.artifactDirectory }} ${{ parameters.artifactDirectory }} ${{ parameters.targetArchitecture }} ${{ parameters.distro }} - workingDirectory: ${{ parameters.tridentRepoDirectory }} - displayName: Extract Trident binary diff --git a/scripts/build-rpms-parallel.sh b/scripts/build-rpms-parallel.sh new file mode 100755 index 000000000..f3e9194c6 --- /dev/null +++ b/scripts/build-rpms-parallel.sh @@ -0,0 +1,124 @@ +#!/bin/bash + +# Builds the azl3 and azl4 Trident RPMs concurrently against a single +# BuildKit daemon, then unpacks each into the artifact layout (azl3 at the +# base directory, azl4 under azl4/). Running both docker builds as background +# jobs in one task avoids a second ADO job and the duplicated OneBranch/setup +# overhead while overlapping the I/O-bound parts of the two builds. +# +# Usage: +# build-rpms-parallel.sh +# +# Args: +# full_version Full Trident version string (e.g. from get-version.py). +# dockerfile Path to Dockerfile.full. +# artifact_dir Base artifact directory (azl3 lands here, azl4 under azl4/). +# work_dir Scratch directory for per-distro build output and logs. +# Should be job-scoped (e.g. $(Agent.TempDirectory)/...). +# +# Expects CARGO_REGISTRIES_BMP_PUBLICPACKAGES_TOKEN in the environment +# (populated by the CargoAuthenticate task) for the docker build secret. + +set -euo pipefail + +# The two builds run as concurrent background subshells. Unconditional xtrace +# (set -x) would interleave trace lines from both on the task's stderr and +# undermine the per-distro log files, so gate it behind an explicit env var. +if [ "${BUILD_RPMS_DEBUG:-}" = "1" ]; then + set -x +fi + +if [ "$#" -ne 4 ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +full_version=$1 +dockerfile=$2 +artifact_dir=$3 +work_dir=$4 + +# Guardrail: work_dir is rm -rf'd below, so refuse empty, root, or any +# non-absolute path to avoid deleting an unintended directory. +case "$work_dir" in + "" | "/") + echo "Refusing to use unsafe work_dir: '$work_dir'" >&2 + exit 1 + ;; + /*) ;; + *) + echo "work_dir must be an absolute path: '$work_dir'" >&2 + exit 1 + ;; +esac + +# Separate into version and prerelease identifier for the RPM build. +version=$(echo "$full_version" | cut -d'-' -f1) +prerelease=$(echo "$full_version" | cut -d'-' -f2-) + +build_one() { + # args: + local distro="$1" + local dest="$2" + local log="$3" + + # Resolve all distro vars from a single make invocation so a make failure + # isn't masked by the grep/cut pipeline (pipefail also guards). + local vars azl_image distro_packages rpm_packages rpm_dest rust_package + vars="$(DISTRO=$distro make azl-version-vars)" + azl_image="$(echo "$vars" | grep '^AZL_IMAGE=' | cut -d '=' -f2)" + distro_packages="$(echo "$vars" | grep '^DISTRO_PACKAGES=' | cut -d '=' -f2)" + rpm_packages="$(echo "$vars" | grep '^RPM_PACKAGES=' | cut -d '=' -f2)" + rpm_dest="$(echo "$vars" | grep '^RPM_DEST=' | cut -d '=' -f2)" + rust_package="$(echo "$vars" | grep '^RUST_PACKAGE=' | cut -d '=' -f2)" + + rm -rf "$dest" + mkdir -p "$dest" + + # Per-distro image tag avoids collisions between the two concurrent builds. + docker build -f "$dockerfile" -t "trident/trident-build:$distro" \ + --secret id=registry_token,env=CARGO_REGISTRIES_BMP_PUBLICPACKAGES_TOKEN \ + --build-arg TRIDENT_VERSION="$full_version" \ + --build-arg RPM_VER="$version" \ + --build-arg RPM_REL="$prerelease.$distro" \ + --build-arg AZL_IMAGE="$azl_image" \ + --build-arg DISTRO_PACKAGES="$distro_packages" \ + --build-arg RPM_PACKAGES="$rpm_packages" \ + --build-arg RUST_PACKAGE="$rust_package" \ + --build-arg RPM_DEST="$rpm_dest" \ + --target artifact \ + --output type=local,dest="$dest" \ + . > "$log" 2>&1 +} + +rm -rf "$work_dir" +mkdir -p "$work_dir" + +# Launch both builds in the background. +build_one azl3 "$work_dir/azl3" "$work_dir/azl3.log" & +pid_azl3=$! +build_one azl4 "$work_dir/azl4" "$work_dir/azl4.log" & +pid_azl4=$! + +# Wait for each and capture exit codes (don't let set -e abort early). +rc_azl3=0 +rc_azl4=0 +wait "$pid_azl3" || rc_azl3=$? +wait "$pid_azl4" || rc_azl4=$? + +# Surface both logs sequentially so concurrent output is readable. +echo "===== azl3 build log =====" +cat "$work_dir/azl3.log" || true +echo "===== azl4 build log =====" +cat "$work_dir/azl4.log" || true + +if [ "$rc_azl3" -ne 0 ] || [ "$rc_azl4" -ne 0 ]; then + echo "Build failed: azl3 rc=$rc_azl3, azl4 rc=$rc_azl4" + exit 1 +fi + +# Unpack azl3 to the base artifact dir, azl4 under azl4/. +mkdir -p "$artifact_dir" +mkdir -p "$artifact_dir/azl4" +tar -xzf "$work_dir/azl3/trident-rpms.tar.gz" -C "$artifact_dir" --strip-components=3 +tar -xzf "$work_dir/azl4/trident-rpms.tar.gz" -C "$artifact_dir/azl4" --strip-components=3