Skip to content

Release

Release #1

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
RUSTFLAGS: "-Zthreads=16"
# 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
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
jobs:
# ==========================================================================
# Prepare - Determine version and what to build
# ==========================================================================
prepare:
name: Prepare Release
runs-on: ubuntu-latest
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
# ==========================================================================
build-cli:
name: CLI ${{ matrix.artifact }}
runs-on: ${{ matrix.os }}
needs: prepare
if: needs.prepare.outputs.build_cli == 'true'
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
target: x86_64-pc-windows-msvc
artifact: cortex-cli-windows-x64
ext: .exe
- os: windows-latest
target: aarch64-pc-windows-msvc
artifact: cortex-cli-windows-arm64
ext: .exe
- os: macos-latest
target: x86_64-apple-darwin
artifact: cortex-cli-macos-x64
ext: ""
- os: macos-latest
target: aarch64-apple-darwin
artifact: cortex-cli-macos-arm64
ext: ""
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
artifact: cortex-cli-linux-x64
ext: ""
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
artifact: cortex-cli-linux-arm64
ext: ""
cross: true
steps:
- uses: actions/checkout@v4
- name: Install Rust nightly
uses: dtolnay/rust-toolchain@nightly
with:
targets: ${{ matrix.target }}
- name: Install cross-compilation tools (Linux ARM64)
if: matrix.cross == true
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
mkdir -p ~/.cargo
echo '[target.aarch64-unknown-linux-gnu]' >> ~/.cargo/config.toml
echo 'linker = "aarch64-linux-gnu-gcc"' >> ~/.cargo/config.toml
- name: Setup Rust cache
uses: Swatinem/rust-cache@v2
with:
prefix-key: "rust-release-cli-${{ matrix.target }}"
shared-key: ${{ needs.prepare.outputs.cache_key }}
- name: Build release binary
run: cargo +nightly build --release --target ${{ matrix.target }} -p cortex-cli
env:
RUSTFLAGS: "-Zthreads=16"
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)
# ==========================================================================
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: windows-latest
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: ubuntu-22.04
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
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
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=16"
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
# ==========================================================================
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: ubuntu-latest
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 }}