Skip to content

IFM Build Pipeline

How the IFM distribution pieces are built, packaged, installed, and published.

This repository builds and packages the products that exist today:

ProductSourceArtifact
@ifm/sdkpackages/sdk/npm tarball (ifm-sdk-<v>.tgz)
ifm-clicrates/cli/native ifm binary (bin/ifm-<os>-<arch>; cargo install ifm-cli, Homebrew)
Station (web)packages/sdk/examples/station/static site web/ifm-station/
Relay (web)dashboard packages/sdk/examples/relay/static site web/ifm-relay/
Listener (web)packages/sdk/examples/listener/static site web/ifm-listener/
Landingpackages/landing/static site web/ifm-landing/
ifm-nodenative ifm binary (crates/cli)bin/ifm-<os>-<arch> (Docker packaging: see packaging.md)
IFM Station (desktop)packages/ifm-station/.dmg, .msi, .AppImage, .deb, .rpm
IFM Relay Station (desktop)packages/ifm-relay-station/.dmg, .msi, .AppImage, .deb, .rpm
IFM Radio Listener (desktop)packages/ifm-radio-listener/.dmg, .msi, .AppImage, .deb, .rpm
IFM Mobile Radio Listener (PWA)packages/ifm-pwa/Static site (HTML/JS/WASM + SW + manifest)

Scripts

ScriptPurpose
bun scripts/start.mjsSuper-easy local setup — runs relay + Station/Relay/Listener + Landing together
bun scripts/build.mjsBuild every piece in place (--skip-rust to skip the native binary)
bun scripts/release.mjsBuild + package everything into releases/v<version>/ with checksums
bash scripts/install.shUniversal CLI installer (npm / local release / GitHub Releases)
npm run devAlias for start.mjs
npm run releaseAlias for release.mjs

Quick Start

bash
# 1. One command — run the whole stack locally (web apps)
npm run dev
#    Station  http://localhost:5173/
#    Relay    http://localhost:5174/   hub at ws://localhost:8790/
#    Listener http://localhost:5175/
#    Landing  http://localhost:5176/

# 2. Build all distribution pieces
npm run build:all        # or: bun scripts/build.mjs --all
#    Includes: SDK, CLI, web demos, landing, desktop apps, PWA

# 3. Produce a release (tarballs + static sites + native binary + desktop + PWA + checksums)
npm run release            # → releases/v0.1.0/

# 4. Install the CLI from that release
bash releases/v0.1.0/install.sh --local releases/v0.1.0
ifm init && ifm connect && ifm tune 91.700

# 5. Install desktop apps (one command each)
curl -fsSL https://ifm.sh/install-station.sh | bash
curl -fsSL https://ifm.sh/install-relay-station.sh | bash
curl -fsSL https://ifm.sh/install-radio-listener.sh | bash

# 6. Access PWA
#    Open https://ifm.sh/pwa in mobile browser → "Add to Home Screen"

Release Layout

bun scripts/release.mjs produces releases/v<version>/:

text
releases/v0.1.0/
├── npm/
│   ├── ifm-sdk-0.1.0.tgz          # @ifm/sdk (npm publish-ready)
│   └── ifm-cli-0.1.0.tgz          # @ifm/cli  (npm publish-ready)
├── web/
│   ├── ifm-station/               # Station static build
│   ├── ifm-relay/                 # Relay dashboard static build
│   ├── ifm-listener/              # Listener static build
│   └── ifm-landing/               # Landing static build
├── bin/
│   └── ifm-darwin-arm64           # native ifm binary (platform-tagged)
├── desktop/
│   ├── ifm-station-darwin-arm64.dmg
│   ├── ifm-station-darwin-x64.dmg
│   ├── ifm-station-windows-x64.msi
│   ├── ifm-station-windows-arm64.msi
│   ├── ifm-station-linux-x64.AppImage
│   ├── ifm-station-linux-arm64.AppImage
│   ├── ifm-station_0.1.0_amd64.deb
│   ├── ifm-station-0.1.0.x86_64.rpm
│   ├── ifm-relay-station-darwin-arm64.dmg
│   ├── ... (same matrix for relay-station)
│   ├── ifm-radio-listener-darwin-arm64.dmg
│   ├── ... (same matrix for radio-listener)
│   └── checksums-desktop.txt
├── pwa/
│   ├── index.html
│   ├── manifest.json
│   ├── sw.js
│   ├── assets/
│   │   ├── index-<hash>.js
│   │   ├── index-<hash>.css
│   │   ├── ifm-sdk-<hash>.js
│   │   └── ifm-sdk_bg-<hash>.wasm
│   ├── icons/
│   └── version.json
├── install.sh                     # universal installer
├── checksums.txt                  # SHA-256 of every artifact
└── version.json                   # version + product manifest

Publishing (one command away)

Publishing requires credentials; the pipeline is built so the commands are trivial to run once you have them.

npm (@ifm/sdk)

bash
npm login                          # once
cd packages/sdk && npm publish --access public
# or, from a release:
npm publish releases/v0.1.0/npm/ifm-sdk-0.1.0.tgz

The package is pre-configured in its package.json (files, exports, engines) for publishing as-is. The CLI ships as the native ifm binary (crates/cli) — see CLI reference.

GitHub Release

Tagging triggers .github/workflows/release.yml, which builds everything and attaches the artifacts to the release:

bash
git tag -s v0.1.0 -m "Release v0.1.0"
git push origin v0.1.0

npm from CI

Add an NPM_TOKEN repository secret (a token with publish scope). The next v* tag then publishes both packages automatically; without the secret, the publish job is skipped and only the GitHub Release is produced.

Channels

channels.md defines the promotion ladder (nightly → beta → stable → lts). The scripts accept a version override (--version 0.2.0-beta.1) and everything flows through the same release.mjs path — only the tag/version and dist-tags differ. There is no automatic channel promotion yet; that is a Release Manager decision per channels.md.

Version Sync (automatic)

A release version is applied to every version-bearing manifest automatically (scripts/sync-versions.mjs):

  • all package.json files (root + sdk, cli, ifm-pwa, the three desktop apps)
  • each desktop app's src-tauri/tauri.conf.json — this is what the INSTALLED apps report (CFBundleShortVersionString, About dialog, installer filename). Without it, a release could bump package.json while the bundles keep advertising an old version.

The sync runs in all three release paths:

  • bun scripts/build-release.mjs 0.3.2-alpha — bumps both families up front
  • bun scripts/release.mjs --version 0.3.2-alpha — bumps both before building
  • bun scripts/build.mjs --desktop — syncs each app's tauri.conf.json from its own package.json right before bundling, so a plain desktop build can never ship a bundle with a stale version

All syncs are idempotent (they only write when the version actually changes).


CI

WorkflowTriggerWhat it does
.github/workflows/ci.ymlpush / PRcompiles SDK+CLI, runs CLI self test, builds web demos + landing, builds desktop apps, builds PWA, cargo test --workspace
.github/workflows/release.ymlv* tagsbuilds everything (SDK, CLI, web, desktop, PWA, native binary), attaches artifacts to GitHub Release, publishes npm when NPM_TOKEN configured
.github/workflows/desktop.ymlpush / PR (desktop paths)builds Tauri apps for all platforms, runs smoke tests
.github/workflows/pwa.ymlpush / PR (pwa paths)builds PWA, runs Lighthouse CI, deploys preview to Cloudflare Pages

Hosting the web apps

The demo apps are designed for the local flow above (dev servers + the vite /relay proxy). To serve the built static sites with a working relay, put the hub behind a reverse proxy that forwards /relay (HTTP probe + WebSocket upgrade) to the hub port — the same pattern the dev servers use. This is what a future ifm-pwa deployment (packaging.md §ifm-pwa) would ride on; no Cloudflare/Netlify configs are generated yet.

Released under the MIT License.