CLI reference¶
Synopsis¶
hyrum [--version] COMMAND ...
Hyrum exposes five subcommands:
hyrum check TARGET [OPTIONS]— runTARGET(a tox environment name or make target, for exampleunit,lint) across many charm repos.hyrum clean [OPTIONS]— remove the build artefacts a run leaves behind, keeping the checkouts.hyrum compare BASELINE CURRENT [OPTIONS]— diff two saved runs.hyrum get-charms [OPTIONS]— clone or update every charm listed in a CSV into the charms directory.hyrum show PATH [OPTIONS]— print the status summary of a saved run.
hyrum check¶
hyrum check [OPTIONS] TARGET
TARGET is the tox environment name or make target to run in each charm (for example, unit, lint, fmt).
Charm selection¶
--charms-dir PATHDirectory containing pre-cloned charm repositories.
Default:
~/.cache/hyrum/charmsEnvironment variable:
HYRUM_CHARMS--config PATHPath to the TOML configuration file. The
[ignore]table and thesavesetting are read.Default:
hyrum.toml(in the current directory; silently ignored if absent)--repo REGEXCase-insensitive regular expression matched against each charm’s directory name. Only matching charms are processed.
Default:
.*(all charms)--limit NStop once N charms have been selected to run (0 = no limit). Charms that the filters skip do not count towards N, but they still appear in the skipped tally, so
--limit 1always gives one run if any charm is runnable at all.Default:
0--framework {scenario,jubilant}Only process charms that use the specified testing framework. Framework detection checks dependency declarations first, then falls back to AST scanning of test files.
Default: (no filter; all frameworks)
--from-results PATHOnly run charms named in this saved results file (for example, one written by
--saveor--auto-save). Always a filesystem path: there is no default location, run id, or target-name lookup. Intersects with--repo,--framework, and[ignore]like every other filter, and is applied before--limit. A narrowed run turns off the default rolling save, since it would record every charm the run skipped asskipped; pass--saveor--auto-saveto save anyway.Default: (not set)
--status STATUS[,STATUS...]With
--from-results, only select charms whose saved outcome is one of these. Repeatable and comma-separated. Accepts the outcome statuses (passed,failed,no_target,timeout,runner_error,patcher_error,skipped) plus two groups:failing(reached the runner or the patcher and did not come out clean) andnot-passing(everything butpassed). Requires--from-results.Default:
failing(with--from-results)
Runner¶
--runner {auto,tox,make}Which runner backend to use.
auto: prefer tox iftox.iniis present, otherwise prefer make; fall back to the other backend if the requested target is absent.tox: always use tox.make: always use make.Default:
auto--workers NNumber of charm repositories to process concurrently (minimum: 1).
Default:
1--tox-executable CMDTox command to use.
Default:
tox--make-executable CMDMake command to use.
Default:
make--timeout SECONDSPer-charm timeout in seconds. Charms that exceed this are marked
timeout.Default:
1800
Dependency patching¶
--no-patchSkip the patching step entirely. Run charms against whatever dependencies they already pin. Mutually exclusive with
--patch.Default: off (the default
--patchofops @ canonical:mainapplies)--patch SPECPatch a dependency.
SPECis a PEP 508 requirement. May be given multiple times (once per package). If--patchis not given (and--no-patchis not set), hyrum applies the defaultops @ canonical:main. Accepted forms:<name>==<version>(or any PEP 440 specifier) — pin to a PyPI version, for exampleops==2.17.0,requests>=1.2,<2.
<name> @ git+<url>[@<ref>][#subdirectory=<sub>]— explicit PEP 508 git source.<ref>is any git ref (branch, tag, commit SHA).
<name> @ <url>[@<ref>]— barehttps://…URL with optional@ref.
<name> @ file://<path>, or a bare path (/abs,./rel,~/checkout) — a local checkout.
ops @ <owner>:<branch>— GitHub shorthand forops; expands tohttps://github.com/<owner>/operatorat that branch.
charmlibs-<name> @ <owner>:<branch>— GitHub shorthand for a charm library; expands tohttps://github.com/<owner>/charmlibsat that branch, with the subdirectory taken from the package name verbatim. Type the separators the way the directory exists in the monorepo (for example,charmlibs-nginx_k8s,charmlibs-interfaces-k8s-service). Charmlibs packages must be patched from a git source: a version pin or a local path is rejected.
charms.<author>.v<n>.<lib> -> <spec>— patch a vendored charm library to a package. The left side is the dotted import path of the vendoredlib/charms/<author>/v<n>/<lib>.pyfile;<spec>is any of the forms above for the replacement package, for examplecharms.operator_libs_linux.v0.apt -> charmlibs-apt==1.0.0.
The
owner:branchshorthand is only accepted foropsandcharmlibs-*packages. Any other package needs an explicitgit+<url>or barehttps://…URL.When the patched package is
ops, hyrum also rewrites theops[testing]andops[tracing]companion packages from matching subdirectories of the operator monorepo.Extras written into
SPECare not honoured: the patcher preserves whatever extras the charm itself declares.Default:
ops @ canonical:main--poetry-executable CMDPoetry command used to regenerate
poetry.lockafter patching.Accepts a shell-quoted string (
"uvx poetry") or a single executable name.Default:
poetry--uv-executable CMDuvcommand used to regenerateuv.lockafter patching.Default:
uv--lock-timeout SECONDSTimeout for
poetry lockoruv lockduring patching. Independent of--timeout(the per-charm runner timeout).Default:
600--auto-python / --no-auto-pythonWhen enabled, hyrum wraps
poetry lockwithuv run --python X.Yso that the lock command runs under an interpreter that satisfies the charm’s declaredrequires-python. Requiresuvon PATH.Default:
--auto-python--no-lockDo not take a per-charm lock. Two runs sharing a charms directory then patch the same charm at the same time, and each reports results for whichever patch won, so only use this when nothing else is running.
Default: off (lock each charm from patch to restore)
Preflight¶
--preflight / --no-preflightBefore any charm runs, check that each
--patchgit ref exists in its remote (onegit ls-remoteper distinct URL and ref) and that the runner executables are installed, so a typo or a missing tool fails once instead of once per charm. A ref that does not exist in the remote is fatal; a full 40-character commit SHA is accepted even thoughgit ls-remotecannot see it, while an abbreviated SHA is rejected because it cannot be fetched. Network and authentication failures warn and let the run continue, since they say nothing about whether the ref exists. Under--runner auto, a backend whose executable is missing is dropped with a warning, and only an empty set of backends is fatal. The ref check needs network access.Default:
--preflight
Host environment¶
--host-env-defaults / --no-host-env-defaultsInject sensible default environment variables (currently
PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1) plus matchingTOX_OVERRIDEpass_env+=entries, so common host build issues are not mis-attributed to the charm. Existing values are preserved. See Host prerequisites for the rationale.Default:
--host-env-defaults
Logging and output¶
--log-dir PATHDirectory to write per-charm log files. Each file contains the run metadata and the runner’s output (stdout and stderr merged, in the order they were written). File names use the charm’s path relative to the charms directory with
/replaced by__.Default: (not set; logs are not written)
--quietSuppress all output except errors. The exit code still reflects pass/fail. Mutually exclusive with
--verboseand--verbosity.Default: off
--briefThe summary tally, without the per-charm offender list. This is what a run does by default; the flag exists to ask for it back after a config file or an alias has turned something else on. Mutually exclusive with
--quiet,--verboseand--verbosity.--verboseInclude the per-charm offender list in the report (failed, timed-out, and errored charms with their error messages, plus all skipped charms with their reasons). Mutually exclusive with
--quietand--verbosity.Default: off
--verbosity {debug,trace}Developer-level log verbosity.
debug: detailed execution logging.trace: currently aliased todebug; reserved for future per-line code tracing.Mutually exclusive with
--quietand--verbose.Default: (not set; INFO level)
--no-headersSuppress the header row in the summary table.
Default: off
--no-failAlways exit with code 0, even if some charms failed. The summary is still printed.
Default: off (exit non-zero on any failure)
Saving results¶
The three save options are mutually exclusive. When none of them is given, hyrum reads the save setting from hyrum.toml; if that is absent too, it falls back to --auto-save with the default directory. An unusable save target is rejected before the run starts, rather than after it.
--save PATHWrite the run’s outcomes as JSON. If
PATHis an existing directory, hyrum writes a timestampedhyrum-<UTC>-<target>.jsonfile inside it; otherwisePATHis the exact output file.Default: (not set)
--auto-save [DIR]Write a rolling pair of files into
DIR:<target>.auto.jsonfor this run, with the previous run’s file rotated to<target>.auto.prev.json. Keyed on the target, so runs of different targets do not clobber each other.Default:
~/.cache/hyrum/results(also the default when no save option is given at all)--no-saveDo not persist results.
Default: off
hyrum clean¶
hyrum clean [OPTIONS]
Remove the build artefacts a check run leaves in each charm — .tox, .venv, tool caches, __pycache__ — while leaving the git checkouts in place, so the next run does not have to clone everything again. Across the whole collection these reach tens of gigabytes.
Options¶
--charms-dir PATHDirectory containing the cloned charm repositories.
Default:
~/.cache/hyrum/charmsEnvironment variable:
HYRUM_CHARMS--dry-runReport what would be removed, and how much it holds, without removing it.
--forceClean a directory that does not look like a charms directory. Without this, hyrum refuses, so that a mistyped
--charms-dircannot recursively delete something else.--no-lockDo not take a per-charm lock before removing that charm’s artefacts. A concurrent
checkrun then has its.toxor.venvdeleted mid-run, so only use this when nothing else is running.--quietSuppress non-error output.
hyrum compare¶
hyrum compare [OPTIONS] BASELINE CURRENT
Diff two saved results files at the status level: which charms newly fail, which are resolved, and which newly error. BASELINE and CURRENT are paths to JSON files written by hyrum check (see Output reference).
Charms are matched by their path relative to the charms directory, so two runs from different hosts or checkouts still compare charm-for-charm. A charm present in only one of the runs is reported as absent rather than as a change. If the two files record different targets, hyrum prints a warning to stderr and compares them anyway.
Options¶
--fail-on-regression / --no-fail-on-regressionExit non-zero if there are any new failures or new errors relative to the baseline.
Default:
--no-fail-on-regression--format {text,markdown,json}text: the colourised status-level summary, preceded by a line of metadata for each run.markdown: a document with a pass-rate paragraph, a list per change category, and a table with one row per non-passing charm, including each run’s failure summary. Suitable for pasting into an issue or pull request.json: the same diff as a machine-readable object, carrying both runs’ metadata, the per-category charm lists, and the pass-rate counts. Versioned separately from the saved-results format. See Output reference.Default:
text
hyrum get-charms¶
hyrum get-charms [OPTIONS]
Clone every repository listed in a CSV file into the charms directory, or git pull it if the directory already exists. Each row in the CSV is one repository; repositories that host multiple charms in subdirectories are cloned once.
Each repository is cloned to <dest>/<owner>/<name>, where <owner> and <name> are the last two components of the repository URL. A row that names a branch is cloned to <dest>/<owner>/<name>-<branch>, so the same repository can be present at more than one branch.
Options¶
--source PATHPath to the charm-list CSV. Only the
Repositorycolumn (required) andBranch (if not the default)column (optional) are read; any other column, such as those in the repository’scharm-list/charms.csv, is ignored.Default:
charms.csvorcharm-list/charms.csvin the current directory.--dest PATHDirectory to clone into.
Default:
~/.cache/hyrum/charmsEnvironment variable:
HYRUM_CHARMS--workers NMaximum number of concurrent
gitsubprocesses. The cap keeps a large charm list from exhausting the process file-descriptor limit.Default:
16--timeout SECONDSSeconds before a single
gitclone or pull is abandoned;0waits forever.Default:
300--repo REGEXRegex matched against the checkout’s folder name, so only the charms it matches are cloned or pulled.
Default:
.*--limit NClone at most this many charms that aren’t in the destination yet. Skips charms that have already been cloned, so that running the same command again clones the next slice rather than pulling the same one again.
0clones or pulls every match. With--repo, the limit applies to the charms that match the pattern.Default:
0--quietSuppress non-error output.
hyrum show¶
hyrum show [OPTIONS] PATH
Print a saved run: its metadata, then the same status table check prints at the end of a run. PATH is always a filesystem path to a results JSON file — there is no run-id or target-name lookup.
Exits 0 whatever the run contained: show displays a run, it does not gate on one. Gating lives in hyrum compare --fail-on-regression.
Options¶
--verboseInclude the per-charm offender list.
--no-headersSuppress the header row of the summary table.
--format {text,markdown,json}text: the colourised status-level summary.markdown: the same table in markdown.json: the same outcomes as a machine-readable object.Default:
text
Top-level options¶
--versionPrint the installed hyrum version and exit.
--helpPrint the help text and exit. Available on each subcommand as well.
Exit codes¶
Code |
Meaning |
|---|---|
|
All non-skipped charms passed (or |
|
At least one charm resulted in |
|
Bad input, reported before or instead of a run: the save target given to |
Environment variables¶
HYRUM_CHARMSDefault charms directory used by both
hyrum check --charms-dirandhyrum get-charms --dest. Overridden by the explicit flag.NO_COLORWhen set to a non-empty value (any value, not just
1), suppresses ANSI colour in the summary table even on a tty. Setting it to the empty string leaves colour enabled.TOX_OVERRIDERead and appended to by
--host-env-defaultsso that toxpass_enventries propagate into the testenv. See Host prerequisites for fleet runs.
Examples¶
# Clone every repository listed in ./charms.csv into the default charms directory:
hyrum get-charms
# Run tox -e unit with ops patched to a dev branch, 8 workers:
hyrum check unit --patch 'ops @ canonical:fix/my-change' --workers 8
# Pin ops to a specific PyPI release across the fleet:
hyrum check unit --patch 'ops==2.17.0'
# Patch a non-ops dependency from a git fork:
hyrum check unit --patch 'requests @ git+https://github.com/psf/requests@main'
# Point a charm library at a branch of canonical/charmlibs:
hyrum check unit --patch 'charmlibs-nginx_k8s @ canonical:main'
# Replace a vendored charm library with its PyPI package:
hyrum check unit --patch 'charms.operator_libs_linux.v0.apt -> charmlibs-apt==1.0.0'
# Patch ops *and* another dependency in the same run:
hyrum check unit \
--patch 'ops @ canonical:fix/my-change' \
--patch 'requests==2.31.0'
# Run without patching:
hyrum check unit --no-patch
# Run only charms that use the Scenario framework:
hyrum check unit --no-patch --framework scenario
# Run only charms matching a name pattern:
hyrum check unit --no-patch --repo '^mysql'
# Save logs for offline triage:
hyrum check unit --no-patch --log-dir ~/hyrum-logs
# Always exit 0 (useful in scripts):
hyrum check unit --no-patch --no-fail
# Show failed charms inline:
hyrum check unit --no-patch --verbose
# Save a baseline, then diff a patched run against it:
hyrum check unit --no-patch --save baseline.json
hyrum check unit --patch 'ops @ canonical:fix/my-change' --save current.json
hyrum compare baseline.json current.json
# Gate on regressions, and produce a table to paste into a pull request:
hyrum compare baseline.json current.json --fail-on-regression
hyrum compare baseline.json current.json --format markdown > report.md