# oglyad repository metadata and health checker for [Terra](https://terra.fyralabs.com). "огляд" is Ukrainian for "review" or "inspection". ```sh git clone https://github.com/terrapkg/packages ../packages oglyad -m ../packages # warnings and errors 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 --only drift --format json ``` from a checkout, `cabal run oglyad -- ` 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. ## output and exit codes `--format` picks `text` (default), `markdown` (made for GitHub job summaries) or `json`. the json is a versioned envelope: ```json {"schema": 1, "oglyad": "0.1.0.0", "generated": "...", "monorepo_head": "...", "summary": {"error": 0, "warn": 0, "info": 0, "ignored": 0}, "findings": [{"check": "...", "package": "...", "path": "...", "severity": "...", "message": "..."}], "ignored": [...]} ``` `schema` changes on any breaking change to the json. | 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: ```json { "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`: ```hcl labels { oglyad_ignore = "duplicate-name, stale" } ``` file entries that no longer match anything are reported as `unused-ignore`, so exceptions don't outlive their reason. `--no-ignores` shows everything. [`ignores.json`](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`) | | `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 | | `unparsed-rhai` | info | update.rhai does something oglyad can't follow, like custom scraping | | `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 | ## 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.json` | HEAD hasn't moved | | `repodata-*.json` | the repo's primary.xml checksum is unchanged | | `upstream.json` | younger than `--cache-hours` (default 6), successes only. entries unused for 30 days are dropped | 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. **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`](.github/workflows/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 terrapkg/packages' default branch 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`](anda.hcl) has two projects: | project | spec | builds | |---|---|---| | `oglyad` | [`oglyad.spec`](oglyad.spec) | the `v` release tag | | `oglyad-git` | [`oglyad-git.spec`](oglyad-git.spec) | the checked-out commit, versioned `^.` | ```sh 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`](.github/workflows/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 1. bump `version` in `oglyad.cabal` and `oglyad.spec`, and add a `CHANGELOG.md` entry 2. push a `v` tag, e.g. `v0.1.0.0` [`hackage.yml`](.github/workflows/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`. ## formatting code is formatted with [ormolu](https://github.com/tweag/ormolu) 0.9.0.0, and CI fails on unformatted files. install the matching version from its [release page](https://github.com/tweag/ormolu/releases/tag/0.9.0.0), then: ```sh ormolu --mode inplace $(git ls-files '*.hs') ``` ## license GPL-3.0-or-later. see [LICENSE](LICENSE).