The two caveats from the dependency investigation. ## The capabilities feature Camera and location attachments are now available behind `features = ["capabilities"]`, which pulls `nigig-uikit` and supplies `UikitAttachmentProvider`. Measured: 89 crates by default, 275 with the feature on. That cost is real and it is inherent, not packaging waste. `camera_widget` imports `send_geocode_request` and `request_map_tile` from `nigig-core`, both of which call `spawn_async` — the shared Tokio runtime — and the first makes an HTTPS call to Nominatim. A camera that geocodes needs an async runtime and an HTTP client; there is no lighter honest version. It is affordable because it is opt-in, and because any app enabling it already depends on `nigig-core`, so that app's own tree grows by nothing. Everything touching `nigig-uikit` is in one module, so the boundary is a file rather than `#[cfg]` scattered through the widget. The provider holds no widgets of its own: the host owns the `CameraWidget` already in its tree and this asks it to open, because a provider that instantiated a second camera would fight the first for the device. A second request while one is outstanding is refused rather than overwriting. The table turns that refusal into `AttachmentUnavailable`, so the user is told the camera is busy instead of watching their first request vanish. File picking is deliberately declined here — `robius-file-picker` already ships unconditionally and costs nothing, and two paths for one job is one too many. Two CI gates, both verified to fail when they should: the opt-in build must keep compiling, and the default build must pull none of `tokio`, `reqwest`, `hyper`, `clap`, `csv`, `image`, `nigig-uikit` or `nigig-core`. The second checks the resolved `cargo tree` rather than the manifest, because feature unification can switch an optional dependency on from a sibling crate. Tests 99 default, 105 with the feature. Both clippy-clean. ## The matrix_client defect Raised in REVIEWS/MATRIX_CLIENT_FEATURE_GATE.md rather than fixed. It is not my crate, nothing depends on the broken combination, and a blind fix could change behaviour someone relies on. `matrix_client` declares `native = ["dep:tokio", "dep:reqwest", "dep:rusqlite"]` but its source gates on `#[cfg(not(target_arch = "wasm32"))]`. Two switches for the same modules, so on a native target with the feature off the modules compile and their dependencies do not — 19 errors, 26 ungated uses across 7 files. There is no CI job for the crate, which is why it rotted unnoticed. The note corrects an overstatement I made while arguing for the trait hook. I said fixing this would unblock wasm. It would not: `matrix_client` already builds clean for wasm32 with `--no-default-features`, and `nigig-core` has 8 wasm errors of its own (`crate::platform::spawn` missing) that have nothing to do with it. The only broken combination is native-target-with-feature-off, which nothing builds. I also said earlier that `matrix_client` was heavy — it is a 7-dependency local crate, not matrix-sdk. That was wrong and it inflated the case for the trait hook; the note records the measured numbers instead.
220 lines
9.6 KiB
YAML
220 lines
9.6 KiB
YAML
name: makepad-table
|
|
|
|
# CI for the nested `makepad_table` workspace: the Table widget, the shared
|
|
# document model, the PDF exporter and the invoicer app.
|
|
#
|
|
# This crate tree had no CI at all, and it showed. Its README stated it was
|
|
# "written without a local cargo/rust toolchain, so the first compile on your
|
|
# machine is the verification step" — and that first compile found three
|
|
# defects in the money code, two of which made every currency string in the
|
|
# application either panic or print the wrong number. None of them survive a
|
|
# `cargo test`. This workflow is that `cargo test`, run on every push.
|
|
#
|
|
# Note the `--manifest-path`. `crates/apps/makepad_table` is named in the root
|
|
# manifest's `workspace.exclude`, so it is invisible to every other workflow
|
|
# here: `cargo test -p ...` from the repo root cannot reach it and
|
|
# `--workspace` does not include it. Each step therefore points at the nested
|
|
# manifest explicitly. Dropping the `--manifest-path` does not fail loudly, it
|
|
# silently tests nothing, so the last job asserts the exclusion still holds.
|
|
|
|
on:
|
|
push:
|
|
paths:
|
|
- 'crates/apps/makepad_table/**'
|
|
- 'rust-toolchain.toml'
|
|
- '.forgejo/workflows/makepad-table.yml'
|
|
pull_request:
|
|
paths:
|
|
- 'crates/apps/makepad_table/**'
|
|
- 'rust-toolchain.toml'
|
|
- '.forgejo/workflows/makepad-table.yml'
|
|
|
|
env:
|
|
MANIFEST: crates/apps/makepad_table/Cargo.toml
|
|
|
|
jobs:
|
|
# The model and the exporter are UI-free — serde, pdf-writer, ttf-parser —
|
|
# so they need no GUI packages and give fast feedback on the arithmetic
|
|
# that actually handles money.
|
|
model:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Document model tests
|
|
run: cargo test --manifest-path "$MANIFEST" -p makepad-doc-model
|
|
|
|
- name: PDF exporter tests
|
|
run: cargo test --manifest-path "$MANIFEST" -p makepad-pdf-export
|
|
|
|
- name: Clippy on the pure crates
|
|
run: |
|
|
cargo clippy --manifest-path "$MANIFEST" \
|
|
-p makepad-doc-model -p makepad-pdf-export \
|
|
--all-targets -- -D warnings
|
|
|
|
# Money is stored in integer minor units precisely so that no rounding
|
|
# error can enter a total. A reviewer adding `f64` to the model is
|
|
# unlikely to be told why that is wrong, so the gate says it.
|
|
- name: Amounts stay in integer minor units
|
|
run: |
|
|
set -euo pipefail
|
|
MODEL=crates/apps/makepad_table/crates/doc-model/src/lib.rs
|
|
if grep -nE '(_minor|amount|price|total|subtotal|discount)[a-z_]*:[[:space:]]*f(32|64)' "$MODEL"; then
|
|
echo "FAIL: a monetary field is declared as a float" >&2
|
|
echo " minor units are i64 on purpose (see 'Data model highlights')" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# The widget needs Makepad's Linux backend to link, which is the slow half.
|
|
widget:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 60
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Install native dependencies
|
|
run: |
|
|
sudo apt-get update -qq
|
|
sudo apt-get install -y -qq \
|
|
pkg-config libwayland-dev libxkbcommon-dev libx11-dev \
|
|
libxi-dev libxcursor-dev libxrandr-dev libgl1-mesa-dev \
|
|
libasound2-dev libpulse-dev libssl-dev
|
|
|
|
- name: Widget tests
|
|
run: cargo test --manifest-path "$MANIFEST" -p makepad-table
|
|
|
|
- name: Widget clippy
|
|
run: |
|
|
cargo clippy --manifest-path "$MANIFEST" -p makepad-table \
|
|
--all-targets -- -D warnings
|
|
|
|
# The invoicer's sidebar presentation logic is unit-tested; the rest
|
|
# of it opens a window, so there is nothing to assert without a
|
|
# display. The standalone demo is compile-only for the same reason.
|
|
# Compiling both still catches the common break, which is a widget API
|
|
# change the consumer was not updated for.
|
|
- name: Invoicer tests
|
|
run: cargo test --manifest-path "$MANIFEST" -p makepad-invoicer
|
|
|
|
- name: Invoicer clippy
|
|
run: |
|
|
cargo clippy --manifest-path "$MANIFEST" -p makepad-invoicer \
|
|
--all-targets -- -D warnings
|
|
|
|
- name: Demo compiles
|
|
run: |
|
|
cargo check --manifest-path "$MANIFEST" \
|
|
-p makepad-example-table --all-targets
|
|
|
|
# The `capabilities` feature pulls nigig-uikit, which takes the
|
|
# dependency tree from 89 crates to 275. That cost is only acceptable
|
|
# because it is opt-in, so both halves have to keep working: the
|
|
# default build must stay light, and the opt-in build must stay
|
|
# buildable. A feature nothing compiles is a feature that rots — which
|
|
# is exactly what happened to matrix_client's `native` (see
|
|
# REVIEWS/MATRIX_CLIENT_FEATURE_GATE.md).
|
|
- name: The capabilities feature still builds
|
|
run: |
|
|
cargo test --manifest-path "$MANIFEST" -p makepad-table \
|
|
--features capabilities
|
|
|
|
- name: The default build pulls none of the heavy tree
|
|
run: |
|
|
set -euo pipefail
|
|
# `cargo tree` on the default features must not mention any of
|
|
# these. Checking the resolved tree rather than the manifest,
|
|
# because an optional dependency can still be switched on by a
|
|
# sibling crate's feature unification.
|
|
leaked=$(cargo tree --manifest-path "$MANIFEST" -p makepad-table \
|
|
--prefix none 2>/dev/null \
|
|
| awk '{print $1}' | sort -u \
|
|
| grep -xE 'tokio|reqwest|hyper|clap|csv|image|nigig-uikit|nigig-core' || true)
|
|
if [ -n "$leaked" ]; then
|
|
echo "FAIL: the default build pulled the capabilities tree:" >&2
|
|
echo "$leaked" >&2
|
|
exit 1
|
|
fi
|
|
echo "default build is clean of the capabilities dependencies"
|
|
|
|
# makepad's own FileDialog is implemented on macOS only: the Linux and
|
|
# Android backends never handle CxOsOp::SelectFileDialog, so a button
|
|
# wired to it does nothing at all on the platform this repo targets.
|
|
# That failure is invisible — it compiles, it runs, the dialog just
|
|
# never appears — so the gate names it rather than trusting a reviewer
|
|
# to remember.
|
|
- name: The file picker is robius, not makepad's macOS-only dialog
|
|
run: |
|
|
set -euo pipefail
|
|
APP=crates/apps/makepad_table/apps/invoicer/src/main.rs
|
|
if grep -nE 'open_system_(openfile|savefile)_dialog' "$APP"; then
|
|
echo "FAIL: uses makepad's FileDialog, which is macOS-only" >&2
|
|
echo " use robius_file_picker::FileDialog instead" >&2
|
|
exit 1
|
|
fi
|
|
grep -q 'robius_file_picker' "$APP" \
|
|
|| { echo "FAIL: the file picker import is gone" >&2; exit 1; }
|
|
echo "file picking goes through robius-file-picker"
|
|
|
|
hygiene:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 15
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Formatting
|
|
run: |
|
|
cargo fmt --manifest-path "$MANIFEST" -- --check \
|
|
|| { echo "run: cargo fmt --manifest-path $MANIFEST"; exit 1; }
|
|
|
|
# Without the exclusion this tree joins every workspace-wide build in
|
|
# the repo and drags a second makepad checkout in with it. Cargo also
|
|
# declines to absorb it because of its own `[workspace]` table, so this
|
|
# asserts both halves: the root says exclude, and the nested manifest
|
|
# still declares itself a workspace.
|
|
- name: The nested workspace stays excluded from the root
|
|
run: |
|
|
set -euo pipefail
|
|
# Match the quoted exclude entry, not the prose. The first version
|
|
# of this check was a bare grep for the path, which the explanatory
|
|
# comment above the exclude list also matches — deleting the entry
|
|
# and keeping the comment passed the gate. Anchoring on the quotes
|
|
# is what makes it able to fail.
|
|
if ! grep -qE '^[[:space:]]*"crates/apps/makepad_table",?[[:space:]]*$' Cargo.toml; then
|
|
echo "FAIL: root Cargo.toml no longer excludes crates/apps/makepad_table" >&2
|
|
exit 1
|
|
fi
|
|
if ! grep -q '^\[workspace\]' "$MANIFEST"; then
|
|
echo "FAIL: $MANIFEST no longer declares its own [workspace]" >&2
|
|
exit 1
|
|
fi
|
|
members=$(cargo metadata --no-deps --format-version 1 --offline 2>/dev/null \
|
|
| tr ',' '\n' | grep -c 'makepad_table' || true)
|
|
if [ "$members" -ne 0 ]; then
|
|
echo "FAIL: makepad_table crates leaked into the root workspace" >&2
|
|
exit 1
|
|
fi
|
|
echo "nested workspace is excluded from the root, and self-declared"
|
|
|
|
# A floating git branch means the same commit builds against different
|
|
# dependencies on different days. Every other crate here pins a rev.
|
|
- name: Makepad dependency is pinned, not tracking a branch
|
|
run: |
|
|
set -euo pipefail
|
|
if grep -rn 'branch[[:space:]]*=' crates/apps/makepad_table --include=Cargo.toml; then
|
|
echo "FAIL: a manifest tracks a git branch instead of pinning a rev" >&2
|
|
exit 1
|
|
fi
|
|
missing=0
|
|
while IFS= read -r manifest; do
|
|
if grep -q 'makepad-widgets' "$manifest" && ! grep -q 'rev[[:space:]]*=' "$manifest"; then
|
|
echo "FAIL: $manifest depends on makepad-widgets without a rev" >&2
|
|
missing=1
|
|
fi
|
|
done < <(find crates/apps/makepad_table -name Cargo.toml)
|
|
[ "$missing" -eq 0 ]
|
|
echo "all makepad-widgets dependencies are pinned to a revision"
|
|
|
|
- name: Reject whitespace errors
|
|
run: git diff --check
|