Release Packaging
How RadioCLI is published through npm and Homebrew.
RadioCLI uses two distribution lanes:
- npm for the scoped Node CLI package:
@ciphore/radiocli - Homebrew for the one-command macOS install path:
ciphore/tap/radiocli
npm, pnpm, and Bun can install the npm package. The shipped executable still
runs with Node.js, and package-manager installs should not install system
packages from lifecycle scripts. The explicit, user-invoked radiocli setup
command may install selected native tools after displaying and confirming its
plan. Native playback and interactive controls come
from mpv, with ffplay from FFmpeg as an optional playback-only fallback.
Install a compatible Node runtime and a native player before using local audio.
The platform matrix records the verified OS/CPU combinations,
runtime minimums, package routes, and limitations. npm installation alone does
not establish native playback or desktop integration.
NPM Release
Before publishing:
npm ci
npm run verify:release
npm pack --dry-runverify:release requires a clean committed checkout. It records each completed
gate, requires native mpv in both packed installation modes, and saves the exact
tested tarball under output/release-evidence/. Its manifest links the source
revision and dependency lockfile digest to the tarball SHA-256, runtime, audit,
and test outcomes. Publish that preserved tarball after reviewing the release
evidence; repacking would create a candidate that needs its own verification.
Set RADIOCLI_SMOKE_EVIDENCE_DIR to choose another destination outside the
tracked source tree. Failed or interrupted checks cannot produce a complete
manifest. An unavailable npm audit now exits unsuccessfully and records
unavailable; it must be rerun successfully before release.
The local release gate also requires built TUI crash recovery and installs, audits, typechecks, and builds the documentation site. This catches documentation security or build failures before they can fail the release's CI run.
Publishing from a source directory runs the same verify:release gate through
prepublishOnly. Publishing a preserved tarball still requires reviewing its
recorded evidence; npm cannot rerun the source verification from that artifact.
Test the packed tarball locally:
VERSION=0.2.5
npm pack
npm install -g "./ciphore-radiocli-$VERSION.tgz"
radiocli --help
radiocli doctor
radiocli checkPublish:
npm login
npm publish ./output/release-evidence/ciphore-radiocli-0.2.5.tgz --access publicAfter publishing, npm install -g @ciphore/radiocli should install the
executable and all JavaScript dependencies. Users can run radiocli setup for
guided native-tool installation or radiocli doctor for read-only diagnostics.
Optional client smoke checks:
pnpm add -g @ciphore/radiocli
bun add -g @ciphore/radiocli
radiocli doctorThe fresh-install harness builds one tarball, installs it into separate temporary
projects with normal dependencies and with --omit=optional, and executes only
that installed artifact. It checks CLI/doctor, offline cached providers, library
import/export, and MCP status. Its temporary paths contain
spaces and non-ASCII characters. Require a real native mpv for release evidence:
npm run fresh:check -- --require-mpv
node --test scripts/packed-smoke.test-node.mjs scripts/install-smoke-mpv.test-node.mjsThe playback check generates a local WAV, uses mpv's null audio output, and
verifies readiness, IPC volume, pause, resume, and stop. It does not prove physical
audio-device output, a receiver connection, wake delivery, or a particular
terminal application. Set RADIOCLI_SMOKE_EVIDENCE_DIR to preserve JSON records
containing the artifact SHA-256, runtime identity, installation mode, and results.
Set RADIOCLI_MPV_PATH when the verified native executable is outside PATH.
The evidence directory also retains the tested tarball and a candidate.json
source record. --require-clean rejects a dirty or unknown source state when
running the fresh-install harness directly.
The same harness upgrades separate installations of published versions 0.2.3
and 0.2.4 to the candidate tarball. It keeps each older storage implementation
running throughout the replacement, then checks all 63 supported receivers while
old and new processes read and write one library. Favorites, recent stations,
track/search history, alarms, and the previously selected receiver must survive.
Each upgrade-<version>.json record is bound to the tested artifact, source, and
runtime; missing or failed upgrade evidence blocks a complete release manifest.
The twelve removed preview scenes fall back to Ultracode when loading a library, a preview receiver-preference file, or a JSON backup. Favorites, history, alarms, and other settings remain intact. Removed scenes are absent from cycling and MCP appearance choices.
Device Rehearsal
The manifest covers one OS, CPU, and Node runtime. Review all required CI matrix jobs for the same source revision and retain their artifacts before publication. A local or CI null-output pass leaves physical speakers, headphones, receiver connections, terminal applications, and suspend/wake delivery unverified.
The required smoke:network check uses only loopback fixtures and null/dummy
audio. It exercises trusted and untrusted certificates, hostname mismatches,
redirects, live EOF, open-but-stalled streams, finite recordings, and Stop after
recovery. Installed fallbacks also exercise decoded readiness, ordinary local
and HTTP media, and blocked unsafe nested requests. The dedicated Linux jobs
require ffplay, FFmpeg and VLC rather than silently skipping missing fallbacks.
Numeric-IP certificate mismatch behavior is recorded as a known upstream
limitation when the native TLS backend accepts it.
The pinned Windows player uses Schannel, which ignores a private CA file.
Windows still requires untrusted-certificate, redirect and nested-request
rejection, finite HTTP playback, recovery and Stop checks. Trusted private-CA
playback and trusted DNS/IP identity fixtures remain explicitly unverified
there; the harness never installs test roots into the user's trust store.
RADIOCLI_SMOKE_PRIVATE_CA=1 opts into those additional fixtures on a Windows
test installation known to use a CA-file-capable backend. The per-check outcomes
are retained in network-playback.json and bound to the release revision/runtime.
On each intended device and output route, retain the candidate SHA-256 and
radiocli doctor --playback --json, then record observed results for these paths:
| Path | Evidence to record |
|---|---|
| Interactive TUI | Start a station, change stations, change volume, pause/resume where supported, then stop; verify audible output and keyboard response. |
| Background CLI and MCP | Play and inspect status, replace a pending tune, stop during startup, and confirm the native child exits. |
| Discovery and saved library | Apply minimum bitrate, load several search/country pages, save a favorite from another process, and confirm the open session refreshes. |
| AirPlay, where supported and enabled | Discover and authenticate the actual receiver, tune twice on the same receiver, change volume, return to local output, and verify cleanup. |
| Alarms and desktop integration, where supported | Exercise a short alarm, snooze/dismiss, restore the previous output, and test media keys and suspend/wake on the actual desktop session. |
| Fallback players | Select installed ffplay or VLC, confirm audible playback and stop, and retain the documented limited-control behavior. |
Record passed, failed, or unverified with the OS/CPU, player version, output
device or receiver, and terminal application. A route that was not exercised
retains unverified; success on a different device does not fill that gap.
Release Gates And Experimental Checks
The required CI matrix preserves Ubuntu, macOS, and Windows on Node 22/24 and adds Linux arm64, macOS Intel, and Windows 11 arm64. All 12 jobs require packed mpv smoke checks in both installation modes; Linux also has source playback checks. Documentation checks run on Node 22/24. All required jobs must pass on the release candidate before publication; a failed required job blocks release. Inspect the recorded runner OS/version and CPU before carrying evidence into a release. Repository protection settings and publication remain owner-managed.
The stable Required Release Checks job aggregates the complete verification, native playback and documentation matrices. Maintainers should select this check in branch protection after the workflow has run. The repository files do not activate remote protection settings. Evidence completeness also requires the source MCP, background CLI and native network checks; a skipped or absent check cannot produce a passing release manifest.
All workflow actions use immutable commit pins. macOS and Windows playback fixtures come from explicitly selected official mpv release assets, checked against pinned SHA-256 values and native CPU headers before execution. Linux uses its runner's distribution package. These are CI fixtures, not packages installed by npm or a new public distribution channel.
Weekly or manually requested FreeBSD, OpenBSD, and NetBSD checks boot full QEMU guest kernels, install the Linux-built JavaScript tarball with guest-native Node and mpv, and label the result as guest verification. They do not infer BSD behavior from a Linux container, and they do not establish foreign-CPU or desktop audio support. The evidence matrix records actual outcomes and remaining verification gaps.
Do not publish a release with a failed required job. An experimental job failure must remain visible and its platform tier must retain the corresponding limit. Repository protection settings, registry access, and release ownership remain under maintainer control.
Homebrew Tap
The public tap is Ciphore/homebrew-tap, and users install with:
brew install ciphore/tap/radiocliThe reusable formula template lives in packaging/homebrew/radiocli.rb.template.
After each npm publish, fetch the npm tarball URL and SHA:
VERSION=0.2.5
TARBALL="$(npm view @ciphore/radiocli@$VERSION dist.tarball)"
curl -L "$TARBALL" -o "/tmp/radiocli-$VERSION.tgz"
shasum -a 256 "/tmp/radiocli-$VERSION.tgz"Copy the template into a separate homebrew-tap repository:
homebrew-tap/
Formula/
radiocli.rbReplace {{VERSION}} and {{SHA256}}, then verify:
brew install --build-from-source ./Formula/radiocli.rb
brew test radiocli
radiocli doctorWindows And Linux Native Playback
Windows x64 setup uses native Node.js and native mpv:
winget install --id OpenJS.NodeJS.LTS -e
winget install --id shinchiro.mpv -e
npm install -g @ciphore/radiocli
radiocli doctorThe shinchiro.mpv WinGet installer does not currently add its MPV Player
directory to PATH. RadioCLI resolves that standard installation directly and
also checks registered Windows application locations. Doctor should report an
mpv_path, its mpv_discovery source, and mpv_launch=ready.
The inspected WinGet mpv recipe is x64-only. Scoop's mpv recipe includes x64 and
arm64 builds; verify the installed executable matches the intended runtime.
Windows x86 remains a Node 22 route requiring separate player and native evidence.
Scoop users can install mpv with:
scoop bucket add extras
scoop install mpvLinux users should install the distro mpv package before the npm package:
sudo apt install mpv # Debian/Ubuntu
sudo dnf install mpv # Fedora/RHEL-like
sudo pacman -S mpv # Arch/Manjaro
sudo apk add mpv # Alpine
sudo zypper install mpv # openSUSE/SUSEffplay may be installed as an optional fallback through FFmpeg, but release
smoke checks should treat it as playback-only. radiocli doctor reports
controls=limited when only ffplay is available and controls=full when
mpv is available.
AirPlay is experimental on macOS and native Linux. Its optional sender dependency
must install and load successfully, and Bonjour or Avahi plus FFmpeg must be
usable. Linux additionally requires the per-library opt-in from
radiocli setup --airplay; installed dependencies do not enable it. The Homebrew
formula installs FFmpeg; that does not establish a working receiver connection.
The fixed sender source ships with RadioCLI; its runtime dependencies remain
optional. The required runtime audit includes those optional dependencies and
fails on advisories at every severity. AirPlay should remain an
explicit session output rather than the automatic startup fallback.
Linux CI includes real D-Bus/mpv-MPRIS checks, isolated PipeWire/PulseAudio output restoration, and Shairport Sync software receiver checks for discovery, decoded PCM, password entry, volume/mute, retuning, metadata, and cleanup. These tests do not establish a Quattro desktop, audible speakers, physical receiver compatibility, or suspend/wake delivery. Fresh and upgraded Quattro desktop acceptance and physical macOS/Linux receiver regression remain release gates for a broader verified-support claim; keep the desktop integration experimental meanwhile.
To roll back the desktop bridge, set RADIOCLI_DESKTOP_MEDIA=off and restart
RadioCLI. A 0.2.4 library rewrite can discard the additive desktopMediaControls
setting, causing it to default to auto after upgrading again; the environment
override survives that downgrade. Existing station/library data stays compatible.
After changing executable paths or restoring an older installation, run
radiocli alarm sync and verify alarm setup again.