Development¶
Developer-facing notes for working on Kerbside itself. See AGENTS.md for build commands, conventions, and common tasks, and testing.md for the test harnesses and CI lanes.
Database migrations¶
Kerbside uses Alembic for database schema migrations. The migration
files are located in the alembic/versions/ directory.
Creating a new migration¶
This will create a new migration file in alembic/versions/. Edit the
generated file to add your schema changes in the upgrade() and
downgrade() functions.
Example:
def upgrade() -> None:
op.add_column('table_name', sa.Column('column_name', sa.Type()))
def downgrade() -> None:
op.drop_column('table_name', 'column_name')
Applying migrations¶
To apply all pending migrations:
To rollback one migration:
Note: Alembic automatically uses the database URL from the kerbside configuration, so ensure your kerbside config is properly set up before running migrations.
Review tracking¶
Kerbside receives periodic whole-file human review in addition to the
usual review of changes at pull request time; the current state is in
REVIEWS.md.
Which files count is set by .vscode/review-scope.toml: Python, Rust,
shell, and Markdown, less the plan archive in docs/plans/ and the
generated protobuf stubs.
The state (REVIEWS.md, .vscode/*.weaudit*) is maintained with
tools/review-tracking.sh, a wrapper around the shared helper in the
shakenfist/development
repository. In a clone it is run by hand, not from git hooks: prune
after a pull to discard reviews of files that have since changed,
stamp before committing new review marks, regen to rebuild
REVIEWS.md, next to pick an unreviewed file, and status to
report effective coverage at HEAD. On develop itself the
prune-reviews workflow runs prune automatically after every push,
committing the result back as shakenfist-bot, and the daily
consistency audit in shakenfist/development files an issue when five
or more in-scope files need review.
Signing review marks¶
A review mark is an attestation, so the commit that introduces one must be signed -- that signature is what binds the reviewer to the exact content reviewed. Signing is configured per clone and is easy to forget in a fresh one; check it before stamping, because an unsigned review commit records a mark that nothing vouches for:
git config gpg.format x509
git config gpg.x509.program gitsign
git config commit.gpgsign true
git config tag.gpgsign true
Verify with git log --format='%h %G? %s'; review commits should
report U (signed, with gitsign's Fulcio chain not in a local trust
store) rather than N (unsigned). gitsign needs an interactive
Sigstore login on first use, so run gitsign-credential-cache & to
authenticate once per session instead of once per commit.
The bot's prune commits are deliberately unsigned: pruning only
ever removes marks, so it cannot manufacture an attestation. Only
the commits that add marks need signatures.
Vendored web assets¶
Bootstrap CSS¶
Kerbside uses bootstrap CSS for styling. This was constructed by
downloading Bootstrap 5.3 and jQuery 3.7.0 and then installing to
kerbside/api/static/js.
Axios¶
Kerbside's web administration API uses Axios for HTTP requests.
Version 1.6.5 is cached at kerbside/api/static/js.
sfui¶
kerbside/api/static/sfui is a vendored copy of
shakenfist/sfui, the Shaken
Fist design system: design tokens (tokens.css), the shared page
stylesheet (sf.css), a theme boot script, the brand logo,
Lit-based web components such as sf-tabs, and the vendored Lit
and morphdom libraries those pages need. It is copied in verbatim
by sfui's own tools/vendor.sh, which also stamps the copy with
its source commit in .sfui-commit.
The design system's own README.md is vendored along with it, so
the full contract -- the token rules, what sf.css provides and
how its cascade layer works, and the component contract -- is
readable at kerbside/api/static/sfui/README.md without leaving
the repository.
Never edit anything under kerbside/api/static/sfui/ in place:
change the canonical sfui checkout instead and re-vendor, or the
next sync will silently discard the local change. To re-vendor from
a clean, up-to-date sfui checkout:
To check whether the vendored copy has drifted from canonical sfui without copying anything:
Both commands are run from the sfui checkout, not from kerbside.
Re-vendor from sfui's develop branch once the change you need has
merged there, not from the branch you made it on. The sfui-vendor
consistency audit compares .sfui-commit against canonical
develop and reports a copy that is behind it, so a stamp naming a
feature branch commit -- or an ancestor of a merge commit -- is
flagged even when every vendored file is byte for byte correct.
Previewing templates¶
sfui has no CI of its own, and nothing in kerbside's tox lanes lints templates or CSS -- flake8 and the unit tests cover Python, and the HTML smoke tests deliberately assert on fixture data, never on markup. The only safety net for a converted page's chrome is a human looking at rendered pixels, in both palettes, without having to stand up a deployed kerbside first.
tools/preview-templates.py renders a converted page through
kerbside.api's own Flask test client -- so routing, context and
Jinja rendering are exactly what a real request would produce -- and
writes it next to a symlink of the real static tree, because the
templates reference their assets as root-relative absolute paths
(/static/sfui/...). Only pages that have actually been converted
onto base-sfui.html are supported; today that is login, which
needs neither authentication nor the database, and consoles, whose
fixtures render two consoles -- one with sessions and active tokens,
one with neither -- so a single screenshot shows both terminate
states (the two-step disclosure and the dim zero badge).
The script imports kerbside.api, so it needs an interpreter with
kerbside's dependencies installed. The tox environment already has
them, which makes .tox/py3/bin/python the interpreter to reach for
after any tox -epy3 run; a virtualenv with pip install -e . works
just as well. A bare system python3 will not.
tox -epy3 # only if .tox/py3 does not exist yet
.tox/py3/bin/python tools/preview-templates.py login /tmp/preview
(cd /tmp/preview && python3 -m http.server 8099) &
chromium --headless --disable-gpu --no-sandbox \
--hide-scrollbars --window-size=1280,1000 \
--virtual-time-budget=4000 \
--screenshot=/tmp/login-dark.png \
http://localhost:8099/login.html
chromium --headless --disable-gpu --no-sandbox \
--hide-scrollbars --window-size=1280,1000 \
--virtual-time-budget=4000 \
--blink-settings=preferredColorScheme=2 \
--screenshot=/tmp/login-light.png \
http://localhost:8099/login.html
Three details are easy to get wrong:
- Pick a port nothing else is using. 8099 is only an example, and a stale server left running from an earlier preview will happily serve 404s from a directory that no longer exists.
- Headless Chromium reports
prefers-color-scheme: darkby default, so the plain run above exercises the dark palette;--blink-settings=preferredColorScheme=2is the only value that gives the light one. - Serve the directory over HTTP, not
file://-- the theme toggle is an ES module, and modules do not load from thefile://scheme.
Then actually look at both PNGs.
This only covers what renders. The interactive paths -- submitting
a form, a wrong password, the theme toggle, logout -- still need a
browser against a running kerbside, or a hand-check of the relevant
fetch calls.