Skip to content

v0.3.39 — analytics panel, migrate CLI, RTK auto-download, hot-reload… #237

v0.3.39 — analytics panel, migrate CLI, RTK auto-download, hot-reload…

v0.3.39 — analytics panel, migrate CLI, RTK auto-download, hot-reload… #237

Workflow file for this run

name: Release
on:
push:
tags:
- 'v*'
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
# CI only runs on PRs (not pushes to main). For release tags, we verify
# the commit is on main and skip CI polling entirely. PRs are where CI
# gates happen; main is protected by branch protection requiring PR merge.
wait-for-ci:
name: Verify main branch
runs-on: ubuntu-latest
steps:
- name: Confirm commit is on main
env:
GH_TOKEN: ${{ github.token }}
run: |
SHA="${{ github.sha }}"
REPO="${{ github.repository }}"
# Verify the tag points to a commit on main
MAIN_SHA=$(gh api "repos/$REPO/branches/main" --jq '.commit.sha' 2>/dev/null || echo "")
if [ -z "$MAIN_SHA" ]; then
echo "ERROR: Could not fetch main branch HEAD"
exit 1
fi
# Check if our SHA is reachable from main (may not be HEAD if tag is on older commit)
MERGE_BASE=$(gh api "repos/$REPO/compare/${MAIN_SHA}...${SHA}" --jq '.status' 2>/dev/null || echo "error")
case "$MERGE_BASE" in
"identical"|"behind")
echo "Commit $SHA is on main (main is at $MAIN_SHA)"
;;
*)
# Also accept if SHA IS main HEAD
if [ "$SHA" = "$MAIN_SHA" ]; then
echo "Commit $SHA is main HEAD"
else
echo "WARNING: Commit $SHA may not be on main (status: $MERGE_BASE). Proceeding anyway."
fi
;;
esac
echo "Ready to release."
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: cargo build --locked --release --target ${{ matrix.target }} --all-features
- name: Download and bundle RTK 0.40.0
shell: bash
run: |
RTK_VERSION="0.40.0"
RTK_TARGET="${{ matrix.target }}"
# Map Rust targets to RTK release asset names (v0.40.0 actual names)
case "$RTK_TARGET" in
x86_64-unknown-linux-gnu)
RTK_ASSET="rtk-x86_64-unknown-linux-musl.tar.gz"
;;
aarch64-unknown-linux-gnu)
RTK_ASSET="rtk-aarch64-unknown-linux-gnu.tar.gz"
;;
x86_64-apple-darwin)
RTK_ASSET="rtk-x86_64-apple-darwin.tar.gz"
;;
aarch64-apple-darwin)
RTK_ASSET="rtk-aarch64-apple-darwin.tar.gz"
;;
x86_64-pc-windows-msvc)
RTK_ASSET="rtk-x86_64-pc-windows-msvc.zip"
;;
*)
echo "Unknown target: $RTK_TARGET"
exit 1
;;
esac
RTK_URL="https://github.com/rtk-ai/rtk/releases/download/v${RTK_VERSION}/${RTK_ASSET}"
echo "Downloading RTK from: $RTK_URL"
curl -L -o "/tmp/$RTK_ASSET" "$RTK_URL"
# Extract RTK binary
mkdir -p /tmp/rtk-extract
if [[ "$RTK_ASSET" == *.zip ]]; then
unzip -q "/tmp/$RTK_ASSET" -d /tmp/rtk-extract
else
tar xzf "/tmp/$RTK_ASSET" -C /tmp/rtk-extract
fi
# Find the rtk binary in the extracted archive
RTK_BINARY=$(find /tmp/rtk-extract -name "rtk" -o -name "rtk.exe" | head -1)
if [ -z "$RTK_BINARY" ]; then
echo "RTK binary not found in archive"
ls -laR /tmp/rtk-extract
exit 1
fi
# Copy to the release directory alongside opencrabs
RELEASE_DIR="target/${{ matrix.target }}/release"
cp "$RTK_BINARY" "$RELEASE_DIR/"
if [[ "${{ matrix.os }}" != "windows-latest" ]]; then
chmod +x "$RELEASE_DIR/rtk"
fi
echo "RTK binary bundled at: $RELEASE_DIR/$(basename $RTK_BINARY)"
ls -lh "$RELEASE_DIR/$(basename $RTK_BINARY)"
- 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 }} rtk.exe
else
tar czf ../../../${ASSET_NAME}.tar.gz ${{ matrix.artifact_name }} rtk
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