Plugins¶
Every step is a plugin's. lely steps lists the ones this project can use, with their
options, what they give, and what they can do.
bundle¶
A Databricks Asset Bundle: planned, deployed, listed and destroyed through the Databricks CLI. lely never reimplements what the CLI resolves — it asks.
- name: app
uses: bundle
with:
path: . # where databricks.yml is; the default
vars:
model_version: ${steps.model.version}
| Option | |
|---|---|
path |
The bundle's folder, from the config's. Default . |
vars |
Bundle variables, passed as --var. A value may come from a step above. |
It gives the bundle's target and name, workspace.<field>, var.<name>, every
resource's own config as resources.<type>.<key>.<field> — known at plan — and
resources.<type>.<key>.id and .url, known once the resource exists.
Every bundle plan carries one run: uploads the bundle's files. A deploy uploads them even
when no resource changes, so a bundle step is never "nothing to do".
The CLI trusts the bundle with your credentials
A bundle whose target names another host is not refused by the CLI: with a token from
the environment it goes to that host and presents the token. lely compares the two hosts
and stops, but only after the CLI's first call. Know whose databricks.yml you run.
bundle.run¶
Runs a job, pipeline or app from a bundle step, with databricks bundle run.
It stands below the bundle step it names, and its plan is a run.
Not run for real yet
No job has been started with bundle.run on a workspace:
What has been tried.
Your own¶
A class in a file in your repo, or one a package registers:
- name: model
uses: ./ops/steps.py:LatestModel # a class in the repo
- name: audit
uses: acme_deploy.steps:Audit # a class in an installed package
→ Writing a step. A program — dbt run, a notification — is a
Program: a step in ten lines, there too.
Planning runs your project's own code — a step in the repo. On a pull request, give
lely plan credentials that can read and nothing more.
stevin¶
Tables, views and the access around them, planned and applied by
stevin. lely runs the stevin command, so
stevin has to be installed where lely runs: version 0.4.0a3 or newer. A plan from an
older stevin fails, and says which version it found.
steps:
- name: tables
uses: stevin
with:
config: stevin.yml # stevin's project file; the default
- name: app
uses: bundle
| Option | |
|---|---|
config |
stevin's project file, from the config's folder. Default stevin.yml |
target |
stevin's target. Default: the -t lely was given |
select |
Only these tables, views or patterns, as stevin's --select |
executable |
How to run stevin. Default [stevin]; [uvx, stevin] runs it in an environment of its own |
The plan has one change per table, with a line for each step stevin would run and that step's risk class:
tables stevin
update main.sales.orders rewrite
STAGE rewritten data [rewrite]
REPLACE TABLE [rewrite]
SET NOT NULL order_id [meta]
⚠ fails unless every existing row already has a value
meta and feature are cheap. rewrite rewrites the table's data, and the table's own
line says so. destructive loses something, and needs lely apply --allow-destructive;
lely passes that on to stevin only then. A risk class lely does not know is treated as
destructive.
The step waits for what another tool has yet to make. There are two things stevin does not plan and does not create, because they are another tool's:
- a table whose schema the bundle declares and has not deployed yet;
- a table a spec only governs, which its owner has not made yet: a table a dlt pipeline loads, before the pipeline's first run.
That is so on a target that was never deployed, and again whenever the bundle gets a
new schema with tables in it or a spec governs a table that is loaded later. stevin
then plans none of its tables, also not the ones it could. So put the stevin step
below the bundle step, and below the step that runs the pipeline:
steps:
- name: app
uses: bundle
- name: load
uses: bundle.run
with: {bundle: app, resource: jobs.load}
- name: tables
uses: stevin
The first plan shows the steps above ready and the tables waiting:
app bundle
+ jobs.load
+ schemas.raw
+ schemas.sales
▶ uploads the bundle's files
load bundle.run
bundle ← app
▶ runs jobs.load
tables stevin
⏸ waiting: stevin needs something another step or tool has yet to make; the tables are planned once it is there
stevin: dev.raw is the bundle's schema 'raw', and isn't there yet — run `databricks bundle deploy` first; stevin won't create it
Plan: 3 changes · 2 runs · 0 destructive · 1 waiting
The first line under the step is lely's, and is the same in both cases. The second is
stevin's own, and names what is missing and whose it is. For a table that is not
loaded yet it reads … isn't there yet, and its spec only governs it — it is dlt's, …,
with the owner as stevin's owned_elsewhere names it.
stevin names one thing at a time, the schema first. With the steps above in place you
do not run what stevin's line speaks of: those steps do.
lely apply -t devdeploys the bundle and runs the job, then plans the tables, shows them and asks. One command gets a new product up.lely apply plan.jsondeploys the bundle, runs the job and stops at the tables with 2: nobody has seen what stevin will do. The nextlely planshows the tables, and that plan applies them. That is two rounds when nothing fails, also when both the schema and the table were missing. On GitHub the second round is the next pull request, or an apply by hand. The job runs again in the second round, as a run does on every apply;--from tablesstarts at the tables.
When the job's run fails, the run ends there with 1 and the tables do not start. The next plan shows them waiting again, now for the table, so through files that is a round more. When stevin finds a mistake in a spec once the table is there, the step fails with 1 after the bundle and the job have run, nothing of stevin is applied, and the next plan fails until the spec is put right.
stevin shapes no table in a schema a dlt pipeline loads into: there it only governs. Keep the tables stevin creates in a schema of their own.
lely does not check that those steps stand above. With the stevin step above them,
or with a bundle that is deployed and a pipeline that is run outside lely, the plan
shows the tables waiting all the same, and every apply is refused when it reaches
them, with nothing of them applied. Move the step down, or deploy and load first.
Once everything exists the step is planned as on any other day. It is a
waiting step like any other. Any other reason stevin
cannot plan still fails the plan, with stevin's message: a governed column the table
does not have, or a governance-only spec for a table nobody is said to own. Say whose
a pipeline's tables are in stevin's owned_elsewhere; before its first run there is
nothing else to know it by. This case fails too with a stevin older than 0.4.0a3: it
ends with 1, and lely cannot tell which stevin it was. lely status and lely
destroy work before the first deploy too: a status lists the tables as not there, and
a destroy skips the step.
Apply runs the plan that was reviewed. stevin's own plan file travels inside lely's
plan file. lely apply hands that file to stevin apply, never a new one. Two checks
stand before it:
- lely plans the step again and compares. If stevin would now do anything else than the reviewed file holds, lely refuses, and stevin is not run.
- stevin compares the file with the live tables. If they changed since the plan was made, stevin refuses the plan as stale.
Either way the run ends with 2 and nothing after the step starts. Plan again, and review the new plan. That is also what to do after stevin failed halfway through a plan: what it applied stays applied, and the next plan shows what is left.
lely tells a refusal from a failure by stevin's
exit code, not by what
stevin prints. A refusal ends the run with 2, and a failure with 1. Both show stevin's
own message. It knows that something is not there yet the same way: by the exit code
of stevin plan.
Destroy drops nothing. A dropped table is its rows. lely destroy skips the step and
says so. To drop a table, take it out of stevin's specs: stevin's own rules say when it
drops one.
Status lists what stevin's specs describe and whether each exists. lely asks
stevin list for it, which makes no plan. It says whether an object is there, not
whether it matches its spec: a plan says that.
The step gives no outputs. A plan file holds stevin's statements, and names of tables, columns and owners: keep it where the pull request's other artifacts are kept. lely prints the names of tables and columns, the titles of stevin's steps and their warnings. A title names the principal of a grant. No owner is printed.
Not run for real yet
The step has not run on a workspace, and not against the real stevin command:
What has been tried.