RADIOCLI

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-run

verify: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 check

Publish:

npm login
npm publish ./output/release-evidence/ciphore-radiocli-0.2.5.tgz --access public

After 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 doctor

The 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.mjs

The 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:

PathEvidence to record
Interactive TUIStart a station, change stations, change volume, pause/resume where supported, then stop; verify audible output and keyboard response.
Background CLI and MCPPlay and inspect status, replace a pending tune, stop during startup, and confirm the native child exits.
Discovery and saved libraryApply minimum bitrate, load several search/country pages, save a favorite from another process, and confirm the open session refreshes.
AirPlay, where supported and enabledDiscover 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 supportedExercise a short alarm, snooze/dismiss, restore the previous output, and test media keys and suspend/wake on the actual desktop session.
Fallback playersSelect 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/radiocli

The 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.rb

Replace {{VERSION}} and {{SHA256}}, then verify:

brew install --build-from-source ./Formula/radiocli.rb
brew test radiocli
radiocli doctor

Windows 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 doctor

The 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 mpv

Linux 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/SUSE

ffplay 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.

On this page