DIY-Avatar image builder: live USB + installer ISO pipelines + cross-platform desktop imager GUI
  • TypeScript 60.1%
  • Shell 25%
  • Svelte 12.4%
  • JavaScript 2.2%
  • HTML 0.3%
Find a file
Thorsten Bus 530ebed623
Some checks failed
imager / build (--linux, false, linux) (push) Failing after 19s
imager / build (--mac, true, macos) (push) Successful in 1s
imager / build (--win, true, windows) (push) Successful in 1s
images / compute (push) Successful in 15s
images / build-live (amd64, , false) (push) Has been skipped
images / build-live (arm64, , true) (push) Has been skipped
images / build-iso (amd64, false) (push) Has been skipped
images / build-iso (arm64, true) (push) Has been skipped
images / finalize (push) Has been skipped
ci: migrate image + imager pipelines to semantic-release
Replace the ad-hoc, manually versioned CI with two independent
semantic-release streams so releases are cut from the commit history
instead of by hand.

Pipelines: merge the separate kiosk-iso.yml and live-image.yml
workflows into a single images.yml. Both built OS images and already
shared triggers, runners and publishing logic, so keeping them apart
duplicated the release plumbing and made it impossible to version them
together.

Releases: add two streams isolated by commit scope --
.releaserc.images.json (tagFormat images-v${version}, scopes
live-image/os-install, changelog CHANGELOG.images.md) and
imager/.releaserc.json (tagFormat imager-v${version}, scope imager,
changelog imager/CHANGELOG.md). Scope-filtered release rules keep a
commit in one area from bumping the other stream. Supporting scripts:
scripts/seed-version-tags.sh for the one-time 0.0.0 seed tags,
imager/scripts/publish-imager-release.sh and
imager/scripts/extract-release-notes.mjs for the Forgejo asset upload,
and imager/scripts/release-mac-local.sh for local macOS builds.

Fix the Forgejo `act` parse failure: `continue-on-error` is unmarshalled
into a bool, so the `${{ matrix.gated }}` expression previously used
there aborted parsing of the whole workflow. Replaced with a gate-step
pattern that evaluates the condition in an `if:` instead.

Gate the optional legs (live-image arm64, imager windows/macos) so they
no-op green until their runners exist, and resolve the monorepo checkout
to wiederdabei/avatar@master.

Add a root .gitignore for the ephemeral npm project the images workflow
creates in the repository root, and per-target electron-builder artifact
names for the new Windows nsis/portable and macOS dmg/zip outputs.
2026-08-11 21:59:47 +02:00
.forgejo/workflows ci: migrate image + imager pipelines to semantic-release 2026-08-11 21:59:47 +02:00
imager ci: migrate image + imager pipelines to semantic-release 2026-08-11 21:59:47 +02:00
live-image ci: migrate image + imager pipelines to semantic-release 2026-08-11 21:59:47 +02:00
os-install ci: migrate image + imager pipelines to semantic-release 2026-08-11 21:59:47 +02:00
scripts ci: migrate image + imager pipelines to semantic-release 2026-08-11 21:59:47 +02:00
shared feat(shared): add ProvisioningPayload schema + type guard 2026-06-16 15:47:15 +02:00
.gitignore ci: migrate image + imager pipelines to semantic-release 2026-08-11 21:59:47 +02:00
.releaserc.images.json ci: migrate image + imager pipelines to semantic-release 2026-08-11 21:59:47 +02:00
AGENTS.md ci: migrate image + imager pipelines to semantic-release 2026-08-11 21:59:47 +02:00
CHANGELOG.images.md ci: migrate image + imager pipelines to semantic-release 2026-08-11 21:59:47 +02:00
DECISIONS.md docs(image-builder): repo overview + imager usage/elevation/safety + decisions 2026-07-05 22:29:23 +02:00
README.md ci: migrate image + imager pipelines to semantic-release 2026-08-11 21:59:47 +02:00

image-builder — DIY-Avatar device images

This repo builds and delivers the device images for the DIY-Avatar telepresence kiosk (a self-hosted system that lets a long-term-sick child attend class remotely). It holds the three ways to get a kiosk onto hardware, plus the shared provisioning contract they all speak.

It contains no application source. The kiosk stack itself — the install-device.sh provisioner, the Python agent, the kiosk-shell web UI, the backend — lives in the main DIY-Avatar monorepo. This repo was migrated (with its git history) out of that monorepo so the image/installer/flasher tooling can build and release independently.

The three parts

Directory What it builds Publishes to Docs
live-image/ A live USB kiosk image (live-build + an appended writable AVATARCFG config partition). Boots the full kiosk from a read-only system in RAM; nothing is installed, the laptop's disk is untouched. rolling kiosk-live release live-image/README.md
os-install/ An unattended installer ISO (Debian netinst repacked with a preseed + a first-boot provisioner). Wipes the target disk and installs a permanent kiosk. rolling kiosk-installer release os-install/README.md
imager/ A cross-platform desktop flasher GUI (Electron + Svelte, RPi-Imager-like). Picks a prebuilt live image, injects settings into its AVATARCFG partition, and flashes a USB — no dd, no manual partitioning. rolling imager release imager/README.md

How they relate

  • live-image/ produces what imager/ consumes. The imager downloads the kiosk-live release asset (or takes a local .img), injects avatar-config.toml into the same AVATARCFG partition build-live.sh seeds, and flashes it. The image pipeline is unchanged by the imager.
  • live-image/ and os-install/ are two paths to the same kiosk. The live image runs from the stick and leaves no trace (good for "a kiosk for a day"); the installer dedicates an old laptop to be a kiosk forever. Both end up with an identical, provisioned kiosk. See the comparison table in live-image/README.md.
  • live-image/ is a cross-repo build. build-live.sh stages the kiosk stack into the live-build chroot, so it needs a checkout of the main DIY-Avatar monorepo passed via the DIY_AVATAR_SRC env var (or --src). Without it the build errors out immediately. In CI, the images.yml workflow checks the monorepo out into a sibling path and passes it through. os-install/ and imager/ have no such dependency.

Shared contract

shared/provisioning.ts defines the ProvisioningPayload contract (server URL + device token + optional wifi/audio/language) and its isProvisioningPayload type guard. It is the single source of truth for the first-boot provisioning payload that:

  • web-admin encodes into the first-run QR code,
  • the live kiosk scans and applies (writing avatar-config.toml back to the stick),
  • the imager accepts to prefill its settings form.

It is vendored here (a copy also lives in the main app repo, where web-admin imports it) and consumed by the imager via a @shared/* path alias — not duplicated inside imager/.

Build & release at a glance

# Live USB image (needs a monorepo checkout via DIY_AVATAR_SRC):
DIY_AVATAR_SRC=/path/to/diy-avatar ./live-image/build-live.sh amd64

# Unattended installer ISO:
./os-install/build-iso.sh amd64

# Desktop flasher (node 2022 + node-gyp toolchain; see imager/README.md):
pnpm --dir imager install && pnpm --dir imager run build

Each part's README documents its own prerequisites, flashing, booting and public URLs.

CI

CI runs on Forgejo Actions, two workflows (images.yml + imager.yml) under .forgejo/workflows/:

Workflow Builds Rolling release tag
images.yml live USB image (amd64 required, arm64 gated) kiosk-live
images.yml installer ISO (per-arch matrix) kiosk-installer
imager.yml desktop flasher (Linux required; win/mac gated) imager

All of them use the same rolling-release pattern: resolve-or-create the release by tag, then per-asset delete-then-upload via the Forgejo API. Requirements to publish:

  • a CI_PUSH_TOKEN secret (repo WRITE) — the same secret every workflow uses to create the release and upload/replace its assets;
  • an enabled Actions runner on this repo (a docker runner with outbound internet for the live-image/imager builds; the live-image build additionally needs a privileged container for loop devices, the imager build does not);
  • for the gated legs (live-image arm64, imager Windows/macOS), a dedicated runner/toolchain before they can be un-gated — a macOS .dmg in particular needs a real macOS runner and cannot be built inside the Debian CI container.

Repo map

image-builder/
├─ live-image/    # live USB kiosk image (live-build + AVATARCFG)   → kiosk-live
├─ os-install/    # unattended installer ISO (netinst + preseed)    → kiosk-installer
├─ imager/        # cross-platform desktop flasher GUI (Electron)   → imager
├─ shared/        # provisioning.ts — ProvisioningPayload contract (vendored)
└─ .forgejo/      # Forgejo Actions workflows (one per part)

See DECISIONS.md for the rationale behind the imager's design and the split-repo migration, and AGENTS.md for a fast orientation map.