NC Audio · Docs

Releasing Audio

Every push to the release branch runs .github/workflows/release.yml. The workflow builds signed installers for macOS, Windows, Linux, FreeBSD and the web, then creates or updates a draft GitHub Release named Audio v<version>. Nobody sees a draft until a maintainer publishes it.

This is Audio's implementation of the shared release playbook. User-facing names say Audio. Files, binaries and ids stay lowercase (audio-<version>-<platform>-<arch>.<ext>, com.ncdev.audio).

Cutting a release

  1. Bump the version on main. The only place it lives is [workspace.package] version in the root Cargo.toml; the app reads it from CARGO_PKG_VERSION (About Audio, audio --version, audio-cli --version).

    cargo xtask version                 # prints the current version, e.g. 0.1.0
    cargo xtask version set 0.2.0       # or 0.2.0-rc.1; updates Cargo.toml and Cargo.lock

    Commit the change (Cargo.toml + Cargo.lock) through the normal review flow.

  2. Merge main into release (or fast-forward it) and push. The workflow starts by itself.

  3. Wait for the draft. After about 30 to 60 minutes (notarization and the FreeBSD VM are the slow parts), the Releases page has a draft Audio v0.2.0, tagged v0.2.0 on the pushed commit, with every artifact and SHA256SUMS.txt. The notes are generated from the merged PRs.

  4. Check it. Download an installer or two and look at the job summaries. Any ::warning:: there means a signing secret was missing and that artifact is unsigned.

  5. Publish the draft in the GitHub UI. Publishing creates the v0.2.0 tag. Versions with a pre-release suffix (-rc.1) are marked as pre-releases.

Pushing to release again before you publish rebuilds the same draft and replaces its assets. After the draft is published, the workflow refuses to touch that version again, so bump it first.

Test runs: Actions → Release → Run workflow runs the whole pipeline by hand. The optional version input (such as 0.2.0-rc.1) overrides Cargo.toml for that run only. The jobs apply it with cargo xtask version set before building, so the binaries report it too. Only the signing jobs (macOS, Windows) and the draft-release job use the release environment, which only the release branch can use. On any other branch the run is a dry run: Linux, Flatpak, FreeBSD and web build and are checked, the signing jobs are refused, and no release is drafted.

What gets built

Platform Artifacts Built on
macOS 11+ (universal: Apple silicon + Intel) audio-<v>-macos-universal.dmg, audio-cli-<v>-macos-universal.zip macos-15
Windows 10+ x64 audio-<v>-windows-x64.msi, audio-<v>-windows-x64-portable.zip windows-latest
Windows 10+ x86 (32-bit) audio-<v>-windows-x86.msi, audio-<v>-windows-x86-portable.zip windows-latest
Windows 11 ARM64 audio-<v>-windows-arm64.msi, audio-<v>-windows-arm64-portable.zip windows-latest (cross-compiled; windows-arm64.yml installs and runs it on ARM64)
Linux x86_64 audio-<v>-linux-x86_64.{AppImage,deb,rpm,tar.gz} ubuntu-22.04
Linux aarch64 audio-<v>-linux-aarch64.{AppImage,deb,rpm,tar.gz} ubuntu-22.04-arm
Linux riscv64 audio-<v>-linux-riscv64.tar.gz (glibc 2.39+) ubuntu-24.04 (cross-compiled; CLI smoke-tested under QEMU)
AppImage updates audio-<v>-linux-{x86_64,aarch64}.AppImage.zsync with the AppImage
Flatpak x86_64, aarch64 audio-<v>-linux-{x86_64,aarch64}.flatpak ubuntu-24.04, ubuntu-24.04-arm (repackages the Linux tarball)
FreeBSD 14 x86_64 audio-<v>-freebsd-x86_64.tar.gz (a /usr/local tree) FreeBSD 14.3 VM on ubuntu-latest
Web audio-web-<v>.zip (static site; see packaging/web/README.md) ubuntu-latest

The from-source Flatpak manifest (packaging/linux/flatpak/com.ncdev.audio.yml) is kept ready for a Flathub submission; the release's .flatpak bundles come from com.ncdev.audio.bundle.yml (see Linux below).

Audio libraries

Audio plays and records through cpal: CoreAudio on macOS, WASAPI on Windows, ALSA on Linux and FreeBSD (PulseAudio and PipeWire through their ALSA plugins), and WebAudio in the browser.

macOS

packaging/macos/package.sh builds aarch64-apple-darwin and x86_64-apple-darwin with MACOSX_DEPLOYMENT_TARGET=11.0, joins them with lipo, and assembles Audio.app:

Locally, without certificates, the script signs ad-hoc (codesign -s -) and skips notarization:

packaging/macos/package.sh                    # universal; needs both rustup targets
packaging/macos/package.sh --arch aarch64     # quicker, host-only
open dist/release/audio-*-macos-*.dmg

Windows

packaging/windows/package.ps1 -Arch x64|x86|arm64 builds with -C target-feature=+crt-static (in CARGO_TARGET_<TRIPLE>_RUSTFLAGS, so host build scripts aren't affected), so neither the MSI nor the portable zip needs the Visual C++ redistributable.

Locally on Windows: dotnet tool install -g wix --version 5.0.2, then pwsh packaging/windows/package.ps1 -Arch x64.

Linux

packaging/linux/package.sh stages one FHS tree and makes every format from it: both binaries, com.ncdev.audio.desktop (with MimeType= for sessions, WAV, AIFF, FLAC, MP3 and MIDI), hicolor icons from 16 px to 512 px plus a scalable SVG, AppStream metainfo, and a shared-mime-info file declaring application/x-audio-session (*.aproj).

Binaries are built on Ubuntu 22.04 and need glibc ≥ 2.35. X11, Wayland, xkbcommon, Vulkan and EGL are loaded at runtime, so the packages declare them as dependencies or recommendations.

Locally (on Linux): install nfpm, then packaging/linux/package.sh (or --formats "deb tar").

FreeBSD

GitHub has no FreeBSD runners, so the freebsd job builds inside a FreeBSD 14.3 VM (vmactions/freebsd-vm, pinned by commit) and runs packaging/freebsd/package.sh, which stages a /usr/local-style tree (binaries, desktop entry, MIME type, metainfo, icons, docs) as a .tar.gz. Install with tar -xzf audio-*-freebsd-x86_64.tar.gz --strip-components 1 -C /usr/local. .github/workflows/freebsd.yml builds and tests the workspace there on every push to main.

Web

packaging/web/package.sh runs trunk build --release in apps/audio-web (output in dist/web, public_url = "./") and zips the site with sample _headers and .htaccess files and the hosting guide. apps/audio-web runs the same SoundApp as the desktop app on the synthesised demo session, rendering with wgpu (WebGPU, or WebGL2 with ?webgl) and playing through WebAudio.

Secrets

All secrets live in the repository's release environment (Settings → Environments → release), restricted to the release branch. Only the jobs that sign (macOS, Windows) and the draft-release job declare environment: release; the others get no secrets. Each secret is optional: if one is missing, that platform's artifacts are unsigned and the run shows a ::warning::. Scripts never echo secret values; certificates are written with umask 077 and deleted in always()/finally steps.

Secret Used for
APPLE_CERTIFICATE base64 .p12 with "Developer ID Application: ()"
APPLE_CERTIFICATE_PASSWORD password for that .p12
KEYCHAIN_PASSWORD password for the temporary CI keychain (random if unset)
APPLE_ID Apple ID used by notarytool
APPLE_PASSWORD app-specific password for that Apple ID
APPLE_TEAM_ID DJ6XS33FX8
WINDOWS_CERTIFICATE / WINDOWS_CERTIFICATE_PASSWORD base64 .pfx (unused: Windows uses Azure)
AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET service principal for Azure Trusted Signing
AZURE_SIGNING_ENDPOINT, AZURE_SIGNING_ACCOUNT, AZURE_CERT_PROFILE Trusted Signing endpoint, account and profile

GITHUB_TOKEN creates the release. Only the final job gets contents: write. Provisioning the environment, ruleset and secrets for a new repo: craftrules release/signing-setup.md.

Icons

assets/app-icon/icon.svg is the canonical icon: a placeholder singing nightingale, drawn as original SVG in the three-colour icon palette (ink, paper, Audio teal #14a9c4); see assets/app-icon/README.md. packaging/icons.sh regenerates the 1024 px PNG, the macOS 512 px window icon, the .icns, the .ico (packed by cargo xtask ico) and the hicolor PNGs from it. It needs resvg, plus iconutil on macOS. The outputs are committed, so packaging never needs those tools. apps/audio/src/main.rs sets the runtime window/Dock/taskbar icon from them.

Checks

.github/workflows/packaging-lint.yml runs in seconds on any change to packaging/, the workflows or the icons: actionlint, shellcheck, a PowerShell parse, xmllint (WiX, plists, AppStream, MIME), desktop-file-validate, appstreamcli validate, and a check that the two Flatpak manifests agree on runtime, finish-args and plugin extensions.