name: Release
on:
push:
tags:
- 'v*'
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
wait-for-ci:
name: Wait for CI
runs-on: ubuntu-latest
steps:
- name: Wait for CI to pass on this commit
env:
GH_TOKEN: ${{ github.token }}
run: |
SHA="${{ github.sha }}"
REPO="${{ github.repository }}"
echo "Waiting for CI workflow to complete on $SHA..."
for i in $(seq 1 60); do
STATUS=$(gh api "repos/$REPO/commits/$SHA/check-runs" \
--jq '.check_runs[] | select(.name == "Test (ubuntu-latest)") | .conclusion' 2>/dev/null || echo "pending")
if [ "$STATUS" = "success" ]; then
echo "CI passed!"
exit 0
elif [ "$STATUS" = "failure" ] || [ "$STATUS" = "cancelled" ]; then
echo "CI failed ($STATUS) — aborting release"
exit 1
fi
echo "CI still running... (attempt $i/60)"
sleep 30
done
echo "Timed out waiting for CI"
exit 1
publish-crate:
name: Publish to crates.io
needs: [wait-for-ci, build-release]
runs-on: ubuntu-latest
outputs:
version: ${{ steps.check.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Check if already published
id: check
run: |
VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
if curl -sf "https://crates.io/api/v1/crates/opencrabs/$VERSION" > /dev/null 2>&1; then
echo "published=true" >> "$GITHUB_OUTPUT"
echo "v$VERSION already on crates.io — skipping build"
else
echo "published=false" >> "$GITHUB_OUTPUT"
fi
- name: Install Rust
if: steps.check.outputs.published == 'false'
uses: dtolnay/rust-toolchain@stable
- name: Install ALSA dev
if: steps.check.outputs.published == 'false'
run: sudo apt-get update && sudo apt-get install -y libasound2-dev
- name: Publish
if: steps.check.outputs.published == 'false'
continue-on-error: true
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: cargo publish --allow-dirty
build-release:
name: Build Release
needs: wait-for-ci
runs-on: ${{ matrix.os }}
permissions:
contents: write
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
artifact_name: opencrabs
asset_suffix: linux-amd64
- os: ubuntu-24.04-arm
target: aarch64-unknown-linux-gnu
artifact_name: opencrabs
asset_suffix: linux-arm64
- os: windows-latest
target: x86_64-pc-windows-msvc
artifact_name: opencrabs.exe
asset_suffix: windows-amd64
- os: macos-latest
target: x86_64-apple-darwin
artifact_name: opencrabs
asset_suffix: macos-amd64
- os: macos-latest
target: aarch64-apple-darwin
artifact_name: opencrabs
asset_suffix: macos-arm64
steps:
- uses: actions/checkout@v4
- name: Set version
id: version
shell: bash
run: echo "version=${GITHUB_REF#refs/tags/v}" >> "$GITHUB_OUTPUT"
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install Linux dependencies
if: contains(matrix.os, 'ubuntu')
run: |
sudo apt-get update
sudo apt-get install -y libasound2-dev
- name: Install CMake (Windows)
if: matrix.os == 'windows-latest'
uses: lukka/get-cmake@latest
- name: Install NASM (Windows)
if: matrix.os == 'windows-latest'
uses: ilammy/setup-nasm@v1
- name: Build
shell: bash
env:
CFLAGS: ${{ matrix.target == 'aarch64-apple-darwin' && '-march=armv8-a+crypto' || '' }}
CXXFLAGS: ${{ matrix.target == 'aarch64-apple-darwin' && '-march=armv8-a+crypto' || '' }}
AWS_LC_SYS_CMAKE_BUILDER: ${{ matrix.os == 'windows-latest' && '1' || '' }}
run: |
if [ "${{ matrix.os }}" = "windows-latest" ]; then
cargo build --release --target ${{ matrix.target }} --no-default-features --features "telegram,discord,slack,whatsapp,trello"
else
cargo build --release --target ${{ matrix.target }} --all-features
fi
- name: Strip binary (Unix)
if: matrix.os != 'windows-latest'
run: strip target/${{ matrix.target }}/release/${{ matrix.artifact_name }}
- name: Create archive
shell: bash
run: |
ASSET_NAME="opencrabs-v${{ steps.version.outputs.version }}-${{ matrix.asset_suffix }}"
cd target/${{ matrix.target }}/release
if [ "${{ matrix.os }}" = "windows-latest" ]; then
7z a ../../../${ASSET_NAME}.zip ${{ matrix.artifact_name }}
else
tar czf ../../../${ASSET_NAME}.tar.gz ${{ matrix.artifact_name }}
fi
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: opencrabs-v${{ steps.version.outputs.version }}-${{ matrix.asset_suffix }}
path: |
opencrabs-v${{ steps.version.outputs.version }}-${{ matrix.asset_suffix }}.tar.gz
opencrabs-v${{ steps.version.outputs.version }}-${{ matrix.asset_suffix }}.zip
create-release:
name: Create Release
needs: [publish-crate, build-release]
if: always() && needs.publish-crate.result != 'cancelled' && needs.build-release.result != 'cancelled'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Extract version from tag
id: version
run: echo "version=${GITHUB_REF#refs/tags/v}" >> "$GITHUB_OUTPUT"
- name: Extract changelog for this version
id: changelog
run: |
# Extract the section for this version from CHANGELOG.md
version="${{ steps.version.outputs.version }}"
body=$(awk "/^## \\[${version}\\]/{found=1; next} /^## \\[/{if(found) exit} found{print}" CHANGELOG.md)
# Write to file to preserve newlines
echo "$body" > /tmp/release_notes.md
# Add full changelog link (extract previous version from CHANGELOG.md)
echo "" >> /tmp/release_notes.md
prev_version=$(awk "/^## \\[${version}\\]/{found=1; next} found && /^## \\[/{match(\$0, /\\[([^]]+)\\]/, m); print m[1]; exit}" CHANGELOG.md)
if [ -n "$prev_version" ]; then
echo "**Full Changelog:** https://github.com/${{ github.repository }}/compare/v${prev_version}...v${version}" >> /tmp/release_notes.md
fi
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Collect release assets
run: |
mkdir -p release-assets
find artifacts -type f \( -name '*.tar.gz' -o -name '*.zip' \) -exec cp {} release-assets/ \;
ls -la release-assets/
- name: Create GitHub Release
env:
GH_TOKEN: ${{ github.token }}
run: |
# Use --clobber so the workflow can attach binaries to a release
# that was already created manually (e.g. via `gh release create`)
if gh release view "v${{ steps.version.outputs.version }}" &>/dev/null; then
# Release exists -- upload assets and ensure it's not a draft
gh release upload "v${{ steps.version.outputs.version }}" \
release-assets/* --clobber
gh release edit "v${{ steps.version.outputs.version }}" --draft=false
else
gh release create "v${{ steps.version.outputs.version }}" \
--title "v${{ steps.version.outputs.version }}" \
--notes-file /tmp/release_notes.md \
--latest \
release-assets/*
fi