Skip to content

chore: bump version to 0.8.2 #3

chore: bump version to 0.8.2

chore: bump version to 0.8.2 #3

Workflow file for this run

name: Release deckbridge
# Trigger: push a tag like deckbridge-v1.2.3
# Or dispatch manually for a smoke-test dry run (skips GitHub Release by default).
on:
push:
tags:
- 'deckbridge-v*'
workflow_dispatch:
inputs:
skip_release:
description: 'Skip creating GitHub Release (dry run)'
required: false
default: 'true'
type: choice
options: ['true', 'false']
permissions:
contents: read
jobs:
build:
name: Build ${{ matrix.platform }}
strategy:
fail-fast: false
matrix:
include:
- os: macos-latest # Apple Silicon (arm64)
platform: macos-arm64
- os: macos-15-intel # Intel (x86_64)
platform: macos-x86_64
# Linux (ubuntu-22.04/linux-x86_64, ubuntu-24.04-arm/linux-arm64) temporarily
# disabled — mise-action's `mise install` blind-installs the unused
# github:lukasMega/txiki.js mise tool and fails on linux-arm64 (no upstream
# asset). See .claude/plans/2026-07-31_linux-build-support.md.
runs-on: ${{ matrix.os }}
# Release builds use the SLIM txiki.js runtime: built from source with
# sqlite3 + wasm/WAMR compiled out and symbols stripped (~2.3 MB smaller).
# tjs-setup reads this flag and runs scripts/tjs-build.mjs instead of
# downloading the prebuilt runtime. (Dev/local builds without the flag still
# use the no-toolchain download path.)
env:
TJS_FROM_SOURCE: '1'
steps:
- uses: actions/checkout@v5
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache Rust
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
rust/target
key: rust-${{ matrix.platform }}-${{ hashFiles('rust/**/Cargo.lock', 'rust/deckbridge-tray/**') }}
restore-keys: rust-${{ matrix.platform }}-
# Cache the slim txiki.js runtime so it builds once per platform per build
# config change (the from-source build clones + compiles txiki.js, which is
# slow). On a hit the binary is restored to $TJS and tjs-setup skips the
# rebuild. Key is invalidated by the pinned version (mise.toml) or the
# build script itself (tjs-build.mjs, e.g. its cmake flags).
- name: Cache slim txiki.js runtime
uses: actions/cache@v4
with:
path: vendor/txiki.js/build/tjs
key: tjs-slim-${{ matrix.platform }}-${{ hashFiles('mise.toml', 'scripts/tjs-build.mjs') }}
- name: Install libhidapi (macOS)
if: startsWith(matrix.os, 'macos')
run: brew install hidapi
# Linux: libhidapi for the app, plus the slim txiki.js build's system deps
# (TJS_FROM_SOURCE=1 compiles txiki from source). cmake + build-essential are
# normally preinstalled on the runner; libffi-dev (ffi.h) is the real gap —
# txiki's CMake find_path(ffi.h) is REQUIRED. macOS uses the preinstalled
# cmake + the Command Line Tools SDK libffi, so it needs no extra step.
- name: Install libhidapi + slim-build deps (Linux)
if: startsWith(matrix.os, 'ubuntu')
run: sudo apt-get update && sudo apt-get install -y libhidapi-dev libudev-dev libffi-dev cmake build-essential libgtk-3-dev libxdo-dev libayatana-appindicator3-dev
- uses: jdx/mise-action@v2
- name: Compile
run: mise run compile
# Ad-hoc sign deckbridge-tray only. The relay (deckbridge) is NOT re-signed:
# `tjs compile` appends the bytecode payload after the Mach-O and ships a
# linker-signed ad-hoc signature the kernel runs leniently; `codesign
# --force` would replace it and fail strict validation on the trailing data
# ("main executable failed strict validation"). The as-compiled signature is
# what ships.
- name: Ad-hoc sign deckbridge-tray (macOS)
if: startsWith(matrix.os, 'macos')
run: |
codesign --force --sign - --timestamp=none rust/target/release/deckbridge-tray
codesign -dv deckbridge 2>&1 | head -3
- name: Package
run: |
if [[ "$GITHUB_REF" == refs/tags/deckbridge-v* ]]; then
VERSION="${GITHUB_REF_NAME#deckbridge-}"
else
VERSION="dev"
fi
node scripts/package.mjs "$VERSION"
- name: Smoke-test deps (Linux)
if: startsWith(matrix.os, 'ubuntu')
run: sudo apt-get install -y netcat-openbsd unzip curl
- name: Smoke test (boots cleanly, serves, shuts down)
run: ./scripts/e2e-smoke.sh "$(find dist -maxdepth 1 -name '*.zip' -printf '%T@ %p\n' | sort -rn | head -1 | cut -d' ' -f2-)"
- uses: actions/upload-artifact@v7
with:
name: ${{ matrix.platform }}
path: dist/*.zip
if-no-files-found: error
# Windows is packaged differently: instead of a raw zip, the binary is wrapped
# in a Tauri v2 supervisor shell (src-tauri/) and shipped as an NSIS installer
# (Scoop/winget-friendly, no Gatekeeper/SmartScreen friction via Scoop). The
# relay binary + deckbridge-tray ride along as Tauri sidecars (externalBin), so they
# must be renamed to the MSVC target triple before `cargo tauri build` (G2).
build-windows:
name: Build windows-x86_64 (Tauri NSIS)
runs-on: windows-latest
# No TJS_FROM_SOURCE here (unlike the Unix jobs above): the slim from-source
# build's Windows branch (scripts/tjs-build.mjs) is unverified against
# MSVC-only runners. tjs-setup instead downloads the official saghul/txiki.js
# prebuilt `txiki-windows-x86_64.zip` (confirmed to exist for the pinned
# TXIKI_VERSION — see the plan's Phase 0 findings) via
# scripts/tjs-download.mjs's win32 branch. Working beats slim for v1.
steps:
- uses: actions/checkout@v5
# Stamps ts/package.json's version (the single source of truth) into
# src-tauri/Cargo.toml + tauri.conf.json so the NSIS installer version
# is correct without a build-time -c override.
- name: Sync version into Cargo.toml/tauri.conf.json
run: node scripts/sync-version.mjs
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache Rust
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
rust/target
src-tauri/target
key: rust-windows-x86_64-${{ hashFiles('rust/**/Cargo.lock', 'rust/deckbridge-tray/**', 'src-tauri/Cargo.lock') }}
restore-keys: rust-windows-x86_64-
- name: Cache txiki.js runtime
uses: actions/cache@v4
with:
path: vendor/txiki.js/build/tjs.exe
key: tjs-windows-x86_64-${{ hashFiles('mise.toml') }}
# Windows ships no system hidapi.dll (B3) — build it from source and point
# HIDAPI_LIB at the result so ts/build.mjs's embed step (Phase 2) can find
# it; without this the compile step below hard-errors by design (a
# Windows binary with no HID backend would silently fail at runtime).
- name: Build hidapi.dll
shell: pwsh
run: |
git clone --depth 1 --branch hidapi-0.15.0 https://github.com/libusb/hidapi C:\hidapi-src
cmake -B C:\hidapi-build -S C:\hidapi-src
cmake --build C:\hidapi-build --config Release
$dll = Get-ChildItem -Path C:\hidapi-build -Filter hidapi.dll -Recurse | Select-Object -First 1
if (-not $dll) { Write-Error "hidapi.dll not found after build"; exit 1 }
echo "HIDAPI_LIB=$($dll.FullName)" >> $env:GITHUB_ENV
- uses: jdx/mise-action@v2
# Produces the Windows relay binary (deckbridge[.exe]) via the same build
# chain the other platforms use. txiki's `compile` emits a PE executable.
- name: Compile relay binary
run: mise run compile
# Boot the just-compiled binary in mock mode (no HID device needed),
# poll the WebUI until it responds, then kill it — mirrors the
# boot/serve/shutdown checks in scripts/e2e-smoke.sh (bash, POSIX-only)
# for the platform that script can't run on.
- name: Smoke test (mock mode, poll WebUI, then kill)
shell: pwsh
run: |
$relay = if (Test-Path "deckbridge.exe") { "deckbridge.exe" } else { "deckbridge" }
$env:DECKBRIDGE_MOCK = "1"
$proc = Start-Process -FilePath ".\$relay" -PassThru `
-RedirectStandardOutput "smoke-stdout.log" -RedirectStandardError "smoke-stderr.log"
$ready = $false
for ($i = 0; $i -lt 60; $i++) {
try {
$resp = Invoke-WebRequest -Uri "http://localhost:3000" -UseBasicParsing -TimeoutSec 2
if ($resp.StatusCode -eq 200) { $ready = $true; break }
} catch {}
if ($proc.HasExited) {
Write-Host "FAIL: process exited before becoming ready"
Get-Content smoke-stderr.log -ErrorAction SilentlyContinue
exit 1
}
Start-Sleep -Milliseconds 500
}
if (-not $ready) {
Write-Host "FAIL: WebUI never responded with 200 after 30s"
Get-Content smoke-stderr.log -ErrorAction SilentlyContinue
Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue
exit 1
}
Write-Host "Smoke test OK: WebUI responded 200"
Stop-Process -Id $proc.Id -Force
# deckbridge-tray.exe — the system-tray sidecar (same Rust crate as macOS/Linux).
- name: Build deckbridge-tray
run: mise run tray-rs
# Tauri's bundler only picks up the sidecar whose filename matches the
# build's target triple (G2), so copy both binaries into src-tauri/binaries/
# with the -x86_64-pc-windows-msvc.exe suffix. `mise run compile` writes
# either deckbridge or deckbridge.exe depending on txiki; handle both.
- name: Stage sidecars for Tauri (target-triple names)
shell: pwsh
run: |
$relay = if (Test-Path "deckbridge.exe") { "deckbridge.exe" } else { "deckbridge" }
Copy-Item $relay "src-tauri/binaries/deckbridge-x86_64-pc-windows-msvc.exe" -Force
Copy-Item "rust/target/release/deckbridge-tray.exe" "src-tauri/binaries/deckbridge-tray-x86_64-pc-windows-msvc.exe" -Force
Get-ChildItem "src-tauri/binaries/"
- name: Install Tauri CLI
run: cargo install tauri-cli --version "^2" --locked
# tauri.conf.json's version was already stamped from ts/package.json by
# the "Sync version" step above — no build-time override needed.
# NSIS is fetched by the bundler; WebView2 is preinstalled on the runner.
- name: Build Tauri NSIS installer
shell: pwsh
working-directory: src-tauri
run: cargo tauri build
- uses: actions/upload-artifact@v7
with:
name: windows-x86_64
path: src-tauri/target/release/bundle/nsis/*-setup.exe
if-no-files-found: error
# macOS Tauri .dmg (ADDITIVE — the zip artifacts in the `build` job above stay
# the Homebrew/manual path). Model A: the relay binary + deckbridge-tray ride along as
# Tauri sidecars (externalBin), renamed to the apple-darwin target triple (G2),
# ad-hoc signed (no Apple Developer ID). The macOS bundle config lives in
# src-tauri/tauri.macos.conf.json (targets app,dmg; signingIdentity "-";
# hardenedRuntime false — keeps the relay's extract+dlopen of native libs
# working, plan gotcha G3). Downloads are unsigned vs Gatekeeper → right-click
# "Open" or `xattr -dr com.apple.quarantine` (documented in README-DISTRIBUTION).
build-macos-tauri:
name: Build ${{ matrix.platform }} (Tauri dmg)
strategy:
fail-fast: false
matrix:
include:
- os: macos-latest # Apple Silicon (arm64)
platform: macos-arm64
triple: aarch64-apple-darwin
- os: macos-15-intel # Intel (x86_64)
platform: macos-x86_64
triple: x86_64-apple-darwin
runs-on: ${{ matrix.os }}
env:
TJS_FROM_SOURCE: '1'
steps:
- uses: actions/checkout@v5
# Stamps ts/package.json's version (the single source of truth) into
# src-tauri/Cargo.toml + tauri.conf.json so the dmg version is correct
# without a build-time -c override.
- name: Sync version into Cargo.toml/tauri.conf.json
run: node scripts/sync-version.mjs
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache Rust
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
rust/target
src-tauri/target
key: rust-tauri-${{ matrix.platform }}-${{ hashFiles('rust/**/Cargo.lock', 'rust/deckbridge-tray/**', 'src-tauri/Cargo.lock') }}
restore-keys: rust-tauri-${{ matrix.platform }}-
- name: Cache slim txiki.js runtime
uses: actions/cache@v4
with:
path: vendor/txiki.js/build/tjs
key: tjs-slim-${{ matrix.platform }}-${{ hashFiles('mise.toml', 'scripts/tjs-build.mjs') }}
- name: Install libhidapi (macOS)
run: brew install hidapi
- uses: jdx/mise-action@v2
- name: Compile relay binary
run: mise run compile
- name: Build deckbridge-tray
run: mise run tray-rs
# Tauri's bundler only picks up the sidecar whose filename matches the
# build's target triple (G2). `mise run compile` writes `deckbridge`.
- name: Stage sidecars for Tauri (apple-darwin triple)
run: |
mkdir -p src-tauri/binaries
cp deckbridge "src-tauri/binaries/deckbridge-${{ matrix.triple }}"
cp rust/target/release/deckbridge-tray "src-tauri/binaries/deckbridge-tray-${{ matrix.triple }}"
# cp can leave a non-executable file if the dest already exists; ensure
# both sidecars are executable so they spawn from inside the .app.
chmod +x "src-tauri/binaries/deckbridge-${{ matrix.triple }}" "src-tauri/binaries/deckbridge-tray-${{ matrix.triple }}"
ls -la src-tauri/binaries/
# Ad-hoc sign deckbridge-tray only. The relay is left with its as-compiled
# linker-signed signature — re-signing a `tjs compile` binary (payload
# appended after the Mach-O) fails strict validation. The Tauri bundler does
# NOT codesign the app either (tauri.macos.conf.json sets no signingIdentity),
# since its deep-sign pass would hit the same wall on the relay sidecar; each
# Mach-O ships its own ad-hoc signature instead.
- name: Ad-hoc sign deckbridge-tray sidecar
run: |
codesign --force --sign - --timestamp=none "src-tauri/binaries/deckbridge-tray-${{ matrix.triple }}"
- name: Install Tauri CLI
run: cargo install tauri-cli --version "^2" --locked
# tauri.conf.json's version was already stamped from ts/package.json by
# the "Sync version" step above — no build-time override needed.
# --bundles dmg is belt-and-suspenders; tauri.macos.conf.json already sets
# targets app,dmg (its array REPLACES the base ["nsis"] via RFC 7396 merge).
- name: Build Tauri dmg
working-directory: src-tauri
run: cargo tauri build --bundles dmg
- uses: actions/upload-artifact@v7
with:
name: ${{ matrix.platform }}-dmg
path: src-tauri/target/release/bundle/dmg/*.dmg
if-no-files-found: error
release:
name: Publish GitHub Release
needs: [build, build-windows, build-macos-tauri]
if: github.event_name == 'push' || inputs.skip_release == 'false'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
# Guards against a stray/manual tag diverging from ts/package.json — the
# normal path is auto-tag.yml, which only ever tags the current version.
- uses: actions/checkout@v5
if: github.event_name == 'push'
- name: Verify tag matches ts/package.json version
if: github.event_name == 'push'
run: |
TAG_VERSION="${GITHUB_REF_NAME#deckbridge-v}"
PKG_VERSION=$(node -p "require('./ts/package.json').version")
if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
echo "::error::tag $GITHUB_REF_NAME (version $TAG_VERSION) does not match ts/package.json version $PKG_VERSION"
exit 1
fi
- uses: actions/download-artifact@v4
with:
path: artifacts
merge-multiple: true
- name: Generate SHA256SUMS
run: |
cd artifacts
sha256sum ./*.zip ./*-setup.exe ./*.dmg > SHA256SUMS.txt
cat SHA256SUMS.txt
- name: Create release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
name: "deckbridge ${{ github.ref_name }}"
body: |
## Install
See [README-DISTRIBUTION.md](https://github.com/${{ github.repository }}/blob/main/README-DISTRIBUTION.md) for full setup instructions.
**macOS (Apple Silicon):** `deckbridge.app` via `*_aarch64.dmg` (drag-install) — or Homebrew, or `deckbridge-*-macos-arm64.zip`
**macOS (Intel):** `deckbridge.app` via `*_x64.dmg` (drag-install) — or Homebrew, or `deckbridge-*-macos-x86_64.zip`
**Windows (x86_64):** download `deckbridge_*-setup.exe` (NSIS installer)
Linux builds are temporarily unavailable — coming back soon.
macOS: open the `.dmg` and drag the app to Applications. The build is **unsigned** (no Apple Developer ID), so the first launch needs **right-click → Open** (or `xattr -dr com.apple.quarantine /Applications/deckbridge.app`). Homebrew (`brew install`) avoids this entirely.
macOS zips: extract and run `./run.sh`. libhidapi is bundled — no prerequisite installs.
Windows: run the `-setup.exe` installer (or `scoop install deckbridge`).
files: |
artifacts/*.zip
artifacts/*-setup.exe
artifacts/*.dmg
artifacts/SHA256SUMS.txt
fail_on_unmatched_files: true