ci: prebuild desktop E2E dependencies (#2789)

This commit is contained in:
Carlos Florêncio
2026-08-19 07:50:00 +01:00
committed by GitHub
parent 9a6240d1d4
commit 22472460a6
10 changed files with 516 additions and 37 deletions
+46 -2
View File
@@ -1,6 +1,6 @@
# Kandev CI base image — split into two tags.
# Kandev CI base image — split into three tags.
#
# Two stages published as separate tags on the same GHCR repo:
# Three stages published as separate tags on the same GHCR repo:
#
# - `runtime` (~700 MB): Playwright + Node + pnpm + Docker CLI. Used by the
# `e2e` shards and `e2e-report` — they consume pre-built backend artifacts
@@ -11,6 +11,9 @@
# the `build` job (compiles backend + web), `backend-tests`, and
# `frontend-tests`. These all need the Go toolchain.
#
# - `desktop`: runtime + Tauri Linux dependencies + Rust. Used by the
# desktop smoke job, so hosted-runner package mirrors are not in its setup.
#
# Pinned versions (keep in sync with apps/backend/go.mod, apps/web/package.json,
# and the matching workflow inputs):
# - Playwright base : v1.61.1-noble (matches @playwright/test ^1.61.1)
@@ -20,6 +23,7 @@
# - golangci-lint : v2.9.0 (matches v2.9 floating pin)
# - go-licenses : v1.6.0
# - Docker CLI : 27.3.1
# - Rust : 1.97.1 (pinned for reproducible desktop CI)
# =============================================================================
# Stage 1: runtime — Playwright + Node + pnpm + Docker CLI (no Go)
@@ -105,3 +109,43 @@ RUN set -eux; \
go version | grep -F 'go1.26.0'; \
golangci-lint --version | grep -F '2.9.0'; \
go version -m "$(which go-licenses)" | grep -F 'v1.6.0'
# =============================================================================
# Stage 3: desktop — runtime + Tauri Linux dependencies + Rust
# =============================================================================
FROM runtime AS desktop
ARG RUST_VERSION=1.97.1
ENV CARGO_HOME=/root/.cargo \
RUSTUP_HOME=/root/.rustup \
PATH=/pnpm:/root/.cargo/bin:$PATH
# Keep hosted-runner package mirrors out of the desktop smoke job. This stage
# also contains the compiler and rpm tooling used by the Tauri bundle step.
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
build-essential \
pkg-config \
libglib2.0-dev \
libwebkit2gtk-4.1-dev \
libgtk-3-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
patchelf \
rpm \
xvfb \
&& rm -rf /var/lib/apt/lists/*
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
| sh -s -- -y --default-toolchain none --profile minimal \
&& rustup toolchain install "${RUST_VERSION}" --profile minimal \
&& rustup default "${RUST_VERSION}"
# --- Desktop smoke checks --------------------------------------------------
RUN set -eux; \
rustc --version | grep -F "${RUST_VERSION}"; \
cargo --version; \
pkg-config --exists webkit2gtk-4.1; \
command -v patchelf; \
command -v xvfb-run
@@ -0,0 +1,107 @@
#!/usr/bin/env python3
"""Contract tests for the prebuilt desktop E2E image path."""
from pathlib import Path
import unittest
REPO_ROOT = Path(__file__).resolve().parents[2]
DOCKERFILE = REPO_ROOT / ".github" / "docker" / "ci-base" / "Dockerfile"
IMAGE_WORKFLOW = REPO_ROOT / ".github" / "workflows" / "ci-base-image.yml"
E2E_WORKFLOW = REPO_ROOT / ".github" / "workflows" / "e2e-tests.yml"
LINT_WORKFLOW = REPO_ROOT / ".github" / "workflows" / "lint-action-pinning.yml"
def job_block(workflow: str, job: str, next_job: str) -> str:
"""Return one workflow job block without parsing YAML anchors or expressions."""
marker = f" {job}:\n"
_, separator, remainder = workflow.partition(marker)
if not separator:
raise AssertionError(f"Workflow has no {job} job")
return remainder.partition(f"\n {next_job}:\n")[0]
class DesktopE2EWorkflowContractTest(unittest.TestCase):
def test_desktop_image_contains_pinned_toolchain_and_system_dependencies(self) -> None:
dockerfile = DOCKERFILE.read_text(encoding="utf-8")
self.assertIn("FROM runtime AS desktop", dockerfile)
self.assertIn("ARG RUST_VERSION=1.97.1", dockerfile)
self.assertIn("rustup toolchain install \"${RUST_VERSION}\" --profile minimal", dockerfile)
for package in (
"build-essential",
"pkg-config",
"libglib2.0-dev",
"libwebkit2gtk-4.1-dev",
"libgtk-3-dev",
"libayatana-appindicator3-dev",
"librsvg2-dev",
"patchelf",
"rpm",
"xvfb",
):
self.assertIn(package, dockerfile)
for smoke_command in (
"rustc --version",
"cargo --version",
"pkg-config --exists webkit2gtk-4.1",
"command -v patchelf",
"command -v xvfb-run",
):
self.assertIn(smoke_command, dockerfile)
def test_image_workflow_publishes_desktop_tags(self) -> None:
workflow = IMAGE_WORKFLOW.read_text(encoding="utf-8")
self.assertIn("target: desktop", workflow)
self.assertIn("desktop-sha-${{ steps.tag.outputs.image_tag }}", workflow)
self.assertIn("${{ env.IMAGE_NAME }}:desktop-latest", workflow)
self.assertIn("type=gha,scope=desktop", workflow)
self.assertIn("type=gha,scope=runtime", workflow)
self.assertIn("desktop-latest", workflow)
def test_desktop_job_uses_image_without_live_bootstrap_downloads(self) -> None:
workflow = E2E_WORKFLOW.read_text(encoding="utf-8")
desktop_job = job_block(workflow, "desktop-e2e", "e2e-report")
self.assertIn("image: ghcr.io/kdlbs/kandev-ci:desktop-latest", desktop_job)
self.assertIn("options: --ipc=host", desktop_job)
self.assertIn("git config --global --add safe.directory", desktop_job)
self.assertIn("path: ~/.local/share/pnpm/store", desktop_job)
self.assertIn("pnpm install --frozen-lockfile", desktop_job)
self.assertIn("pnpm --filter @kandev/desktop e2e", desktop_job)
for forbidden in (
"pnpm/action-setup",
"actions/setup-node",
"rustup toolchain install",
"apt-get",
"sudo",
):
self.assertNotIn(forbidden, desktop_job)
changes_job = job_block(workflow, "changes", "build")
for pattern in (
".github/docker/ci-base/**",
".github/workflows/ci-base-image.yml",
):
self.assertIn(pattern, changes_job)
def test_contract_runs_in_the_unfiltered_required_workflow(self) -> None:
workflow = LINT_WORKFLOW.read_text(encoding="utf-8")
self.assertIn(
"python3 .github/scripts/e2e-tests-workflow-contract_test.py",
workflow,
)
for trigger in ("push", "pull_request", "merge_group"):
trigger_marker = f" {trigger}:"
_, separator, trigger_block_text = workflow.partition(trigger_marker)
self.assertTrue(separator, f"Lint workflow has no {trigger} trigger")
self.assertNotIn(" paths:", trigger_block_text.split("\n ", 1)[0])
if __name__ == "__main__":
unittest.main()
+19
View File
@@ -132,6 +132,24 @@ jobs:
${{ env.IMAGE_NAME }}:build-sha-${{ steps.tag.outputs.image_tag }}
${{ env.IMAGE_NAME }}:build-latest
# Stage 3: desktop (runtime + Tauri Linux dependencies + Rust).
# Consumed by the host-independent desktop smoke job.
- name: Build and push desktop
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
with:
context: .
file: .github/docker/ci-base/Dockerfile
target: desktop
platforms: linux/amd64
push: ${{ github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/main') }}
cache-from: |
type=gha,scope=desktop
type=gha,scope=runtime
cache-to: type=gha,scope=desktop,mode=max
tags: |
${{ env.IMAGE_NAME }}:desktop-sha-${{ steps.tag.outputs.image_tag }}
${{ env.IMAGE_NAME }}:desktop-latest
- name: Summarize
run: |
{
@@ -139,6 +157,7 @@ jobs:
echo ""
echo "- runtime: \`${IMAGE_NAME}:runtime-sha-${IMAGE_TAG}\` / \`runtime-latest\`"
echo "- build: \`${IMAGE_NAME}:build-sha-${IMAGE_TAG}\` / \`build-latest\`"
echo "- desktop: \`${IMAGE_NAME}:desktop-sha-${IMAGE_TAG}\` / \`desktop-latest\`"
echo "- postgres: \`${POSTGRES_TARGET}@${POSTGRES_DIGEST}\`"
echo "- pushed: \`${PUSHED}\`"
} >> "$GITHUB_STEP_SUMMARY"
+16 -27
View File
@@ -67,6 +67,8 @@ jobs:
apps/packages/**
apps/pnpm-lock.yaml
apps/pnpm-workspace.yaml
.github/docker/ci-base/**
.github/workflows/ci-base-image.yml
.github/workflows/e2e-tests.yml
.github/scripts/changed-paths.py
!**/*.md
@@ -602,8 +604,11 @@ jobs:
needs: changes
if: needs.changes.outputs.run == 'true'
runs-on: ubuntu-latest
# Includes Ubuntu WebKit/Tauri system package installation; GitHub apt mirrors
# can spend more than 20 minutes there before tests start.
# The desktop image contains the Rust toolchain, Tauri dependencies, and
# Xvfb. This job does not depend on hosted-runner apt mirrors.
container:
image: ghcr.io/kdlbs/kandev-ci:desktop-latest
options: --ipc=host
timeout-minutes: 45
defaults:
run:
@@ -615,20 +620,17 @@ jobs:
with:
persist-credentials: false
- name: Set up pnpm
uses: pnpm/action-setup@a8198c4bff370c8506180b035930dea56dbd5288 # v5
with:
version: "9.15.9"
- name: Mark workspace safe for git
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
# The desktop image uses /root as HOME, so this matches the store path
# used by the other container jobs.
- name: Cache pnpm store
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
node-version: "24"
cache: pnpm
cache-dependency-path: apps/pnpm-lock.yaml
- name: Set up Rust
run: rustup toolchain install stable --profile minimal
path: ~/.local/share/pnpm/store
key: pnpm-${{ runner.os }}-${{ hashFiles('apps/pnpm-lock.yaml') }}
restore-keys: pnpm-${{ runner.os }}-
- name: Cache Rust build
uses: Swatinem/rust-cache@42dc69e1aa15d09112580998cf2ef0119e2e91ae # v2
@@ -636,19 +638,6 @@ jobs:
workspaces: apps/desktop/src-tauri -> target
key: desktop-e2e
- name: Install desktop system dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
pkg-config \
libglib2.0-dev \
libwebkit2gtk-4.1-dev \
libgtk-3-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
patchelf \
xvfb
- name: Install dependencies
working-directory: apps
run: pnpm install --frozen-lockfile
@@ -59,6 +59,9 @@ jobs:
- name: Test release workflow contract
run: python3 .github/scripts/release-workflow-contract_test.py
- name: Test E2E workflow contract
run: python3 .github/scripts/e2e-tests-workflow-contract_test.py
- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
@@ -0,0 +1,132 @@
---
spec: docs/specs/e2e-duration-aware-sharding/spec.md
created: 2026-08-18
status: complete
---
# Implementation Plan: Prebuilt Desktop E2E Image
## Overview
Merge-queue run `32170420828` confirmed that `apt-get update` can stall until
the 45-minute desktop job limit expires. The repair moves stable system and
Rust toolchain dependencies into a dedicated GHCR image. The desktop smoke job
then starts from that image and retains only source-specific installation,
build, and test work.
## CI image
### Desktop stage
Extend `.github/docker/ci-base/Dockerfile` with a `desktop` stage based on the
existing `runtime` stage. Install these items while the image is built:
- Rust `1.97.1` with the minimal profile,
- `build-essential` and `rpm` for the Tauri bundle,
- `pkg-config`,
- `libglib2.0-dev`,
- `libwebkit2gtk-4.1-dev`,
- `libgtk-3-dev`,
- `libayatana-appindicator3-dev`,
- `librsvg2-dev`,
- `patchelf`, and
- `xvfb`.
Set `RUST_VERSION=1.97.1`, `CARGO_HOME=/root/.cargo`, and the Rust path in the
image. Add image-build smoke commands for `rustc`, `cargo`,
`pkg-config webkit2gtk-4.1`, `patchelf`, and `xvfb-run`. Do not add Go or the
backend build tools to this stage.
### Image publisher
Extend `.github/workflows/ci-base-image.yml` with one Buildx invocation for the
`desktop` target. Publish `desktop-sha-<content-hash>` and `desktop-latest` with
the same rules as the existing images. Use a `desktop` cache scope for new
layers and the `runtime` scope as a lower-layer source. Add the desktop tag to
the workflow summary.
## Desktop smoke workflow
Update the `desktop-e2e` job in `.github/workflows/e2e-tests.yml` to use
`ghcr.io/kdlbs/kandev-ci:desktop-latest` as its job container. Add the safe Git
directory step and the pnpm-store cache pattern from the existing container
jobs. Use `--ipc=host` so WebKit receives the same shared-memory configuration
as the validated local smoke command. Include the image Dockerfile and
publisher workflow in the job's in-workflow change patterns.
Remove the pnpm setup, Node.js setup, Rust setup, and system-package steps.
Keep the Rust build cache, workspace install, and desktop smoke command. Reduce
the timeout only if a measured image-based run supports a smaller limit.
## Tests
- **What:** the desktop image contains the required packages and toolchain, and
the publisher emits both desktop tags.
**File:** `.github/scripts/e2e-tests-workflow-contract_test.py`
**How:** add textual contract assertions for the Docker stage, package list,
smoke commands, Buildx target, cache scopes, tags, and summary.
- **What:** the desktop smoke job consumes the desktop image without live
system-package or Rust toolchain installation.
**File:** `.github/scripts/e2e-tests-workflow-contract_test.py`
**How:** isolate the `desktop-e2e` job and assert its image, caches, retained
smoke command, and forbidden setup commands.
- **What:** the contract test remains part of a required, unfiltered check.
**File:** `.github/workflows/lint-action-pinning.yml`
**How:** add the contract test to the existing workflow-contract test steps.
- **What:** the desktop job keeps the validated IPC setting and runs when its
image inputs change.
**File:** `.github/scripts/e2e-tests-workflow-contract_test.py`
**How:** assert `options: --ipc=host` and the image paths in the E2E change
detection block.
The regression test must fail against the current workflow because the desktop
image and its publisher do not exist. It must also report the current live
`apt-get` and `rustup` setup as contract errors.
## Rollout
The new `desktop-latest` tag does not exist before this change. After the
implementation branch is pushed, start `ci-base-image.yml` with
`workflow_dispatch` on that branch. Wait for a successful image publish before
the pull request E2E workflow consumes the tag. Use the workflow publisher for
this bootstrap. Do not create or retag the image manually.
The first pull request E2E run must show that `Desktop E2E Smoke` enters the
container and runs the desktop command. Its log must contain no Ubuntu mirror
or Rust toolchain download step.
## Verification Results
Local contract, action-pinning, Dockerfile, and desktop smoke checks pass. The
branch image publisher run `32191451396` completed successfully, including the
`Build and push desktop` job `95886471809`. PR E2E run `32188852911` attempt 2
completed successfully; `Desktop E2E Smoke` job `95887857830` entered the
published container, built DEB/RPM bundles, and reported a successful WebView
smoke. Its log contains source dependency installation but no live apt or Rust
toolchain setup.
## Implementation Waves And Parallel Candidates
Execute sequentially in the primary session:
### Wave 1
- [x] [task-01-prebuild-desktop-image](task-01-prebuild-desktop-image.md)
### Wave 2
- [x] [task-02-consume-and-prove-image](task-02-consume-and-prove-image.md)
## Risks
- GitHub job containers can expose display or WebKit behavior that differs
from the hosted runner. The image-based desktop smoke run is the acceptance
test for this boundary.
- A new GHCR tag creates a bootstrap dependency. The documented branch
dispatch resolves it before the consumer check runs.
- pnpm and Cargo can still contact their registries on a cold cache. This
repair removes the unbounded operating-system and toolchain setup path only.
## Open Questions
None.
@@ -0,0 +1,76 @@
---
id: "01-prebuild-desktop-image"
title: "Prebuild desktop CI dependencies"
status: done
wave: 1
depends_on: []
plan: "plan.md"
spec: "../../specs/e2e-duration-aware-sharding/spec.md"
---
# Task 01: Prebuild Desktop CI Dependencies
## Acceptance
- A dedicated `desktop` image stage contains Rust `1.97.1`, the compiler and
rpm bundle tools, and all Linux packages that the current desktop smoke job
installs.
- The CI image workflow publishes content-addressed and `desktop-latest` tags
through the existing GHCR path.
- A workflow contract test fails if the image stage, dependencies, smoke
commands, publisher, or required test invocation disappears.
## Verification
```bash
python3 .github/scripts/e2e-tests-workflow-contract_test.py
python3 .github/scripts/lint-action-pinning_test.py
python3 .github/scripts/lint-action-pinning.py
docker build --target desktop --tag kandev-ci:desktop-local --file .github/docker/ci-base/Dockerfile .
```
## Files likely touched
- `.github/docker/ci-base/Dockerfile`
- `.github/workflows/ci-base-image.yml`
- `.github/scripts/e2e-tests-workflow-contract_test.py`
- `.github/workflows/lint-action-pinning.yml`
## Dependencies
None.
## Parallelism
sequential
## Inputs
- `docs/specs/e2e-duration-aware-sharding/spec.md`, especially the CI lifecycle
and reliability contract.
- `docs/plans/desktop-e2e-prebuilt-image/plan.md`, CI image and Tests sections.
- The `runtime` and `build` patterns in the current CI base Dockerfile and
publisher workflow.
## Output contract
Report the RED contract result, image contents, exact command results, changed
files, blockers, risks, and the synchronized task and plan status.
## Results
- RED: `python3 .github/scripts/e2e-tests-workflow-contract_test.py` failed with
the expected missing desktop stage, publisher, consumer, and lint invocation.
- GREEN: the same contract test passed (`4 tests`).
- `python3 .github/scripts/lint-action-pinning_test.py` passed (`9 tests`).
- `python3 .github/scripts/lint-action-pinning.py` passed for all 18 workflows.
- `docker build --target desktop --tag kandev-ci:desktop-local --file
.github/docker/ci-base/Dockerfile .` passed. The image smoke checks reported
Rust 1.97.1, WebKitGTK, patchelf, and Xvfb.
- Changed files: the desktop Dockerfile, CI image publisher, E2E workflow,
action-pinning workflow, contract test, and the associated plan/spec/task
records.
- Blockers: none after the authorized branch image dispatch.
- Risks: cold pnpm/Cargo project dependency downloads still depend on external
registries; the unbounded OS and Rust bootstrap path is prebuilt.
- Synchronized status: Task 01 is done and Wave 1 is complete in the plan.
@@ -0,0 +1,81 @@
---
id: "02-consume-and-prove-image"
title: "Consume and prove desktop image"
status: done
wave: 2
depends_on: ["01-prebuild-desktop-image"]
plan: "plan.md"
spec: "../../specs/e2e-duration-aware-sharding/spec.md"
---
# Task 02: Consume And Prove The Desktop Image
## Acceptance
- `Desktop E2E Smoke` uses `ghcr.io/kdlbs/kandev-ci:desktop-latest` and contains
no live Node.js, pnpm, Rust toolchain, or Ubuntu package setup step.
- The container uses `--ipc=host`, and E2E change detection includes the image
Dockerfile and publisher workflow.
- The job retains the pnpm and Rust caches, workspace install, and real desktop
smoke command.
- The branch image publish and the image-based desktop smoke job succeed, with
no Ubuntu mirror or Rust toolchain download in the smoke-job log.
## Verification
```bash
python3 .github/scripts/e2e-tests-workflow-contract_test.py
python3 .github/scripts/lint-action-pinning_test.py
python3 .github/scripts/lint-action-pinning.py
docker run --rm --ipc=host --volume "$PWD:/work" --workdir /work/apps kandev-ci:desktop-local bash -lc 'pnpm install --frozen-lockfile && pnpm --filter @kandev/desktop e2e'
```
The branch image publisher run `32191451396` succeeded. The PR E2E run
`32188852911` attempt 2 succeeded after rerunning its failed jobs; its
`Desktop E2E Smoke` job `95887857830` passed container initialization,
dependency installation, DEB/RPM bundling, and the WebView smoke.
## Files likely touched
- `.github/workflows/e2e-tests.yml`
- `.github/scripts/e2e-tests-workflow-contract_test.py`
## Dependencies
Task 01. The remote smoke check also requires the branch image publish.
## Parallelism
sequential
## Inputs
- `docs/specs/e2e-duration-aware-sharding/spec.md`, especially the desktop
smoke bootstrap requirements.
- `docs/plans/desktop-e2e-prebuilt-image/plan.md`, Desktop smoke workflow,
Rollout, and Risks sections.
- The pnpm and safe-directory patterns in the existing container jobs.
## Output contract
Report the RED and GREEN contract results, local image smoke result, GHCR
publish run, Actions desktop smoke result, changed files, blockers, risks, and
the synchronized task and plan status.
## Results
- The desktop workflow contract passed (`4 tests`) after the image consumer was
added.
- Review remediation added the host IPC setting and image paths to change
detection; the contract test first failed on the missing IPC mapping and
then passed after both changes.
- The local desktop image smoke passed in the container. `pnpm install
--frozen-lockfile` completed, the Tauri application built both DEB and RPM
bundles, and `node e2e/desktop-launch-smoke.mjs` reported a successful
WebView request after backend health.
- Remote image publication and pull-request verification are complete. The
smoke log records `pnpm install --frozen-lockfile`, successful DEB/RPM
bundles, and `Desktop smoke passed: WebView requested / after backend health`;
it contains no live apt or Rust toolchain setup.
- Changed files, blockers, risks, and synchronized plan status are recorded in
this task and the parent plan.
+1 -1
View File
@@ -296,7 +296,7 @@ System pages (Radarr/Sonarr-style) for status, disk usage, database maintenance,
| [automation-runs-delete-all-by-status](automation-runs-delete-all-by-status/spec.md) | draft |
| [no-silent-model-fallback](no-silent-model-fallback/spec.md) | approved |
| [portable-agent-configuration](portable-agent-configuration/spec.md) | draft |
| [e2e-duration-aware-sharding](e2e-duration-aware-sharding/spec.md) | implemented |
| [e2e-duration-aware-sharding](e2e-duration-aware-sharding/spec.md) | shipped |
| [board-step-visibility-filter](board-step-visibility-filter/spec.md) | draft |
| [shutdown-turn-failure-suppression](shutdown-turn-failure-suppression/spec.md) | draft |
| [executor-profile-env-precedence](executor-profile-env-precedence/spec.md) | building |
+35 -7
View File
@@ -1,6 +1,7 @@
---
status: implemented
status: shipped
created: 2026-08-10
updated: 2026-08-18
---
# Duration-aware E2E sharding and CI reliability
@@ -24,6 +25,12 @@ This capability makes shard assignment adapt to the suite as it grows. It
also turns the known reliability findings into deterministic regression work
and makes passed-after-retry results visible.
A merge-queue run later exposed a separate bootstrap problem in the desktop
smoke job. The job contacted Ubuntu package mirrors for WebKit and Tauri build
packages during each run. A mirror stopped responding, so the job reached its
45-minute limit before the desktop smoke test started. The aggregate E2E gate
then removed a valid pull request from the merge queue.
## What
The E2E workflow gains two duration-aware cohorts:
@@ -107,6 +114,11 @@ contract.
retry summary, prediction-versus-actual summary, and plan health counters.
5. Only a successful `main` result is eligible to become the next baseline.
The desktop smoke job uses a dedicated image from `ghcr.io/kdlbs/kandev-ci`.
The image contains Node.js, pnpm, Rust, Xvfb, and the Linux packages that Tauri
needs. The job does not contact Ubuntu or Rust toolchain servers during setup.
It can still use the existing pnpm and Cargo caches for project dependencies.
If the `main` artifact is unavailable, the workflow continues with the
validated count-based fallback and records that fallback in the report. A
missing or malformed manifest is a hard planning failure, not permission to
@@ -132,6 +144,14 @@ The desktop and mobile multi-PR failures share the same fixture defect and
must be fixed together. Each repaired failure gets a regression assertion that
fails for the observed cause, not only a larger timeout.
The desktop smoke bootstrap has these requirements:
- the CI image build installs the Linux and Rust toolchain dependencies,
- the image publisher produces content-addressed and `desktop-latest` tags,
- the desktop smoke job uses the published image,
- the job contains no live `apt-get` or `rustup toolchain install` step, and
- a workflow contract test protects the image producer and consumer together.
Playwright output also includes a retry summary containing the test key,
attempt count, final status, error context, and links to retry artifacts. The
normal PR lane may retain its current retry gate while the summary is being
@@ -168,6 +188,10 @@ they improve wall time without increasing flakes.
visible in the retry summary with actionable evidence.
- The timing profile can be unavailable without blocking a correct,
deterministic fallback plan.
- The desktop smoke job reaches its build and test commands without contacting
Ubuntu package mirrors or Rust toolchain servers.
- A contract test fails if the desktop image publisher disappears or the smoke
job restores live system-package or toolchain installation.
## Out of scope
@@ -176,12 +200,16 @@ they improve wall time without increasing flakes.
- committing per-shard test lists that require manual maintenance;
- moving timing data to an external database;
- globally deleting Docker resources to make a test pass;
- masking flakes by increasing timeouts or retries.
- masking flakes by increasing timeouts or retries;
- removing network access that project package installation or source checkout
still requires.
## Implementation status
Implemented in the CI workflow, E2E tooling, scoped Docker fixtures, and
focused reliability tests. The generated manifests, rolling profile, retry
diagnostics, and fail-closed runner are covered by unit/type checks and local
targeted E2E runs. The three-successful-main-run balance target remains an
operational check after merge.
The duration-aware sharding work is implemented in the CI workflow, E2E
tooling, scoped Docker fixtures, and focused reliability tests. The generated
manifests, rolling profile, retry diagnostics, and fail-closed runner have
unit, type, and targeted E2E coverage. The prebuilt desktop image amendment is
implemented and was proven by the branch image publish and pull-request smoke
run recorded in `docs/plans/desktop-e2e-prebuilt-image/plan.md`. The balance
target remains an operational check after merge.