You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Liminal Notes maintains its own parallel CI image pipeline — .github/workflows/build-ci-images.yml building three targets (test, desktop, android) from docker/ci/Dockerfile.ci-base, published as ghcr.io/scottmorris/liminal-notes-ci-base-*. The liminal-hq shared images now cover everything those images provide, plus more (Bun, GStreamer base+bad, fonts-noto-color-emoji, xvfb, gh, cargo-nextest, a pinned Rust 1.96.1). Retire the parallel pipeline and adopt the shared tiers:
Concrete wins: the entire build-ci-images.yml workflow, docker/ci/Dockerfile.ci-base, and three GHCR packages stop needing maintenance; image contents (toolchain bumps, security refreshes, the scheduled YYYYMMDD rebuilds) are handled centrally; and the desktop image gains the pieces this repo's own image lacks (GStreamer, xvfb, emoji fonts, a pinned toolchain instead of floating stable). The hand-rolled .devcontainer/Dockerfile — which independently re-creates what tauri-dev-mobile ships, including the portable-AppImage tauri-cli fork — can be replaced by the shared dev image too.
Current state
.github/workflows/build-ci-images.yml — builds/pushes ghcr.io/scottmorris/liminal-notes-ci-base-{desktop,android,test} on pushes touching the Dockerfile (desktop is linux/amd64,linux/arm64; android and test are amd64).
docker/ci/Dockerfile.ci-base — three-stage build as summarised above. Notably: Node 20 (NODE_MAJOR=20), pnpm 10.28.2, Rust installed as floating stable under /usr/local/rustup, NDK 27.1.12297006, and python3 in the test target.
.github/workflows/test-ci.yml:
packages-tests, desktop-lint-typecheck, desktop-tests, desktop-coverage-summary — all in ghcr.io/scottmorris/liminal-notes-ci-base-test:latest with pnpm/action-setup@v4 + actions/setup-node@v4 (.node-version = 20.19.6), pnpm run ci:install, then the respective ci:* scripts.
rust-tests — in ghcr.io/scottmorris/liminal-notes-ci-base-desktop:latest; awk-resolves the channel from rust-toolchain.toml (pins 1.93.0) and installs it via dtolnay/rust-toolchain@stable, rust-cache on apps/desktop/src-tauri -> target, pnpm run ci:test:rust.
.github/workflows/publish-desktop.yml:
publish-linux — matrix ubuntu-24.04 / ubuntu-24.04-arm, container liminal-notes-ci-base-desktop:latest, same toolchain-resolve dance, then tauri-apps/tauri-action@v0.6.1 with TAURI_BUNDLER_NEW_APPIMAGE_FORMAT: "true".
.github/workflows/build-android.yml — build-android job in liminal-notes-ci-base-android:latest; pnpm run ci:android:prepare then EAS profile builds (ci:android:build:{preview,development,production}).
Keep pnpm/action-setup and setup-node — the repo is pinned to Node 20.19.6 (engines: >=20.19.6 <21) while the image ships Node 24, and the setup actions enforce the repo pin inside the container. Drop the credentials: block only if the shared images are public (see caveats); a personal-repo GITHUB_TOKEN cannot authenticate to liminal-hq packages either way.
container:
image: ghcr.io/liminal-hq/tauri-ci-desktop:lateststeps:
# Resolve/Setup Rust toolchain steps deleted — the image ships 1.96.1,# matching rust-toolchain.toml, so rustup needs no download.
In .github/workflows/publish-desktop.yml, publish-linux: replace the container the same way (ghcr.io/liminal-hq/tauri-ci-desktop:latest is multi-arch, so both ubuntu-24.04 and ubuntu-24.04-arm legs keep working) and delete the two toolchain steps. Bonus: the shared image ships the portable-AppImage tauri-cli fork and defaults consistent with TAURI_BUNDLER_NEW_APPIMAGE_FORMAT: "true" (keep the env var — it is harmless and explicit). publish-windows is untouched.
In .github/workflows/build-android.yml, build-android:
then delete .devcontainer/Dockerfile. Everything it built by hand — GUI stack, JDK 17, Android SDK 36/NDK 28.2, Rust, gh, the portable-AppImage tauri-cli fork, TAURI_BUNDLER_NEW_APPIMAGE_FORMAT — ships in tauri-dev-mobile (user vscode, toolchains under $HOME). The audio runArgs/Pulse mounts in devcontainer.json carry over unchanged.
Validation
The shared images pull anonymously (this repo is under ScottMorris, outside the liminal-hq org): docker pull ghcr.io/liminal-hq/ci-web:latest, .../tauri-ci-desktop:latest, .../tauri-ci-mobile:latest all succeed without docker login.
docker run --rm ghcr.io/liminal-hq/ci-web:latest bash -lc 'node --version && pnpm --version && python3 --version' — confirm python3 availability; the old test image installed it explicitly for node-gyp builds. If absent, add an apt-get install -y python3 step to the JS jobs or request it in the shared base.
docker run --rm ghcr.io/liminal-hq/tauri-ci-desktop:latest rustc --version prints 1.96.1; pnpm run ci:test:rust passes on a PR branch with no toolchain download in the log.
pnpm run ci:install and all four JS jobs green in ci-web (Node 20.19.6 still selected by setup-node — check node --version output in the job log).
Dispatch build-android.yml with the preview profile against a test tag: EAS build completes under NDK 28.2 and the APK/AAB artefacts attach to the draft release.
Dispatch publish-desktop.yml on a draft desktop-v* tag: both Linux legs (x64 + arm64) bundle and upload; Windows legs unaffected.
After deletion, confirm no workflow references liminal-notes-ci-base- (git grep liminal-notes-ci-base).
Caveats
Cross-org image access is the gating question.GITHUB_TOKEN in a ScottMorris repo cannot read liminal-hq private packages. The shared images must be public, or each container: needs a PAT stored as a repo secret in a credentials: block. Verify before starting; everything else depends on it.
NDK delta: 27.1.12297006 → 28.2. The Expo/EAS Android build (ci:android:build:*) may pin an NDK via Gradle properties; if it hard-requires 27.x, either update the app's NDK pin or add an sdkmanager --install "ndk;27.1.12297006" step (slow — prefer updating the pin).
Node major delta: image ships Node 24; repo engines say >=20.19.6 <21. Keeping setup-node with .node-version in every JS job is mandatory until the repo is ready to move to a newer Node line. Do not run pnpm/node steps before setup-node in those jobs.
Rust moves from floating stable (whatever the last local image build captured) to pinned 1.96.1 — deterministic, but check clippy/compiler output once.
The old test image's python3 has no guaranteed equivalent in ci-web — validated above.
Summary
Liminal Notes maintains its own parallel CI image pipeline —
.github/workflows/build-ci-images.ymlbuilding three targets (test,desktop,android) fromdocker/ci/Dockerfile.ci-base, published asghcr.io/scottmorris/liminal-notes-ci-base-*. The liminal-hq shared images now cover everything those images provide, plus more (Bun, GStreamer base+bad,fonts-noto-color-emoji, xvfb, gh, cargo-nextest, a pinned Rust 1.96.1). Retire the parallel pipeline and adopt the shared tiers:Dockerfile.ci-base)liminal-notes-ci-base-testghcr.io/liminal-hq/ci-web:latest(Node 24, pnpm 10.28.2, Bun 1.3.14)liminal-notes-ci-base-desktop(amd64+arm64)stableghcr.io/liminal-hq/tauri-ci-desktop:latest(amd64+arm64, Rust 1.96.1 pinned, GStreamer/xvfb/emoji fonts, tauri-cli)liminal-notes-ci-base-androidghcr.io/liminal-hq/tauri-ci-mobile:latest(JDK 17, SDK 36, NDK 28.2, Android Rust targets)Concrete wins: the entire
build-ci-images.ymlworkflow,docker/ci/Dockerfile.ci-base, and three GHCR packages stop needing maintenance; image contents (toolchain bumps, security refreshes, the scheduledYYYYMMDDrebuilds) are handled centrally; and the desktop image gains the pieces this repo's own image lacks (GStreamer, xvfb, emoji fonts, a pinned toolchain instead of floatingstable). The hand-rolled.devcontainer/Dockerfile— which independently re-creates whattauri-dev-mobileships, including the portable-AppImage tauri-cli fork — can be replaced by the shared dev image too.Current state
.github/workflows/build-ci-images.yml— builds/pushesghcr.io/scottmorris/liminal-notes-ci-base-{desktop,android,test}on pushes touching the Dockerfile (desktop islinux/amd64,linux/arm64; android and test are amd64).docker/ci/Dockerfile.ci-base— three-stage build as summarised above. Notably: Node 20 (NODE_MAJOR=20), pnpm 10.28.2, Rust installed as floatingstableunder/usr/local/rustup, NDK 27.1.12297006, andpython3in thetesttarget..github/workflows/test-ci.yml:packages-tests,desktop-lint-typecheck,desktop-tests,desktop-coverage-summary— all inghcr.io/scottmorris/liminal-notes-ci-base-test:latestwithpnpm/action-setup@v4+actions/setup-node@v4(.node-version= 20.19.6),pnpm run ci:install, then the respectiveci:*scripts.rust-tests— inghcr.io/scottmorris/liminal-notes-ci-base-desktop:latest; awk-resolves the channel fromrust-toolchain.toml(pins1.93.0) and installs it viadtolnay/rust-toolchain@stable, rust-cache onapps/desktop/src-tauri -> target,pnpm run ci:test:rust..github/workflows/publish-desktop.yml:publish-linux— matrixubuntu-24.04/ubuntu-24.04-arm, containerliminal-notes-ci-base-desktop:latest, same toolchain-resolve dance, thentauri-apps/tauri-action@v0.6.1withTAURI_BUNDLER_NEW_APPIMAGE_FORMAT: "true".publish-windows— nativewindows-latest, unaffected..github/workflows/build-android.yml—build-androidjob inliminal-notes-ci-base-android:latest;pnpm run ci:android:preparethen EAS profile builds (ci:android:build:{preview,development,production})..github/workflows/publish-site.yml— plain runner, Pages; unaffected.rust-toolchain.tomlpins1.93.0(no components listed).package.jsonengines:"node": ">=20.19.6 <21";packageManager: pnpm@10.28.2;.node-version= 20.19.6..devcontainer/Dockerfile— hand-built devcontainer: full Tauri apt list incl. GStreamer/xvfb/emoji fonts, JDK 17, Node 20, gh, Android SDK 36 + NDK 28.2.13676358, rustup stable, andcargo install tauri-cli --git ... --branch feat/truly-portable-appimage. This is functionallyghcr.io/liminal-hq/tauri-dev-mobilere-implemented locally.Migration steps
Bump
rust-toolchain.tomlto the shared pin so container jobs use the preinstalled toolchain:In
.github/workflows/test-ci.yml, swap containers. JS jobs (packages-tests,desktop-lint-typecheck,desktop-tests,desktop-coverage-summary):Before:
After:
Keep
pnpm/action-setupandsetup-node— the repo is pinned to Node 20.19.6 (engines: >=20.19.6 <21) while the image ships Node 24, and the setup actions enforce the repo pin inside the container. Drop thecredentials:block only if the shared images are public (see caveats); a personal-repoGITHUB_TOKENcannot authenticate to liminal-hq packages either way.Same file,
rust-tests:Before:
After:
In
.github/workflows/publish-desktop.yml,publish-linux: replace the container the same way (ghcr.io/liminal-hq/tauri-ci-desktop:latestis multi-arch, so bothubuntu-24.04andubuntu-24.04-armlegs keep working) and delete the two toolchain steps. Bonus: the shared image ships the portable-AppImage tauri-cli fork and defaults consistent withTAURI_BUNDLER_NEW_APPIMAGE_FORMAT: "true"(keep the env var — it is harmless and explicit).publish-windowsis untouched.In
.github/workflows/build-android.yml,build-android:Before:
After:
Verify the EAS/Gradle build against NDK 28.2 first (see caveats) — this is the one mapping with a real version delta (27.1.12297006 → 28.2).
Once all four workflows are green on the shared images, delete:
.github/workflows/build-ci-images.ymldocker/ci/Dockerfile.ci-baseand mark the three
liminal-notes-ci-base-*GHCR packages deprecated (or delete them after a grace period).Devcontainer: replace
.devcontainer/Dockerfilewith the shared dev image in.devcontainer/devcontainer.json:Before:
After:
then delete
.devcontainer/Dockerfile. Everything it built by hand — GUI stack, JDK 17, Android SDK 36/NDK 28.2, Rust, gh, the portable-AppImage tauri-cli fork,TAURI_BUNDLER_NEW_APPIMAGE_FORMAT— ships intauri-dev-mobile(uservscode, toolchains under$HOME). The audiorunArgs/Pulse mounts indevcontainer.jsoncarry over unchanged.Validation
ScottMorris, outside the liminal-hq org):docker pull ghcr.io/liminal-hq/ci-web:latest,.../tauri-ci-desktop:latest,.../tauri-ci-mobile:latestall succeed withoutdocker login.docker run --rm ghcr.io/liminal-hq/ci-web:latest bash -lc 'node --version && pnpm --version && python3 --version'— confirmpython3availability; the oldtestimage installed it explicitly for node-gyp builds. If absent, add anapt-get install -y python3step to the JS jobs or request it in the shared base.docker run --rm ghcr.io/liminal-hq/tauri-ci-desktop:latest rustc --versionprints 1.96.1;pnpm run ci:test:rustpasses on a PR branch with no toolchain download in the log.pnpm run ci:installand all four JS jobs green inci-web(Node 20.19.6 still selected bysetup-node— checknode --versionoutput in the job log).build-android.ymlwith thepreviewprofile against a test tag: EAS build completes under NDK 28.2 and the APK/AAB artefacts attach to the draft release.publish-desktop.ymlon a draftdesktop-v*tag: both Linux legs (x64 + arm64) bundle and upload; Windows legs unaffected.liminal-notes-ci-base-(git grep liminal-notes-ci-base).Caveats
GITHUB_TOKENin aScottMorrisrepo cannot read liminal-hq private packages. The shared images must be public, or eachcontainer:needs a PAT stored as a repo secret in acredentials:block. Verify before starting; everything else depends on it.ci:android:build:*) may pin an NDK via Gradle properties; if it hard-requires 27.x, either update the app's NDK pin or add ansdkmanager --install "ndk;27.1.12297006"step (slow — prefer updating the pin).>=20.19.6 <21. Keepingsetup-nodewith.node-versionin every JS job is mandatory until the repo is ready to move to a newer Node line. Do not runpnpm/nodesteps beforesetup-nodein those jobs.stable(whatever the last local image build captured) to pinned 1.96.1 — deterministic, but check clippy/compiler output once.testimage'spython3has no guaranteed equivalent inci-web— validated above.docker manifest inspect ghcr.io/liminal-hq/tauri-ci-desktop:latestsucceeds. For the release workflow (publish-desktop.yml), consider pinningsha-<commit>tags once stable instead oflatest.publish-site.yml(Pages) are out of scope.References