ci: unify binary release artifacts #74
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Release | |
on: | |
workflow_dispatch: | |
push: | |
branches: | |
- master | |
paths: | |
- Cargo.toml | |
- Cargo.lock | |
- 'crates/**' | |
- .github/workflows/rust_binaries_release.yml | |
pull_request: | |
types: [opened, synchronize, reopened] | |
branches: | |
- master | |
paths: | |
- Cargo.toml | |
- Cargo.lock | |
- 'crates/**' | |
- .github/workflows/release.yml | |
jobs: | |
prepare: | |
runs-on: ubuntu-latest | |
outputs: | |
binary_matrix: ${{ steps.setup_matrix.outputs.binary_matrix }} | |
version: ${{ steps.version_info.outputs.version }} | |
release_required: ${{ steps.version_info.outputs.release_required }} | |
build_required: ${{ steps.version_info.outputs.build_required }} | |
target_commit: ${{ steps.version_info.outputs.target_commit }} | |
prerelease: ${{ steps.version_info.outputs.prerelease }} | |
overwrite_release: ${{ steps.version_info.outputs.overwrite_release }} | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
- name: Setup matrix | |
id: setup_matrix | |
run: | | |
echo 'binary_matrix=["merod", "meroctl"]' >> "$GITHUB_OUTPUT" | |
- name: Get version info | |
id: version_info | |
shell: bash | |
run: | | |
echo "Validating binary versions" | |
version_candidate="" | |
for binary in $(echo '${{ steps.setup_matrix.outputs.binary_matrix }}' | jq -r '.[]'); do | |
binary_version=$(cargo metadata --format-version 1 --no-deps | jq -r --arg binary "$binary" '.packages[] | select(.name == $binary) | .version') | |
echo " binary: $binary, version: $binary_version" | |
if [ -z "$version_candidate" ]; then | |
version_candidate="$binary_version" | |
elif [ "$version_candidate" != "$binary_version" ]; then | |
echo "Version mismatch between binaries" | |
echo "Make sure all binaries have the same version" | |
echo "All binaries: '${{ steps.setup_matrix.outputs.binary_matrix }}'" | |
exit 1 | |
fi | |
done | |
echo "Valid version candidate: $version_candidate" | |
echo "target_commit=${{ github.sha }}" >> $GITHUB_OUTPUT | |
if [ "${{ github.ref }}" == "refs/heads/master" ]; then | |
version="$version_candidate" | |
echo "Master version: $version" | |
if gh release view "$version" --repo ${{ github.repository }} >/dev/null 2>&1; then | |
echo "Master release for this version already exists" | |
echo "release_required=false" >> $GITHUB_OUTPUT | |
else | |
echo "New master release required" | |
echo "release_required=true" >> $GITHUB_OUTPUT | |
fi | |
echo "build_required=true" >> $GITHUB_OUTPUT | |
echo "prerelease=false" >> $GITHUB_OUTPUT | |
echo "overwrite_release=false">> $GITHUB_OUTPUT | |
echo "version=$version" >> $GITHUB_OUTPUT | |
elif [ "${{ github.event_name }}" == "pull_request" ] && [[ "${{ github.head_ref }}" == release/* ]]; then | |
version="prerelease-${{ github.event.number }}" | |
echo "Prerelease version: $version" | |
echo "build_required=true" >> $GITHUB_OUTPUT | |
echo "release_required=true" >> $GITHUB_OUTPUT | |
echo "prerelease=true" >> $GITHUB_OUTPUT | |
echo "overwrite_release=true">> $GITHUB_OUTPUT | |
echo "version=$version" >> $GITHUB_OUTPUT | |
else | |
echo "This is not a master branch or a release PR" | |
echo "build_required=false" >> $GITHUB_OUTPUT | |
echo "release_required=false" >> $GITHUB_OUTPUT | |
fi | |
build: | |
if: needs.prepare.outputs.build_required == 'true' | |
strategy: | |
matrix: | |
include: | |
- os: ubuntu-latest | |
target: x86_64-unknown-linux-gnu | |
- os: ubuntu-latest | |
target: aarch64-unknown-linux-gnu | |
- os: macos-latest | |
target: x86_64-apple-darwin | |
- os: macos-latest | |
target: aarch64-apple-darwin | |
runs-on: ${{ matrix.os }} | |
needs: prepare | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
- name: Craft cargo arguments | |
id: cargo_args | |
run: | | |
binaries=$(echo '${{ needs.prepare.outputs.binary_matrix }}' | jq -r 'join(" ") | split(" ") | map("-p " + .) | join(" ")') | |
args="$binaries --release --target ${{ matrix.target }}" | |
echo "Cargo build arguments: $args" | |
echo args="$args" >> "$GITHUB_OUTPUT" | |
- name: Install rustup and Rust toolchain | |
if: matrix.os == 'macos-latest' | |
run: | | |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y | |
source $HOME/.cargo/env | |
rustup toolchain install stable | |
rustup default stable | |
- name: Setup rust toolchain | |
run: rustup toolchain install stable --profile minimal | |
- name: Setup rust cache | |
uses: Swatinem/rust-cache@v2 | |
with: | |
key: rust-binaries-${{ runner.os }}-${{ matrix.target }} | |
- name: Install target for ${{ matrix.target }} | |
run: rustup target add ${{ matrix.target }} | |
- name: Install dependencies for cross-compilation | |
if: matrix.os == 'ubuntu-latest' | |
run: | | |
sudo apt-get update | |
sudo apt-get install -y \ | |
gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \ | |
libstdc++-11-dev-arm64-cross \ | |
zlib1g-dev \ | |
libsnappy-dev \ | |
libbz2-dev \ | |
liblz4-dev \ | |
libzstd-dev \ | |
clang \ | |
libc6-dev-arm64-cross | |
- name: Download and set up OpenSSL for cross-compilation | |
if: matrix.target == 'aarch64-unknown-linux-gnu' | |
run: | | |
wget https://www.openssl.org/source/openssl-1.1.1g.tar.gz | |
tar -xzf openssl-1.1.1g.tar.gz | |
cd openssl-1.1.1g | |
# More restrictive C99 flags and additional compiler options | |
export CROSS_COMPILE="" # Clear CROSS_COMPILE to prevent double prefix | |
export CC="aarch64-linux-gnu-gcc" | |
export CXX="aarch64-linux-gnu-g++" | |
export CFLAGS="-std=gnu99 -O2 -fPIC -D_GNU_SOURCE -I/usr/aarch64-linux-gnu/include" | |
export LDFLAGS="-L/usr/aarch64-linux-gnu/lib" | |
./Configure linux-aarch64 --prefix=$HOME/openssl-aarch64 \ | |
no-asm \ | |
no-shared \ | |
no-async \ | |
no-engine \ | |
no-dso \ | |
no-deprecated | |
make -j$(nproc) CFLAGS="$CFLAGS" LDFLAGS="$LDFLAGS" | |
make install_sw | |
cd .. | |
echo "OPENSSL_DIR=$HOME/openssl-aarch64" >> $GITHUB_ENV | |
echo "OPENSSL_LIB_DIR=$HOME/openssl-aarch64/lib" >> $GITHUB_ENV | |
echo "OPENSSL_INCLUDE_DIR=$HOME/openssl-aarch64/include" >> $GITHUB_ENV | |
echo "PKG_CONFIG_PATH=$HOME/openssl-aarch64/lib/pkgconfig" >> $GITHUB_ENV | |
echo "PKG_CONFIG_ALLOW_CROSS=1" >> $GITHUB_ENV | |
echo "PKG_CONFIG_SYSROOT_DIR=/" >> $GITHUB_ENV | |
echo "OPENSSL_STATIC=1" >> $GITHUB_ENV | |
- name: Build binaries | |
if: matrix.target == 'aarch64-unknown-linux-gnu' | |
env: | |
CC_aarch64_unknown_linux_gnu: aarch64-linux-gnu-gcc | |
CXX_aarch64_unknown_linux_gnu: aarch64-linux-gnu-g++ | |
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc | |
OPENSSL_DIR: ${{ env.OPENSSL_DIR }} | |
OPENSSL_LIB_DIR: ${{ env.OPENSSL_LIB_DIR }} | |
OPENSSL_INCLUDE_DIR: ${{ env.OPENSSL_INCLUDE_DIR }} | |
PKG_CONFIG_PATH: ${{ env.PKG_CONFIG_PATH }} | |
PKG_CONFIG_ALLOW_CROSS: ${{ env.PKG_CONFIG_ALLOW_CROSS }} | |
PKG_CONFIG_SYSROOT_DIR: ${{ env.PKG_CONFIG_SYSROOT_DIR }} | |
OPENSSL_STATIC: ${{ env.OPENSSL_STATIC }} | |
run: | | |
cargo build ${{ steps.cargo_args.outputs.args }} | |
- name: Build binaries | |
if: matrix.target != 'aarch64-unknown-linux-gnu' | |
run: cargo build ${{ steps.cargo_args.outputs.args }} | |
- name: Compress artifacts using gzip | |
run: | | |
mkdir -p artifacts | |
echo '${{ needs.prepare.outputs.binary_matrix }}' | jq -r '.[]' | while read binary; do | |
tar -czf artifacts/"$binary"_${{ matrix.target }}.tar.gz -C target/${{ matrix.target }}/release "$binary" | |
done | |
- name: Upload artifacts | |
uses: actions/upload-artifact@v4 | |
with: | |
name: artifacts-${{ matrix.target }} | |
path: artifacts/* | |
retention-days: 2 | |
release: | |
if: needs.prepare.outputs.release_required == 'true' | |
runs-on: ubuntu-latest | |
needs: [prepare, build] | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
- name: Download Artifact | |
uses: actions/download-artifact@v4 | |
with: | |
path: artifacts/ | |
merge-multiple: true | |
- name: Upload binaries to release | |
uses: svenstaro/upload-release-action@v2 | |
with: | |
repo_token: ${{ secrets.GITHUB_TOKEN }} | |
file: artifacts/* | |
file_glob: true | |
tag: ${{ needs.prepare.outputs.version }} | |
prerelease: ${{ needs.prepare.outputs.prerelease }} | |
overwrite: ${{ needs.prepare.outputs.overwrite_release }} | |
target_commit: ${{ needs.prepare.outputs.target_commit }} | |
brew-update: | |
if: | | |
needs.prepare.outputs.release_required == 'true' && | |
github.ref == 'refs/heads/master' | |
runs-on: ubuntu-latest | |
needs: [prepare, build, release] | |
steps: | |
- name: Get release matrix outputs | |
id: release-matrix-outputs | |
uses: cloudposse/github-action-matrix-outputs-read@v1 | |
with: | |
matrix-step-name: release | |
- name: Create GitHub App Token | |
uses: actions/create-github-app-token@v1 | |
id: app-token | |
with: | |
app-id: ${{ vars.GH_APP_ID }} | |
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} | |
owner: ${{ github.repository_owner }} | |
repositories: | | |
homebrew-tap | |
- name: Checkout homebrew-tap | |
uses: actions/checkout@v4 | |
with: | |
repository: ${{ github.repository_owner }}/homebrew-tap | |
token: ${{ steps.app-token.outputs.token }} | |
persist-credentials: false | |
- name: Get GitHub App User ID | |
id: get-user-id | |
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT" | |
env: | |
GH_TOKEN: ${{ steps.app-token.outputs.token }} | |
- name: Configure Git | |
env: | |
GH_TOKEN: ${{ steps.app-token.outputs.token }} | |
run: | | |
gh auth setup-git | |
git config --global user.name '${{ steps.app-token.outputs.app-slug }}[bot]' | |
git config --global user.email '${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com' | |
- name: Update Formula | |
env: | |
GH_TOKEN: ${{ steps.app-token.outputs.token }} | |
run: | | |
target_branch="chore/bump-formulas-version" | |
git fetch origin "${target_branch}" || true | |
git checkout "${target_branch}" || git checkout -b "${target_branch}" | |
for binary in $(echo '${{ needs.prepare.outputs.binary_matrix }}' | jq -r '.[]'); do | |
release_required=$(echo '${{ steps.release-matrix-outputs.outputs.result }}' | jq -r --arg key "${binary}" '.releaseRequired[$key]') | |
version=$(echo '${{ steps.release-matrix-outputs.outputs.result }}' | jq -r --arg key "${binary}" '.version[$key]') | |
if [ "${release_required}" == "true" ]; then | |
echo "Updating formula for ${binary}, version: ${version}" | |
./generate-formula.sh "${binary}" "${version}" | |
else | |
echo "No formula update required for ${binary}" | |
fi | |
done | |
git status | |
if git diff-index --quiet HEAD --; then | |
echo "There are no changes to commit" | |
exit 0 | |
fi | |
git add Formula/ | |
git commit -m "chore: bump formulas version" | |
git push origin "${target_branch}" | |
gh pr create -f || true |