name: CI
on:
push:
branches: [main]
tags: ["v*"]
pull_request:
branches: [main]
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: short
OPENSSL_STATIC: true
PKG_CONFIG_ALLOW_CROSS: true
permissions:
contents: write
jobs:
test:
name: Test
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
components: rustfmt,clippy
- name: Install OpenSSL (Ubuntu)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y pkg-config libssl-dev
- name: Install OpenSSL (macOS)
if: runner.os == 'macOS'
run: |
brew install openssl@3
echo "OPENSSL_DIR=$(brew --prefix openssl@3)" >> $GITHUB_ENV
echo "PKG_CONFIG_PATH=$(brew --prefix openssl@3)/lib/pkgconfig" >> $GITHUB_ENV
- name: Install OpenSSL (Windows)
if: runner.os == 'Windows'
run: |
choco install openssl --params='/InstallationPath:C:\OpenSSL\'
echo "OPENSSL_DIR=C:\OpenSSL" >> $env:GITHUB_ENV
echo "PKG_CONFIG_PATH=C:\OpenSSL\lib\pkgconfig" >> $env:GITHUB_ENV
- name: Run tests
run: cargo test --verbose
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
components: rustfmt,clippy
- name: Clippy
run: cargo clippy -- -D warnings
- name: Format check
run: cargo fmt --check
build:
name: Build ${{ matrix.target }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
artifact_name: yek
asset_name: yek-x86_64-unknown-linux-gnu.tar.gz
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
artifact_name: yek
asset_name: yek-aarch64-unknown-linux-gnu.tar.gz
- os: ubuntu-latest
target: x86_64-unknown-linux-musl
artifact_name: yek
asset_name: yek-x86_64-unknown-linux-musl.tar.gz
- os: ubuntu-latest
target: aarch64-unknown-linux-musl
artifact_name: yek
asset_name: yek-aarch64-unknown-linux-musl.tar.gz
- os: macos-latest
target: x86_64-apple-darwin
artifact_name: yek
asset_name: yek-x86_64-apple-darwin.tar.gz
- os: macos-latest
target: aarch64-apple-darwin
artifact_name: yek
asset_name: yek-aarch64-apple-darwin.tar.gz
- os: windows-latest
target: x86_64-pc-windows-msvc
artifact_name: yek.exe
asset_name: yek-x86_64-pc-windows-msvc.zip
- os: windows-latest
target: aarch64-pc-windows-msvc
artifact_name: yek.exe
asset_name: yek-aarch64-pc-windows-msvc.zip
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/build
id: build
with:
upload_artifacts: "false"
- name: Package binary (Unix)
if: runner.os != 'Windows'
shell: bash
run: |
mkdir -p release-artifacts
staging="yek-${{ matrix.target }}"
mkdir -p "$staging"
cp "${{ steps.build.outputs.binary_path }}" "$staging/"
tar czf "release-artifacts/${{ matrix.asset_name }}" "$staging"
# Verify the artifact exists and has size > 0
if [ ! -f "release-artifacts/${{ matrix.asset_name }}" ]; then
echo "::error::Packaged artifact not found: release-artifacts/${{ matrix.asset_name }}"
exit 1
fi
if [ ! -s "release-artifacts/${{ matrix.asset_name }}" ]; then
echo "::error::Packaged artifact is empty: release-artifacts/${{ matrix.asset_name }}"
exit 1
fi
echo "Successfully packaged ${{ matrix.asset_name }}"
ls -lh "release-artifacts/${{ matrix.asset_name }}"
- name: Package binary (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path release-artifacts
$staging = "yek-${{ matrix.target }}"
New-Item -ItemType Directory -Force -Path $staging
Copy-Item "${{ steps.build.outputs.binary_path }}" -Destination $staging
7z a "release-artifacts\${{ matrix.asset_name }}" "$staging"
# Verify the artifact exists and has size > 0
if (-not (Test-Path "release-artifacts\${{ matrix.asset_name }}")) {
Write-Error "Packaged artifact not found: release-artifacts\${{ matrix.asset_name }}"
exit 1
}
$fileInfo = Get-Item "release-artifacts\${{ matrix.asset_name }}"
if ($fileInfo.Length -eq 0) {
Write-Error "Packaged artifact is empty: release-artifacts\${{ matrix.asset_name }}"
exit 1
}
Write-Host "Successfully packaged ${{ matrix.asset_name }}"
Get-Item "release-artifacts\${{ matrix.asset_name }}" | Select-Object Length,LastWriteTime
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: build-${{ matrix.target }}-${{ matrix.asset_name }}
path: release-artifacts/${{ matrix.asset_name }}
if-no-files-found: error
release:
name: Release
needs: [test, lint, build]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
path: dist
- name: Prepare release files
shell: bash
run: |
echo "Release artifacts:"
# Move all artifacts to a flat directory structure and rename them
mkdir -p dist/flat
for file in dist/build-*/yek-*; do
# Extract just the final part of the filename (yek-*.tar.gz or yek-*.zip)
filename=$(basename "$file")
cp "$file" "dist/flat/$filename"
done
# List what we found
echo "Found artifacts:"
ls -lh dist/flat/
# Verify we have all expected artifacts
expected_count=8 # Total number of builds in matrix
actual_count=$(find dist/flat -type f -name "yek-*" | wc -l)
if [[ "$actual_count" -ne "$expected_count" ]]; then
echo "::error::Expected $expected_count artifacts but found $actual_count"
echo "Contents of dist directory:"
find dist -type f
exit 1
fi
echo "Found all $expected_count expected artifacts"
- name: Generate release notes
uses: orhun/git-cliff-action@v2
id: git-cliff
with:
config: cliff.toml
args: --current --strip header
env:
OUTPUT: CHANGES.md
- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: dist/flat/yek-*
body_path: CHANGES.md
tag_name: ${{ github.ref_name }}
publish:
name: Publish
needs: [test, lint, build]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.84.0"
- run: |
# ensure version does not already exist
if cargo search --color=never yek | grep -q ${{ github.ref_name }}; then
echo "yek@${{ github.ref_name }} already published"
exit 0
fi
cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }}
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}