Release #10
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 }} | |