Make targets
Everything is driven from the Makefile at the repository root. It works with the system make on macOS (3.81) and Linux and uses nothing newer. make on its own runs make help, which lists every target with one line. The Makefile follows the convention of the Kit repositories (help, all, build, lint, test, fmt) so the CI job and people use the same names.
The scripts behind the targets are in tools/. They are plain POSIX sh (and Python 3 for the link checker and the website tools), so they run on the /bin/sh and /bin/bash 3.2 that ship with macOS. The one exception is the catalog generator behind docs-catalog, a maintainer tool that also needs PyYAML; nothing a reader runs depends on it. docs-pull is also a maintainer tool, but it is Python 3 standard library only.
Variables
| Variable | Used by | Meaning |
|---|---|---|
KITS | setup, fetch | public (default) fetches only public Kits. all also fetches private Kits and needs a GitHub login |
SETUP_INSTALL | setup | 1 prints the install commands for missing required tools and runs them after a y/N question. Default: never installs |
APP | app, run | An app folder name under apps/: calculator, minesweeper, notepad, stopwatch, render-queue, launcher, notes_rust |
KIT | lock | A Kit name from kits.lock. Empty means every Kit |
LOCK_YES | lock | 1 skips the y/N question before kits.lock is rewritten (the difference is still printed) |
GITHUB_TOKEN | fetch, lock | A token that can read private Kits, used when the GitHub CLI is not logged in |
VENUE_TOKEN_FILE | test, test-integration | A resource-broker token file. When set, the render-queue tests that need a running broker run for real instead of skipping |
CHECK_PUBLIC_STRICT | check-public | 1 also fails while kits.lock lists a private Kit with role core. A private app-dep Kit only prints a warning that names the apps that will be skipped for a stranger. go-public-check sets it |
REGISTRY | docs-catalog | Maintainers only: the path of the Kit registry file (kits.yaml). If empty, KITS_REGISTRY from the environment is used. With neither, docs-catalog stops with a message |
KITS_REGISTRY | docs-catalog | The same path, from the environment, for people who regenerate often |
REGISTRY_COMMIT | docs-catalog | The commit of the registry the catalog is generated from; the catalog index prints it as “Generated from registry commit abc1234” |
OFFLINE | docs-catalog | 1 does not ask GitHub who is public and reuses tools/kit-visibility.tsv as it is |
PYTHON | docs-catalog, test-tools, test-fast | The Python 3 to use for the catalog generator and the tool tests (default python3). For docs-catalog it must have PyYAML; docs-pull always uses plain python3 |
SITE_DOMAIN | site, site-check, publish-site | The domain the website is served at (default kits.slashbuilder.com). It is written into the CNAME file of the published branch. Set in the Makefile, in one place |
SITE_TITLE | site | The title shown on every page of the website (default The Kit Book) |
SITE_REPO_URL | site | The repository address used for the “edit this page” links and for the rewritten links into apps/ and labs/ (default https://github.com/slash-builder/kit-book) |
SITE_REPO_BRANCH | site, publish-site | The branch those links point at (default main); publish-site only pushes from this branch |
SITE_REWRITE_REPO_LINKS | site | 1 (default) rewrites links from the book to files outside book/ into GitHub addresses; 0 leaves them as relative links, which do not work on the website |
SITE_INDEXABLE | site, site-check, publish-site | 0 (default) puts <meta name="robots" content="noindex"> on every page; 1 leaves it out so search engines may index the site (mdBook keeps noindex on its own print.html and toc.html). Set 1 only when the site is meant to be found |
PUBLISH | publish-site | 1 allows the push to the gh-pages branch, and only if the repository behind origin is public. Anything else is a dry run |
MDBOOK | site | The path of the mdbook program to use. Otherwise mdbook on the PATH, then ~/.cache/mdbook/mdbook |
STRANGER_REPO_URL | stranger-test | The repository to clone (default https://github.com/slash-builder/kit-book; a local path works for a rehearsal) |
STRANGER_DRY | stranger-test | 1 prints the plan and what would be removed from the environment, and clones nothing |
STRANGER_STEPS | stranger-test | Which steps to run after the clone, in order (default doctor setup test-fast) |
STRANGER_KEEP | stranger-test | 1 keeps the temporary clone and prints where it is |
GO_PUBLIC_OFFLINE | go-public-check | 1 skips the checks that need the network and ends INCOMPLETE (exit 3), never a pass |
LINUX_SMOKE_APPS | linux-smoke | The app folders to test, separated by spaces (default: every app that can be built here; an app whose Kit is not fetched is skipped with a line) |
LINUX_SMOKE_IMAGE | linux-smoke, linux-smoke-image | The Docker image name (default kit-book-linux-smoke:flutter-3.41.9) |
LINUX_SMOKE_PLATFORM | linux-smoke, linux-smoke-image | A Docker --platform (default: the daemon’s own, linux/arm64 on Apple silicon) |
LINUX_SMOKE_UP, LINUX_SMOKE_SHOT_AT | linux-smoke | Seconds to keep each app up (default 10) and the second of the screenshot (default 6) |
LINUX_SMOKE_PEERS | linux-smoke | The apps for the two-copies check (default stopwatch notepad; empty skips it) |
LINUX_SMOKE_NOCACHE | linux-smoke | 1 does not use the two cache volumes for Dart packages and Rust crates |
NO_COLOR | all | Turns off colours in the output |
KITS_LOCK | the scripts in tools/ | Read a different lock file (used for testing the fetch script) |
Targets
help
The default. Prints every target with a one-line description.
all
What CI runs: lint, then test-fast, then build.
doctor
Checks the toolchain and prints a table: tool, whether it is required, status, the version found and the version wanted. For each missing or too-old tool it prints the install command for your system (Homebrew on macOS, apt on Debian and Ubuntu). It installs nothing and exits non-zero only when a required tool is missing or too old.
- Required: Flutter and Dart (the minimum Dart is read from the apps’
pubspec.yamlfiles), Rust and Cargo (the minimum is the highestrust-versionamong the Kits’ crates and the apps’ Rust cores),curl,tar, Python 3, and on macOS the Xcode command line tools.flutter_rust_bridge_codegenis required only when an app with a Rust core (apps/notes_rust) is one that can be built here, at exactly the version that app pins in itspubspec.yaml(flutter_rust_bridge: 2.12.0today); it is looked for onPATHand in~/.cargo/bin, wherecargo installputs it. An app that is skipped (its Kit is not fetched) adds no requirement: not this tool, not its Rust version, not CocoaPods;doctorlists the skipped apps under the table. - For apps: needed only to build and run the demo apps. On macOS, the full Xcode app, and CocoaPods when an app has a native plugin (the Rust core’s build glue is one). On Linux,
clang,cmake,ninja,pkg-configand the GTK 3 development files.make appsrequires them;make test-fastdoes not. - Optional: Docker (for the Linux container checks),
redis-server,protoc,flutter_rust_bridge_codegen(optional only when no app in the tree needs it), the GitHub CLI (gh, for private Kits) andmdbook(formake site).
setup
Runs doctor, then fetch, then flutter pub get or dart pub get for every app and package, then cargo fetch for each Kit’s Rust crate and for each app’s own Rust core. An app whose Kit is not fetched is skipped with a one-line reason (see kits.need); that is never an error. Safe to run again. KITS=all also fetches private Kits. SETUP_INSTALL=1 offers to run the install commands for missing required tools; the default never installs.
fetch
Reads kits.lock and, for each Kit (unless kits.override names a local checkout for it), downloads the archive at the pinned commit into .kits/<name>/. Public Kits come from a plain curl download. Private Kits come from the GitHub CLI (gh api) when it is logged in, else from GITHUB_TOKEN; with neither, the Kit is skipped with a one-line explanation. After unpacking it writes .kits/<name>/.fetched containing the commit, and skips the Kit next time when that matches. It checks that the archive really is the commit asked for. No git and no submodules are involved. One line per Kit: fetched, up to date, or skipped (why).
lock
Shows how the pins would move: for each Kit it asks GitHub for the newest commit of the default branch and prints old -> new with a compare link. It rewrites kits.lock only after you answer y. make lock KIT=app-context-kit does one Kit. After it, run make setup and make test-fast.
test
Runs every fetched Kit’s own test suite, then labs/interface_candidates, then every demo app (an app with a Rust core runs its cargo test first and then flutter test, which uses an in-memory stand-in for the core, so it needs no native build), then the integration suites that can run on this computer. A Kit with a Makefile is tested with its own make test-fast (or make test); a Kit without one is tested with its native commands (dart test, flutter test, cargo test). Prints a table with each suite’s passed, failed and skipped counts and its seconds; logs are in build/test-logs/. An app whose Kit is not fetched is skipped whole, with a line such as skipped apps/notes_rust: needs storage-kit (private; fetch with KITS=all) and one row in the table that says so. Exits non-zero when any suite that ran has a failure; a skip is not one.
test-fast
The same as test without anything that needs Docker, Redis or a running daemon. This is the everyday command. It also runs the unit tests of the maintainer tools (see test-tools) when PyYAML is installed, and skips them with a message when it is not.
test-integration
Only the suites that need Docker, Redis or a daemon. Each one skips with a clear message when its tool is absent; skipping is never a failure.
| Suite | Needs | What it does |
|---|---|---|
| Linux containers | The Docker daemon running | Runs a Kit’s Dart and Rust cores in throwaway Linux containers, if the Kit ships a script for it (the App Context Kit does) |
| Resource-broker daemon tests | VENUE_TOKEN_FILE set to a token for a running broker | Runs the render-queue tests that otherwise skip themselves |
| App integration tests | A desktop to run an app on (macOS, or Linux with a display) and the Kits the app builds against | Runs an app’s integration_test/ inside the built app with flutter test integration_test -d macos, so a Rust core is the real native library. apps/notes_rust has one |
| Message-hub probes | Redis | These run in a private repository; nothing here runs yet |
apps
Builds every demo app in debug mode for this computer (macOS or Linux desktop). Needs the “for apps” tools from doctor and a fetched App Context Kit. An app whose Kit is not fetched is skipped with a one-line reason, and the others are built.
app
Builds one app: make app APP=notepad. If you name an app whose Kit is not fetched, it says which Kit and what to run (make setup KITS=all for a private one) and exits non-zero, because the app you asked for was not built.
run
Runs one app on this computer: make run APP=notepad. Like app, it stops with the fix if the app’s Kit is not fetched.
frb
Regenerates the Flutter-to-Rust bridge (flutter_rust_bridge_codegen generate) for every app that has a flutter_rust_bridge.yaml, which is apps/notes_rust today. Run it after changing the bridge layer (rust/src/api/*.rs) and commit the result: the generated files are committed so that make app works without this tool. It refuses to run with a codegen whose version differs from the one the app pins. An app that is skipped is passed over with a one-line reason.
build
An alias for apps.
lint
Runs flutter analyze over labs/interface_candidates and every app, then docs and check-public.
fmt
Runs dart format over apps/ and labs/.
docs
Checks every relative link and heading anchor in book/ and in the README files (tools/check-links.py), using GitHub’s heading slugs. A code fence whose opening line says copied-from=<path> is also checked against that file. It also checks that every Makefile target is described on this page. It writes nothing.
docs-catalog
For maintainers; readers never need it. Regenerates the Kit catalog in book/30-kits/ from the Kit registry, then runs docs and check-public on the result. The registry is in a private repository, so the generated pages are committed here and make docs never asks for it.
python3 -m pip install pyyaml # once; only this tool needs PyYAML
gh auth login # once; the tool asks GitHub which repositories are public
make docs-catalog REGISTRY=/path/to/kits.yaml REGISTRY_COMMIT=abc1234
make docs-catalog REGISTRY=/path/to/kits.yaml OFFLINE=1 # no GitHub calls: reuse tools/kit-visibility.tsv
With neither REGISTRY nor KITS_REGISTRY set it stops and says so. It runs tools/gen-catalog.py, which writes:
- one status card per Kit in the registry (
book/30-kits/<name>.md), for every Kit: public, private, banked and archived; this repository is the book, not a Kit, and is never listed; - the catalog index (
book/30-kits/README.md), the status vocabulary and the page for things that are not in the registry; - the block of
book/SUMMARY.mdbetween<!-- catalog:start -->and<!-- catalog:end -->(the markers are added if missing; nothing else in that file is touched); tools/kit-visibility.tsv: for each Kit (by name),public,private,missing(the repository does not exist) orunknown, whether a public repository has a README ordocs/, and the day that was checked. It never records where a Kit lives: no repository name and no organisation, so it is safe in a public repository. A repository link appears on a card only forpublic, and the repository is read from the registry each time.
Running it twice gives identical files: nothing in the output carries a time. Prose from the registry passes through a sanitiser that drops, rather than rewords, any sentence or parenthetical that names people, roles, agents, tickets, hostnames, local paths, notes-vault paths, Jira or dates, and check-public scans the result again. A card that comes out thin is thin because the registry entry behind it is.
docs-pull
For maintainers; readers never need it. Pulls each Kit’s own reference documentation out of the fetched Kit (.kits/<name>/) into the book, then runs docs and check-public on the result. The Kits keep their documentation with their code, so it versions with the code; the book copies it in at the commit kits.lock pins, and the copies are committed, so a reader needs neither make setup nor access to a private Kit’s repository to read them. make docs never needs .kits/.
make setup KITS=all # once, and after every pin move: the Kit must be fetched at the pinned commit
make docs-pull # then commit book/ (the pulled pages and the docs-pull block of SUMMARY.md)
It runs tools/pull-docs.py (Python 3 standard library only), which reads kits.lock and tools/docs-pull.conf. For each Kit with a section in the configuration, the configuration says where the pages land, which source files are pulled, their order in the table of contents, and whether the Kit is public (auto reads the visibility column of kits.lock). Today only the App Context Kit is pulled; because it is not in the registry its pages land in book/30-kits/app-context-kit/. A Kit in the registry lands in book/30-kits/<slug>/reference/, and its catalog card then lists the pages.
What it writes, and nothing else:
index.md: the Kit’sdocs/README.md, followed by a generated table of every pulled page (title from the page’s first heading, one line from its first paragraph);- one page per pulled source file, named in lower case with
docs/dropped (docs/GETTING-STARTED.mdbecomesgetting-started.md, a folder’sREADME.mdis named after the folder), and any images those pages use underassets/; - the block of
book/SUMMARY.mdbetween<!-- docs-pull:start -->and<!-- docs-pull:end -->, which nests the pages under one entry per Kit. It sits in its own part, “Kit reference”, after the catalog (the part and the markers are added before “Reference” if missing). It is not inside the catalog’s list because mdBook drops the first of two lists in one item when an HTML comment separates them, which would delete the catalog from the contents.
Every pulled page starts with a banner comment and carries the visible line “Reference page pulled from <kit> at commit <sha7>. Edit it in the Kit, not here.” Do not edit a pulled page: the next pull overwrites it, and a content fix belongs in the Kit. The tool changes the Kit’s text only by rewriting links:
| A link in a Kit page that points to… | becomes |
|---|---|
| a web address, a mail address, or a heading on the same page | itself, unchanged |
a pulled page, or a folder whose README.md is pulled, or the Kit’s docs/README.md | a relative link to the pulled copy, with its #anchor kept (the headings are not changed, so the anchors still exist) |
the Kit’s root README.md or CONTRIBUTING.md, when the Kit is private and its section sets home | a link to the Kit’s entry in the catalog (the page named by home), so “Repository home” and “Contributing” lead somewhere |
| anything else in the Kit (code, other folders), when the Kit is private | plain text: the link text stays, the link goes |
| anything else in the Kit, when the Kit is public | a GitHub address at the pinned commit (blob for a file, tree for a folder), with any #anchor or #L10 kept |
| a place outside the Kit, or a file the Kit does not have | plain text, and a warning naming the page, so it can be fixed in the Kit |
an #anchor that is not a heading of the page it names | the link to the page without the anchor, and a warning |
| an image that exists in the Kit | a copy under assets/, linked there |
| an image that does not exist | dropped, with the visible note “image not included” |
Code blocks and code spans are never touched. Running it twice gives identical files: nothing in the output carries a time.
It refuses to run if .kits/<name>/.fetched is not exactly the commit kits.lock pins (or the Kit is not fetched at all), so the book never mixes versions. It says so, writes nothing, and prints the fix: make fetch (with KITS=all for a private Kit). It exits with status 2 on a refusal and on a configuration error.
docs-pull-check
For maintainers. Pulls in memory exactly as docs-pull does and writes nothing; it fails (status 1) and prints each file that differs, is missing, or is stale, with the first changed lines, if the committed pages are not what a fresh pull would give. Use it to see that nobody edited a pulled page by hand and that the pages were re-pulled after a pin moved. A Kit that is not in .kits/ is skipped with a message (and the SUMMARY.md block is then not judged), so it is safe to run anywhere. A Kit fetched at a different commit than the pin is a refusal, as for docs-pull.
test-tools
For maintainers. Runs the unit tests of the tools in tools/:
- the catalog generator,
tools/tests/test_gen_catalog.py: sanitising, idempotence, the public/private link rule, the missing-repository note, status grouping, the rewrite of theSUMMARY.mdblock and the listing of pulled reference pages on the cards. They need PyYAML and skip themselves with a message when it is missing; - the reference-docs puller,
tools/tests/test_pull_docs.py: each class of link rewrite, the banner, anchors, idempotence, the pin refusal and--check. They need nothing beyond Python 3; - the launch tooling:
tools/tests/test_go_public_check.py(every check ofgo-public-check, withcurlandghreplaced by stand-ins),tools/tests/test_stranger_env.py(the sanitised environment ofstranger-testand its dry run),tools/tests/test_app_needs.py(the per-app Kit requirements: the gate,test-run.shandfrb.shin a throwaway repository with stand-influtterandcargo),tools/tests/test_check_public_strict.py(the strict mode ofcheck-public),tools/tests/test_render_community.pyandtools/tests/test_gen_third_party_notices.py. They need nothing beyond Python 3; - the website tools:
tools/tests/test_site_stage.py(the link rewrite and the README rename),tools/tests/test_site_check.py(the checks ofsite-check) andtools/tests/test_publish_site.py(the dry run and the push guard, against a local bare repository and a stubgh; needsgit). They need nothing beyond Python 3.
Plain python3 -m unittest from the repository root runs the same tests.
site
Builds the book as a website with mdBook into build/site/, ready to be served at the root of SITE_DOMAIN. If mdBook is not installed it prints how to install it and exits 0. How the site is built and published is in Publishing the website; the steps in order:
- Stage the book.
book/is copied tobuild/site-src/, and in the copy every link to a path outsidebook/(the demo apps, the labs,CONTRIBUTING.mdand so on) becomes a link to the file or folder on GitHub (SITE_REPO_URL,SITE_REPO_BRANCH). Links that stay inside the book are left alone. It prints how many links it rewrote. While the repository is private those addresses answer 404 for everyone without access, andmake sitesays so.SITE_REWRITE_REPO_LINKS=0turns the rewrite off. The same step renames an in-book link to a folder’sREADME.mdtoindex.md, because mdBook publishes that page asindex.html.book/itself is never changed. - Build with mdBook. The configuration is generated into
build/mdbook/, so there is nobook.tomlto keep in step. The site is served at/, search is on, every page has an “edit this page” link, and there is a404.html. The default favicon is kept. - Diagrams. The
mermaidcode blocks of the pulled Kit pages are drawn in the browser by a pinned copy of mermaid that is committed intools/site/vendor/and copied into the site; nothing is loaded from another host. The page’s light or dark theme decides the diagram colours. - Robots. Every page gets
<meta name="robots" content="noindex">unlessSITE_INDEXABLE=1. - Third-party notices.
THIRD-PARTY-NOTICES.mdis copied into the site root, because the vendored diagram script is a bundle of other people’s code and their licences ask for the notice to travel with it. - Publish staging.
build/site-publish/isbuild/site/plus aCNAMEfile holding exactlySITE_DOMAINand an empty.nojekyll. That folder is whatpublish-sitecommits.
Preview it with python3 -m http.server -d build/site 8000. The first line of output says how many pages were built. make docs does not need mdBook and is unchanged.
site-check
Checks the site that make site built (it stops with a message if there is none) and prints one line per check. It uses only Python 3.
- Links: every internal link, script, stylesheet and image address in every generated page resolves to a file, and every
#fragmentis an id that exists in the page it points to. Addresses to other sites are counted but not fetched. - Publish folder:
build/site-publish/has aCNAMEthat holds exactlySITE_DOMAIN, an empty.nojekyll, anindex.html, and the same pages asbuild/site/. - Robots: with
SITE_INDEXABLE=0every page carries the noindex tag; withSITE_INDEXABLE=1none does, exceptprint.htmlandtoc.html, which mdBook always marks noindex itself. - Diagrams: the vendored
tools/site/vendor/mermaid.min.js, the copy in the site and the copy in the publish folder all match the sha256 recorded intools/site/vendor/README.md, the licence file is there, and every page with a diagram loads the init script. - Notices:
THIRD-PARTY-NOTICES.mdis in the site root and in the publish folder, byte for byte the one at the repository root.
Exits non-zero on any failure.
publish-site
Maintainers only. Removes the old build, runs make site and make site-check, then commits the contents of build/site-publish/ as one orphan commit (no parent) to a local branch gh-pages. The commit is built with a scratch index and no checkout, so main, your index and your working tree are never touched.
- By default it is a dry run: it prints the commit, its file count, its
CNAME, the repository’s visibility, the push it would have made, and why it did not, and exits 0. - It pushes only when all of these hold:
PUBLISH=1;gh repo viewsays the repository behindoriginisPUBLIC; the working tree has no uncommitted changes; and the current branch isSITE_REPO_BRANCH. The push isgit push --force-with-lease=refs/heads/gh-pages:<what origin holds now> origin gh-pages:refs/heads/gh-pages. It never pushes any other branch. - It does not turn GitHub Pages on, change any GitHub setting, or touch DNS. Those steps, and their order, are in Publishing the website.
Undo the local branch with git branch -D gh-pages.
check-public
Scans every file in the tree except .kits/ and build output for anything that must not be public: internal hostnames, ticket ids, local paths that contain a user name (except the invented user name ada that the Kits’ reference pages use in examples), notes-vault paths, references to agent instruction files, repository URLs that are not public Kits in kits.lock or on the card of a Kit that tools/kit-visibility.tsv marks public, people named in credits or licence text other than the author, and secret patterns. The generator tools/gen-catalog.py and its tests are not scanned, because they spell the patterns out; what they generate, including tools/kit-visibility.tsv, is. This repository’s own address (github.com/slash-builder/kit-book, which the website links to) is allowed. One more file is skipped: the vendored tools/site/vendor/mermaid.min.js, third-party minified JavaScript that nobody here edits. It is skipped only while its sha256 equals the one recorded in tools/site/vendor/README.md; if the file changes it is scanned like everything else, and the README and licence next to it are always scanned. It prints file:line for every hit and exits non-zero on any. While kits.lock lists private Kits it also prints a note that each must become public (or stop being needed) before this repository is made public.
With CHECK_PUBLIC_STRICT=1 it also fails while kits.lock lists a private Kit with role core. A private app-dep Kit is only a warning that names the apps that will be skipped for a stranger. THIRD-PARTY-NOTICES.md is exempt from the rule about people named in credits (it exists to name the copyright holders that third-party licences require); every other rule scans it.
community
For maintainers. Renders CODE_OF_CONDUCT.md and SECURITY.md at the repository root from the templates in tools/community/ and the two contacts in tools/contacts.conf (CONDUCT_CONTACT, SECURITY_CONTACT), with tools/render-community.py. The Code of Conduct is the Contributor Covenant 2.1, unmodified except for the enforcement contact; the template records the sha256 of the official text it was made from. An empty contact is written as the visible marker [CONTACT NOT SET] with a draft banner at the top of the file, and the tool prints a warning; python3 tools/render-community.py --final fails instead, and --check fails if the committed files are not a fresh render. Edit the template or contacts.conf, never the rendered file.
go-public-check
For maintainers. The launch gate: a single read-only run that prints a checklist table, one row per check, ok, warn or FAIL, and exits 1 if any row is FAIL (a warn says what to do and does not fail it). It writes nothing to the working tree (the website is built in a temporary copy), changes no GitHub setting and touches no DNS. It takes about a minute.
| # | Check |
|---|---|
| a | CHECK_PUBLIC_STRICT=1 make check-public passes; a private app-dep Kit is a warning that names the apps that will be skipped |
| b | kits.lock: each URL ends in a repository named like the Kit; no RENAMED AT RELEASE comment; no application-kit, application_kit or appkit; every pin is 40 hex characters |
| c | every core Kit’s repository answers HTTP 200 to a plain curl with no token, and github.com/<org>/<repo>/commit/<sha> of its pin answers 200 (a redirect is not a 200) |
| d | make docs, make docs-pull-check, and make site and make site-check with SITE_INDEXABLE=1 pass |
| e | LICENSE and LICENSE-docs exist; THIRD-PARTY-NOTICES.md exists and names the vendored mermaid version and sha256 recorded in tools/site/vendor/README.md |
| f | tools/contacts.conf has both contacts (else: set CONDUCT_CONTACT and SECURITY_CONTACT in tools/contacts.conf); CODE_OF_CONDUCT.md and SECURITY.md exist, carry no placeholder ([CONTACT NOT SET], [INSERT, {{) and are a fresh render |
| g | no tracked file spells an old Kit name (appkit, application_kit, application-kit) except where tools/go-public-allow.txt allows it (never kits.lock) |
| h | this repository’s own visibility, from gh repo view --json visibility: PRIVATE is a warning, “make the repo public, then re-run” |
| i | the built site’s CNAME equals SITE_DOMAIN |
| j | the working tree is clean and on main (a warning), and every apps/<app>/kits.need names a Kit that kits.lock lists |
GO_PUBLIC_OFFLINE=1 skips the two network checks (c, h) and ends INCOMPLETE with exit 3, never a pass. The pure parts of the checks have unit tests (tools/tests/test_go_public_check.py). What to do with the result, and in which order, is in Publishing the website.
stranger-test
For maintainers, after the repository is public. Does what a newcomer with no GitHub account would do, and shows what they would see: clones STRANGER_REPO_URL into a fresh temporary folder, then runs make doctor, make setup (with KITS=public, the default) and make test-fast in the clone. Everything runs in a sanitised environment (tools/stranger-env.py): the GitHub token variables are removed, git may not ask for a password and has no credential helper (-c credential.helper=, GIT_TERMINAL_PROMPT=0), and gh is not on PATH, so a step that needs a login cannot quietly use yours. A fetch that skips for want of a login does not make make setup fail, so afterwards the script checks that every core Kit in kits.lock is public and was fetched at its pinned commit, and fails loudly if not (A STRANGER COULD NOT GET ...). It ends with a table of steps and exits non-zero if anything failed.
STRANGER_DRY=1 make stranger-test prints the plan and the names (never the values) of the environment variables it would remove, and clones nothing. STRANGER_STEPS="doctor setup" runs fewer steps; STRANGER_KEEP=1 keeps the clone. Apps that need a Kit that is still private are skipped with a message, which is the expected result; see kits.need.
linux-smoke-image
Builds the Docker image that linux-smoke runs in, from tools/linux-smoke/Dockerfile: Ubuntu 24.04, the Flutter Linux desktop prerequisites (clang, cmake, ninja-build, pkg-config, GTK 3 and liblzma development files), Xvfb, ImageMagick and xdotool, a software OpenGL (Mesa) and one font (without a font Flutter draws no text), Flutter 3.41.9 (the version of the maintainer’s machine; Flutter publishes no Linux arm64 archive, so it is cloned at the tag and the commit is checked) and Rust 1.98.1 through a hash-checked rustup-init. The sources and hashes of the pins are printed at the end and kept in the image as /opt/linux-smoke-pins.txt. The first build takes a few minutes and the image is about 4 GB; after that it is cached by Docker and linux-smoke does not need the network except for flutter pub get and Rust crates (cached in two named Docker volumes). The build log is build/linux-smoke/image-build.log.
linux-smoke
Builds every demo app for Linux in a container and launches each one under a virtual display. Needs Docker, make linux-smoke-image once, and the Kits fetched (make setup KITS=all while they are private; notes_rust is skipped without the Storage Kit like everywhere else). The repository is mounted read-only; each app is copied to the container’s own disk (without build/ and .dart_tool) and the host is never written to except build/linux-smoke/.
For each app: flutter pub get, flutter build linux --release (seconds and bundle size are recorded), then a launch under xvfb-run (1280x800x24, no window manager) with APP_CONTEXT_SCRATCH set, APP_CONTEXT_MODE=mock and the app’s own <PREFIX>_MODE=mock. It keeps the app up 10 s, takes a screenshot at 6 s, sends SIGTERM and records whether it was still running, the exit code, whether the roster entry appeared and was withdrawn, any Gtk/GLib warning and uncaught exception in the output, and the files the app wrote under the scratch directory. For calculator, stopwatch, minesweeper and notepad the first launch is driven with xdotool (a few clicks, and typing in the notepad) so there is something to save; a second launch on the same scratch directory then shows what survived. Finally two copies of stopwatch and of notepad run side by side in real mode with the same APP_CONTEXT_PEERS_DIR and different APP_CONTEXT_DEVICE values; the harness checks that each announced, that the two processes hold a TCP connection to each other, and, for notepad, that a note written into copy A’s folder arrives in copy B’s. The apps do not log peers, so the screenshot (each footer reads Peers: 1) is the other evidence.
Results: build/linux-smoke/results.md (a table, the environment and the details, including every warning line) and the screenshots build/linux-smoke/<app>-first.png, -second.png and -peers.png. build/ is ignored by git. Exit status is non-zero when an app did not build or was not running at 10 s.
What it proves and what it does not. It shows that the Linux runners build and that the apps start, draw, take mouse and keyboard input, write their state, survive a relaunch and exit on SIGTERM, headless under a virtual display, in a container, on one architecture (linux/arm64 on Apple silicon; linux/amd64 was not run). It is not a real desktop session (no window manager or compositor, no window-close click, no notification daemon), not a real GPU (software rendering), and says nothing about other distributions, Wayland, or X11 with a real server. The GTK accessibility-bus warnings it lists come from the container having no accessibility bus.
clean
Removes build output: build/, each app’s and lab’s build/ and .dart_tool/, an app’s Rust core target/ folder, and Rust target/ folders inside .kits/. Keeps the fetched Kits.
clean-all
clean, and also deletes .kits/. Run make setup to fetch again.
kits.lock
One line per Kit, five fields separated by spaces; lines starting with # are comments.
name repo-url commit visibility role
| Field | Meaning |
|---|---|
name | The folder it is fetched into: .kits/<name>/ |
repo-url | https://github.com/<org>/<repo> |
commit | The full 40-character commit id. A branch name is not accepted, so a pin never moves by itself |
visibility | public (plain download, no account) or private (needs a GitHub login and KITS=all) |
role | core (documented here, tests run by make test), app-dep (another Kit needs it to build; fetched, not tested) or reference (documented here, not needed to build the apps) |
kits.override
Optional. One line per Kit, name path, with the path relative to the repository root; lines starting with # are comments. For a Kit named here, make fetch and make setup link .kits/<name>/ to that local checkout instead of downloading the pin in kits.lock, and say so in their output, because the pin is then not what you are building against. The file exists for a Kit change that is not merged yet. It is not meant to stay: the steps for removing a line are in the file’s own header comment.
kits.need
Optional, one per app, at apps/<app>/kits.need. The Kits the app needs besides the App Context Kit, one name from kits.lock per line; # starts a comment. Today only apps/notes_rust/kits.need exists, and it lists storage-kit, which is private for now.
An app whose needed Kit is not in .kits/ is skipped, never failed, by setup, test, test-fast, test-integration, apps, frb and doctor. Each prints one line such as
skipped apps/notes_rust: needs storage-kit (private; fetch with KITS=all)
and the test table shows the app as skipped, so a stranger who runs make setup && make test-fast with only public Kits gets a green run that says plainly what it left out. doctor also drops everything that app alone required (flutter_rust_bridge_codegen, its Rust version, CocoaPods). make app APP=<app> and make run APP=<app>, which name one app, say which Kit is missing and what to run, and exit non-zero. With KITS=all (and a GitHub login) nothing is skipped and every test runs as before. make go-public-check fails if a kits.need names a Kit that kits.lock does not list.