perf(loader): parallel track loading via rayon dispatch #45
Workflow file for this run
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*' | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'Version tag (e.g., v0.8.4)' | |
| required: true | |
| type: string | |
| permissions: | |
| contents: write | |
| pages: write | |
| id-token: write | |
| # Only one release workflow per tag at a time — re-pushing a tag cancels the | |
| # previous run so stale builds don't race with the new one. | |
| concurrency: | |
| group: release-${{ github.ref_name }} | |
| cancel-in-progress: true | |
| env: | |
| CARGO_TERM_COLOR: always | |
| CACHE_NAME: mesh-cache | |
| jobs: | |
| # =========================================================================== | |
| # Job 1: Create draft release and validate version | |
| # =========================================================================== | |
| create-release: | |
| name: Create Release | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.version.outputs.version }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Get version | |
| id: version | |
| run: | | |
| if [ -n "${{ github.event.inputs.version }}" ]; then | |
| echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT | |
| else | |
| echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Validate version against Cargo.toml | |
| run: | | |
| TAG_VERSION="${{ steps.version.outputs.version }}" | |
| # Strip 'v' prefix for comparison (v0.8.4 -> 0.8.4) | |
| TAG_SEMVER="${TAG_VERSION#v}" | |
| # Also strip any pre-release suffix for base version check (0.8.4-rc1 -> 0.8.4) | |
| TAG_BASE="${TAG_SEMVER%%-*}" | |
| CARGO_VERSION=$(grep -A2 '^\[workspace\.package\]' Cargo.toml | grep '^version' | sed 's/.*"\(.*\)".*/\1/') | |
| echo "Tag version: $TAG_SEMVER (base: $TAG_BASE)" | |
| echo "Cargo version: $CARGO_VERSION" | |
| if [ "$TAG_BASE" != "$CARGO_VERSION" ]; then | |
| echo "::error::Version mismatch! Tag base '$TAG_BASE' does not match Cargo.toml version '$CARGO_VERSION'" | |
| echo "Update [workspace.package] version in Cargo.toml before tagging." | |
| exit 1 | |
| fi | |
| echo "Version check passed." | |
| - name: Extract changelog for this version | |
| id: changelog | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| SEMVER="${VERSION#v}" | |
| # Extract the section between ## [this version] and the next ## [ | |
| # Uses awk: start printing at the matching header, stop at the next ## [ | |
| NOTES=$(awk -v ver="$SEMVER" ' | |
| /^## \[/ { | |
| if (found) exit | |
| if (index($0, "[" ver "]")) found=1 | |
| } | |
| found { print } | |
| ' CHANGELOG.md) | |
| if [ -z "$NOTES" ]; then | |
| echo "No changelog entry found for $SEMVER" | |
| NOTES="*No changelog entry for this version.*" | |
| fi | |
| # Write to file (multiline strings in GITHUB_OUTPUT need delimiters) | |
| echo "$NOTES" > /tmp/changelog-notes.md | |
| - name: Build release body | |
| run: | | |
| REPO="${{ github.repository }}" | |
| cat > /tmp/release-body.md << EOF | |
| ## Installation | |
| ### Linux (.deb) | |
| Download the \`.deb\` package for your system: | |
| - **mesh-player** — Performance mode (4-deck stem player) | |
| - **mesh-cue** — Editing mode (waveform editor, analysis, stem separation) | |
| - **mesh-cue-cuda** — Editing mode with NVIDIA GPU acceleration | |
| \`\`\`bash | |
| sudo dpkg -i mesh-player_*.deb mesh-cue_*.deb | |
| # Install dependencies if needed: | |
| sudo apt-get install -f | |
| \`\`\` | |
| **Requirements:** PipeWire (Ubuntu 22.04+, Fedora 34+) or JACK2, glibc 2.35+ | |
| ### Windows (.zip) | |
| Extract the zip and run the \`.exe\` directly. No installation required. | |
| GPU acceleration via DirectML (any DirectX 12 GPU). | |
| ### NixOS / Nix | |
| Run directly (x86_64-linux): | |
| \`\`\`bash | |
| nix run github:${REPO}#mesh-player | |
| nix run github:${REPO}#mesh-cue | |
| \`\`\` | |
| **aarch64-linux** (Orange Pi 5 / embedded): A pre-built binary cache is published to GitHub Pages with every release. Configure the cache and update: | |
| \`\`\`bash | |
| # /etc/nix/nix.conf (or flake nixConfig) | |
| extra-substituters = https://datao1.github.io/Mesh/ | |
| extra-trusted-public-keys = mesh-embedded:vLo1l3Abp0Uzcn21wR3oXvmZxZb1Z1rbk+ggTOIGmeQ= | |
| # Update an embedded device | |
| nixos-rebuild switch --flake github:${REPO}#mesh-embedded --no-write-lock-file | |
| \`\`\` | |
| ML models are downloaded automatically on first use. | |
| --- | |
| EOF | |
| # Append changelog notes | |
| cat /tmp/changelog-notes.md >> /tmp/release-body.md | |
| - name: Create Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| name: Mesh ${{ steps.version.outputs.version }} | |
| tag_name: ${{ steps.version.outputs.version }} | |
| draft: true | |
| prerelease: ${{ contains(steps.version.outputs.version, '-rc') || contains(steps.version.outputs.version, '-beta') || contains(steps.version.outputs.version, '-alpha') }} | |
| body_path: /tmp/release-body.md | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # =========================================================================== | |
| # Job 2: Build Linux .deb packages (CPU) | |
| # =========================================================================== | |
| build-deb: | |
| name: Build .deb (CPU) | |
| needs: create-release | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Free disk space | |
| run: | | |
| echo "Before cleanup:" | |
| df -h / | |
| sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /usr/local/share/boost | |
| sudo rm -rf /usr/local/graalvm /usr/local/share/chromium /usr/local/.ghcup | |
| echo "After cleanup:" | |
| df -h / | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Install Nix | |
| uses: cachix/install-nix-action@v31 | |
| with: | |
| extra_nix_config: | | |
| experimental-features = nix-command flakes | |
| - name: Nix store cache | |
| uses: DeterminateSystems/magic-nix-cache-action@v8 | |
| with: | |
| use-flakehub: false | |
| - name: Restore native deps cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| target/deb-build/deps | |
| target/deb-build/onnxruntime-cpu-* | |
| key: deb-native-deps-${{ hashFiles('nix/apps/build-deb.nix') }} | |
| - name: Restore Rust build cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| target/deb-build/rustup | |
| target/deb-build/cargo | |
| target/deb-build/target | |
| key: deb-rust-${{ hashFiles('Cargo.lock') }} | |
| restore-keys: deb-rust- | |
| - name: Build .deb packages | |
| run: nix run .#build-deb | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: deb-cpu | |
| path: dist/deb/*.deb | |
| if-no-files-found: error | |
| retention-days: 5 | |
| # =========================================================================== | |
| # Job 3: Build Linux .deb packages (CUDA) | |
| # =========================================================================== | |
| build-deb-cuda: | |
| name: Build .deb (CUDA) | |
| needs: create-release | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Free disk space | |
| run: | | |
| echo "Before cleanup:" | |
| df -h / | |
| sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /usr/local/share/boost | |
| sudo rm -rf /usr/local/graalvm /usr/local/share/chromium /usr/local/.ghcup | |
| echo "After cleanup:" | |
| df -h / | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Install Nix | |
| uses: cachix/install-nix-action@v31 | |
| with: | |
| extra_nix_config: | | |
| experimental-features = nix-command flakes | |
| - name: Nix store cache | |
| uses: DeterminateSystems/magic-nix-cache-action@v8 | |
| with: | |
| use-flakehub: false | |
| - name: Restore native deps cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| target/deb-build/deps | |
| target/deb-build/onnxruntime-gpu-* | |
| key: deb-native-deps-${{ hashFiles('nix/apps/build-deb.nix') }} | |
| - name: Restore Rust build cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| target/deb-build/rustup | |
| target/deb-build/cargo | |
| target/deb-build/target | |
| key: deb-cuda-rust-${{ hashFiles('Cargo.lock') }} | |
| restore-keys: deb-cuda-rust- | |
| - name: Build CUDA .deb packages | |
| run: nix run .#build-deb-cuda | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: deb-cuda | |
| path: dist/deb/*.deb | |
| if-no-files-found: error | |
| retention-days: 5 | |
| # =========================================================================== | |
| # Job 4: Build Windows packages | |
| # =========================================================================== | |
| build-windows: | |
| name: Build Windows | |
| needs: create-release | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Free disk space | |
| run: | | |
| echo "Before cleanup:" | |
| df -h / | |
| sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /usr/local/share/boost | |
| sudo rm -rf /usr/local/graalvm /usr/local/share/chromium /usr/local/.ghcup | |
| echo "After cleanup:" | |
| df -h / | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Install Nix | |
| uses: cachix/install-nix-action@v31 | |
| with: | |
| extra_nix_config: | | |
| experimental-features = nix-command flakes | |
| - name: Nix store cache | |
| uses: DeterminateSystems/magic-nix-cache-action@v8 | |
| with: | |
| use-flakehub: false | |
| - name: Restore native deps cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| target/windows/essentia-win | |
| target/windows/essentia-host | |
| target/windows/essentia-src | |
| target/windows/onnxruntime-directml-* | |
| key: win-native-deps-${{ hashFiles('nix/apps/build-windows.nix') }} | |
| - name: Restore Rust build cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: target/windows/x86_64-pc-windows-gnu | |
| key: win-rust-${{ hashFiles('Cargo.lock') }} | |
| restore-keys: win-rust- | |
| - name: Build Windows packages | |
| run: nix run .#build-windows | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: windows | |
| path: dist/windows/*.zip | |
| if-no-files-found: error | |
| retention-days: 5 | |
| # =========================================================================== | |
| # Job 5: Build and sync ML models to permanent 'models' release | |
| # =========================================================================== | |
| build-models: | |
| name: Sync Models | |
| needs: create-release | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Install Nix | |
| uses: cachix/install-nix-action@v31 | |
| with: | |
| extra_nix_config: | | |
| experimental-features = nix-command flakes | |
| - name: Nix store cache | |
| uses: DeterminateSystems/magic-nix-cache-action@v8 | |
| with: | |
| use-flakehub: false | |
| - name: Restore model cache | |
| id: model-cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: models/ | |
| key: models-${{ hashFiles('nix/apps/convert-beat-model.nix', 'nix/apps/convert-ml-model.nix', 'nix/apps/convert-model.nix') }} | |
| - name: Convert Beat This! model | |
| if: steps.model-cache.outputs.cache-hit != 'true' | |
| run: | | |
| mkdir -p models | |
| # Remove existing to force reconversion | |
| rm -f models/beat_this_small.onnx | |
| nix run .#convert-beat-model | |
| - name: Convert genre classification head | |
| if: steps.model-cache.outputs.cache-hit != 'true' | |
| run: | | |
| rm -f models/genre_discogs400-discogs-effnet-1.onnx | |
| nix run .#convert-ml-model | |
| - name: Convert Demucs models | |
| if: steps.model-cache.outputs.cache-hit != 'true' | |
| run: | | |
| rm -f models/htdemucs.onnx models/htdemucs.onnx.data | |
| nix run .#convert-model -- htdemucs ./models | |
| rm -f models/htdemucs_ft.onnx models/htdemucs_ft.onnx.data | |
| nix run .#convert-model -- htdemucs_ft ./models | |
| - name: Download pre-built Essentia models | |
| if: steps.model-cache.outputs.cache-hit != 'true' | |
| run: | | |
| # EffNet embedding model (ONNX published directly by Essentia) | |
| curl --fail --location --progress-bar \ | |
| -o models/discogs-effnet-bsdynamic-1.onnx \ | |
| "https://essentia.upf.edu/models/music-style-classification/discogs-effnet/discogs-effnet-bsdynamic-1.onnx" | |
| # Jamendo mood/theme classification head (ONNX published directly by Essentia) | |
| curl --fail --location --progress-bar \ | |
| -o models/mtg_jamendo_moodtheme-discogs-effnet-1.onnx \ | |
| "https://essentia.upf.edu/models/classification-heads/mtg_jamendo_moodtheme/mtg_jamendo_moodtheme-discogs-effnet-1.onnx" | |
| - name: Upload models to permanent release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| echo "Models to upload:" | |
| ls -lh models/*.onnx* 2>/dev/null || true | |
| # Ensure the 'models' release exists | |
| if ! gh release view models &>/dev/null; then | |
| gh release create models \ | |
| --title "ML Models" \ | |
| --notes "Pre-built ONNX models for stem separation, beat detection, and audio classification. Downloaded automatically by mesh-cue on first use." | |
| fi | |
| # Upload all model files (--clobber overwrites existing) | |
| gh release upload models models/*.onnx* --clobber | |
| echo "Models synced to 'models' release." | |
| # =========================================================================== | |
| # Job 6: Build aarch64 mesh-player and publish Nix binary cache | |
| # =========================================================================== | |
| # Runs on every version tag so embedded devices can update via nixos-rebuild. | |
| # The binary cache is served as static files from GitHub Pages at | |
| # https://datao1.github.io/Mesh/ — the Orange Pi fetches pre-built packages | |
| # from here instead of compiling from source. | |
| build-aarch64: | |
| name: Build aarch64 + Cache | |
| needs: create-release | |
| runs-on: ubuntu-24.04-arm # Native aarch64 runner (free for public repos) | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Install Nix | |
| uses: cachix/install-nix-action@v31 | |
| with: | |
| extra_nix_config: | | |
| experimental-features = nix-command flakes | |
| extra-substituters = https://datao1.github.io/Mesh/ | |
| extra-trusted-public-keys = mesh-embedded:vLo1l3Abp0Uzcn21wR3oXvmZxZb1Z1rbk+ggTOIGmeQ= | |
| - name: Nix store cache | |
| uses: DeterminateSystems/magic-nix-cache-action@v8 | |
| with: | |
| use-flakehub: false | |
| - name: Set up signing key | |
| run: | | |
| echo "${{ secrets.NIX_CACHE_PRIV_KEY }}" > /tmp/cache-priv-key.pem | |
| # Validate key format: must be "name:base64key" | |
| if ! grep -qE '^[a-zA-Z0-9_.-]+:' /tmp/cache-priv-key.pem; then | |
| echo "::error::NIX_CACHE_PRIV_KEY must be in format 'keyname:base64key'" | |
| exit 1 | |
| fi | |
| echo "Signing key loaded ($(wc -c < /tmp/cache-priv-key.pem) bytes)" | |
| - name: Build mesh-player | |
| run: | | |
| echo "Building mesh-player for aarch64-linux..." | |
| nix build -L .#packages.aarch64-linux.mesh-player | |
| - name: Create binary cache | |
| run: | | |
| CACHE_DIR="$(pwd)/${{ env.CACHE_NAME }}" | |
| CACHE_STORE="file://${CACHE_DIR}" | |
| mkdir -p "${CACHE_DIR}" | |
| echo "Copying store paths to binary cache..." | |
| nix copy --to "${CACHE_STORE}" ./result | |
| echo "Signing all paths in cache..." | |
| nix store sign \ | |
| --store "${CACHE_STORE}" \ | |
| --key-file /tmp/cache-priv-key.pem \ | |
| --all | |
| echo "" | |
| echo "Cache contents:" | |
| ls -lh "${CACHE_DIR}"/ | |
| echo "" | |
| du -sh "${CACHE_DIR}" | |
| echo "" | |
| echo "narinfo count: $(ls "${CACHE_DIR}"/*.narinfo 2>/dev/null | wc -l)" | |
| - name: Verify cache signatures | |
| run: | | |
| CACHE_DIR="$(pwd)/${{ env.CACHE_NAME }}" | |
| # Extract the store path hash (base32, first component after /nix/store/) | |
| STORE_PATH=$(readlink ./result) | |
| STORE_HASH=$(basename "$STORE_PATH" | cut -d'-' -f1) | |
| echo "Checking narinfo for mesh-player (${STORE_HASH})..." | |
| NARINFO="${CACHE_DIR}/${STORE_HASH}.narinfo" | |
| if [ ! -f "$NARINFO" ]; then | |
| echo "::error::narinfo not found: ${NARINFO}" | |
| echo "Available narinfos:" | |
| ls "${CACHE_DIR}"/*.narinfo 2>/dev/null | head -5 | |
| exit 1 | |
| fi | |
| if grep -q "^Sig:" "$NARINFO"; then | |
| echo "Signature found:" | |
| grep "^Sig:" "$NARINFO" | |
| else | |
| echo "::error::mesh-player narinfo has no signature! Check NIX_CACHE_PRIV_KEY secret." | |
| echo "narinfo contents:" | |
| cat "$NARINFO" | |
| exit 1 | |
| fi | |
| - name: Generate index.html | |
| run: | | |
| cd ./${{ env.CACHE_NAME }} | |
| VERSION="${{ needs.create-release.outputs.version }}" | |
| NARINFO_COUNT=$(ls *.narinfo 2>/dev/null | wc -l) | |
| CACHE_SIZE=$(du -sh . | cut -f1) | |
| NAR_COUNT=$(ls nar/*.nar.xz 2>/dev/null | wc -l) | |
| cat > index.html << 'HEADER' | |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>Mesh — Nix Binary Cache</title> | |
| <style> | |
| body { font-family: -apple-system, system-ui, sans-serif; max-width: 800px; margin: 2rem auto; padding: 0 1rem; color: #e0e0e0; background: #1a1a2e; } | |
| h1 { color: #fff; } | |
| .stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin: 1.5rem 0; } | |
| .stat { background: #16213e; padding: 1rem; border-radius: 8px; text-align: center; } | |
| .stat-value { font-size: 1.5rem; font-weight: bold; color: #0ea5e9; } | |
| .stat-label { font-size: 0.85rem; color: #94a3b8; margin-top: 0.25rem; } | |
| code { background: #16213e; padding: 0.2rem 0.5rem; border-radius: 4px; font-size: 0.9rem; } | |
| pre { background: #16213e; padding: 1rem; border-radius: 8px; overflow-x: auto; } | |
| a { color: #0ea5e9; } | |
| table { width: 100%; border-collapse: collapse; margin-top: 1rem; } | |
| th, td { text-align: left; padding: 0.5rem; border-bottom: 1px solid #2a2a4a; } | |
| th { color: #94a3b8; font-weight: normal; font-size: 0.85rem; } | |
| td a { text-decoration: none; } | |
| .version-banner { margin: 1.5rem 0; padding: 1.25rem; background: linear-gradient(135deg, #16213e 0%, #1a1a3e 100%); border: 1px solid #0ea5e9; border-radius: 10px; display: flex; align-items: center; justify-content: space-between; } | |
| .version-banner .ver { font-size: 2rem; font-weight: bold; color: #fff; } | |
| .version-banner .ver a { color: #fff; text-decoration: none; } | |
| .version-banner .ver a:hover { color: #0ea5e9; } | |
| .version-banner .release-link { background: #0ea5e9; color: #1a1a2e; padding: 0.5rem 1.25rem; border-radius: 6px; text-decoration: none; font-weight: 600; font-size: 0.9rem; } | |
| .version-banner .release-link:hover { background: #38bdf8; } | |
| .footer { margin-top: 2rem; color: #64748b; font-size: 0.85rem; } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>Mesh — Nix Binary Cache</h1> | |
| <p>Pre-built aarch64-linux packages for the | |
| <a href="https://github.com/dataO1/Mesh">Mesh DJ Player</a> | |
| embedded deployment (Orange Pi 5).</p> | |
| HEADER | |
| REPO="${{ github.repository }}" | |
| RELEASE_URL="https://github.com/${REPO}/releases/tag/${VERSION}" | |
| cat >> index.html << EOF | |
| <div class="version-banner"> | |
| <div class="ver"><a href="${RELEASE_URL}">${VERSION}</a></div> | |
| <a class="release-link" href="${RELEASE_URL}">View Release →</a> | |
| </div> | |
| <div class="stats"> | |
| <div class="stat"><div class="stat-value">${NARINFO_COUNT}</div><div class="stat-label">packages</div></div> | |
| <div class="stat"><div class="stat-value">${CACHE_SIZE}</div><div class="stat-label">total size</div></div> | |
| </div> | |
| <h2>Usage</h2> | |
| <p>Add to <code>/etc/nix/nix.conf</code> or flake config:</p> | |
| <pre>extra-substituters = https://datao1.github.io/Mesh/ | |
| extra-trusted-public-keys = mesh-embedded:vLo1l3Abp0Uzcn21wR3oXvmZxZb1Z1rbk+ggTOIGmeQ=</pre> | |
| <p>Update the embedded device:</p> | |
| <pre>nixos-rebuild switch --flake github:dataO1/Mesh/${VERSION}#mesh-embedded --no-write-lock-file</pre> | |
| <h2>Packages</h2> | |
| <table> | |
| <tr><th>Package</th><th>Size</th></tr> | |
| EOF | |
| for f in *.narinfo; do | |
| STORE_PATH=$(grep "^StorePath:" "$f" | sed 's/StorePath: \/nix\/store\///') | |
| NAR_SIZE=$(grep "^NarSize:" "$f" | awk '{printf "%.1f MB", $2/1048576}') | |
| echo " <tr><td><a href=\"$f\">$STORE_PATH</a></td><td>$NAR_SIZE</td></tr>" >> index.html | |
| done | |
| cat >> index.html << EOF | |
| </table> | |
| <p class="footer">Last updated: $(date -u '+%Y-%m-%d %H:%M UTC') · <a href="${RELEASE_URL}">${VERSION}</a></p> | |
| </body> | |
| </html> | |
| EOF | |
| - name: Deploy to GitHub Pages | |
| uses: peaceiris/actions-gh-pages@v4 | |
| with: | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| publish_dir: ./${{ env.CACHE_NAME }} | |
| keep_files: true | |
| commit_message: "cache: ${{ needs.create-release.outputs.version }} aarch64-linux" | |
| - name: Clean up signing key | |
| if: always() | |
| run: rm -f /tmp/cache-priv-key.pem | |
| # =========================================================================== | |
| # Job 7: Collect artifacts and publish release | |
| # =========================================================================== | |
| publish-release: | |
| name: Publish Release | |
| needs: [create-release, build-deb, build-deb-cuda, build-windows, build-models, build-aarch64] | |
| runs-on: ubuntu-latest | |
| if: always() && needs.create-release.result == 'success' | |
| steps: | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts/ | |
| merge-multiple: true | |
| - name: List artifacts | |
| run: | | |
| echo "Downloaded artifacts:" | |
| find artifacts/ -type f 2>/dev/null | sort || echo "No artifacts found" | |
| - name: Upload artifacts to release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| VERSION="${{ needs.create-release.outputs.version }}" | |
| echo "Uploading to release: $VERSION" | |
| # Upload all .deb and .zip files found | |
| shopt -s nullglob | |
| FILES=(artifacts/*.deb artifacts/*.zip) | |
| if [ ${#FILES[@]} -gt 0 ]; then | |
| gh release upload "$VERSION" "${FILES[@]}" --clobber --repo "${{ github.repository }}" | |
| echo "Uploaded ${#FILES[@]} file(s)" | |
| else | |
| echo "::warning::No artifacts to upload" | |
| fi | |
| - name: Publish release if all builds succeeded | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| VERSION="${{ needs.create-release.outputs.version }}" | |
| DEB_OK="${{ needs.build-deb.result }}" | |
| CUDA_OK="${{ needs.build-deb-cuda.result }}" | |
| WIN_OK="${{ needs.build-windows.result }}" | |
| MODELS_OK="${{ needs.build-models.result }}" | |
| if [ "$DEB_OK" = "success" ] && [ "$CUDA_OK" = "success" ] && [ "$WIN_OK" = "success" ]; then | |
| echo "All app builds succeeded — publishing release" | |
| gh release edit "$VERSION" --draft=false --repo "${{ github.repository }}" | |
| else | |
| echo "::warning::Some builds failed — leaving release as draft" | |
| echo " build-deb: $DEB_OK" | |
| echo " build-deb-cuda: $CUDA_OK" | |
| echo " build-windows: $WIN_OK" | |
| echo " build-models: $MODELS_OK" | |
| fi | |
| - name: Summary | |
| if: always() | |
| run: | | |
| VERSION="${{ needs.create-release.outputs.version }}" | |
| echo "## Release $VERSION" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Job | Status |" >> $GITHUB_STEP_SUMMARY | |
| echo "|-----|--------|" >> $GITHUB_STEP_SUMMARY | |
| echo "| Create Release | ${{ needs.create-release.result }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Build .deb (CPU) | ${{ needs.build-deb.result }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Build .deb (CUDA) | ${{ needs.build-deb-cuda.result }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Build Windows | ${{ needs.build-windows.result }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Build aarch64 + Cache | ${{ needs.build-aarch64.result }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Sync Models | ${{ needs.build-models.result }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| DEB_OK="${{ needs.build-deb.result }}" | |
| CUDA_OK="${{ needs.build-deb-cuda.result }}" | |
| WIN_OK="${{ needs.build-windows.result }}" | |
| if [ "$DEB_OK" = "success" ] && [ "$CUDA_OK" = "success" ] && [ "$WIN_OK" = "success" ]; then | |
| echo "**Release published.**" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "**Release left as draft** — some builds failed." >> $GITHUB_STEP_SUMMARY | |
| fi |