CLI reference
celine-utils
├── governance
│ ├── generate marquez
│ └── graph
└── pipeline
├── init app
└── run (envs | meltano | seed | dbt | build | prefect)
Installed as the celine-utils entry point. From a checkout of this repository,
task cli -- <args> runs the same CLI from the working tree.
This page describes what each command is for and the traps in using it. For the exact current options, ask the CLI:
celine-utils <command> --help
An option table copied into a document is stale the next time an option is added, with nothing to detect it. The tables below are orientation, not the source of truth.
governance
governance generate marquez
Generate a governance.yaml scaffold from the datasets Marquez already knows about,
so a governance file starts from what the pipeline actually produces rather than from
a blank page.
celine-utils governance generate marquez --app <app-name> [options]
| Option | Meaning |
|---|---|
--app |
CELINE app name. Required |
--output, -o |
Output path. Defaults to PIPELINES_ROOT/apps/<app>/governance.yaml |
--marquez |
Marquez base URL, overriding OPENLINEAGE_URL |
--namespace |
OpenLineage namespace, overriding OPENLINEAGE_NAMESPACE |
--yes, -y |
Non-interactive — write a skeleton using defaults |
Interactively it prompts per dataset for licence, access level, classification,
ownership and tags, and offers pattern scopes (exact name, schema wildcard, prefix
wildcard) so one answer can cover many datasets. With --yes it writes a skeleton
with an empty rule per discovered dataset.
# Interactive, against the configured Marquez
celine-utils governance generate marquez --app om
# Scaffold only, no prompts
celine-utils governance generate marquez --app om --yes
# Explicit endpoint, namespace and destination
celine-utils governance generate marquez \
--app om \
--marquez http://marquez.internal:5000 \
--namespace ds_prod_silver \
--output ./governance.yaml
The generated file is a starting point, not a finished one: it cannot know licence terms or who owns the data. Review it, then validate it.
Reads OPENLINEAGE_URL, OPENLINEAGE_NAMESPACE, PIPELINES_ROOT, and the
KEYCLOAK_* variables when Marquez requires authentication — see
environment.
governance graph
Show which pipelines feed which, resolved from the depends_on and sources blocks of
every governance.yaml matched.
celine-utils governance graph [PATHS...] [--format tree|order|json|mermaid]
[--schedules FILE] [--strict]
PATHS are shell globs naming pipelines or governance files. A directory contributes
the governance.yaml inside it, so apps/* and apps/*/governance.yaml are
equivalent; governance.<name>.yaml is a deployer overlay and is skipped with a note.
The default is apps/*.
# one repository
celine-utils governance graph 'apps/*'
# a deployment and the open-source pipelines together
celine-utils governance graph 'apps/*' '/path/to/deployment/pipelines/apps/*'
# the run order, flattened — what to run, in sequence
celine-utils governance graph 'apps/*' --format order
# a diagram, or something to feed a scheduler
celine-utils governance graph 'apps/*' --format mermaid
celine-utils governance graph 'apps/*' --format json
The tree output is a topological ordering: everything in tier N may run in parallel once tier N-1 is done. Findings and the summary go to stderr, so the graph itself stays pipeable.
Which trees you glob is a judgement, not a detail. A deployment repository may hold unmaintained copies of open-source apps, and including them reports every dataset those copies declare as having two producers.
Findings
| Finding | Meaning |
|---|---|
unresolved |
No scanned pipeline produces the dataset and it is not marked external: true — a typo, or a tree you did not glob |
multiple-producers |
Two governance files declare the same dataset: two answers to who owns it, and an ambiguous producer for anything depending on it |
cycle |
No run order exists for the pipelines named |
self-dependency |
A pipeline depends on a dataset it also declares as its own output |
inactive-producer |
An active pipeline reads from one marked active: false — whatever it reads is as old as the last time that pipeline ran |
external-satisfied |
An entry marked external: true that a producer in this scan satisfies. Informational — it means a wider scan closed the graph |
--strict exits 1 when anything other than external-satisfied or
schedule-unverified is reported, which is the form for CI.
Checking the deployed schedules
Pass a deployment's scheduled flows and the crons are checked against the graph:
celine-utils governance graph 'apps/*' --schedules staging-flows.yaml
flows:
- name: weather
path: /pipelines/apps/weather/flows/pipeline.py # or: app: weather
schedule:
cron: "15 * * * *"
app may be given directly or derived from path, which is the shape a Prefect
deployment manifest already has. An entry with no cron is skipped — a flow triggered by
hand has no ordering to check.
| Finding | Meaning |
|---|---|
schedule-inversion |
Both fire hourly, never in the same minute, and the consumer is always earlier in the hour. It succeeds every time, on the previous run's output |
schedule-collision |
Producer and consumer can start in the same minute; which run the consumer sees depends on which starts first |
schedule-unverified |
The same two problems, on a pair where an app deploys several flows. Governance is per app and cannot say which flow produces which dataset, so the pairing may not be the one that moves the data. Advisory, and excluded from --strict |
not-deployed |
A pipeline marked active that no scheduled flow in this deployment runs |
Schedules are not read from governance.yaml, deliberately. A cron is a deployment
fact: one app runs several flows on independent schedules, and the same pipeline runs on
different schedules in different deployments. A governance file is one per app and is
shared by every deployment that installs it, so it is the wrong place for either.
pipeline
pipeline init app
Scaffold a new pipeline application.
celine-utils pipeline init app <app-name> [--force]
Produces:
<app-name>/
├── meltano/
├── dbt/
├── flows/pipeline.py
├── .env
└── README.md
Templates are rendered with the app name substituted, and database values in .env
are populated from POSTGRES_* in the environment when present. The command aborts
if the target already looks like a CELINE pipeline app; --force overwrites it.
See the pipeline tutorial for what to do with the result.
pipeline run
Executes one stage of a pipeline. Every subcommand discovers the app root by walking
upward from the current directory looking for meltano/, dbt/ or flows/, so run
these from inside the app.
| Command | Does |
|---|---|
pipeline run envs |
Print the pipeline run environment as export lines |
pipeline run meltano [command] |
Run a Meltano command in the app's meltano/. Default: run import |
pipeline run seed |
dbt seed in the app's dbt/ |
pipeline run dbt <spec> |
One dbt stage in the app's dbt/ — see the spec grammar below |
pipeline run build [select] |
dbt build — every model followed immediately by its own tests |
pipeline run prefect |
Load and execute a @flow function from flows/ |
# Make the pipeline's own environment available to your shell
source <(celine-utils pipeline run envs)
celine-utils pipeline run meltano
celine-utils pipeline run meltano "run import --select my_stream"
celine-utils pipeline run seed
celine-utils pipeline run dbt staging
celine-utils pipeline run dbt "test -s tag:meters"
celine-utils pipeline run build silver
celine-utils pipeline run prefect
celine-utils pipeline run prefect --flow pipeline --function om_flow
The dbt spec grammar
pipeline run dbt takes one string, not a bare tag, and the same string that a
flow passes to dbt_run(). It may open with a dbt subcommand — run, build,
test, seed, snapshot — and defaults to run when it does not:
| Spec | Runs |
|---|---|
silver |
dbt run --select silver |
staging --exclude tag:meters |
dbt run --select staging --exclude tag:meters |
-s gold,tag:wind |
dbt run -s gold,tag:wind |
build -s silver |
dbt build -s silver |
test |
dbt test |
test -s tag:meters |
dbt test -s tag:meters |
--select is injected only when the spec names nodes without a selection flag of
its own. A consequence of the leading-subcommand rule: a model actually named
run, build, test, seed or snapshot is shadowed, and has to be named
through an explicit -s.
Which verb. build is the one to use when the point is to populate the
database: it interleaves each model's tests with the model, so a layer that
populated badly fails where it broke rather than several layers downstream. run
is for iterating on a single model, where the tests are noise until the model is
right.
pipeline run prefect auto-detects both the flow module and the decorated function
when --flow / --function are omitted. Pass them explicitly when a module holds
more than one flow.
Each run emits OpenLineage events and resolves governance for the datasets it touches; see governance.
Configuration
Every command is configured by environment variables, loaded from the process
environment and from .env, .env.dev, .env.prod where present. The full list is
in the environment reference.