Skip to content

Release

Release #46

Workflow file for this run

name: Release
# Tag a version (e.g. `git tag v0.0.5 && git push --tags`) to build the
# Duckle binary for every OS and attach it directly to a draft GitHub
# release. We do NOT ship installers (no NSIS / MSI / DMG / DEB / RPM /
# AppImage). The asset on the release is the raw executable that the
# user can run.
on:
push:
tags: ['v*']
workflow_dispatch:
inputs:
release_tag:
description: 'Existing release tag to (re)upload binaries to. Builds from the branch this run is dispatched on; the tag itself is never moved. Leave empty on a normal tag push.'
required: false
default: ''
jobs:
# Single point that creates the draft release so the per-OS build
# jobs don't race on `gh release create`.
prepare-release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Create draft release if missing
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
REL_TAG: ${{ github.event.inputs.release_tag || github.ref_name }}
run: |
set -e
tag="$REL_TAG"
if gh release view "$tag" >/dev/null 2>&1; then
echo "release $tag already exists"
else
gh release create "$tag" --draft --title "Duckle $tag"
fi
build:
needs: prepare-release
strategy:
fail-fast: false
matrix:
include:
# Linux
- os: ubuntu-24.04
binary: duckle
asset: Duckle-linux-x64
- os: ubuntu-24.04-arm
binary: duckle
asset: Duckle-linux-arm64
# macOS - both built on the macos-14 (Apple Silicon) runner.
# The Intel binary is cross-compiled to x86_64-apple-darwin
# rather than run on a macos-13 runner: GitHub's Intel-mac
# runners are scarce / being deprecated and routinely leave
# the job queued for hours, stalling the whole release.
- os: macos-14
binary: duckle
asset: Duckle-macos-x64
target: x86_64-apple-darwin
- os: macos-14
binary: duckle
asset: Duckle-macos-arm64
# Windows
- os: windows-latest
binary: duckle.exe
asset: Duckle-windows-x64.exe
- os: windows-11-arm
binary: duckle.exe
asset: Duckle-windows-arm64.exe
runs-on: ${{ matrix.os }}
permissions:
contents: write
steps:
- uses: actions/checkout@v4
# Reclaim ~20 GB on Linux runners so the full workspace + embedded
# binaries build does not hit "No space left on device".
- name: Free disk space (Linux)
if: runner.os == 'Linux'
shell: bash
run: |
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc \
/opt/hostedtoolcache/CodeQL /usr/local/.ghcup /usr/local/share/boost \
/usr/local/share/powershell /usr/share/swift || true
sudo docker image prune --all --force || true
df -h /
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: frontend/package-lock.json
- name: Install frontend deps
run: npm --prefix frontend ci
- name: Build frontend (embedded into the Rust binary)
run: npm --prefix frontend run build
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target || '' }}
- uses: Swatinem/rust-cache@v2
- name: Install Linux WebView deps (Tauri)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y \
libwebkit2gtk-4.1-dev libgtk-3-dev \
libayatana-appindicator3-dev librsvg2-dev libsoup-3.0-dev
- name: Build duckle-runner + duckle-mcp (bundled into the app)
shell: bash
run: |
set -e
if [ -n "${{ matrix.target }}" ]; then
cargo build --profile release-runner --target "${{ matrix.target }}" -p duckle-runner -p duckle-mcp
base="target/${{ matrix.target }}/release-runner"
else
cargo build --profile release-runner -p duckle-runner -p duckle-mcp
base="target/release-runner"
fi
mkdir -p apps/desktop/bin
if [ "${{ runner.os }}" = "Windows" ]; then
cp "$base/duckle-runner.exe" apps/desktop/bin/duckle-runner.exe
cp "$base/duckle-mcp.exe" apps/desktop/bin/duckle-mcp.exe
else
cp "$base/duckle-runner" apps/desktop/bin/duckle-runner
cp "$base/duckle-mcp" apps/desktop/bin/duckle-mcp
fi
- name: Build raw Duckle binary
shell: bash
run: |
set -e
if [ -n "${{ matrix.target }}" ]; then
cargo build --release --target "${{ matrix.target }}" \
--manifest-path apps/desktop/Cargo.toml --features custom-protocol
else
cargo build --release \
--manifest-path apps/desktop/Cargo.toml --features custom-protocol
fi
- name: Stage release assets
shell: bash
run: |
set -e
mkdir -p _release
if [ -n "${{ matrix.target }}" ]; then
src="target/${{ matrix.target }}/release/${{ matrix.binary }}"
else
src="target/release/${{ matrix.binary }}"
fi
# The standalone desktop binary, named exactly as before so the
# in-app update check (apps/desktop/src/update_check.rs) and the
# README download table keep matching it. Single file, double-click.
# The duckle-runner is embedded into this binary at compile time
# (no separate with-runner asset needed).
cp "$src" "_release/${{ matrix.asset }}"
- name: Upload to release
shell: bash
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
REL_TAG: ${{ github.event.inputs.release_tag || github.ref_name }}
run: |
set -e
asset="${{ matrix.asset }}"
gh release upload "$REL_TAG" "_release/$asset" --clobber