name: Release
on:
push:
tags:
- 'v*.*.*'
workflow_dispatch:
inputs:
tag:
description: 'Existing tag to package binaries for (e.g. v0.1.5)'
required: true
type: string
env:
CARGO_TERM_COLOR: always
jobs:
build-release:
name: Build Release
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
asset_os: linux-x86_64
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
asset_os: linux-aarch64
- os: macos-latest
target: x86_64-apple-darwin
asset_os: macos-x86_64
- os: macos-latest
target: aarch64-apple-darwin
asset_os: macos-aarch64
- os: windows-latest
target: x86_64-pc-windows-msvc
asset_os: windows-x86_64
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
ref: ${{ inputs.tag || github.ref }}
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install aarch64 cross linker
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu
echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV
- name: Build library
run: cargo build --release --target ${{ matrix.target }}
- name: Run library tests
if: "!contains(matrix.target, 'aarch64')"
run: cargo test --release --target ${{ matrix.target }}
- name: Build minlz-tools binaries
working-directory: minlz-tools
run: cargo build --release --target ${{ matrix.target }}
- name: Upload binaries
uses: actions/upload-artifact@v7
with:
name: tools-${{ matrix.asset_os }}
path: |
minlz-tools/target/${{ matrix.target }}/release/s2c
minlz-tools/target/${{ matrix.target }}/release/s2d
minlz-tools/target/${{ matrix.target }}/release/s2c.exe
minlz-tools/target/${{ matrix.target }}/release/s2d.exe
if-no-files-found: error
retention-days: 7
release-assets:
name: Package and attach to GitHub release
runs-on: ubuntu-latest
needs: build-release
if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch'
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
ref: ${{ inputs.tag || github.ref }}
fetch-depth: 0
- name: Install llvm-lipo (for macOS universal binary)
run: |
# Ubuntu's `llvm` package installs versioned binaries like
# /usr/lib/llvm-18/bin/llvm-lipo but does NOT create a bare
# `llvm-lipo` symlink. Install the package, then expose
# whichever version is available as a plain `llvm-lipo`.
sudo apt-get update
sudo apt-get install -y llvm
LIPO=$(ls /usr/lib/llvm-*/bin/llvm-lipo /usr/bin/llvm-lipo-* 2>/dev/null | sort -V | tail -1)
if [ -z "$LIPO" ]; then
echo "::error::llvm-lipo not found after installing the llvm package" >&2
exit 1
fi
sudo ln -sf "$LIPO" /usr/local/bin/llvm-lipo
llvm-lipo --version
- name: Download build artifacts
uses: actions/download-artifact@v8
with:
path: artifacts
- name: Build archives
env:
INPUT_TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
VERSION="${INPUT_TAG:-$GITHUB_REF_NAME}"
mkdir -p dist
package_unix() {
local label="$1" # e.g. linux-x86_64
local srcdir="$2" # path containing s2c and s2d
local name="minlz-tools-${VERSION}-${label}"
local stage="stage/${name}"
mkdir -p "${stage}"
cp "${srcdir}/s2c" "${srcdir}/s2d" "${stage}/"
chmod +x "${stage}/s2c" "${stage}/s2d"
cp LICENSE "${stage}/"
cp minlz-tools/README.md "${stage}/"
tar -C stage -czf "dist/${name}.tar.gz" "${name}"
(cd dist && sha256sum "${name}.tar.gz" > "${name}.tar.gz.sha256")
}
# Linux x86_64
package_unix linux-x86_64 artifacts/tools-linux-x86_64
# Linux aarch64
package_unix linux-aarch64 artifacts/tools-linux-aarch64
# macOS universal (lipo x86_64 + aarch64)
mkdir -p artifacts/tools-macos-universal
llvm-lipo -create \
artifacts/tools-macos-x86_64/s2c \
artifacts/tools-macos-aarch64/s2c \
-output artifacts/tools-macos-universal/s2c
llvm-lipo -create \
artifacts/tools-macos-x86_64/s2d \
artifacts/tools-macos-aarch64/s2d \
-output artifacts/tools-macos-universal/s2d
package_unix macos-universal artifacts/tools-macos-universal
# Windows x86_64
name="minlz-tools-${VERSION}-windows-x86_64"
stage="stage/${name}"
mkdir -p "${stage}"
cp artifacts/tools-windows-x86_64/s2c.exe "${stage}/"
cp artifacts/tools-windows-x86_64/s2d.exe "${stage}/"
cp LICENSE "${stage}/"
cp minlz-tools/README.md "${stage}/"
(cd stage && zip -r "../dist/${name}.zip" "${name}")
(cd dist && sha256sum "${name}.zip" > "${name}.zip.sha256")
echo "::group::Archive contents"
ls -lh dist/
echo "::endgroup::"
- name: Create or update GitHub release
env:
GH_TOKEN: ${{ github.token }}
INPUT_TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
VERSION="${INPUT_TAG:-$GITHUB_REF_NAME}"
# Use the annotated tag message as the release body so we don't
# have to duplicate the notes. Fall back to a stub for lightweight
# tags or empty messages.
NOTES="$(git tag -l --format='%(contents)' "${VERSION}")"
if [ -z "${NOTES}" ]; then
NOTES="Release ${VERSION}"
fi
if ! gh release view "${VERSION}" >/dev/null 2>&1; then
gh release create "${VERSION}" \
--title "${VERSION}" \
--notes "${NOTES}"
fi
gh release upload "${VERSION}" dist/* --clobber
publish-crate:
name: Publish to crates.io
runs-on: ubuntu-latest
needs: build-release
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
environment: release
permissions:
id-token: write
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Authenticate to crates.io
uses: rust-lang/crates-io-auth-action@v1
id: auth
- name: Publish minlz
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
run: cargo publish
- name: Wait for crates.io index to update
run: sleep 30
- name: Publish minlz-tools
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
working-directory: minlz-tools
run: cargo publish