From e1accb6e7564795ab837bc8e1d1e6a96996b08db Mon Sep 17 00:00:00 2001 From: Scott Morris Date: Wed, 22 Jul 2026 18:54:23 -0400 Subject: [PATCH 1/3] build: re-layer shared images into granular single-concern tiers Split the monolithic image chains into granular tiers so consumers pull exactly the toolchain they need (liminal-hq/.github#22). - **Base tiers:** `ci-base` and `dev-base` now carry universal CLI tooling only. The GTK/webkit/appindicator/rsvg/patchelf/xdg-utils/libssl-dev stack moved down into the desktop tiers. - **New lean tiers:** `ci-rust`/`dev-rust` (pinned Rust + clippy + rustfmt + cargo-nextest) and `ci-web`/`dev-web` (Node + pnpm + Bun) publish as standalone images for pure-Rust and JS/TS-only consumers. - **Bun added:** pinned via `ARG BUN_VERSION`, installed in every JS-carrying tier (`/usr/local/bun` on CI images, `$HOME/.bun` on dev images). Ends foyer-style per-run Bun installs. - **Desktop tiers grew the threshold set:** GStreamer (base + bad), `fonts-noto-color-emoji`, and `xvfb` now ship in `ci-desktop` and `dev-desktop`, so threshold's desktop jobs no longer need host apt installs. - **Dev QoL:** `dev-base` adds ripgrep, fd (with `fd` symlink), and jq; `dev-desktop` adds X11 inspection tools (`x11-utils`, `wmctrl`, `xdotool`) per issue #10. - **Smoke checks extracted** to `docker/ci/smoke-check.sh` with per-tier profiles, including leanness assertions (`ci-rust` has no Node/Bun; `ci-web` has no cargo) and writable-path checks for the new dev tiers. The published `tauri-ci-*`/`tauri-dev-*` names, env models, and tag policy are unchanged; `ci-mobile` now inherits from `ci-desktop` (previously both hung off `ci-toolchain`) with identical effective contents. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_013jcVj1DUgYpbhkutJy5mAC --- docker/ci/Dockerfile | 199 ++++++++++++++++++++++++++++++--------- docker/ci/smoke-check.sh | 126 +++++++++++++++++++++++++ 2 files changed, 278 insertions(+), 47 deletions(-) create mode 100644 docker/ci/smoke-check.sh diff --git a/docker/ci/Dockerfile b/docker/ci/Dockerfile index 3143e53..762acac 100644 --- a/docker/ci/Dockerfile +++ b/docker/ci/Dockerfile @@ -3,18 +3,20 @@ ARG DEVCONTAINER_BASE_IMAGE=mcr.microsoft.com/devcontainers/base:ubuntu-24.04 ARG DEBIAN_FRONTEND=noninteractive ARG NODE_MAJOR=24 ARG PNPM_VERSION=10.28.2 +ARG BUN_VERSION=1.3.14 ARG RUST_TOOLCHAIN=1.96.1 ARG ANDROID_CMDLINE_TOOLS=11076708 ARG ANDROID_PLATFORM=36 ARG ANDROID_BUILD_TOOLS=36.0.0 ARG ANDROID_NDK=28.2.13676358 -# CI base: shared Linux packages for Tauri desktop builds in CI. +# CI base: universal CLI tooling shared by every CI image tier. +# GUI/Tauri system libraries intentionally live in the desktop tier, not here, +# so lean tiers (ci-rust, ci-web) never inherit them. FROM ubuntu:${UBUNTU_VERSION} AS ci-base ARG DEBIAN_FRONTEND -# Core Linux build dependencies required by Tauri desktop CI builds. # Install GitHub CLI from the official GitHub package repository so shared images # track the supported upstream release channel instead of the distro package lag. RUN apt-get update && apt-get install -y --no-install-recommends \ @@ -26,25 +28,15 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \ > /etc/apt/sources.list.d/github-cli.list \ && apt-get update && apt-get install -y --no-install-recommends \ - git gh file build-essential pkg-config patchelf \ - libssl-dev libglib2.0-dev libgtk-3-dev libwebkit2gtk-4.1-dev \ - libayatana-appindicator3-dev librsvg2-dev xdg-utils zip unzip \ + git gh file build-essential pkg-config zip unzip \ && rm -rf /var/lib/apt/lists/* -# CI toolchain: pinned Node, pnpm, Rust, and Tauri tooling for shared CI images. -FROM ci-base AS ci-toolchain +# CI Rust tier: pinned Rust toolchain and Rust QA tooling on the universal base. +# Published as ghcr.io/liminal-hq/ci-rust for pure-Rust consumers. +FROM ci-base AS ci-rust -ARG DEBIAN_FRONTEND -ARG NODE_MAJOR -ARG PNPM_VERSION ARG RUST_TOOLCHAIN -# Node + pnpm baseline pinned for workspace consistency. -RUN curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash - \ - && apt-get update && apt-get install -y --no-install-recommends nodejs \ - && npm install -g "pnpm@${PNPM_VERSION}" \ - && rm -rf /var/lib/apt/lists/* - # Rust toolchain paths for root-friendly CI usage. ENV RUSTUP_HOME=/usr/local/rustup \ CARGO_HOME=/usr/local/cargo \ @@ -56,6 +48,59 @@ RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \ && rustup component add clippy rustfmt \ && cargo install cargo-nextest --locked +# CI web tier: pinned Node, pnpm, and Bun on the universal base. +# Published as ghcr.io/liminal-hq/ci-web for JS/TS-only consumers. +# Bun ships alongside Node/pnpm because consumer repos mix runtimes +# (pnpm or npm for dependencies, Bun for compiled release binaries). +FROM ci-base AS ci-web + +ARG DEBIAN_FRONTEND +ARG NODE_MAJOR +ARG PNPM_VERSION +ARG BUN_VERSION + +# Node + pnpm baseline pinned for workspace consistency. +RUN curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash - \ + && apt-get update && apt-get install -y --no-install-recommends nodejs \ + && npm install -g "pnpm@${PNPM_VERSION}" \ + && rm -rf /var/lib/apt/lists/* + +# Bun pinned for root-friendly CI usage. +ENV BUN_INSTALL=/usr/local/bun \ + PATH=/usr/local/bun/bin:${PATH} + +RUN curl -fsSL https://bun.sh/install | bash -s -- "bun-v${BUN_VERSION}" + +# Final CI desktop image: Tauri desktop toolchain on the Rust tier. +# Adds the JS runtimes (same pins as ci-web), the GTK/webkit system stack, +# and GStreamer/xvfb/emoji fonts so desktop test jobs need no host apt installs. +FROM ci-rust AS ci-desktop + +ARG DEBIAN_FRONTEND +ARG NODE_MAJOR +ARG PNPM_VERSION +ARG BUN_VERSION + +# Tauri desktop system dependencies, kept at this tier so lean images stay lean. +RUN apt-get update && apt-get install -y --no-install-recommends \ + patchelf libssl-dev libglib2.0-dev libgtk-3-dev libwebkit2gtk-4.1-dev \ + libayatana-appindicator3-dev librsvg2-dev xdg-utils \ + gstreamer1.0-plugins-base gstreamer1.0-plugins-bad fonts-noto-color-emoji \ + xvfb \ + && rm -rf /var/lib/apt/lists/* + +# Node + pnpm baseline pinned for workspace consistency. +RUN curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash - \ + && apt-get update && apt-get install -y --no-install-recommends nodejs \ + && npm install -g "pnpm@${PNPM_VERSION}" \ + && rm -rf /var/lib/apt/lists/* + +# Bun pinned for root-friendly CI usage. +ENV BUN_INSTALL=/usr/local/bun \ + PATH=/usr/local/bun/bin:${PATH} + +RUN curl -fsSL https://bun.sh/install | bash -s -- "bun-v${BUN_VERSION}" + # Keep parity with existing app repos for portable AppImage behaviour. RUN cargo install tauri-cli \ --git https://github.com/tauri-apps/tauri \ @@ -65,11 +110,8 @@ RUN cargo install tauri-cli \ # Ensures portable AppImage behaviour matches existing repo expectations. ENV TAURI_BUNDLER_NEW_APPIMAGE_FORMAT=true -# Final CI desktop image. -FROM ci-toolchain AS ci-desktop - # Final CI mobile image. -FROM ci-toolchain AS ci-mobile +FROM ci-desktop AS ci-mobile ARG DEBIAN_FRONTEND ARG ANDROID_CMDLINE_TOOLS @@ -106,20 +148,21 @@ RUN yes | sdkmanager --licenses \ && rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android \ && rm -rf "${ANDROID_HOME}/cmdline-tools/latest/.downloadIntermediates" -# Dev base: interactive devcontainer-friendly system packages and Node baseline. +# Dev base: universal interactive tooling on the devcontainer base image. +# GUI/Tauri system libraries intentionally live in the dev desktop tier, not here. FROM ${DEVCONTAINER_BASE_IMAGE} AS dev-base ARG DEBIAN_FRONTEND -ARG NODE_MAJOR -ARG PNPM_VERSION -ENV TZ=America/Toronto +ENV TZ=America/Toronto \ + EDITOR=vim # Align devcontainers with the expected studio timezone by default. RUN ln -snf /usr/share/zoneinfo/${TZ} /etc/localtime \ && echo "${TZ}" > /etc/timezone -# Desktop devcontainer dependencies and quality-of-life packages. +# Universal CLI tooling plus interactive quality-of-life packages +# (editor, fast search, JSON inspection) for toolbox-style container use. # Install GitHub CLI from the official GitHub package repository so devcontainers # get the supported upstream release channel for interactive GitHub workflows. RUN apt-get update && apt-get install -y --no-install-recommends \ @@ -131,35 +174,27 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \ > /etc/apt/sources.list.d/github-cli.list \ && apt-get update && apt-get install -y --no-install-recommends \ - git gh file build-essential pkg-config patchelf \ - libssl-dev libglib2.0-dev libgtk-3-dev libwebkit2gtk-4.1-dev \ - libayatana-appindicator3-dev librsvg2-dev xdg-utils zip unzip \ - gstreamer1.0-plugins-base gstreamer1.0-plugins-bad fonts-noto-color-emoji \ - xvfb vim \ - && rm -rf /var/lib/apt/lists/* - -# Node + pnpm baseline pinned for shared devcontainer usage. -RUN curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash - \ - && apt-get update && apt-get install -y --no-install-recommends nodejs \ - && npm install -g "pnpm@${PNPM_VERSION}" \ + git gh file build-essential pkg-config zip unzip \ + vim ripgrep fd-find jq \ + && ln -s "$(command -v fdfind)" /usr/local/bin/fd \ && rm -rf /var/lib/apt/lists/* -# Dev toolchain: Threshold-style user-home layout for writable devcontainer tooling. -FROM dev-base AS dev-toolchain +# Dev Rust tier: pinned Rust toolchain under the devcontainer user home. +# Published as ghcr.io/liminal-hq/dev-rust for pure-Rust devcontainers and +# host-side toolbox use. +FROM dev-base AS dev-rust ARG RUST_TOOLCHAIN # Tool paths live under the non-root user home for a smoother devcontainer DX. ENV HOME=/home/vscode \ - EDITOR=vim \ - PNPM_HOME=/home/vscode/.local/share/pnpm \ RUSTUP_HOME=/home/vscode/.rustup \ CARGO_HOME=/home/vscode/.cargo \ - PATH=/home/vscode/.local/share/pnpm:/home/vscode/.cargo/bin:${PATH} + PATH=/home/vscode/.cargo/bin:${PATH} # Pre-create writable directories before switching to the devcontainer user. -RUN mkdir -p "${PNPM_HOME}" "${RUSTUP_HOME}" "${CARGO_HOME}" \ - && chown -R vscode:vscode /home/vscode/.local /home/vscode/.rustup /home/vscode/.cargo +RUN mkdir -p "${RUSTUP_HOME}" "${CARGO_HOME}" \ + && chown -R vscode:vscode "${RUSTUP_HOME}" "${CARGO_HOME}" USER vscode @@ -169,6 +204,79 @@ RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \ && rustup component add clippy rustfmt \ && cargo install cargo-nextest --locked +# Dev web tier: pinned Node, pnpm, and Bun with user-home tool paths. +# Published as ghcr.io/liminal-hq/dev-web for JS/TS devcontainers and +# host-side toolbox use. +FROM dev-base AS dev-web + +ARG DEBIAN_FRONTEND +ARG NODE_MAJOR +ARG PNPM_VERSION +ARG BUN_VERSION + +# Node + pnpm baseline pinned for shared devcontainer usage. +RUN curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash - \ + && apt-get update && apt-get install -y --no-install-recommends nodejs \ + && npm install -g "pnpm@${PNPM_VERSION}" \ + && rm -rf /var/lib/apt/lists/* + +# Writable JS tool paths under the non-root user home. +ENV HOME=/home/vscode \ + PNPM_HOME=/home/vscode/.local/share/pnpm \ + BUN_INSTALL=/home/vscode/.bun \ + PATH=/home/vscode/.local/share/pnpm:/home/vscode/.bun/bin:${PATH} + +# Pre-create writable directories before switching to the devcontainer user. +RUN mkdir -p "${PNPM_HOME}" \ + && chown -R vscode:vscode /home/vscode/.local + +USER vscode + +# Bun pinned under the devcontainer user home. +RUN curl -fsSL https://bun.sh/install | bash -s -- "bun-v${BUN_VERSION}" + +# Final dev desktop image: Tauri desktop toolchain on the dev Rust tier. +# Adds the JS runtimes (same pins as dev-web), the GTK/webkit system stack, +# GStreamer/xvfb/emoji fonts, and X11 inspection tools for Linux desktop +# integration debugging from the container. +FROM dev-rust AS dev-desktop + +ARG DEBIAN_FRONTEND +ARG NODE_MAJOR +ARG PNPM_VERSION +ARG BUN_VERSION + +USER root + +# Tauri desktop system dependencies and desktop-debugging tools, kept at this +# tier so lean dev images stay lean. +RUN apt-get update && apt-get install -y --no-install-recommends \ + patchelf libssl-dev libglib2.0-dev libgtk-3-dev libwebkit2gtk-4.1-dev \ + libayatana-appindicator3-dev librsvg2-dev xdg-utils \ + gstreamer1.0-plugins-base gstreamer1.0-plugins-bad fonts-noto-color-emoji \ + xvfb x11-utils wmctrl xdotool \ + && rm -rf /var/lib/apt/lists/* + +# Node + pnpm baseline pinned for shared devcontainer usage. +RUN curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash - \ + && apt-get update && apt-get install -y --no-install-recommends nodejs \ + && npm install -g "pnpm@${PNPM_VERSION}" \ + && rm -rf /var/lib/apt/lists/* + +# Writable JS tool paths under the non-root user home. +ENV PNPM_HOME=/home/vscode/.local/share/pnpm \ + BUN_INSTALL=/home/vscode/.bun \ + PATH=/home/vscode/.local/share/pnpm:/home/vscode/.bun/bin:${PATH} + +# Pre-create writable directories before switching back to the devcontainer user. +RUN mkdir -p "${PNPM_HOME}" \ + && chown -R vscode:vscode /home/vscode/.local + +USER vscode + +# Bun pinned under the devcontainer user home. +RUN curl -fsSL https://bun.sh/install | bash -s -- "bun-v${BUN_VERSION}" + # Keep parity with existing app repos for portable AppImage behaviour. RUN cargo install tauri-cli \ --git https://github.com/tauri-apps/tauri \ @@ -178,11 +286,8 @@ RUN cargo install tauri-cli \ # Ensures portable AppImage behaviour matches existing repo expectations. ENV TAURI_BUNDLER_NEW_APPIMAGE_FORMAT=true -# Final dev desktop image. -FROM dev-toolchain AS dev-desktop - # Final dev mobile image. -FROM dev-toolchain AS dev-mobile +FROM dev-desktop AS dev-mobile ARG DEBIAN_FRONTEND ARG ANDROID_CMDLINE_TOOLS diff --git a/docker/ci/smoke-check.sh b/docker/ci/smoke-check.sh new file mode 100644 index 0000000..146c5ba --- /dev/null +++ b/docker/ci/smoke-check.sh @@ -0,0 +1,126 @@ +#!/usr/bin/env bash +# Smoke checks for the shared CI/dev image families. +# Usage: smoke-check.sh +# Profiles mirror the published image tiers; each check runs inside the image +# and enforces the tier's tool availability, environment, and leanness contract. +set -euo pipefail + +profile="${1:?usage: smoke-check.sh }" +image="${2:?usage: smoke-check.sh }" + +run_in_image() { + docker run --rm "${image}" bash -lc "set -euo pipefail; $1" +} + +case "${profile}" in + ci-rust) + run_in_image ' + command -v cargo rustup cargo-nextest gh + cargo --version + rustup --version + cargo nextest --version + gh --version + ! command -v node + ! command -v bun + ' + ;; + ci-web) + run_in_image ' + command -v node pnpm bun gh + node --version + pnpm --version + bun --version + gh --version + ! command -v cargo + ' + ;; + ci-desktop) + run_in_image ' + command -v cargo rustup node pnpm bun cargo-tauri gh + cargo --version + node --version + pnpm --version + bun --version + gh --version + ' + ;; + ci-mobile) + run_in_image ' + command -v cargo rustup node pnpm bun cargo-tauri gh sdkmanager java + cargo --version + node --version + pnpm --version + bun --version + gh --version + sdkmanager --version + ' + ;; + dev-rust) + run_in_image ' + [[ "$(id -u)" != "0" ]] + command -v cargo rustup cargo-nextest gh rg fd jq + cargo --version + gh --version + [[ "$CARGO_HOME" == "$HOME/.cargo" ]] + [[ "$RUSTUP_HOME" == "$HOME/.rustup" ]] + touch "$CARGO_HOME/.smoke-write" + rm "$CARGO_HOME/.smoke-write" + ' + ;; + dev-web) + run_in_image ' + [[ "$(id -u)" != "0" ]] + command -v node pnpm bun gh rg fd jq + node --version + pnpm --version + bun --version + gh --version + [[ "$PNPM_HOME" == "$HOME/.local/share/pnpm" ]] + [[ "$BUN_INSTALL" == "$HOME/.bun" ]] + touch "$PNPM_HOME/.smoke-write" + rm "$PNPM_HOME/.smoke-write" + touch "$BUN_INSTALL/.smoke-write" + rm "$BUN_INSTALL/.smoke-write" + ' + ;; + dev-desktop) + run_in_image ' + [[ "$(id -u)" != "0" ]] + command -v cargo rustup node pnpm bun cargo-tauri gh rg fd jq + cargo --version + node --version + pnpm --version + bun --version + gh --version + [[ "$CARGO_HOME" == "$HOME/.cargo" ]] + [[ "$RUSTUP_HOME" == "$HOME/.rustup" ]] + [[ "$PNPM_HOME" == "$HOME/.local/share/pnpm" ]] + [[ "$BUN_INSTALL" == "$HOME/.bun" ]] + touch "$CARGO_HOME/.smoke-write" + rm "$CARGO_HOME/.smoke-write" + touch "$PNPM_HOME/.smoke-write" + rm "$PNPM_HOME/.smoke-write" + ' + ;; + dev-mobile) + run_in_image ' + [[ "$(id -u)" != "0" ]] + command -v cargo rustup node pnpm bun cargo-tauri gh sdkmanager java + cargo --version + node --version + pnpm --version + bun --version + gh --version + sdkmanager --version + [[ "$ANDROID_HOME" == "$HOME/Android/Sdk" ]] + touch "$CARGO_HOME/.smoke-write" + rm "$CARGO_HOME/.smoke-write" + mkdir -p "$ANDROID_HOME/.smoke" + rmdir "$ANDROID_HOME/.smoke" + ' + ;; + *) + echo "Unknown smoke profile: ${profile}" >&2 + exit 1 + ;; +esac From 2f2e7971825d3a565414a8b4fe3db995277b5b2d Mon Sep 17 00:00:00 2001 From: Scott Morris Date: Wed, 22 Jul 2026 18:54:39 -0400 Subject: [PATCH 2/3] ci: publish the granular image tiers from the shared workflow Extend the publish workflow from four to eight images (liminal-hq/.github#22). - **Generalised multi-arch path:** `build-ci-desktop`/`merge-ci-desktop` became `build-multiarch`/`merge-multiarch`, matrixed over the three multi-arch images (`ci-rust`, `ci-web`, `tauri-ci-desktop`) x two native runners, keeping the push-by-digest + manifest-merge pattern that avoids QEMU. - **Single-arch matrix grew:** `publish-images` adds `dev-rust` and `dev-web` alongside CI mobile and the two Tauri dev images. - **Smoke checks call `docker/ci/smoke-check.sh`** instead of inline case blocks, so both jobs share one per-tier contract definition and the script is shellcheck-covered. - Per-image registry buildcache refs and the `latest`/`staging`/`sha-*`/date tag policy carry over unchanged. Workflow display name updated to reflect that it publishes both CI and dev families. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_013jcVj1DUgYpbhkutJy5mAC --- .github/workflows/shared-tauri-ci-images.yml | 182 +++++++++---------- 1 file changed, 91 insertions(+), 91 deletions(-) diff --git a/.github/workflows/shared-tauri-ci-images.yml b/.github/workflows/shared-tauri-ci-images.yml index 50555c4..a230772 100644 --- a/.github/workflows/shared-tauri-ci-images.yml +++ b/.github/workflows/shared-tauri-ci-images.yml @@ -1,4 +1,4 @@ -name: Shared Tauri CI Images +name: Shared CI and Dev Images on: push: @@ -16,10 +16,6 @@ permissions: env: IMAGE_NAMESPACE: ghcr.io/liminal-hq - CI_DESKTOP_IMAGE: tauri-ci-desktop - CI_MOBILE_IMAGE: tauri-ci-mobile - DEV_DESKTOP_IMAGE: tauri-dev-desktop - DEV_MOBILE_IMAGE: tauri-dev-mobile jobs: cadence: @@ -54,6 +50,7 @@ jobs: echo "" >> "${GITHUB_STEP_SUMMARY}" echo "Skipping this scheduled run to keep a two-week publish cadence." >> "${GITHUB_STEP_SUMMARY}" + # Single-platform images build, smoke-check, and push in one matrixed job. publish-images: name: Publish ${{ matrix.label }} runs-on: ubuntu-latest @@ -72,6 +69,18 @@ jobs: platforms: linux/amd64 smoke_platform: linux/amd64 smoke_profile: ci-mobile + - label: Dev Rust image + image: dev-rust + target: dev-rust + platforms: linux/amd64 + smoke_platform: linux/amd64 + smoke_profile: dev-rust + - label: Dev web image + image: dev-web + target: dev-web + platforms: linux/amd64 + smoke_platform: linux/amd64 + smoke_profile: dev-web - label: Dev desktop image image: tauri-dev-desktop target: dev-desktop @@ -124,57 +133,7 @@ jobs: cache-to: type=registry,ref=${{ env.IMAGE_NAMESPACE }}/${{ matrix.image }}:buildcache,mode=max - name: Run smoke checks - shell: bash - run: | - case "${{ matrix.smoke_profile }}" in - ci-mobile) - docker run --rm "local-smoke:${{ matrix.target }}" bash -lc ' - command -v cargo rustup node pnpm cargo-tauri gh sdkmanager java - cargo --version - node --version - pnpm --version - gh --version - sdkmanager --version - ' - ;; - dev-desktop) - docker run --rm "local-smoke:${{ matrix.target }}" bash -lc ' - [[ "$(id -u)" != "0" ]] - command -v cargo rustup node pnpm cargo-tauri gh - cargo --version - node --version - pnpm --version - gh --version - [[ "$CARGO_HOME" == "$HOME/.cargo" ]] - [[ "$RUSTUP_HOME" == "$HOME/.rustup" ]] - [[ "$PNPM_HOME" == "$HOME/.local/share/pnpm" ]] - touch "$CARGO_HOME/.smoke-write" - rm "$CARGO_HOME/.smoke-write" - touch "$PNPM_HOME/.smoke-write" - rm "$PNPM_HOME/.smoke-write" - ' - ;; - dev-mobile) - docker run --rm "local-smoke:${{ matrix.target }}" bash -lc ' - [[ "$(id -u)" != "0" ]] - command -v cargo rustup node pnpm cargo-tauri gh sdkmanager java - cargo --version - node --version - pnpm --version - gh --version - sdkmanager --version - [[ "$ANDROID_HOME" == "$HOME/Android/Sdk" ]] - touch "$CARGO_HOME/.smoke-write" - rm "$CARGO_HOME/.smoke-write" - mkdir -p "$ANDROID_HOME/.smoke" - rmdir "$ANDROID_HOME/.smoke" - ' - ;; - *) - echo "Unknown smoke profile: ${{ matrix.smoke_profile }}" >&2 - exit 1 - ;; - esac + run: bash docker/ci/smoke-check.sh "${{ matrix.smoke_profile }}" "local-smoke:${{ matrix.target }}" - name: Build and push image id: build @@ -205,13 +164,13 @@ jobs: echo "### Tags" >> "${GITHUB_STEP_SUMMARY}" echo "${TAGS}" | sed 's/^/- `/; s/$/`/' >> "${GITHUB_STEP_SUMMARY}" - # CI desktop is the only multi-platform image (linux/amd64,linux/arm64). Building both - # platforms in one buildx invocation on an amd64 runner emulates arm64 via QEMU, which - # takes 2+ hours for this Dockerfile's from-source cargo installs. Building each platform - # natively (arm64 on GitHub's native arm64 runner, public-repo GA) and merging the - # resulting digests into one manifest avoids emulation entirely. - build-ci-desktop: - name: Build CI desktop image (${{ matrix.platform }}) + # Multi-platform images (linux/amd64,linux/arm64): building both platforms in one + # buildx invocation on an amd64 runner emulates arm64 via QEMU, which takes 2+ hours + # for this Dockerfile's from-source cargo installs. Building each platform natively + # (arm64 on GitHub's native arm64 runner, public-repo GA) and merging the resulting + # digests into one manifest avoids emulation entirely. + build-multiarch: + name: Build ${{ matrix.label }} (${{ matrix.platform }}) runs-on: ${{ matrix.runner }} needs: cadence if: needs.cadence.outputs.run_publish == 'true' @@ -222,9 +181,41 @@ jobs: fail-fast: false matrix: include: - - platform: linux/amd64 + - label: CI Rust image + image: ci-rust + target: ci-rust + smoke_profile: ci-rust + platform: linux/amd64 runner: ubuntu-24.04 - - platform: linux/arm64 + - label: CI Rust image + image: ci-rust + target: ci-rust + smoke_profile: ci-rust + platform: linux/arm64 + runner: ubuntu-24.04-arm + - label: CI web image + image: ci-web + target: ci-web + smoke_profile: ci-web + platform: linux/amd64 + runner: ubuntu-24.04 + - label: CI web image + image: ci-web + target: ci-web + smoke_profile: ci-web + platform: linux/arm64 + runner: ubuntu-24.04-arm + - label: CI desktop image + image: tauri-ci-desktop + target: ci-desktop + smoke_profile: ci-desktop + platform: linux/amd64 + runner: ubuntu-24.04 + - label: CI desktop image + image: tauri-ci-desktop + target: ci-desktop + smoke_profile: ci-desktop + platform: linux/arm64 runner: ubuntu-24.04-arm steps: - name: Checkout @@ -251,24 +242,17 @@ jobs: with: context: . file: docker/ci/Dockerfile - target: ci-desktop + target: ${{ matrix.target }} platforms: ${{ matrix.platform }} load: true - tags: local-smoke:ci-desktop-${{ steps.platform_pair.outputs.pair }} - cache-from: type=registry,ref=${{ env.IMAGE_NAMESPACE }}/${{ env.CI_DESKTOP_IMAGE }}:buildcache - cache-to: type=registry,ref=${{ env.IMAGE_NAMESPACE }}/${{ env.CI_DESKTOP_IMAGE }}:buildcache,mode=max + tags: local-smoke:${{ matrix.target }}-${{ steps.platform_pair.outputs.pair }} + cache-from: type=registry,ref=${{ env.IMAGE_NAMESPACE }}/${{ matrix.image }}:buildcache + cache-to: type=registry,ref=${{ env.IMAGE_NAMESPACE }}/${{ matrix.image }}:buildcache,mode=max - name: Run smoke checks env: PLATFORM_PAIR: ${{ steps.platform_pair.outputs.pair }} - run: | - docker run --rm "local-smoke:ci-desktop-${PLATFORM_PAIR}" bash -lc ' - command -v cargo rustup node pnpm cargo-tauri gh - cargo --version - node --version - pnpm --version - gh --version - ' + run: bash docker/ci/smoke-check.sh "${{ matrix.smoke_profile }}" "local-smoke:${{ matrix.target }}-${PLATFORM_PAIR}" - name: Build and push by digest id: build @@ -276,11 +260,11 @@ jobs: with: context: . file: docker/ci/Dockerfile - target: ci-desktop + target: ${{ matrix.target }} platforms: ${{ matrix.platform }} - outputs: type=image,name=${{ env.IMAGE_NAMESPACE }}/${{ env.CI_DESKTOP_IMAGE }},push-by-digest=true,name-canonical=true,push=true - cache-from: type=registry,ref=${{ env.IMAGE_NAMESPACE }}/${{ env.CI_DESKTOP_IMAGE }}:buildcache - cache-to: type=registry,ref=${{ env.IMAGE_NAMESPACE }}/${{ env.CI_DESKTOP_IMAGE }}:buildcache,mode=max + outputs: type=image,name=${{ env.IMAGE_NAMESPACE }}/${{ matrix.image }},push-by-digest=true,name-canonical=true,push=true + cache-from: type=registry,ref=${{ env.IMAGE_NAMESPACE }}/${{ matrix.image }}:buildcache + cache-to: type=registry,ref=${{ env.IMAGE_NAMESPACE }}/${{ matrix.image }}:buildcache,mode=max - name: Export digest env: @@ -292,27 +276,40 @@ jobs: - name: Upload digest uses: actions/upload-artifact@v4.6.2 with: - name: digests-ci-desktop-${{ steps.platform_pair.outputs.pair }} + name: digests-${{ matrix.target }}-${{ steps.platform_pair.outputs.pair }} path: /tmp/digests/* if-no-files-found: error retention-days: 1 - merge-ci-desktop: - name: Publish CI desktop image (merged manifest) + merge-multiarch: + name: Publish ${{ matrix.label }} (merged manifest) runs-on: ubuntu-24.04 - needs: build-ci-desktop + needs: build-multiarch # No checkout in this job (only artifact download + buildx/GHCR), so # `contents: read` is deliberately omitted here. If a future step needs # repo content, add it back explicitly. permissions: actions: read packages: write + strategy: + fail-fast: false + matrix: + include: + - label: CI Rust image + image: ci-rust + target: ci-rust + - label: CI web image + image: ci-web + target: ci-web + - label: CI desktop image + image: tauri-ci-desktop + target: ci-desktop steps: - name: Download digests uses: actions/download-artifact@v4.3.0 with: path: /tmp/digests - pattern: digests-ci-desktop-* + pattern: digests-${{ matrix.target }}-* merge-multiple: true - name: Set up Docker Buildx @@ -329,7 +326,7 @@ jobs: id: meta uses: docker/metadata-action@v5.10.0 with: - images: ${{ env.IMAGE_NAMESPACE }}/${{ env.CI_DESKTOP_IMAGE }} + images: ${{ env.IMAGE_NAMESPACE }}/${{ matrix.image }} tags: | type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} type=raw,value=staging,enable=${{ github.ref != 'refs/heads/main' }} @@ -338,23 +335,26 @@ jobs: - name: Create manifest list and push working-directory: /tmp/digests + env: + IMAGE_REF: ${{ env.IMAGE_NAMESPACE }}/${{ matrix.image }} run: | docker buildx imagetools create \ $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ - $(printf '${{ env.IMAGE_NAMESPACE }}/${{ env.CI_DESKTOP_IMAGE }}@sha256:%s ' *) + $(printf "${IMAGE_REF}@sha256:%s " *) - name: Inspect image env: VERSION: ${{ steps.meta.outputs.version }} - run: docker buildx imagetools inspect "${{ env.IMAGE_NAMESPACE }}/${{ env.CI_DESKTOP_IMAGE }}:${VERSION}" + IMAGE_REF: ${{ env.IMAGE_NAMESPACE }}/${{ matrix.image }} + run: docker buildx imagetools inspect "${IMAGE_REF}:${VERSION}" - name: Write publish summary env: TAGS: ${{ steps.meta.outputs.tags }} run: | - echo "## CI desktop image published" >> "${GITHUB_STEP_SUMMARY}" + echo "## ${{ matrix.label }} published" >> "${GITHUB_STEP_SUMMARY}" echo "" >> "${GITHUB_STEP_SUMMARY}" - echo "- Image: \`${{ env.IMAGE_NAMESPACE }}/${{ env.CI_DESKTOP_IMAGE }}\`" >> "${GITHUB_STEP_SUMMARY}" + echo "- Image: \`${{ env.IMAGE_NAMESPACE }}/${{ matrix.image }}\`" >> "${GITHUB_STEP_SUMMARY}" echo "- Platforms: \`linux/amd64,linux/arm64\` (built natively per-arch, merged)" >> "${GITHUB_STEP_SUMMARY}" echo "" >> "${GITHUB_STEP_SUMMARY}" echo "### Tags" >> "${GITHUB_STEP_SUMMARY}" From ba66c7c2c91146371781795e1fc6bdd3bfe56885 Mon Sep 17 00:00:00 2001 From: Scott Morris Date: Wed, 22 Jul 2026 18:54:40 -0400 Subject: [PATCH 3/3] docs: align shared image docs with the granular tier layout Update every doc the image contract touches (liminal-hq/.github#22). - **Layout reference:** rewritten around the eight published images, the tier tree, per-tier platform coverage, a "choosing an image" section, and the JS-runtime duplication rationale. - **Implementation spec:** stage layout, environment models, versioning rules (Bun added), platform policy, and the smoke-validation contract now describe the tiered architecture and `smoke-check.sh` profiles. - **Runbook:** publish steps cover the split multi-arch/single-arch jobs; contract checks are expressed per tier, including the leanness assertions. - **README and CLAUDE.md:** image list, platform support, Docker target list, and architecture notes updated. - **Cross-repo tracker:** added Wave 9 (granular tier rollout) and per-repo adoption targets from the July 2026 org toolchain survey. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_013jcVj1DUgYpbhkutJy5mAC --- CLAUDE.md | 14 +- README.md | 30 ++- .../shared-image-implementation-spec.md | 184 ++++++++---------- docs/reference/shared-image-layout.md | 102 +++++++--- docs/runbooks/image-publish-and-rollback.md | 81 ++++---- docs/tracking/cross-repo-ci-alignment.md | 16 ++ 6 files changed, 247 insertions(+), 180 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 4801bca..00183dc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -29,20 +29,22 @@ There is no app build/lint/test suite (no package.json, no source code to compil ### Shared container images (`docker/ci/Dockerfile`) -A single multi-stage Dockerfile produces four published image targets, built from two parallel stage chains: +A single multi-stage Dockerfile produces eight published image targets, built from two parallel tier chains where each tier adds exactly one concern: -- `ci-base` → `ci-toolchain` → **`ci-desktop`** / **`ci-mobile`**: root-friendly images for GitHub Actions and other automated pipelines. Tool paths live under `/usr/local` (`CARGO_HOME`, `RUSTUP_HOME`); mobile adds Android SDK/NDK under `/opt/android-sdk`. -- `dev-base` → `dev-toolchain` → **`dev-desktop`** / **`dev-mobile`**: non-root devcontainer images (runs as `vscode` user) for interactive local development. Tool paths live under `/home/vscode` instead; mobile adds Android SDK/NDK under `$HOME/Android/Sdk`. +- `ci-base` (unpublished, universal CLI only) → **`ci-rust`** (pinned Rust + clippy/rustfmt/cargo-nextest) → **`ci-desktop`** (+ Node/pnpm/Bun + GTK/webkit GUI stack + GStreamer/xvfb/emoji fonts + tauri-cli) → **`ci-mobile`** (+ Java + Android SDK/NDK); plus **`ci-web`** (Node/pnpm/Bun, no Rust) as a sibling off `ci-base`. Root-friendly images for automated pipelines; tool paths under `/usr/local`, Android under `/opt/android-sdk`, Bun under `/usr/local/bun`. +- `dev-base` (unpublished, universal CLI + vim/ripgrep/fd/jq) → **`dev-rust`** → **`dev-desktop`** (adds X11 inspection tools too) → **`dev-mobile`**; plus **`dev-web`** off `dev-base`. Non-root devcontainer images (run as `vscode`); tool paths under `/home/vscode`, Android under `$HOME/Android/Sdk`, Bun under `$HOME/.bun`. -Both families install the GitHub CLI (`gh`) from GitHub's own apt repo (not the distro package) and pin Node/pnpm/Rust versions via build args at the top of the file. `cargo-tauri` is installed from a Tauri fork branch (`feat/truly-portable-appimage`) to get portable AppImage behaviour, paired with `TAURI_BUNDLER_NEW_APPIMAGE_FORMAT=true`. Images publish to `ghcr.io/liminal-hq/tauri-{ci,dev}-{desktop,mobile}`. +The GUI/Tauri system libraries live only in the desktop tiers — never in a base or lean tier. The Node+pnpm+Bun install block deliberately appears in both the web and Tauri-desktop tiers of each family (single inheritance can't give the desktop tier two parents); all sites consume the same `ARG` pins so versions can't drift. + +Both families install the GitHub CLI (`gh`) from GitHub's own apt repo (not the distro package) and pin Node/pnpm/Bun/Rust versions via build args at the top of the file. `cargo-tauri` is installed from a Tauri fork branch (`feat/truly-portable-appimage`) to get portable AppImage behaviour, paired with `TAURI_BUNDLER_NEW_APPIMAGE_FORMAT=true`. Images publish to `ghcr.io/liminal-hq/{ci,dev}-{rust,web}` and `ghcr.io/liminal-hq/tauri-{ci,dev}-{desktop,mobile}`. Smoke checks live in `docker/ci/smoke-check.sh` (per-tier profiles, including leanness assertions on `ci-rust`/`ci-web`). Keep version pins aligned across the CI and dev chains, and keep target names using the `ci-`/`dev-` prefixes — see `docs/reference/shared-image-layout.md` for the full layout contract and `docs/reference/shared-image-implementation-spec.md` for the implementation spec. ### Image publish workflow (`.github/workflows/shared-tauri-ci-images.yml`) -Builds and pushes the four images above on push to `main` (when `docker/ci/**` or the workflow itself changes), on `workflow_dispatch`, and on a weekly `schedule` gated to a two-week publish cadence (an ISO week-number parity check in the `cadence` job). Each image build does a local single-platform "smoke" build/run first, then the real (possibly multi-platform) build/push. +Builds and pushes the eight images above on push to `main` (when `docker/ci/**` or the workflow itself changes), on `workflow_dispatch`, and on a weekly `schedule` gated to a two-week publish cadence (an ISO week-number parity check in the `cadence` job). Each image build does a local single-platform "smoke" build/run first (via `docker/ci/smoke-check.sh`), then the real build/push. -`ci-desktop` is the only multi-platform image (`linux/amd64` + `linux/arm64`) and is handled specially to avoid QEMU emulation: `build-ci-desktop` builds each platform natively on its own runner (including GitHub's native `ubuntu-24.04-arm` runner) and pushes by digest, then `merge-ci-desktop` combines the digests into one multi-arch manifest via `docker buildx imagetools create`. The other three images build directly in a single matrixed job (`publish-images`). +`ci-rust`, `ci-web`, and `ci-desktop` are multi-platform (`linux/amd64` + `linux/arm64`) and are handled specially to avoid QEMU emulation: `build-multiarch` builds each image×platform combination natively on its own runner (including GitHub's native `ubuntu-24.04-arm` runner) and pushes by digest, then `merge-multiarch` combines each image's digests into one multi-arch manifest via `docker buildx imagetools create`. The five single-platform images build directly in one matrixed job (`publish-images`). ### Reusable AppImage packaging workflow (`.github/workflows/package-arch-appimage.yml`) diff --git a/README.md b/README.md index f24079b..06cc698 100644 --- a/README.md +++ b/README.md @@ -8,29 +8,35 @@ This repository is the shared home for Liminal HQ CI infrastructure, container i - Shared GitHub Actions workflows for CI image publication - Shared, reusable GitHub Actions workflows consumer repos call directly (e.g. AppImage packaging) -- Shared Docker images for Tauri desktop and mobile workloads +- Shared Docker images in granular tiers: pure Rust, JS/TS, Tauri desktop, and Tauri mobile - Runbooks for publish, rollback, and digest pinning ## Shared Images -- `ghcr.io/liminal-hq/tauri-ci-desktop` -- `ghcr.io/liminal-hq/tauri-ci-mobile` -- `ghcr.io/liminal-hq/tauri-dev-desktop` -- `ghcr.io/liminal-hq/tauri-dev-mobile` +Pick the leanest tier that covers the repo's toolchain: + +- `ghcr.io/liminal-hq/ci-rust` — Rust toolchain + clippy/rustfmt/cargo-nextest (pure-Rust CI) +- `ghcr.io/liminal-hq/ci-web` — Node + pnpm + Bun (JS/TS-only CI) +- `ghcr.io/liminal-hq/tauri-ci-desktop` — Rust + JS runtimes + Tauri desktop system stack +- `ghcr.io/liminal-hq/tauri-ci-mobile` — desktop stack + Java + Android SDK/NDK +- `ghcr.io/liminal-hq/dev-rust` — Rust toolchain for devcontainers/toolbox use +- `ghcr.io/liminal-hq/dev-web` — JS runtimes for devcontainers/toolbox use +- `ghcr.io/liminal-hq/tauri-dev-desktop` — Tauri desktop devcontainers +- `ghcr.io/liminal-hq/tauri-dev-mobile` — Tauri Android devcontainers ## Platform Support -- `tauri-ci-desktop` publishes `linux/amd64` and `linux/arm64`. +- `ci-rust`, `ci-web`, and `tauri-ci-desktop` publish `linux/amd64` and `linux/arm64`. - `tauri-ci-mobile` currently publishes `linux/amd64` only. -- `tauri-dev-desktop` currently publishes `linux/amd64` only. -- `tauri-dev-mobile` currently publishes `linux/amd64` only. +- Dev images (`dev-rust`, `dev-web`, `tauri-dev-desktop`, `tauri-dev-mobile`) currently publish `linux/amd64` only. -The ARM variant exists today to support downstream Linux ARM runners such as `ubuntu-24.04-arm` release jobs that consume `tauri-ci-desktop`. +The ARM variants exist today to support downstream Linux ARM runners such as `ubuntu-24.04-arm` release and binary-compile jobs. ## Image Families - CI images are for GitHub Actions and other automated pipelines that want a lean, root-friendly toolchain baseline. -- Dev images are for devcontainers and interactive local work, with a non-root user-home layout for Cargo, Rustup, pnpm, and Android tooling. +- Dev images are for devcontainers and interactive local work — including host-side toolbox use — with a non-root user-home layout for Cargo, Rustup, pnpm, Bun, and Android tooling. +- Every tier that carries JavaScript tooling ships both pnpm (via Node) and Bun, so pnpm-based and Bun-based repos use the same images. - Both image families include the GitHub CLI (`gh`) for release, issue, and workflow operations that run inside the shared containers. ## GitHub CLI Auth @@ -46,8 +52,12 @@ The ARM variant exists today to support downstream Linux ARM runners such as `ub ## Layout Scheme - Docker targets: + - `ci-rust` + - `ci-web` - `ci-desktop` - `ci-mobile` + - `dev-rust` + - `dev-web` - `dev-desktop` - `dev-mobile` - Shared image layout reference: [`docs/reference/shared-image-layout.md`](https://github.com/liminal-hq/.github/blob/main/docs/reference/shared-image-layout.md) diff --git a/docs/reference/shared-image-implementation-spec.md b/docs/reference/shared-image-implementation-spec.md index 2a2bbff..e6bf22f 100644 --- a/docs/reference/shared-image-implementation-spec.md +++ b/docs/reference/shared-image-implementation-spec.md @@ -1,71 +1,79 @@ # Shared Image Implementation Spec -This document describes the intended implementation and operating contract for the shared Tauri image families published from this repository. +This document describes the intended implementation and operating contract for the shared image families published from this repository. It exists to make the image architecture, stage layout, workflow responsibilities, and verification expectations explicit for maintainers and downstream consumers. ## Scope -The shared image system covers two related image families: +The shared image system covers two related image families, each built from granular single-concern tiers: 1. CI images for automated pipelines -2. Dev images for devcontainers and interactive local development +2. Dev images for devcontainers, interactive local development, and host-side toolbox use Published image set: | Image | Docker target | Primary use | | --- | --- | --- | -| `ghcr.io/liminal-hq/tauri-ci-desktop` | `ci-desktop` | Desktop CI jobs | +| `ghcr.io/liminal-hq/ci-rust` | `ci-rust` | Pure-Rust CI jobs | +| `ghcr.io/liminal-hq/ci-web` | `ci-web` | JS/TS-only CI jobs | +| `ghcr.io/liminal-hq/tauri-ci-desktop` | `ci-desktop` | Tauri desktop CI jobs | | `ghcr.io/liminal-hq/tauri-ci-mobile` | `ci-mobile` | Android/mobile CI jobs | +| `ghcr.io/liminal-hq/dev-rust` | `dev-rust` | Pure-Rust devcontainers and toolbox use | +| `ghcr.io/liminal-hq/dev-web` | `dev-web` | JS/TS devcontainers and toolbox use | | `ghcr.io/liminal-hq/tauri-dev-desktop` | `dev-desktop` | Desktop devcontainers | | `ghcr.io/liminal-hq/tauri-dev-mobile` | `dev-mobile` | Android/mobile devcontainers | -The platform contract is intentionally asymmetric today: `tauri-ci-desktop` publishes both `linux/amd64` and `linux/arm64`, while the mobile and dev image families currently publish `linux/amd64` only. +The platform contract is intentionally asymmetric: the `ci-rust`, `ci-web`, and `tauri-ci-desktop` images publish both `linux/amd64` and `linux/arm64`, while the mobile and dev image families currently publish `linux/amd64` only. ## Design Goals -1. Keep the CI image family stable for current consumers. -2. Add devcontainer-oriented images without forcing CI images to absorb interactive-user assumptions. -3. Keep version pins aligned across CI and dev families. +1. Keep every published image single-concern-per-tier: consumers pull exactly the toolchain they need and nothing heavier. +2. Keep the pre-existing `tauri-ci-*` / `tauri-dev-*` names, environment models, and tag policy stable for current consumers. +3. Keep version pins aligned across all tiers and both families through shared `ARG` values. 4. Make Docker targets and published image names easy to reason about. -5. Catch regressions during publication with lightweight smoke validation. +5. Catch regressions during publication with per-tier smoke validation, including leanness checks on the lean tiers. ## Dockerfile Architecture -The shared Dockerfile intentionally separates: - -- intermediate setup stages -- final CI targets -- final dev targets +The shared Dockerfile builds two parallel tier chains. Each tier adds exactly one concern to its parent; GUI/Tauri system libraries live only in the desktop tiers. ### Stage Layout #### CI stages -- `ci-base` - - Ubuntu-based shared package baseline for Tauri desktop CI builds -- `ci-toolchain` - - installs pinned Node, pnpm, Rust, `cargo-nextest`, and `cargo-tauri` - - uses root-friendly tool paths +- `ci-base` (unpublished) + - Ubuntu base with universal CLI tooling only: certificates, curl/wget, git, gh, file, build-essential, pkg-config, zip/unzip +- `ci-rust` + - adds the pinned Rust toolchain, clippy, rustfmt, and `cargo-nextest` + - root-friendly tool paths under `/usr/local` +- `ci-web` + - adds pinned Node, pnpm, and Bun on `ci-base` (no Rust) - `ci-desktop` - - final desktop CI image + - builds on `ci-rust` + - adds the JS runtimes (same pins as `ci-web`), the GTK/webkit2gtk/appindicator/rsvg system stack, patchelf/xdg-utils/libssl-dev, GStreamer (base + bad), emoji fonts, xvfb, and `cargo-tauri` - `ci-mobile` - - final mobile CI image - - extends the CI toolchain with Java, Android SDK/NDK, and Android Rust targets + - builds on `ci-desktop` + - adds Java, Android SDK/NDK, and Android Rust targets #### Dev stages -- `dev-base` - - devcontainer-oriented base image - - interactive development packages and desktop-friendly tooling -- `dev-toolchain` - - installs pinned Node, pnpm, Rust, `cargo-nextest`, and `cargo-tauri` - - uses a Threshold-style non-root home-directory tool layout +- `dev-base` (unpublished) + - devcontainer-oriented base image with universal CLI tooling and interactive quality-of-life packages (vim, ripgrep, fd, jq) +- `dev-rust` + - adds the pinned Rust toolchain, clippy, rustfmt, and `cargo-nextest` under the user home +- `dev-web` + - adds pinned Node, pnpm, and Bun with user-home tool paths (no Rust) - `dev-desktop` - - final desktop devcontainer image + - builds on `dev-rust` + - adds the JS runtimes, the GUI system stack, GStreamer/xvfb/emoji fonts, X11 inspection tools (xprop/xev via x11-utils, wmctrl, xdotool), and `cargo-tauri` - `dev-mobile` - - final mobile devcontainer image - - extends the dev toolchain with Java, Android SDK/NDK, and Android Rust targets + - builds on `dev-desktop` + - adds Java and Android SDK/NDK under the user home + +### JS runtime duplication + +The Node + pnpm + Bun install block appears in both the web tier and the Tauri desktop tier of each family. This is deliberate: single inheritance cannot give the desktop tier two parents, and every install site consumes the same shared `ARG` pins (`NODE_MAJOR`, `PNPM_VERSION`, `BUN_VERSION`), so versions cannot drift between tiers. ## Environment Model @@ -75,46 +83,42 @@ CI images remain root-friendly and automation-oriented. Expected paths: -- `RUSTUP_HOME=/usr/local/rustup` -- `CARGO_HOME=/usr/local/cargo` +- Rust tiers (`ci-rust`, `ci-desktop`, `ci-mobile`): + - `RUSTUP_HOME=/usr/local/rustup` + - `CARGO_HOME=/usr/local/cargo` +- JS tiers (`ci-web`, `ci-desktop`, `ci-mobile`): + - `BUN_INSTALL=/usr/local/bun` - mobile images: - `ANDROID_HOME=/opt/android-sdk` - `ANDROID_SDK_ROOT=/opt/android-sdk` ### Dev images -Dev images are optimised for non-root devcontainer use. +Dev images are optimised for non-root devcontainer and toolbox use. Expected paths: - `HOME=/home/vscode` -- `RUSTUP_HOME=$HOME/.rustup` -- `CARGO_HOME=$HOME/.cargo` -- `PNPM_HOME=$HOME/.local/share/pnpm` -- mobile images: - - `ANDROID_HOME=$HOME/Android/Sdk` - - `ANDROID_SDK_ROOT=$HOME/Android/Sdk` - -Dev images should pre-create writable user-owned directories for: +- Rust tiers: `RUSTUP_HOME=$HOME/.rustup`, `CARGO_HOME=$HOME/.cargo` +- JS tiers: `PNPM_HOME=$HOME/.local/share/pnpm`, `BUN_INSTALL=$HOME/.bun` +- mobile images: `ANDROID_HOME=$HOME/Android/Sdk`, `ANDROID_SDK_ROOT=$HOME/Android/Sdk` -- `.cargo` -- `.rustup` -- `.local/share/pnpm` -- `Android/Sdk` where applicable +Dev images should pre-create writable user-owned directories for the tool paths their tier owns (`.cargo`/`.rustup` on Rust tiers, `.local/share/pnpm` on JS tiers, `Android/Sdk` where applicable). ## Shared Versioning Rules -The image families should stay aligned on: +The image tiers stay aligned on: - Ubuntu baseline - Node major version - pnpm version +- Bun version - Rust toolchain version - Android command-line tools version - Android platform, build-tools, and NDK versions - Tauri CLI source and branch -When these values change, update them centrally in the shared Dockerfile rather than drifting CI and dev families independently. +When these values change, update them centrally in the shared Dockerfile `ARG`s rather than drifting tiers or families independently. ## Workflow Contract @@ -123,13 +127,13 @@ The shared image publication workflow is responsible for: 1. evaluating the publish cadence 2. publishing all intended image families 3. applying a consistent tag policy -4. running smoke validation before push +4. running smoke validation (via `docker/ci/smoke-check.sh`) before push 5. reusing persistent build cache across runs 6. writing digest and tag summaries for each image ### Cadence -- Pushes to `main` that touch the Dockerfile or workflow should publish. +- Pushes to `main` that touch `docker/ci/**` or the workflow should publish. - Manual dispatch should publish. - Scheduled runs should follow the configured bi-weekly cadence gate. @@ -137,68 +141,36 @@ The shared image publication workflow is responsible for: Each published image should receive: -1. `latest` +1. `latest` (on `main`) or `staging` (on other refs) 2. `sha-` -3. `YYYYMMDD` +3. `YYYYMMDD` (scheduled runs) ### Platform policy -Initial expected platform coverage: - | Image | Platforms | | --- | --- | +| `ci-rust` | `linux/amd64`, `linux/arm64` | +| `ci-web` | `linux/amd64`, `linux/arm64` | | `tauri-ci-desktop` | `linux/amd64`, `linux/arm64` | | `tauri-ci-mobile` | `linux/amd64` | +| `dev-rust` | `linux/amd64` | +| `dev-web` | `linux/amd64` | | `tauri-dev-desktop` | `linux/amd64` | | `tauri-dev-mobile` | `linux/amd64` | -The `linux/arm64` desktop CI variant is required by downstream Linux ARM release jobs that run on `ubuntu-24.04-arm`. +The `linux/arm64` CI variants are required by downstream Linux ARM jobs that run on `ubuntu-24.04-arm` (desktop releases, Rust release builds, and ARM binary-compile jobs). + +Multi-platform CI images build each platform natively (amd64 on `ubuntu-24.04`, arm64 on `ubuntu-24.04-arm`), push by digest, and merge digests into one manifest — QEMU emulation of the from-source cargo installs is deliberately avoided. If multi-arch dev images or mobile images become necessary later, that should be treated as an explicit follow-up rather than assumed by default. ## Smoke Validation Contract -Publication should validate the image before pushing it. - -### CI desktop - -Minimum checks: - -- `cargo` -- `rustup` -- `node` -- `pnpm` -- `cargo-tauri` -- `gh` -- version commands for core tooling - -### CI mobile +Publication validates every image before pushing it, using the per-tier profiles in `docker/ci/smoke-check.sh`. The profiles enforce three kinds of contract: -Minimum checks: - -- CI desktop checks -- `sdkmanager` -- `java` -- Android version command checks - -### Dev desktop - -Minimum checks: - -- CI desktop checks -- confirm effective user is non-root -- confirm user-home tool paths are set correctly -- confirm writable paths for Cargo and pnpm locations - -### Dev mobile - -Minimum checks: - -- dev desktop checks -- confirm Android SDK path is under the devcontainer user home -- confirm writable Android path access -- `sdkmanager` -- `java` +1. **Tool availability** — the tier's tools exist and respond to `--version` (`cargo`/`rustup`/`cargo-nextest` on Rust tiers; `node`/`pnpm`/`bun` on JS tiers; `cargo-tauri` on Tauri tiers; `sdkmanager`/`java` on mobile tiers; `gh` everywhere). +2. **Leanness** — lean CI tiers must not contain other tiers' toolchains (`ci-rust` has no Node or Bun; `ci-web` has no cargo). +3. **Dev ergonomics** — dev images run as a non-root user, expose the expected user-home environment variables, and have writable tool paths. ## GitHub CLI Authentication @@ -220,7 +192,7 @@ Expected behaviour: - publish builds write the refreshed cache back to the registry for future runs - cache references stay image-specific rather than sharing one global cache across all targets -This keeps repeat runs faster while avoiding cache collisions between desktop, mobile, CI, and dev image families. +Because the tiers share ancestor stages (`ci-base`, `ci-rust`, `dev-base`, `dev-rust`), per-image caches will contain overlapping layers; this is expected and keeps cache keys simple. Repeat runs stay fast while avoiding cache collisions between image families. ## Smoke Build Strategy @@ -229,10 +201,8 @@ Smoke validation should use Buildx rather than a separate plain `docker build`. Expected behaviour: - smoke builds load a single-platform image into the local Docker engine for `docker run` validation -- smoke validation targets the runner-compatible platform only -- final publish builds remain the place where multi-platform images are assembled and pushed - -This avoids maintaining two disconnected build paths and improves cache reuse between smoke validation and the final publish step. +- smoke validation targets the runner-compatible platform only (each platform of a multi-arch image is smoke-checked on its own native runner) +- final publish builds remain the place where multi-platform manifests are assembled and pushed ## Documentation Contract @@ -243,7 +213,7 @@ When the shared image layout changes, keep these docs aligned: - `docs/reference/shared-image-implementation-spec.md` - `docs/runbooks/image-publish-and-rollback.md` -The layout doc should explain the image family from a consumer perspective. +The layout doc should explain the image tiers from a consumer perspective. This implementation spec should explain the architecture and maintainer contract. @@ -251,6 +221,13 @@ The runbook should explain how to publish, validate, roll back, and consume the ## Consumer Guidance +Pick the leanest tier that covers the repo's toolchain: + +- Pure Rust: `ci-rust` / `dev-rust` +- JS/TS only: `ci-web` / `dev-web` +- Tauri desktop (pnpm or Bun): `tauri-ci-desktop` / `tauri-dev-desktop` +- Tauri Android/mobile: `tauri-ci-mobile` / `tauri-dev-mobile` + Use the CI images when: - the workload is a GitHub Actions or other automated pipeline job @@ -259,7 +236,7 @@ Use the CI images when: Use the dev images when: -- the workload is a devcontainer +- the workload is a devcontainer or an interactive host-side toolbox container - the default user is non-root - writable tool paths are needed without repo-local overrides - interactive local development ergonomics matter @@ -268,7 +245,8 @@ Use the dev images when: These items are intentionally out of scope for the shared image contract unless a later issue expands the design: +- per-JS-runtime image splits (`ci-bun` / `ci-node-pnpm`) — both runtimes ship together in the JS tiers - emulator tooling in the mobile dev image -- repo-specific developer customisations +- repo-specific developer customisations (e.g. ffmpeg for codec validation — layer those in the consuming repo) - per-repo mounted cache contracts as a required baseline - automatic downstream rollout to consumer repositories diff --git a/docs/reference/shared-image-layout.md b/docs/reference/shared-image-layout.md index c505e0d..0a4b82f 100644 --- a/docs/reference/shared-image-layout.md +++ b/docs/reference/shared-image-layout.md @@ -1,82 +1,128 @@ # Shared Image Layout -This repository publishes two related Tauri image families: +This repository publishes two related image families built from granular tiers: 1. CI images for automated pipelines -2. Dev images for devcontainers and interactive local development +2. Dev images for devcontainers, interactive local development, and host-side toolbox use + +Each tier adds exactly one concern on top of its parent, so consumer repos pick the leanest image that covers their toolchain instead of inheriting everything. ## Published Images -| Image | Docker target | Primary use | -| --- | --- | --- | -| `ghcr.io/liminal-hq/tauri-ci-desktop` | `ci-desktop` | Desktop CI jobs | -| `ghcr.io/liminal-hq/tauri-ci-mobile` | `ci-mobile` | Android/mobile CI jobs | -| `ghcr.io/liminal-hq/tauri-dev-desktop` | `dev-desktop` | Desktop devcontainers | -| `ghcr.io/liminal-hq/tauri-dev-mobile` | `dev-mobile` | Android/mobile devcontainers | +| Image | Docker target | Tier contents | Primary consumers | +| --- | --- | --- | --- | +| `ghcr.io/liminal-hq/ci-rust` | `ci-rust` | Rust toolchain (pinned), clippy, rustfmt, cargo-nextest, gh | Pure-Rust libraries, CLIs, and TUIs | +| `ghcr.io/liminal-hq/ci-web` | `ci-web` | Node, pnpm, Bun (pinned), gh | JS/TS-only projects | +| `ghcr.io/liminal-hq/tauri-ci-desktop` | `ci-desktop` | ci-rust + Node/pnpm/Bun + GTK/webkit2gtk system stack + GStreamer/xvfb/emoji fonts + tauri-cli | Tauri desktop CI jobs | +| `ghcr.io/liminal-hq/tauri-ci-mobile` | `ci-mobile` | ci-desktop + Java 17 + Android SDK/NDK + Android Rust targets | Android/mobile CI jobs | +| `ghcr.io/liminal-hq/dev-rust` | `dev-rust` | Rust toolchain under the user home | Pure-Rust devcontainers and toolbox use | +| `ghcr.io/liminal-hq/dev-web` | `dev-web` | Node/pnpm/Bun with user-home tool paths | JS/TS devcontainers and toolbox use | +| `ghcr.io/liminal-hq/tauri-dev-desktop` | `dev-desktop` | dev-rust + JS runtimes + GUI stack + X11 inspection tools + tauri-cli | Desktop devcontainers | +| `ghcr.io/liminal-hq/tauri-dev-mobile` | `dev-mobile` | dev-desktop + Java 17 + Android SDK/NDK | Android/mobile devcontainers | + +## Tier Tree + +```text +ci-base (unpublished) universal CLI: certs, curl, wget, git, gh, file, +│ build-essential, pkg-config, zip, unzip +├── ci-rust + rustup (pinned) + clippy + rustfmt + cargo-nextest +│ └── ci-desktop + Node/pnpm/Bun + GTK/webkit stack + GStreamer/xvfb/ +│ │ emoji fonts + tauri-cli +│ └── ci-mobile + Java 17 + Android SDK/NDK + Android Rust targets +└── ci-web + Node/pnpm/Bun + +dev-base (unpublished) devcontainers base + universal CLI + vim/ripgrep/fd/jq +├── dev-rust + rustup (pinned) + clippy + rustfmt + cargo-nextest +│ └── dev-desktop + Node/pnpm/Bun + GUI stack + GStreamer/xvfb/fonts + +│ │ X11 inspection tools (xprop/xev/wmctrl/xdotool) + +│ │ tauri-cli +│ └── dev-mobile + Java 17 + Android SDK/NDK under $HOME +└── dev-web + Node/pnpm/Bun with user-home tool paths +``` + +The JS runtime install block (Node + pnpm + Bun) intentionally appears in both the web tiers and the Tauri desktop tiers: single inheritance cannot give the desktop tier two parents, and all sites install from the same shared `ARG` pins so versions cannot drift. ## Platform Coverage | Image | Platforms | Notes | | --- | --- | --- | -| `ghcr.io/liminal-hq/tauri-ci-desktop` | `linux/amd64`, `linux/arm64` | Multi-arch because downstream Linux desktop release jobs already run on both x64 and ARM runners. | -| `ghcr.io/liminal-hq/tauri-ci-mobile` | `linux/amd64` | ARM is not part of the current shared mobile CI contract. | -| `ghcr.io/liminal-hq/tauri-dev-desktop` | `linux/amd64` | Devcontainer ARM support is a possible follow-up, not a current baseline. | -| `ghcr.io/liminal-hq/tauri-dev-mobile` | `linux/amd64` | Devcontainer ARM support is a possible follow-up, not a current baseline. | +| `ci-rust` | `linux/amd64`, `linux/arm64` | Multi-arch because Rust release jobs already run on both x64 and ARM runners. | +| `ci-web` | `linux/amd64`, `linux/arm64` | Multi-arch for ARM binary-compile jobs (e.g. Bun `--target` builds on `ubuntu-24.04-arm`). | +| `tauri-ci-desktop` | `linux/amd64`, `linux/arm64` | Multi-arch because downstream Linux desktop release jobs already run on both x64 and ARM runners. | +| `tauri-ci-mobile` | `linux/amd64` | ARM is not part of the current shared mobile CI contract. | +| `dev-rust` | `linux/amd64` | Dev-family ARM support is a possible follow-up, not a current baseline. | +| `dev-web` | `linux/amd64` | Dev-family ARM support is a possible follow-up, not a current baseline. | +| `tauri-dev-desktop` | `linux/amd64` | Dev-family ARM support is a possible follow-up, not a current baseline. | +| `tauri-dev-mobile` | `linux/amd64` | Dev-family ARM support is a possible follow-up, not a current baseline. | -Only the desktop CI image is multi-arch today. Consumers that need Linux ARM should use `tauri-ci-desktop` and should not assume ARM variants exist for the mobile or dev image families. +Consumers that need Linux ARM should use the multi-arch CI images and should not assume ARM variants exist for the mobile or dev image families. ## Design Intent ### CI Images -- Keep them lean and predictable +- Keep them lean and predictable — each tier carries only its own concern - Keep them suitable for root-friendly automation -- Preserve the existing CI contract for shared consumers +- Preserve the existing `tauri-ci-*` contract for shared consumers ### Dev Images - Use a devcontainer-friendly base image - Default to a non-root user model - Put writable tool paths under the user home -- Reduce repo-local overrides for Cargo, Rustup, pnpm, and Android SDK paths +- Serve both devcontainer use and interactive host-side toolbox use +- Reduce repo-local overrides for Cargo, Rustup, pnpm, Bun, and Android SDK paths + +## Choosing an Image + +1. Pure Rust (libraries, CLIs, TUIs): `ci-rust` / `dev-rust` +2. JS/TS only (sites, CLIs, Bun-compiled binaries): `ci-web` / `dev-web` +3. Tauri desktop (with either pnpm or Bun): `tauri-ci-desktop` / `tauri-dev-desktop` +4. Tauri Android/mobile: `tauri-ci-mobile` / `tauri-dev-mobile` + +Both JS runtimes (pnpm via Node, plus Bun) ship in every image tier that carries JavaScript tooling, so pnpm-based and Bun-based repos use the same images. ## Expected Environment Model ### CI Images -- `CARGO_HOME=/usr/local/cargo` -- `RUSTUP_HOME=/usr/local/rustup` +- `CARGO_HOME=/usr/local/cargo` (Rust tiers) +- `RUSTUP_HOME=/usr/local/rustup` (Rust tiers) +- `BUN_INSTALL=/usr/local/bun` (JS tiers) - mobile images install Android tooling under `/opt/android-sdk` ### Dev Images - `HOME=/home/vscode` -- `CARGO_HOME=$HOME/.cargo` -- `RUSTUP_HOME=$HOME/.rustup` -- `PNPM_HOME=$HOME/.local/share/pnpm` +- `CARGO_HOME=$HOME/.cargo` (Rust tiers) +- `RUSTUP_HOME=$HOME/.rustup` (Rust tiers) +- `PNPM_HOME=$HOME/.local/share/pnpm` (JS tiers) +- `BUN_INSTALL=$HOME/.bun` (JS tiers) - mobile images install Android tooling under `$HOME/Android/Sdk` ## Layout Rules -1. Keep version pins aligned across CI and dev image families. +1. Keep version pins aligned across CI and dev image families through the shared `ARG` values at the top of the Dockerfile. 2. Keep final CI targets clearly named with the `ci-` prefix. 3. Keep final dev targets clearly named with the `dev-` prefix. -4. Keep emulator tooling out of the shared mobile dev image unless a real consumer need appears. -5. Prefer shared intermediate stages where that does not compromise CI or dev ergonomics. +4. Keep each tier single-concern: GUI/Tauri system libraries live only in the desktop tiers, never in a base or lean tier. +5. Keep emulator tooling out of the shared mobile dev image unless a real consumer need appears. +6. Prefer shared intermediate stages where that does not compromise CI or dev ergonomics. ## Validation Expectations -Every published image should be smoke-validated for: +Every published image is smoke-validated by `docker/ci/smoke-check.sh` (invoked by the publish workflow before push) for: -- core tool availability: `cargo`, `rustup`, `node`, `pnpm`, `cargo-tauri`, `gh` +- tier tool availability (e.g. `cargo`/`rustup`/`cargo-nextest` on Rust tiers, `node`/`pnpm`/`bun` on JS tiers, `cargo-tauri` on Tauri tiers, `gh` everywhere) +- tier leanness on the lean CI tiers (`ci-rust` must not contain Node or Bun; `ci-web` must not contain cargo) - Android tooling on mobile images - writable user-home paths on dev images - expected environment variables for the image family -- expected platform coverage for the image family, especially the `linux/arm64` variant on `tauri-ci-desktop` +- expected platform coverage for the image family `gh` installation is part of the shared image contract, but authentication is still environment-driven. GitHub Actions jobs should pass `GH_TOKEN` or `GITHUB_TOKEN` when invoking the CLI inside the container. -Smoke validation should build a single local runner-compatible image variant for fast checks. Multi-platform publication should happen only in the final publish step. +Smoke validation builds a single local runner-compatible image variant for fast checks. Multi-platform publication happens only in the final publish step. ## Related Docs diff --git a/docs/runbooks/image-publish-and-rollback.md b/docs/runbooks/image-publish-and-rollback.md index f198423..0369e04 100644 --- a/docs/runbooks/image-publish-and-rollback.md +++ b/docs/runbooks/image-publish-and-rollback.md @@ -3,47 +3,59 @@ ## Publish Workflow 1. Trigger `.github/workflows/shared-tauri-ci-images.yml` manually or via push/schedule. -2. Confirm the publish matrix completes for all intended image families: - - CI desktop - - CI mobile - - Dev desktop - - Dev mobile -3. Record published digest and tags from the step summary. +2. Confirm all jobs complete for the intended image set: + - `publish-images` matrix: CI mobile, dev Rust, dev web, dev desktop, dev mobile + - `build-multiarch` + `merge-multiarch`: CI Rust, CI web, CI desktop (per-platform native builds merged into one manifest) +3. Record published digest and tags from the step summaries. +4. First publish of a NEW image only: GHCR creates new packages with private visibility. Set the package to public (org settings → Packages → the new package → Change visibility) and connect it to this repository, or cross-org consumers (e.g. `ScottMorris/*` repos) cannot pull it and `GITHUB_TOKEN`-based pulls outside the org will fail. ## Image Families Published images: -1. `ghcr.io/liminal-hq/tauri-ci-desktop` +1. `ghcr.io/liminal-hq/ci-rust` Current platforms: `linux/amd64`, `linux/arm64` -2. `ghcr.io/liminal-hq/tauri-ci-mobile` +2. `ghcr.io/liminal-hq/ci-web` + Current platforms: `linux/amd64`, `linux/arm64` +3. `ghcr.io/liminal-hq/tauri-ci-desktop` + Current platforms: `linux/amd64`, `linux/arm64` +4. `ghcr.io/liminal-hq/tauri-ci-mobile` + Current platforms: `linux/amd64` +5. `ghcr.io/liminal-hq/dev-rust` Current platforms: `linux/amd64` -3. `ghcr.io/liminal-hq/tauri-dev-desktop` +6. `ghcr.io/liminal-hq/dev-web` Current platforms: `linux/amd64` -4. `ghcr.io/liminal-hq/tauri-dev-mobile` +7. `ghcr.io/liminal-hq/tauri-dev-desktop` + Current platforms: `linux/amd64` +8. `ghcr.io/liminal-hq/tauri-dev-mobile` Current platforms: `linux/amd64` -Only `tauri-ci-desktop` is multi-arch today. Downstream consumers should not assume ARM support exists for the mobile or dev image families unless that contract is expanded in a later change. +Downstream consumers should not assume ARM support exists for the mobile or dev image families unless that contract is expanded in a later change. Docker targets: -1. `ci-desktop` -2. `ci-mobile` -3. `dev-desktop` -4. `dev-mobile` +1. `ci-rust` +2. `ci-web` +3. `ci-desktop` +4. `ci-mobile` +5. `dev-rust` +6. `dev-web` +7. `dev-desktop` +8. `dev-mobile` Usage guidance: -1. Use `tauri-ci-*` images for CI and other automated pipeline contexts. -2. Use `tauri-dev-*` images for devcontainers and interactive local development. -3. Expect CI images to remain root-friendly and minimal. -4. Expect dev images to use a non-root home-directory layout for writable tool paths. +1. Pick the leanest tier that covers the repo's toolchain: `*-rust` for pure Rust, `*-web` for JS/TS only, `tauri-*-desktop` for Tauri desktop, `tauri-*-mobile` for Android. +2. Use CI images (`ci-rust`, `ci-web`, `tauri-ci-*`) for CI and other automated pipeline contexts. +3. Use dev images (`dev-rust`, `dev-web`, `tauri-dev-*`) for devcontainers and interactive local development or toolbox use. +4. Expect CI images to remain root-friendly and minimal. +5. Expect dev images to use a non-root home-directory layout for writable tool paths. ## Tag Policy Each image publish produces: -1. `latest` +1. `latest` (from `main`) or `staging` (from other refs) 2. `sha-` 3. `YYYYMMDD` schedule tag @@ -65,20 +77,23 @@ Each image publish produces: ## Contract Checks -Before promoting a new image to broad usage, validate: - -1. `command -v` checks for `cargo`, `rustup`, `node`, `pnpm`, `cargo-tauri` -2. Environment values: - - `TAURI_BUNDLER_NEW_APPIMAGE_FORMAT=true` +The publish workflow runs `docker/ci/smoke-check.sh ` against every image before pushing; the same script can be run locally against a candidate image. Before promoting a new image to broad usage, validate: + +1. Tool availability for the tier: + - Rust tiers: `cargo`, `rustup`, `cargo-nextest` + - JS tiers: `node`, `pnpm`, `bun` + - Tauri tiers: `cargo-tauri` + - mobile tiers: `sdkmanager`, `java` + - all tiers: `gh` +2. Leanness on lean CI tiers: + - `ci-rust` must not contain `node` or `bun` + - `ci-web` must not contain `cargo` +3. Environment values: + - `TAURI_BUNDLER_NEW_APPIMAGE_FORMAT=true` (Tauri tiers) - `JAVA_HOME` (mobile images) - `ANDROID_HOME` (mobile images) - - user-home tool paths for dev images -3. Smoke commands: - - `cargo --version` - - `node --version` - - `pnpm --version` - - `sdkmanager --version` (mobile images) + - user-home tool paths for dev images (`CARGO_HOME`, `RUSTUP_HOME`, `PNPM_HOME`, `BUN_INSTALL` as applicable) 4. Writable-path checks for dev images: - - `CARGO_HOME` - - `PNPM_HOME` + - `CARGO_HOME` (Rust tiers) + - `PNPM_HOME`, `BUN_INSTALL` (JS tiers) - `ANDROID_HOME` (dev mobile) diff --git a/docs/tracking/cross-repo-ci-alignment.md b/docs/tracking/cross-repo-ci-alignment.md index b2daee5..b0283a5 100644 --- a/docs/tracking/cross-repo-ci-alignment.md +++ b/docs/tracking/cross-repo-ci-alignment.md @@ -11,6 +11,22 @@ - [x] Wave 6: `threshold` migration - [ ] Wave 7: `liminal-notes` migration - [ ] Wave 8: Cross-repo normalisation +- [ ] Wave 9: Granular tier rollout (`ci-rust` / `ci-web` / Bun-in-Tauri-images; see liminal-hq/.github#22) + +## Granular Tier Adoption Targets (issue #22) + +- [ ] `liminal-hq/flow` → `ci-rust` +- [ ] `liminal-hq/flicker` → `ci-rust` +- [ ] `liminal-hq/libhdmv` → `ci-rust` +- [ ] `liminal-hq/libvc1` → `ci-rust` (CI) and `dev-rust` (local toolchain container) +- [ ] `liminal-hq/mudroom` → `ci-rust` (no CI today; adopt when CI is added) +- [ ] `liminal-hq/smdu` → `ci-web` (Linux jobs) +- [ ] `liminal-hq/coherence-chat-exporter` → `ci-web` (Linux jobs) +- [ ] `liminal-hq/the-lab` → `ci-web` +- [ ] `liminal-hq/liminal-hq.github.io` → `ci-web` +- [ ] `liminal-hq/foyer` → drop per-run Bun install (Bun now in `tauri-ci-mobile`) +- [ ] `liminal-hq/threshold` → move desktop jobs into `tauri-ci-desktop` (GStreamer/xvfb/fonts now included) +- [ ] `ScottMorris/city-sim-1000` → `tauri-ci-desktop` (drops per-run webkit apt installs) ## Repositories