fix(ci): unbreak notebook quality gates first CI exercise
All checks were successful
CI / lint (push) Successful in 29s
CI / notebooks-smoke (push) Successful in 1m31s
Deploy / notebooks (push) Has been skipped
Deploy / zotero (push) Has been skipped
Deploy / docs (push) Has been skipped
Deploy / api (push) Has been skipped
Deploy / mc (push) Has been skipped
Deploy / report (push) Successful in 14s
CI / test (push) Successful in 13m32s

Three failures from 187e776's first run:

- notebooks-smoke: _template.py opens data/aco.duckdb read-only, which
  doesn't exist in a fresh checkout — stub an empty-but-valid database
  before the run (information_schema queries return zero rows, fine).
- notebooks-integration FE smoke: treat CellNotInitializedError as
  benign. The nightly run regenerates session snapshots; with
  auto_instantiate=false the editor renders cached UI elements whose
  cells aren't running and logs this error on every such notebook.
  Verified against live prod: editor probe now passes.
- notebooks-integration failure step: 'uv: command not found' — the job
  had no setup-uv step, so api.diag.ci could never file on failure.
This commit is contained in:
kert
2026-07-10 11:36:25 -04:00
parent 187e77615d
commit 15ab55a286
4 changed files with 40 additions and 3 deletions

View File

@@ -79,8 +79,17 @@ jobs:
- name: Install dependencies
run: uv sync --dev
- name: Stub aco.duckdb
# The checkout has no data volume; conf.connect.duckdb() opens
# data/aco.duckdb read-only and fails if it doesn't exist. An
# empty-but-valid database is enough for the ci-smoke notebooks
# (schema browsing over information_schema returns zero rows).
run: |
[ -f data/aco.duckdb ] || uv run python -c \
"import duckdb; duckdb.connect('data/aco.duckdb').close()"
- name: Run data-independent notebooks headless
# Executes the [ci_smoke] set from notebooks/nb-tests.toml via
# Executes the [ci_smoke] set from infra/marimo/nb-tests.toml via
# `marimo export session` and fails on any cell error. Runs on
# every push (not path-gated): notebooks import src/ modules, so
# src changes can break them too.

View File

@@ -15,6 +15,11 @@ jobs:
- name: Checkout
uses: https://github.com/actions/checkout@v4
- name: Set up uv
run: curl -LsSf https://astral.sh/uv/install.sh | sh
env:
UV_INSTALL_DIR: /usr/local/bin
- name: Run full notebook set in prod container
env:
GITEA_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

View File

@@ -196,8 +196,17 @@ jobs:
- name: Install dependencies
run: uv sync --dev
- name: Stub aco.duckdb
# The checkout has no data volume; conf.connect.duckdb() opens
# data/aco.duckdb read-only and fails if it doesn't exist. An
# empty-but-valid database is enough for the ci-smoke notebooks
# (schema browsing over information_schema returns zero rows).
run: |
[ -f data/aco.duckdb ] || uv run python -c \\
"import duckdb; duckdb.connect('data/aco.duckdb').close()"
- name: Run data-independent notebooks headless
# Executes the [ci_smoke] set from notebooks/nb-tests.toml via
# Executes the [ci_smoke] set from infra/marimo/nb-tests.toml via
# `marimo export session` and fails on any cell error. Runs on
# every push (not path-gated): notebooks import src/ modules, so
# src changes can break them too.
@@ -226,6 +235,7 @@ def _gen_deploy(
name = img["name"]
job_names.append(name)
paths = img.get("path_filter", [])
# Gitea Actions doesn't support `paths:` on push without branches,
# so use `if: contains(...)` on the modified files list.
def _clean_path(p: str) -> str:
@@ -505,7 +515,9 @@ jobs:
return (".gitea/workflows/infra-ci.yml", content)
def _gen_notebooks_integration(runner: str, **_kw: object) -> tuple[str, str]:
def _gen_notebooks_integration(
runner: str, uv_version: str, **_kw: object
) -> tuple[str, str]:
"""Nightly full-set notebook run inside the production container.
The full notebook set needs real data (aco.duckdb, ./data mounts) that
@@ -530,6 +542,8 @@ jobs:
steps:
{_checkout_step()}
{_setup_uv_step(uv_version)}
- name: Run full notebook set in prod container
env:
GITEA_TOKEN: ${{{{ secrets.DEPLOY_TOKEN }}}}

View File

@@ -36,6 +36,14 @@ PLAYWRIGHT_IMAGE = "mcr.microsoft.com/playwright/python:v1.61.0-noble"
PLAYWRIGHT_PKG = "playwright==1.61.0"
READY_TIMEOUT_S = 120
# Console errors that are expected marimo behavior, not bundle breakage.
# CellNotInitializedError: with auto_instantiate=false the editor renders
# cached UI elements from a session snapshot whose cells aren't running in
# the kernel — every interaction with them logs this error. Appears on any
# notebook that has a __marimo__/session snapshot (e.g. right after the
# nightly integration run regenerates them).
BENIGN_ERRORS = ("CellNotInitializedError",)
# Dependency-free notebook shipped into the throwaway container. Markdown
# only, so it renders identically under any runtime config (the bare image
# has no marimo.toml, and marimo's default auto_instantiate=true would run
@@ -146,6 +154,7 @@ def check(result: dict, *, expect_text: str | None) -> list[str]:
f"[{e['type']}] {e['text'][:300]}"
for e in result["events"]
if e["type"] in ("error", "pageerror")
and not any(b in e["text"] for b in BENIGN_ERRORS)
]
if expect_text and expect_text not in result["body"]:
problems.append(