oglyad
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
from 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.
releases
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. --repo points one release at a
different Terra repo.
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}).
output and exit codes
--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": "...",
"releases": [{"release": "43", "label": "f43", "ref": "origin/f43",
"commit": "...", "terra_repo": "https://repos.fyralabs.com/terra43"}],
"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.
| 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 |
ignoring known findings
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.
checks
| 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 |
scripts oglyad can't follow
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.
how it works
┌──────────── 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
let
bindings into rpm.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 update would
- versions compare with a port of rpm's
rpmvercmp, plus some forgiveness
for respelled tags (v1.2, 1.2-rc1 vs 1.2~rc1, 2026-07-27 vs 20260727)
caching
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.
security
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.
nightly report
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.
packaging
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_64
both 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.
releasing
- bump
version in oglyad.cabal and oglyad.spec, and add a
CHANGELOG.md entry
- 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')
license
GPL-3.0-or-later. see LICENSE.