name: Release
on:
push:
tags:
- 'v*.*.*'
workflow_dispatch:
permissions:
contents: write
env:
BINARY_NAME: dircat
STAGING_DIR: staging
jobs:
build-release:
name: Build Release Binaries (${{ matrix.target }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: ubuntu-latest
target: x86_64-unknown-linux-musl
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
- os: macos-latest
target: x86_64-apple-darwin
- os: macos-14
target: aarch64-apple-darwin
- os: windows-latest
target: x86_64-pc-windows-msvc
- os: windows-latest
target: i686-pc-windows-msvc
- os: windows-latest
target: aarch64-pc-windows-msvc
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
target: ${{ matrix.target }}
cache: 'cargo'
- name: Install cross-compilation dependencies (Linux)
if: runner.os == 'Linux'
run: |
set -e
# Install musl-tools for the static MUSL build.
if [[ "${{ matrix.target }}" == "x86_64-unknown-linux-musl" ]]; then
sudo apt-get update && sudo apt-get install -y musl-tools
# Install the GCC cross-compiler and linker for the ARM64 build.
elif [[ "${{ matrix.target }}" == "aarch64-unknown-linux-gnu" ]]; then
sudo apt-get update && sudo apt-get install -y gcc-aarch64-linux-gnu
fi
- name: Configure Cargo linker for ARM64 cross-compile
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: |
mkdir -p .cargo
# Instruct Cargo to use the cross-compiler's linker for this specific target.
cat > .cargo/config.toml <<EOF
[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
EOF
- name: Build release binary
run: cargo build --release --target ${{ matrix.target }} --verbose
- name: Prepare artifact details
id: artifact_details
shell: bash
run: |
# Determine the binary suffix and archive format based on the target platform.
# This centralizes naming logic and keeps the matrix definition clean.
BIN_SUFFIX=""
ARCHIVE_FORMAT="tar.gz"
if [[ "${{ matrix.target }}" == *"-windows-"* ]]; then
BIN_SUFFIX=".exe"
ARCHIVE_FORMAT="zip"
fi
echo "BIN_SUFFIX=${BIN_SUFFIX}" >> $GITHUB_ENV
echo "ARCHIVE_FORMAT=${ARCHIVE_FORMAT}" >> $GITHUB_ENV
# Define the final archive filename, e.g., "dircat-v1.2.3-x86_64-unknown-linux-gnu.tar.gz".
ARCHIVE_FILENAME="${{ env.BINARY_NAME }}-${{ github.ref_name }}-${{ matrix.target }}.${ARCHIVE_FORMAT}"
echo "archive_filename=${ARCHIVE_FILENAME}" >> $GITHUB_OUTPUT
- name: Create staging directory and copy files (Unix)
if: runner.os != 'Windows'
shell: bash
run: |
set -e
mkdir -p ${{ env.STAGING_DIR }}
cp "target/${{ matrix.target }}/release/${{ env.BINARY_NAME }}${{ env.BIN_SUFFIX }}" "${{ env.STAGING_DIR }}/"
cp LICENSE README.md "${{ env.STAGING_DIR }}/"
- name: Create staging directory and copy files (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
New-Item -ItemType Directory -Path "${{ env.STAGING_DIR }}" -Force
Copy-Item -Path "target\${{ matrix.target }}\release\${{ env.BINARY_NAME }}${{ env.BIN_SUFFIX }}" -Destination "${{ env.STAGING_DIR }}\"
Copy-Item -Path "LICENSE", "README.md" -Destination "${{ env.STAGING_DIR }}\"
- name: Prepare archive structure for binstall
shell: bash
run: |
# Create a root directory inside the archive. This is a best practice that
# prevents "tarbombs" and is the preferred structure for `cargo binstall`.
ARCHIVE_ROOT_DIR=$(basename "${{ steps.artifact_details.outputs.archive_filename }}" .${{ env.ARCHIVE_FORMAT }})
echo "ARCHIVE_ROOT_DIR=${ARCHIVE_ROOT_DIR}" >> $GITHUB_ENV
# Move all staged files into this new root directory.
mkdir "$ARCHIVE_ROOT_DIR"
mv ${{ env.STAGING_DIR }}/* "$ARCHIVE_ROOT_DIR/"
- name: Package release artifacts (Unix)
if: runner.os != 'Windows'
shell: bash
run: tar czf "${{ steps.artifact_details.outputs.archive_filename }}" "${{ env.ARCHIVE_ROOT_DIR }}"
- name: Package release artifacts (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: Compress-Archive -Path "${{ env.ARCHIVE_ROOT_DIR }}" -DestinationPath "${{ steps.artifact_details.outputs.archive_filename }}"
- name: Upload release asset
uses: actions/upload-artifact@v4
with:
name: release-asset-${{ matrix.target }}
path: ${{ steps.artifact_details.outputs.archive_filename }}
create-release:
name: Create GitHub Release
needs: build-release
runs-on: ubuntu-latest
steps:
- name: Download all release assets
uses: actions/download-artifact@v4
with:
path: release-assets
- name: List downloaded files (for debugging)
run: ls -R release-assets
- name: Create Release and Upload Assets
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
generate_release_notes: true
files: release-assets/*/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}