From 1aed3338b501be408d32c38928f5783e2128f841 Mon Sep 17 00:00:00 2001 From: Rain Date: Tue, 6 Oct 2026 15:57:33 -0700 Subject: [PATCH] buildomat: publish macOS aarch64 dpd and swadm binaries Let's try this again. --- .github/buildomat/fetch-gh-artifacts.sh | 61 +++++++++++++++++ .github/buildomat/jobs/macos.sh | 62 ++++++++++++++++++ .github/workflows/build-macos.yml | 87 +++++++++++++++++++++++++ 3 files changed, 210 insertions(+) create mode 100755 .github/buildomat/fetch-gh-artifacts.sh create mode 100755 .github/buildomat/jobs/macos.sh create mode 100644 .github/workflows/build-macos.yml diff --git a/.github/buildomat/fetch-gh-artifacts.sh b/.github/buildomat/fetch-gh-artifacts.sh new file mode 100755 index 00000000..dc6cfb59 --- /dev/null +++ b/.github/buildomat/fetch-gh-artifacts.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +# Copyright Oxide Computer Company + +set -o errexit +set -o pipefail +set -o xtrace + +# We use `--netrc` in these calls in order to use the GitHub token Buildomat +# provides us. This is not strictly necessary for all APIs we use, _except_ the +# one to download artifacts. But using it for all calls keeps us from hitting +# rate limits! + +API_BASE="https://api.github.com/repos/$GITHUB_REPOSITORY" + +# Buildomat creates at most one check suite per commit per repository, but +# GitHub Actions will generally make several. We need to choose which run we +# care about, ideally picking the one most closely related to this Buildomat +# check suite. We first look for the most recently-created "push" run, but if +# none are found we fall back to the most recently-created "pull_request" run. +# +# We check 10 times with 30 second pauses in between; if we don't have a check +# run within about five minutes it'll probably never show up. +for attempt in {1..10}; do + runs=$(curl -sSfL --netrc "$API_BASE/actions/runs?head_sha=$GITHUB_SHA" \ + | jq -r --arg name "$1" ' + .workflow_runs + | sort_by(.created_at) | reverse + | .[] | select(.name == $name) + | {id: .id, event: .event} + ') + for event in push pull_request; do + run_id=$(jq -r --arg event "$event" 'select(.event == $event) | .id' <<<"$runs" | head -n 1) + [[ -n "$run_id" ]] && break 2 + done + sleep 30 +done +if [[ -z "$run_id" ]]; then + echo >&2 "no check run found" + exit 1 +fi + +# Wait for the run to complete. +until [[ $(curl -sSfL --netrc "$API_BASE/actions/runs/$run_id" | jq -r .status) == completed ]]; do + sleep 60 +done + +# Get information about artifacts and download them. +artifacts=$(curl -sSfL --netrc "$API_BASE/actions/runs/$run_id/artifacts" \ + | jq -r '.artifacts[] | {id: .id, name: .name}') +for artifact_id in $(jq -r '.id' <<<"$artifacts"); do + artifact_name=$(jq -r --argjson id "$artifact_id" 'select(.id == $id) | .name' <<<"$artifacts") + # Artifact names are not allowed to contain special filesystem characters: + # https://github.com/actions/upload-artifact/issues/22 + curl -sSfL --netrc -o "$artifact_name.zip" \ + "$API_BASE/actions/artifacts/$artifact_id/zip" +done diff --git a/.github/buildomat/jobs/macos.sh b/.github/buildomat/jobs/macos.sh new file mode 100755 index 00000000..33aa358b --- /dev/null +++ b/.github/buildomat/jobs/macos.sh @@ -0,0 +1,62 @@ +#!/bin/bash +#: +#: name = "macos" +#: variety = "basic" +#: target = "ubuntu-22.04" +#: output_rules = [ +#: "=/work/macos-aarch64/dpd", +#: "=/work/macos-aarch64/dpd.sha256.txt", +#: "=/work/macos-aarch64/swadm", +#: "=/work/macos-aarch64/swadm.sha256.txt", +#: ] +#: +#: [[publish]] +#: series = "macos-aarch64" +#: name = "dpd" +#: from_output = "/work/macos-aarch64/dpd" +#: +#: [[publish]] +#: series = "macos-aarch64" +#: name = "dpd.sha256.txt" +#: from_output = "/work/macos-aarch64/dpd.sha256.txt" +#: +#: [[publish]] +#: series = "macos-aarch64" +#: name = "swadm" +#: from_output = "/work/macos-aarch64/swadm" +#: +#: [[publish]] +#: series = "macos-aarch64" +#: name = "swadm.sha256.txt" +#: from_output = "/work/macos-aarch64/swadm.sha256.txt" + +set -o errexit +set -o pipefail +set -o xtrace + +function digest { + shasum -a 256 "$1" | awk -F ' ' '{print $1}' +} + +banner "packages" +sudo apt update +sudo apt install -y jq unzip + +# Buildomat doesn't have macOS workers. Our workaround is to build in GitHub +# Actions and poll for that over here. +banner "fetch" +# Set a 40 minute timeout since buildomat's GitHub token lasts an hour. +# +# XXX This is definitely not great and it would be much better to only kick off +# the buildomat job once GHA is complete. +timeout 40m .github/buildomat/fetch-gh-artifacts.sh build-macos + +banner "unpack" + +staging=/work/staging +mkdir -p "${staging}" +unzip macos-aarch64.zip -d "${staging}" +for bin in dpd swadm; do + digest "${staging}/${bin}" > "${staging}/${bin}.sha256.txt" +done +mv "${staging}" /work/macos-aarch64 diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml new file mode 100644 index 00000000..f74d48a7 --- /dev/null +++ b/.github/workflows/build-macos.yml @@ -0,0 +1,87 @@ +name: build-macos + +on: + push: + pull_request: + +permissions: + contents: read + +defaults: + run: + shell: bash + +jobs: + build: + # TODO: An optimization we can do here is to skip duplicate + # push/pull_request runs on branches created in this repo, e.g.: + # + # if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository + # + # But the fetch-gh-artifacts.sh script (which is copied verbatim from + # github.com/oxidecomputer/cockroach) doesn't handle skipped jobs at the + # moment. We should consider fixing both copies of the script. + # + # --- + # + # Use the oldest supported arm64 image to do this build. Building on a newer + # image risks binaries that fail on older versions of macOS. + runs-on: macos-15 + # This must stay below the 40 minutes the macos buildomat job waits for this + # run. See the XXX in .github/buildomat/jobs/macos.sh. + timeout-minutes: 35 + env: + CARGO_TERM_COLOR: always + CARGO_INCREMENTAL: "0" + # Omicron downloads these binaries onto machines that may lack Homebrew's + # OpenSSL dylibs, so link it statically. + OPENSSL_STATIC: "1" + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + # Build the tip of the PR, not the merge commit GitHub synthesizes, so + # that the artifact matches the buildomat commit. + ref: ${{ github.event.pull_request.head.sha }} + persist-credentials: false + - name: Report toolchain versions + run: | + cargo --version + rustc --version + - name: Find or install Homebrew OpenSSL + run: | + openssl_dir="$(brew --prefix openssl@3)" + if [[ ! -f "${openssl_dir}/lib/libssl.a" ]]; then + brew install openssl@3 + fi + echo "OPENSSL_DIR=${openssl_dir}" >> "${GITHUB_ENV}" + - name: Build dpd and swadm + run: cargo build --release --locked --features=tofino_stub --bin dpd --bin swadm + - name: Check binaries + run: | + set -o nounset + for bin in dpd swadm; do + bin_path="target/release/${bin}" + + archs="$(lipo -archs "${bin_path}")" + if [[ "${archs}" != "arm64" ]]; then + echo "::error::${bin} was built for '${archs}', expected 'arm64'" + exit 1 + fi + + linked="$(otool -L "${bin_path}")" + echo "${linked}" + non_system="$(awk 'NR > 1 && $1 !~ /^(\/usr\/lib\/|\/System\/Library\/)/ { print $1 }' <<<"${linked}")" + if [[ -n "${non_system}" ]]; then + echo "::error::${bin} links against non-system libraries that machines downloading it may not have: ${non_system//$'\n'/ }" + exit 1 + fi + + "${bin_path}" --help >/dev/null + done + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: macos-aarch64 + path: | + target/release/dpd + target/release/swadm + if-no-files-found: error