repository metadata and health checker for Terra. "огляд" is Ukrainian for "review" or "inspection," but also for a health checkup.
git clone https://github.com/terrapkg/packages ../packages
oglyad -m ../packages # warnings and errors, every maintained release
oglyad -m ../packages -v # plus info findings
oglyad -m ../packages -u # plus upstream version drift
oglyad -m ../packages --fedora # plus overlap with Fedora
oglyad -m ../packages -r 44 -r rawhide # just these releases
oglyad -m ../packages -r rawhide --worktree # your local edits, against rawhide
oglyad -m ../packages --only drift --format json
oglyad -m ../um-packages # Ultramarine-Linux/packages works toofrom a checkout, cabal run oglyad -- <args> and cabal test. it needs
rpmspec (rpm-build), git, and the zstd, gzip and xz commands.
upstream lookups use GITHUB_TOKEN, or gh auth token when that's unset.
by default oglyad checks every Fedora release
Bodhi hasn't archived (the
current ones, a branched one before its release, and rawhide), so branching
and EOL need no changes here. -r/--release picks releases instead, as
43, f43 or rawhide, and can be repeated.
each release is checked against its own Terra repo (terra43,
terrarawhide) and its own branch of terrapkg/packages (origin/f43,
origin/frawhide), with that release's %{dist} (.fc43, .fcrawhide)
and %fedora. branches are read with git archive into
~/.cache/oglyad/trees, so your checkout is never touched. nothing is
fetched either, so git fetch first if the branches are old. --remote
picks a remote other than origin.
--worktree reads specs from the checkout as it is, local edits included,
and needs exactly one --release.
oglyad checks any anda monorepo it knows the layout of. -d/--distro
picks one, and by default it's whichever one's package directory the
checkout has:
| distro | monorepo | packages in | branches | repos | dist |
|---|---|---|---|---|---|
terra |
terrapkg/packages | anda/ |
f43, frawhide |
terra43, terra43-extras |
.fc43, .fcrawhide |
ultramarine |
Ultramarine-Linux/packages | ultramarine/ |
um43, umrawhide |
um43 |
.um43, .umrawhide |
builds are looked up through the same metalink dnf uses,
https://tetsudou.fyralabs.com/metalink?repo={repo}&arch=x86_64. oglyad
tries its mirrors by preference and takes the first repomd.xml whose sha256
the metalink lists, so a mirror that hasn't synced yet is skipped.
--repo swaps that url for another one. {repo} becomes the repo's name
(terra43, or terra43-extras for a subrepo) and {release} the release's
(43, rawhide). a url with metalink in it is read as a metalink, and
anything else as a base url with repodata/ under it:
oglyad -m ../packages --repo 'https://repos.fyralabs.com/{repo}' # skip the metalink
oglyad -m ../packages -r 44 --repo https://mirror.example/terra44 # one release's repoa url with neither placeholder only names one release's repo, so it needs
exactly one --release. subrepos get -extras and so on added to its end.
findings from several releases are merged. one that comes up in only some
of them is tagged, like anki [f44, f45]: ..., and messages that only
differ by dist tag are merged with %{dist} in its place:
never published (spec is at 26.09.4-1%{dist}).
--format picks text (default), markdown (made for GitHub job
summaries) or json. the json is a versioned envelope:
{"schema": 2, "oglyad": "0.1.0.0", "generated": "...", "distro": "terra",
"releases": [{"release": "43", "label": "f43", "ref": "origin/f43",
"commit": "...", "terra_repo": "https://tetsudou.fyralabs.com/metalink?repo=terra43&arch=x86_64"}],
"upstream": true,
"summary": {"error": 0, "warn": 0, "info": 0, "ignored": 0},
"findings": [{"check": "...", "package": "...", "path": "...", "severity": "...",
"message": "...", "releases": ["43", "44"]}],
"ignored": [...]}schema changes on any breaking change to the json. schema 2 replaced
monorepo_head and terra_repo with releases, and added releases to
findings. ref is null with --worktree. distro was added later, and
terra_repo keeps its name for whichever distro's repo was checked.
| exit | meaning |
|---|---|
| 0 | ran, nothing at or above --fail-on (default error, or warn, info, never) |
| 1 | ran, and found something at or above --fail-on |
| 2 | couldn't run: bad flags, unreadable ignore file, not a monorepo oglyad knows |
some findings are intentional, like a bootstrap package sharing its real package's name. there are two ways to silence one, and ignored findings don't count toward the exit code.
an ignore file, passed with --ignore-file. every entry needs a reason, and
names one check plus a monorepo path or a package name:
{
"ignore": [
{
"check": "duplicate-name",
"path": "anda/langs/zig/master-bootstrap",
"reason": "bootstrap build of zig-master"
}
]
}or a label in the package's own anda.hcl:
labels {
oglyad_ignore = "duplicate-name, stale"
}file entries that no longer match anything in any checked release are
reported as unused-ignore, so exceptions don't outlive their reason. --no-ignores shows everything.
ignores.json is the file the nightly report uses.
| check | severity | what it means |
|---|---|---|
build-lag |
error / warn / info | never published, merged but not built, or the repo is newer than your checkout |
drift |
warn | upstream released something newer than the spec (-u). scripts oglyad can't follow on its own are run with the real anda, see below. packages with a nightly or quaterdaily label build from a branch head, so they're only checked for a mangled version, not held to the latest release |
duplicate-name |
warn | two directories build the same package name into the same repo, so they overwrite each other |
stale |
info, warn with -u |
last build is older than --stale-days, warn if upstream also moved on |
upstream-error |
warn | the upstream from update.rhai 404s or fails, so anda update is failing too |
fedora-overlap |
warn | Fedora ships the same source package (--fedora). subrepos are exempt |
no-autoupdate |
info | no update.rhai, or one with only comments in it |
unparsed-rhai |
info, warn when anda can't run it either | update.rhai looks a version up in a way oglyad can't follow, like custom scraping, and anda didn't answer for it (no -u, or anda isn't installed). scripts that only bump Release, track a commit, or copy Version from another spec aren't listed |
collect |
error | rpmspec failed, a repo couldn't be fetched, or an upstream rate-limited us (warn) |
unused-ignore |
info / warn | an ignore entry matches nothing, or an oglyad_ignore label names an unknown check |
oglyad reads most update.rhai scripts itself: it finds the helper the version
comes from (gh, pypi, crates, ...), asks that upstream, and replays the
script's guards and edits over the answer, regexes included. scripts that do
more than that, like scraping a page with get() and find(), filtering
gh_tags(), or reading a file with gh_rawfile(), go to the real anda
instead. with -u and anda on the PATH, oglyad copies the release's tree
to a throwaway directory, runs anda update --labels branch=<f43> <project>
for each of those, and reads the Version rpmspec sees in the rewritten spec.
a script that throws shows up as upstream-error. answers are cached like
upstream ones.
┌──────────── IO shell (Collect) ──┐ ┌──── pure core ────┐ ┌─ IO ─┐
monorepo: rpmspec, git ──┐
Terra repodata + subrepos├─► Snapshot ───► [Check] ─► [Finding] ─► Report
Fedora repodata ─────────┤
upstream APIs ───────────┘
checks are Snapshot -> Package -> [Finding] and never do IO, so each one is
tested with a fake Snapshot in test/Spec.hs.
- specs are evaluated with
rpmspec - update.rhai is lexed and pattern-matched, not run. it follows
letbindings intorpm.version(...)/rpm.global("ver", ...)and recognizes the andax helpers (gh,pypi,crates,gitlab, ...) - upstream lookups hit the same endpoints anda does, so oglyad sees what
anda updatewould - versions compare with a port of rpm's
rpmvercmp, plus some forgiveness for respelled tags (v1.2,1.2-rc1vs1.2~rc1,2026-07-27vs20260727)
everything lives in ~/.cache/oglyad.
| file | reused while |
|---|---|
specs.fcNN.json |
the package dir's files, the dist tag, and rpm's whole macro state (rpm --showrc) are unchanged |
commits.fcNN.json |
the release's commit hasn't moved |
repodata-*.json |
the repo's primary.xml checksum is unchanged |
upstream.json |
younger than --cache-hours (default 6), or fetched earlier in the same run. successes only. entries unused for 30 days are dropped |
anda.fcNN.json |
like upstream.json, keyed by the package dir's files instead of the upstream |
trees/<commit>/ |
extracted release branches, one per commit. ones unused for a week are deleted |
a run with warm caches takes about 0.4s. a cold run takes about 5s, almost
all of it in rpmspec. --fedora adds about 8s and peaks around 200MB of
memory the first time, since Fedora's repodata is 260MB decompressed. after
that it's cached until Fedora publishes again.
rpmspec runs the shell code in specs (%(...), %{lua:...}), so running
oglyad on a checkout means running that checkout's code as you. child
processes get an allowlisted environment, so GITHUB_TOKEN and other
secrets in oglyad's environment never reach them, but code running as your
user can still read your files.
the one exception is anda. with -u, update.rhai scripts oglyad can't follow
run under the real anda, which refuses GitHub helpers without
GITHUB_TOKEN, so anda gets the token. those scripts can run shell commands
with sh(), and those commands see the token too. use a read-only token.
don't run oglyad on untrusted code, like an unmerged pull request, with credentials around. the nightly report only checks out terrapkg/packages' default branch, and its token is read-only and expires with the job.
the GitHub token is only ever sent to api.github.com, and is dropped if a
request gets redirected anywhere else.
report.yml runs every night in Terra's
builder image (ghcr.io/terrapkg/builder:f45), so specs evaluate with the
same macros Terra builds with. it builds oglyad-git with anda, checks
every maintained release's branch of terrapkg/packages with -u --fedora,
writes the markdown
report to the run's summary, and keeps the markdown and json for 30 days.
caches carry over between nights.
built the same way as anda itself. anda.hcl has two projects:
| project | spec | builds |
|---|---|---|
oglyad |
oglyad.spec |
the v<version> release tag |
oglyad-git |
oglyad-git.spec |
the checked-out commit, versioned <version>^<date>.<commit> |
anda build oglyad-git --package rpm -c terra-45-x86_64both build in mock, offline, against Fedora's Haskell libraries with
%ghc_bin_build, run the test suite in %check, and ship one self-contained
binary. package-git.yml builds
oglyad-git on x86_64 and aarch64 for every push to main and every pull
request that touches the code.
- bump
versioninoglyad.cabalandoglyad.spec, and add aCHANGELOG.mdentry - push a
v<version>tag, e.g.v0.1.0.0
hackage.yml checks the tag matches the
cabal version, builds and tests the sdist, and publishes it to Hackage. the
same tag is what oglyad.spec downloads. to preview a release first, run the
workflow by hand, which uploads a Hackage package candidate instead.
it needs a HACKAGE_TOKEN secret (an API token from your Hackage account) in
a GitHub environment named hackage.
code is formatted with ormolu 0.9.0.0, and CI fails on unformatted files. install the matching version from its release page, then:
ormolu --mode inplace $(git ls-files '*.hs')GPL-3.0-or-later. see LICENSE.