log: slog through auxilia's scribe, not logrus
This was the last service on the instance still logging through logrus,
and the ecore bump made it a correctness question rather than a
consistency one: the recovery middleware reports a panic through slog's
DEFAULT logger, so without a SetDefault the one record carrying a stack
trace would have gone to Go's plain handler while every other line went
through logrus.
initLogging installs a scribe tint handler on stderr — source on, colour
only when stderr is a terminal, and the mask rules for token, cookie and
authorization. It reads -d out of the argument vector rather than
waiting for core-go's parse, because a daemon that only became verbose
after it had finished starting would be silent for exactly the part of
its life an operator passes -d to watch. Terminal detection is a stdlib
Stat rather than golang.org/x/term, which would be a new dependency for
one predicate.
The startup config check keeps its shape deliberately: it still
accumulates every missing key and reports them in ONE record before ONE
exit, so an operator fixes the config in a single pass. The keys go in
as a slice attribute rather than a joined string, so the structured
sinks keep them as a list.
culpa goes in at the one boundary where it pays: the errors web.New
returns, which are the only errors this package produces and all of
which arrive at that single startup record. Each carries a hint naming
the config key or the build step that fixes it, and scribe.Err unfolds
message, code, hint and stacktrace into fields of their own. The failing
request path logs the same way, with ErrorContext so a cancelled request
reads as cancelled rather than as an unexplained 500.
TestPanicIsAnErrorPage covers the wiring end to end through Register: a
panicking authorizer yields the chrome-wrapped 500, the panic value does
not reach the viewer, and the report reaches slog's default logger.
deps: sr-ht-ecore whose middleware reports panics through slog
The panic report is structured now (method, path, panic, stack) and goes
through slog's default logger, so a service that never calls
slog.SetDefault gets those reports in Go's plain stderr handler. The
next commit is what makes this one land somewhere useful.
readme: name the ecore packages the web tier draws from
cmd: ecoretest keys in the startup smoke test
The synthesized config.ini takes ecore's fixed keyset and origins rather
than generating a keypair per run; a constant cannot be malformed by
accident, which matters for a subprocess whose only way of complaining
is a log.Fatal. Also notes the three middlewares Register now installs
inside its own group, and why chi's Recoverer stays out here.
authz: bootstrap the tests from ecoretest
Drops the fernet key generation and the ed25519 seed both test files
were doing by hand, and the ini.File assembled around them. The keys are
constants now, which is what makes InitCrypto idempotent across the two
packages of this service that both call it.
web: draw assets, pages, csrf and the middleware from ecore
Deletes this package's copies of five things the instance now shares.
assets replaces hashedCSSRe, hashedBundleRe, resolveCSSHref,
resolveBundleHref and the bare StripPrefix(FileServer) route. That route
was a fix and not only a dedupe: an http.FileServer answers a directory
with a listing, so /static/ published the whole inventory of the binary
— every vendored artefact and the hashed names that fingerprint the
build — as a public, hour-cacheable page. It also wrote the cache
directives onto the header map before delegating, where a panic later
would have carried public, max-age=3600 onto a viewer's error page, and
left the Vary the private-cache policy sets, which is enough to stop any
shared cache from ever reusing an asset whose name was hashed for that
purpose. assets.Handler refuses the listing, stamps the policy on the
bytes rather than on the map, and drops the Vary per asset.
pages replaces pageNames, the package-level template map, render and
errorData. Pages are discovered from the embedded tree instead of listed
by hand, so templates/x.html is now the whole registration of a page,
and a page that defines no content block is refused at startup rather
than served as chrome around a hole with a 200 — neither of which this
service checked before. Render answers the response itself; the error it
returns is a log line and never reaches fail.
The local error.html goes with them: ecore ships the page and the
srht-error partial. renderError stays here, because building this
service's view struct is this service's business, and it now passes
for every status but 400 so a repository the viewer may not see and one
that never existed produce the same sentence.
csrf.Require and the middleware group are new rather than replacements:
compare has no POST today, so the guard covers the day somebody adds
one, and PrivateCache states the policy every per-viewer page here was
serving without.
BundleHref leaves viewData for chrome.Service.Assets, keyed bundle.js
and read through an emptiness guard, next to StyleHref where the other
hashed artefact already lived. The date helper goes to chrome's reltime
and abstime: listings show 3 days ago and hover to the exact stamp.
deps: sr-ht-ecore with the web-tier packages
Brings in assets, pages, csrf, middleware and ecoretest, plus the
chrome that grew Service.Assets — the slot three services had each
added a field of their own for.
web: draw the chrome from sr-ht-ecore
The nav/service-switcher, the brand, the login block and the environment
banner were a copy of code every custom service on this instance carries.
The copies drifted, so they now come from one place:
sourcecraft.dev/bigbes/sr-ht-ecore/chrome.
web/chrome.go is gone. New builds one chrome.Service from the shared
config.ini and hands it the discovered stylesheet; view() asks it for a
chrome.Page per request, which viewData embeds rather than copies, so a
field ecore adds later arrives here without an edit. The layout invokes
"srht-env-banner" and "srht-nav" instead of the markup it used to spell
out, the landing's repository listing goes through "srht-repo-list", and
the FuncMap starts from chrome.Funcs() with the local shortsha deleted.
Two ecore policies differ from what this service did and are adopted as
the instance's: the brand label is the config section's short name rather
than a literal "compare", and the origins are trimmed of a trailing
slash. What stays local is what is compare's alone: the bundle href, the
diff-status colours, and the container-fluid the two diff views ask for.
Tests move to testify; the nav ordering/exclusion test goes with the code
it tested, and a test that the layout invokes the chrome at all replaces
it.
web: git.sr.ht-style dashboard and unified nav brand
Adopt the family look the dolt service already has. The nav brand
becomes circle icon + site name + red service label (dropping the
hub-origin variant), matching every other service on the instance. The
logged-in index turns into the two-column dashboard: a sidebar with the
service blurb and the jump form, and the repositories as shared-theme
event-list cards — name, visibility as small muted text (non-public
only, lowercase), description underneath. The big in-page h2 lives on
only for anonymous visitors, where the nav has no user context yet.
fix(apk): keep -modcacherw when overriding GOFLAGS
The GOFLAGS="-trimpath" passed to make clobbers abuild's default
GOFLAGS, dropping -modcacherw: the module cache then extracts read-only
and the CI cache tarball made from it fails to unpack on the next build
(mkdir into 0555 dirs, permission denied — warm build #266). The
poisoned gomod tarball was deleted by ad-hoc job #268.
ci: cache Go module and build dirs via cacher
Same scheme as sr-ht-dolt (dbd8638 there): restore ~/go/pkg/mod and
~/.cache/go-build from the Garage docker-cache bucket before abuild and
seed them back after, keyed by go.sum via cacher (go.bigb.es/cacher).
GOCACHE/GOMODCACHE are re-pinned inside build() because abuild redirects
them into its throwaway $tmpdir and an upstream typo in abuild.in
clobbers any pre-set GOMODCACHE with GOCACHE's value.
ci: commit the apk build, packaging and mirror-trigger files
These three were written but never committed, so the whole publishing chain
was dead from its first link: with no .build.yml on sourcecraft there is none
on the git.srht.bigb.es mirror either, so the push hook has no manifest to
submit, no build ever ran, and compare.sr.ht has never appeared in the apk
index at repo.bigb.es — which today holds spec.sr.ht alone.
The code itself was mirroring fine, which is what made this hard to see: the
sourcehut side sits on the current commit, so everything looks configured
until you ask git which of these files it actually knows about.
.sourcecraft/webhooks.yaml is in the same state and has the same consequence
one level up: the gitsync webhook was never registered, so the mirror has been
riding its hourly safety-net poll rather than updating on push.
The build secret apk-ci-s3 the publish task needs now exists on the account,
so nothing else blocks the first build.
web: add long-line wrapping toggle
web: hide the per-row diff scrollbar gutter (overlay style)
Each @pierre/diffs code row scrolls horizontally on its own
(overflow-x: scroll) and reserves a classic scrollbar track sized to the
measured --diffs-scrollbar-gutter. On platforms with non-overlay
scrollbars (e.g. macOS "always show"), that track shows as a grey strip
under any row with a long line, reading as leftover bottom padding.
Set --diffs-scrollbar-gutter-override: 0 on #diff-root so the track is
never reserved; long lines stay scrollable by trackpad / wheel. Verified
the override wins over a simulated 14px measured gutter (reserved height
stays 0).
Re-hashed the embedded stylesheet.
web: remove grey padding bands inside each diff file
Two sources put a grey strip above the first line and below the last,
inside the file border:
- The shared `base` partial's `section { padding-bottom: .5rem }` applied
to .diff-file (it is a <section>). Cancel it with padding-bottom: 0.
- The @pierre/diffs vertical code padding (--diffs-gap-block, default 8px)
above the first and below the last line. Set it to 0 on #diff-root; it
inherits into the DIFFS-CONTAINER shadow DOM. Horizontal code padding
(--diffs-gap-inline) is left on its default.
Re-hashed the embedded stylesheet.
web: tighten diff and file-tree spacing
Address three spacing complaints in the compare view:
- File tree: switch @pierre/trees to the "compact" density preset (24px
rows, 0.8 factor) so folder/child rows read as a dense index rather
than an airy list.
- Diff blocks: drop the inter-file gap (.diff-file margin-bottom
1rem -> .5rem) and the tree/diff column gap (1rem -> .75rem).
- File tree height: size the mount to its content height (capped at the
viewport) instead of always filling the viewport, removing the band of
empty tree background below the last file. Re-measures on resize and on
every tree change (expand/collapse) via tree.subscribe.
Rebuilt and re-hashed the embedded bundle and stylesheet.
web: content-hash the bundle and colour the status column
Two diff-view fixes reported against the deployed instance.
Empty file tree after an upgrade: bundle.js had a stable filename served with a
1-hour cache, so browsers kept a pre-fix bundle and the @pierre/trees sidebar
rendered blank. Content-hash the bundle like the stylesheet — Makefile emits
bundle.<sha256[:8]>.js, server.go globs it into a BundleHref, the templates
reference {{.BundleHref}}, and the hashed name is served immutable. A deploy now
always busts the cache.
Unreadable status column: the changed-files table rendered A/M/D in the dim
default cell colour, near-invisible on the dark chrome. Render each status as a
semantic .diff-status badge (added=green, modified=amber, deleted=red,
renamed=blue) with light/dark variants, via statusClass/statusLabel helpers and
a small self-contained style block (the sourcehut-derived main.min.css can't be
rebuilt locally).
Tests resolve the hashed bundle via a bundleName helper and assert its immutable
cache header; docs updated for the hashed filename.
docs: note that static assets are embedded and need a rebuild
web/static is //go:embed-ed into the binary, so `make css`/`make bundle` alone
don't reach the running daemon — re-run `make build` after either. Documents the
stale-asset gotcha in the build section.
docs: spec for inline code comments on diffs
Draft proposal for line-anchored comment threads on the commit and compare
pages. Records the key finding that storage is already available through
sr-ht-core (Postgres via the database package + connection-string), that the
service is stateless by choice rather than by limitation, and that @pierre/diffs
already provides the thread UI primitives (DiffLineAnnotation + renderAnnotation
+ onDiffLineClick). Covers the anchoring model, schema, authz reuse, HTTP API,
frontend wiring, config, and a commit-first phasing.
web: full-width split diff view with a rendered file tree
Make the commit and compare pages full-bleed and default to the side-by-side
(split) diff, so the layout matches a conventional diff viewer: file tree on
the left, split diff on the right.
- layout.html renders the content wrapper with a per-page ContainerClass;
chrome() defaults it to "container" and the commit/compare handlers set
"container-fluid" for the diff views (the nav stays centered).
- app.ts defaults DEFAULT_LAYOUT to "split".
- Fix the @pierre/trees sidebar rendering blank: its virtualized container
collapses to zero height unless the mount has a definite height, so
sizeTreeRoot() gives #tree-root a viewport-height panel (re-applied on
resize). Rebuilt the vendored bundle.