Skip to content

Release

Release #10

Workflow file for this run

name: Release
on:
push:
tags:
- 'v*'
- 'cli-v*'
- 'gui-v*'
workflow_dispatch:
inputs:
version:
description: 'Version to release (e.g., 0.1.0)'
required: true
release_type:
description: 'Release type'
required: true
type: choice
options:
- both
- cli
- gui
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
BINARY_NAME: Cortex
# Nightly multithreaded frontend for faster compilation (32 threads for 32 vCPU runners)
RUSTFLAGS: "-Zthreads=32"
# Sparse registry for faster index updates
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
# Incremental compilation off for release builds
CARGO_INCREMENTAL: 0
permissions:
contents: write
# Ensure only one release at a time - prevents overloading when multiple tags pushed
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
jobs:
# ==========================================================================
# Prepare - Determine version and what to build (lightweight - 4 vCPU)
# ==========================================================================
prepare:
name: Prepare Release
runs-on: blacksmith-4vcpu-ubuntu-2404
outputs:
version: ${{ steps.version.outputs.version }}
tag: ${{ steps.version.outputs.tag }}
build_cli: ${{ steps.version.outputs.build_cli }}
build_gui: ${{ steps.version.outputs.build_gui }}
cache_key: ${{ steps.cache.outputs.key }}
steps:
- uses: actions/checkout@v4
- name: Generate cache key
id: cache
run: |
echo "key=rust-release-${{ hashFiles('**/Cargo.lock', '**/Cargo.toml') }}" >> $GITHUB_OUTPUT
- name: Determine version and build targets
id: version
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
VERSION="${{ inputs.version }}"
if [ "${{ inputs.release_type }}" = "cli" ]; then
TAG="cli-v${VERSION}"
echo "build_cli=true" >> $GITHUB_OUTPUT
echo "build_gui=false" >> $GITHUB_OUTPUT
elif [ "${{ inputs.release_type }}" = "gui" ]; then
TAG="gui-v${VERSION}"
echo "build_cli=false" >> $GITHUB_OUTPUT
echo "build_gui=true" >> $GITHUB_OUTPUT
else
TAG="v${VERSION}"
echo "build_cli=true" >> $GITHUB_OUTPUT
echo "build_gui=true" >> $GITHUB_OUTPUT
fi
else
TAG="${GITHUB_REF#refs/tags/}"
if [[ "$TAG" == cli-v* ]]; then
VERSION="${TAG#cli-v}"
echo "build_cli=true" >> $GITHUB_OUTPUT
echo "build_gui=false" >> $GITHUB_OUTPUT
elif [[ "$TAG" == gui-v* ]]; then
VERSION="${TAG#gui-v}"
echo "build_cli=false" >> $GITHUB_OUTPUT
echo "build_gui=true" >> $GITHUB_OUTPUT
else
VERSION="${TAG#v}"
echo "build_cli=true" >> $GITHUB_OUTPUT
echo "build_gui=true" >> $GITHUB_OUTPUT
fi
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "tag=$TAG" >> $GITHUB_OUTPUT
- name: Verify CLI version consistency
if: steps.version.outputs.build_cli == 'true'
run: |
CLI_RELEASE_VERSION="${{ steps.version.outputs.version }}" ./scripts/check-cli-version.sh
# ==========================================================================
# Build CLI Binaries (32 vCPU for compilation)
# ==========================================================================
build-cli:
name: CLI ${{ matrix.artifact }}
runs-on: ${{ matrix.runner }}
needs: prepare
if: needs.prepare.outputs.build_cli == 'true'
strategy:
fail-fast: false
matrix:
include:
- runner: blacksmith-32vcpu-windows-2025
target: x86_64-pc-windows-msvc
artifact: cortex-cli-windows-x64
ext: .exe
- runner: blacksmith-32vcpu-windows-2025
target: aarch64-pc-windows-msvc
artifact: cortex-cli-windows-arm64
ext: .exe
- runner: macos-latest
target: x86_64-apple-darwin
artifact: cortex-cli-macos-x64
ext: ""
- runner: macos-latest
target: aarch64-apple-darwin
artifact: cortex-cli-macos-arm64
ext: ""
- runner: blacksmith-32vcpu-ubuntu-2404
target: x86_64-unknown-linux-gnu
artifact: cortex-cli-linux-x64
ext: ""
- runner: blacksmith-32vcpu-ubuntu-2404-arm
target: aarch64-unknown-linux-gnu
artifact: cortex-cli-linux-arm64
ext: ""
steps:
- uses: actions/checkout@v4
- name: Install Rust nightly
uses: dtolnay/rust-toolchain@nightly
with:
targets: ${{ matrix.target }}
- name: Setup Rust cache (Blacksmith optimized)
if: contains(matrix.runner, 'blacksmith')
uses: useblacksmith/rust-cache@v3
with:
prefix-key: "rust-release-cli-${{ matrix.target }}"
shared-key: ${{ needs.prepare.outputs.cache_key }}
- name: Setup Rust cache (non-Blacksmith)
if: "!contains(matrix.runner, 'blacksmith')"
uses: Swatinem/rust-cache@v2
with:
prefix-key: "rust-release-cli-${{ matrix.target }}"
shared-key: ${{ needs.prepare.outputs.cache_key }}
# Windows ARM64 requires clang from Visual Studio for building the ring crate
# See: https://github.com/briansmith/ring/blob/main/BUILDING.md
- name: Setup LLVM for Windows ARM64
if: matrix.target == 'aarch64-pc-windows-msvc'
shell: pwsh
run: |
# Try all known Visual Studio LLVM paths for cross-compiling to ARM64
$llvmPaths = @(
"C:\Program Files (x86)\Microsoft Visual Studio\2022\Enterprise\VC\Tools\Llvm\x64\bin",
"C:\Program Files (x86)\Microsoft Visual Studio\2022\Professional\VC\Tools\Llvm\x64\bin",
"C:\Program Files (x86)\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm\x64\bin",
"C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\Llvm\x64\bin",
"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\Llvm\x64\bin",
"C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\Llvm\x64\bin",
"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm\x64\bin",
"C:\Program Files\Microsoft Visual Studio\2022\BuildTools\VC\Tools\Llvm\x64\bin"
)
$found = $false
foreach ($path in $llvmPaths) {
if (Test-Path $path) {
echo "Found LLVM at: $path"
echo "$path" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
$found = $true
break
}
}
if (-not $found) {
echo "Visual Studio LLVM not found in standard paths. Searching..."
$clangExe = Get-ChildItem -Path "C:\Program Files*" -Recurse -Filter "clang.exe" -ErrorAction SilentlyContinue |
Where-Object { $_.FullName -match "LLVM|Llvm" } |
Select-Object -First 1
if ($clangExe) {
$clangDir = Split-Path $clangExe.FullName -Parent
echo "Found clang at: $clangDir"
echo "$clangDir" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
$found = $true
}
}
if (-not $found) {
echo "::error::LLVM/Clang not found. Installing via choco..."
choco install llvm -y --no-progress
echo "C:\Program Files\LLVM\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
}
- name: Build release binary
run: cargo +nightly build --release --target ${{ matrix.target }} -p cortex-cli
env:
RUSTFLAGS: "-Zthreads=32"
CARGO_PROFILE_RELEASE_LTO: thin
- name: Prepare artifact (Unix)
if: runner.os != 'Windows'
run: |
mkdir -p dist
cp target/${{ matrix.target }}/release/${{ env.BINARY_NAME }}${{ matrix.ext }} dist/
cd dist
tar -czvf ../${{ matrix.artifact }}.tar.gz *
- name: Prepare artifact (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path dist
Copy-Item "target/${{ matrix.target }}/release/${{ env.BINARY_NAME }}${{ matrix.ext }}" dist/
Compress-Archive -Path dist/* -DestinationPath "${{ matrix.artifact }}.zip"
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact }}
path: |
${{ matrix.artifact }}.tar.gz
${{ matrix.artifact }}.zip
if-no-files-found: ignore
# ==========================================================================
# Build GUI Binaries (Tauri) - 32 vCPU for compilation
# ==========================================================================
build-gui:
name: GUI ${{ matrix.artifact }}
runs-on: ${{ matrix.runner }}
needs: prepare
if: needs.prepare.outputs.build_gui == 'true'
strategy:
fail-fast: false
matrix:
include:
- name: Windows x64
runner: blacksmith-32vcpu-windows-2025
target: x86_64-pc-windows-msvc
artifact: cortex-gui-windows-x64
bundle: msi
- name: macOS x64
runner: macos-13
target: x86_64-apple-darwin
artifact: cortex-gui-macos-x64
bundle: dmg
- name: macOS ARM64
runner: macos-latest
target: aarch64-apple-darwin
artifact: cortex-gui-macos-arm64
bundle: dmg
- name: Linux x64
runner: blacksmith-32vcpu-ubuntu-2204
target: x86_64-unknown-linux-gnu
artifact: cortex-gui-linux-x64
bundle: appimage
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "pnpm"
cache-dependency-path: cortex-gui/pnpm-lock.yaml
- name: Install Rust nightly
uses: dtolnay/rust-toolchain@nightly
with:
targets: ${{ matrix.target }}
- name: Install system dependencies (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y \
libwebkit2gtk-4.1-dev \
librsvg2-dev \
patchelf \
libssl-dev \
libgtk-3-dev \
libayatana-appindicator3-dev
- name: Setup Rust cache (Blacksmith optimized)
if: contains(matrix.runner, 'blacksmith')
uses: useblacksmith/rust-cache@v3
with:
prefix-key: "rust-release-gui-${{ matrix.target }}"
shared-key: ${{ needs.prepare.outputs.cache_key }}
workspaces: |
.
cortex-gui/src-tauri
- name: Setup Rust cache (non-Blacksmith)
if: "!contains(matrix.runner, 'blacksmith')"
uses: Swatinem/rust-cache@v2
with:
prefix-key: "rust-release-gui-${{ matrix.target }}"
shared-key: ${{ needs.prepare.outputs.cache_key }}
workspaces: |
.
cortex-gui/src-tauri
- name: Cache pnpm (Blacksmith 4x faster cache)
uses: actions/cache@v4
with:
path: |
cortex-gui/node_modules
~/.local/share/pnpm/store
key: pnpm-${{ matrix.runner }}-${{ hashFiles('cortex-gui/pnpm-lock.yaml') }}
restore-keys: |
pnpm-${{ matrix.runner }}-
- name: Install frontend dependencies
working-directory: cortex-gui
run: pnpm install --frozen-lockfile
- name: Build Tauri app
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
RUSTFLAGS: "-Zthreads=32"
with:
projectPath: cortex-gui
tauriScript: pnpm tauri
args: --target ${{ matrix.target }} --bundles ${{ matrix.bundle }}
- name: Collect artifacts (Unix)
if: runner.os != 'Windows'
run: |
mkdir -p collected
BUNDLE_DIR="target/${{ matrix.target }}/release/bundle"
if [ ! -d "$BUNDLE_DIR" ]; then
BUNDLE_DIR="cortex-gui/src-tauri/target/${{ matrix.target }}/release/bundle"
fi
if [ "${{ matrix.bundle }}" = "dmg" ]; then
find "$BUNDLE_DIR" -name "*.dmg" -exec cp {} collected/ \;
elif [ "${{ matrix.bundle }}" = "appimage" ]; then
find "$BUNDLE_DIR" -name "*.AppImage" -exec cp {} collected/ \;
fi
ls -la collected/
- name: Collect artifacts (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path collected
$bundleDir = "target/${{ matrix.target }}/release/bundle"
if (-not (Test-Path $bundleDir)) {
$bundleDir = "cortex-gui/src-tauri/target/${{ matrix.target }}/release/bundle"
}
Get-ChildItem -Path $bundleDir -Recurse -Include *.msi | Copy-Item -Destination collected/
Get-ChildItem collected/
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact }}
path: collected/*
if-no-files-found: warn
# ==========================================================================
# Create GitHub Release (lightweight - 4 vCPU)
# ==========================================================================
release:
name: Create Release
needs: [prepare, build-cli, build-gui]
if: always() && needs.prepare.result == 'success' && (needs.build-cli.result == 'success' || needs.build-cli.result == 'skipped') && (needs.build-gui.result == 'success' || needs.build-gui.result == 'skipped')
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- uses: actions/checkout@v4
- name: Create tag (workflow_dispatch only)
if: github.event_name == 'workflow_dispatch'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag ${{ needs.prepare.outputs.tag }} || true
git push origin ${{ needs.prepare.outputs.tag }} || true
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Flatten and generate checksums
run: |
cd artifacts
find . -type f \( -name "*.tar.gz" -o -name "*.zip" -o -name "*.dmg" -o -name "*.msi" -o -name "*.AppImage" \) -exec mv {} . \;
rm -rf cortex-*/ || true
sha256sum * > checksums-sha256.txt 2>/dev/null || true
echo "=== Release artifacts ==="
ls -la
echo "=== Checksums ==="
cat checksums-sha256.txt
- name: Determine release name
id: release_name
run: |
TAG="${{ needs.prepare.outputs.tag }}"
VERSION="${{ needs.prepare.outputs.version }}"
if [[ "$TAG" == cli-v* ]]; then
echo "name=Cortex CLI v${VERSION}" >> $GITHUB_OUTPUT
elif [[ "$TAG" == gui-v* ]]; then
echo "name=Cortex GUI v${VERSION}" >> $GITHUB_OUTPUT
else
echo "name=Cortex v${VERSION}" >> $GITHUB_OUTPUT
fi
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.prepare.outputs.tag }}
name: ${{ steps.release_name.outputs.name }}
draft: false
prerelease: ${{ contains(needs.prepare.outputs.version, '-') }}
generate_release_notes: true
files: |
artifacts/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}