duckhaven-cli 0.3.0__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/CHANGELOG.md +48 -0
- duckhaven_cli-0.5.0/PKG-INFO +206 -0
- duckhaven_cli-0.5.0/README.md +177 -0
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/pyproject.toml +1 -1
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/src/dh/_version.py +2 -2
- duckhaven_cli-0.5.0/src/dh/commands/dashboard.py +907 -0
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/src/dh/commands/dbt.py +94 -11
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/src/dh/commands/query.py +1 -0
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/src/dh/execute.py +17 -4
- duckhaven_cli-0.5.0/src/dh/exposures.py +146 -0
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/src/dh/main.py +2 -0
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/src/dh/repl.py +1 -1
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/src/dh/rest.py +70 -13
- duckhaven_cli-0.3.0/PKG-INFO +0 -88
- duckhaven_cli-0.3.0/README.md +0 -59
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/.gitignore +0 -0
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/LICENSE +0 -0
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/src/dh/__init__.py +0 -0
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/src/dh/commands/__init__.py +0 -0
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/src/dh/commands/admin.py +0 -0
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/src/dh/commands/api.py +0 -0
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/src/dh/commands/auth.py +0 -0
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/src/dh/commands/catalog.py +0 -0
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/src/dh/commands/grant.py +0 -0
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/src/dh/commands/health.py +0 -0
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/src/dh/commands/profile.py +0 -0
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/src/dh/commands/saved.py +0 -0
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/src/dh/commands/session.py +0 -0
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/src/dh/config.py +0 -0
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/src/dh/context.py +0 -0
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/src/dh/errors.py +0 -0
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/src/dh/introspect.py +0 -0
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/src/dh/output/__init__.py +0 -0
- {duckhaven_cli-0.3.0 → duckhaven_cli-0.5.0}/src/dh/resolve.py +0 -0
|
@@ -6,6 +6,54 @@ All notable changes to `duckhaven-cli` are documented here. The format follows
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.5.0] - 2026-11-15
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Dashboards in git.** `dh dashboard apply` applies `<name>.dashboard.yml` files, or every
|
|
14
|
+
one under a directory, to the dashboards they are named after, byte for byte. It takes
|
|
15
|
+
`--publish`, `--dry-run`, `--source-id`, `--source-ref`, `--expected-hash` and `--force`,
|
|
16
|
+
applies every file and reports each problem as `file:line:col`, and exits with the first
|
|
17
|
+
failure's status. `--prune` detaches and deletes the dashboards applied from
|
|
18
|
+
`--source-id` whose files are gone, after asking (or with `--yes`), and only when every
|
|
19
|
+
file applied.
|
|
20
|
+
- `dh dashboard validate`, `rename`, `adopt`, `detach`, `publish`, `delete`, `list` and
|
|
21
|
+
`get`. `dh dashboard export` prints a dashboard's YAML and `dh dashboard schema` its JSON
|
|
22
|
+
Schema, each as the file itself rather than the JSON envelope.
|
|
23
|
+
- `dh dashboard test` runs dashboard files without saving them and reports every widget as
|
|
24
|
+
`ok`, `empty`, `error` or `no_access`, with its time. It exits `6` when a widget fails or
|
|
25
|
+
cannot be read, and marks widgets slower than `--slow` seconds. It takes `--no-cache`.
|
|
26
|
+
- `dh dashboard preview FILE` holds a file as a preview, prints its page's address, and
|
|
27
|
+
sends the file again each time it is saved, starting a new preview when the last one has
|
|
28
|
+
ended. `--open` opens the page. `--once` runs the file once and prints each widget's state,
|
|
29
|
+
rows and SQL instead, which suits a coding agent checking its work.
|
|
30
|
+
- `dh dashboard impact REF` lists the dashboards that read a semantic model, metric or
|
|
31
|
+
dimension.
|
|
32
|
+
- `dh dashboard exposures MANIFEST` prints dbt exposures for the published dashboards that
|
|
33
|
+
read a dbt project's models, seeds, snapshots and sources, with `--owner-name` and
|
|
34
|
+
`--owner-email` for the owner dbt requires.
|
|
35
|
+
- `dh semantic import --dry-run` reports what an import would create, update and remove, and
|
|
36
|
+
the published dashboards it would break. `--allow-breaking` on `dh semantic import`,
|
|
37
|
+
`purge` and `deprecate` makes a change that breaks published dashboards; without it, the
|
|
38
|
+
change is refused and every dashboard it breaks is listed.
|
|
39
|
+
- The README has an `AGENTS.md` snippet for coding agents that write dashboard files.
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
|
|
43
|
+
- The pinned API contract is refreshed and covers the dashboard endpoints, including
|
|
44
|
+
previews and their runs.
|
|
45
|
+
|
|
46
|
+
## [0.4.0] - 2026-10-15
|
|
47
|
+
|
|
48
|
+
### Changed
|
|
49
|
+
|
|
50
|
+
- `dh sql` and the REPL ask the server to hold the submit for up to two seconds, so a quick
|
|
51
|
+
query finishes in one request instead of a submit and a poll. `--no-wait` still returns at
|
|
52
|
+
once. A server that predates the hold ignores it.
|
|
53
|
+
- Ready for DuckHaven `api_version` 3: `DECIMAL` and wide integers, which it sends as
|
|
54
|
+
strings, print as before in table and CSV output and stay exact strings with
|
|
55
|
+
`--format json`.
|
|
56
|
+
|
|
9
57
|
## [0.3.0] - 2026-10-13
|
|
10
58
|
|
|
11
59
|
Brings the CLI up to date with the DuckHaven server's result cache, DuckLake catalogs,
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: duckhaven-cli
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: dh — the command-line interface for DuckHaven
|
|
5
|
+
Project-URL: Homepage, https://github.com/tamasmrtn/duckhaven-clients
|
|
6
|
+
Project-URL: Source, https://github.com/tamasmrtn/duckhaven-clients
|
|
7
|
+
Author: DuckHaven
|
|
8
|
+
License-Expression: Apache-2.0
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: cli,duckdb,duckhaven,iceberg,lakehouse
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: System Administrators
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Topic :: Database
|
|
21
|
+
Classifier: Topic :: Utilities
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.11
|
|
24
|
+
Requires-Dist: click>=8.5.0
|
|
25
|
+
Requires-Dist: duckhaven-sql-connector>=0.4.1
|
|
26
|
+
Requires-Dist: httpx>=0.28
|
|
27
|
+
Requires-Dist: typer>=0.28.0
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# duckhaven-cli
|
|
31
|
+
|
|
32
|
+
`dh` — the command-line interface for [DuckHaven](https://github.com/tamasmrtn/duckhaven).
|
|
33
|
+
|
|
34
|
+
It gives analysts, CI pipelines and operators scriptable access to DuckHaven's REST API:
|
|
35
|
+
running SQL, browsing the catalog, publishing dbt lineage and semantic models, applying
|
|
36
|
+
dashboards from git, managing grants, and operating service accounts, users and agents.
|
|
37
|
+
|
|
38
|
+
Like every member of this repo it is a **pure HTTP client of DuckHaven's public REST
|
|
39
|
+
API**, authenticating with a Personal Access Token (`dh_pat_…`). The interactive REPL
|
|
40
|
+
holds a SQL session through
|
|
41
|
+
[`duckhaven-sql-connector`](../duckhaven-sql-connector/README.md); everything else,
|
|
42
|
+
including one-shot `dh sql`, goes over the REST API directly, because the one-shot query
|
|
43
|
+
route works on deployments where SQL sessions are switched off.
|
|
44
|
+
|
|
45
|
+
## Install
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
pip install duckhaven-cli
|
|
49
|
+
# or, to get an isolated tool install:
|
|
50
|
+
uv tool install duckhaven-cli
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Getting started
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
dh auth login --host https://duckhaven.example.com
|
|
57
|
+
dh sql -q "select 1"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## The result cache
|
|
61
|
+
|
|
62
|
+
DuckHaven answers a repeated read from its result cache when nothing the query reads has
|
|
63
|
+
changed. `dh sql` says so on stderr when it happens; pass `--no-cache` to run the query
|
|
64
|
+
anyway (also on `dh session open` and `dh session exec`). An owner can switch the cache off
|
|
65
|
+
for a whole workspace with `dh workspace update --no-result-cache`. The interactive REPL
|
|
66
|
+
does not take `--no-cache`: it holds its session through the connector, which has its own
|
|
67
|
+
`use_cache` option, and the shell does not expose it yet.
|
|
68
|
+
|
|
69
|
+
## Newer server features
|
|
70
|
+
|
|
71
|
+
A server that supports them offers: DuckLake catalogs (`dh catalog kinds`,
|
|
72
|
+
`dh catalog create --kind ducklake`, `dh catalog rotate-role`, `dh catalog export`), the
|
|
73
|
+
DuckDB runtimes agents run (`dh agent runtimes`, `--runtime` on
|
|
74
|
+
`dh admin agent elastic-create`), per-agent monitoring over a window or range and the
|
|
75
|
+
queries behind it (`dh admin agent monitoring`, `dh admin agent queries`), applying
|
|
76
|
+
maintenance recommendations (`dh admin maintenance recommendations|apply`), and your
|
|
77
|
+
private editor tabs (`dh worksheet`). Against an older server these commands answer with the
|
|
78
|
+
server's own not-found or validation error.
|
|
79
|
+
|
|
80
|
+
## Dashboards in git
|
|
81
|
+
|
|
82
|
+
A DuckHaven dashboard is one YAML file. Keep each one in a repository as
|
|
83
|
+
`<name>.dashboard.yml`, named after the `name` inside it, review changes in pull
|
|
84
|
+
requests, and let CI apply them. `dh dashboard` is that CI step:
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
# On a pull request: check every file as publishing would, and change nothing.
|
|
88
|
+
dh dashboard apply dashboards/ --dry-run --publish
|
|
89
|
+
|
|
90
|
+
# On main: apply and publish each file, and remove the dashboards whose files were deleted.
|
|
91
|
+
dh dashboard apply dashboards/ --publish --prune --yes \
|
|
92
|
+
--source-id "$GITHUB_REPOSITORY" --source-ref "$GITHUB_SHA"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`apply` sends each file byte for byte, so DuckHaven keeps it as written, comments included,
|
|
96
|
+
and reports a problem at the line its author wrote: `dashboards/sales.dashboard.yml:12:16:
|
|
97
|
+
error: ...`. A dashboard applied this way is managed in git: DuckHaven refuses every other
|
|
98
|
+
change to it until it is detached. Without `--publish` a file becomes the dashboard's draft
|
|
99
|
+
and readers keep the published one; publishing needs a workspace `owner`, but a dry run
|
|
100
|
+
needs only `writer`, so a pull request's check can run with a token that cannot publish. Every
|
|
101
|
+
file is applied and every failure reported, not only the first, and the exit status is the
|
|
102
|
+
first failure's: `5` for a file the server refused, such as `source_changed` (another
|
|
103
|
+
repository manages that dashboard) or `managed_dashboard`.
|
|
104
|
+
|
|
105
|
+
Applying never deletes a dashboard. `--prune` does, for the dashboards applied from
|
|
106
|
+
`--source-id` that no file names any more: it lists them and asks before anything is applied
|
|
107
|
+
(or takes `--yes`), and it deletes nothing when any file failed to apply.
|
|
108
|
+
|
|
109
|
+
The other commands:
|
|
110
|
+
|
|
111
|
+
- `validate` checks files against the workspace without storing anything: a metric that
|
|
112
|
+
does not exist, or a model that is not published.
|
|
113
|
+
- `test PATH...` runs each file once without saving it, with your grants, and reports every
|
|
114
|
+
widget: `ok`, `empty`, `error`, or `no_access` when you may not read what it reads, with
|
|
115
|
+
how long it took to arrive. It exits `6` when a widget fails or cannot be read, so a pull
|
|
116
|
+
request catches a query that checks out but does not run. A widget slower than `--slow`
|
|
117
|
+
seconds (5 by default) is marked slow, which does not fail the run. `--no-cache` runs every
|
|
118
|
+
query rather than answering from the result cache.
|
|
119
|
+
- `impact REF` lists the dashboards that read a model, or a metric or dimension of one
|
|
120
|
+
(`orders.revenue`): what renaming or removing it would touch.
|
|
121
|
+
- `exposures MANIFEST` prints [dbt exposures](https://docs.getdbt.com/docs/build/exposures)
|
|
122
|
+
for the published dashboards that read your dbt project's tables, so dbt's lineage shows
|
|
123
|
+
which dashboards read each model. Keep the file in the project and write it again when
|
|
124
|
+
dashboards change:
|
|
125
|
+
`dh --output models/duckhaven_exposures.yml dashboard exposures target/manifest.json`. Each
|
|
126
|
+
exposure has the dashboard's title, description and link, and depends on the models, seeds,
|
|
127
|
+
snapshots and sources it reads, through its semantic models' datasets or its SQL. A table
|
|
128
|
+
matches a dbt relation when its catalog, schema and name are the relation's database,
|
|
129
|
+
schema and alias, as when DuckHaven imports from dbt. dbt requires an owner:
|
|
130
|
+
`--owner-name` (DuckHaven by default) and `--owner-email`.
|
|
131
|
+
- `rename OLD FILE` renames a dashboard whose file you renamed. Run it once before merging:
|
|
132
|
+
applying a renamed file as usual creates a second dashboard. The old name keeps working in
|
|
133
|
+
links.
|
|
134
|
+
- `adopt NAME DIR` takes over a dashboard made in DuckHaven: it saves the dashboard's file
|
|
135
|
+
into `DIR` and applies it, publishing nothing. Commit the file.
|
|
136
|
+
- `detach`, `publish`, `delete`, `list` and `get`, each one request.
|
|
137
|
+
- `export NAME` prints a dashboard's file and `schema` the JSON Schema for your editor. Both
|
|
138
|
+
print the file itself rather than the usual JSON envelope, so
|
|
139
|
+
`dh dashboard export sales > dashboards/sales.dashboard.yml` works.
|
|
140
|
+
|
|
141
|
+
`dh` does not read or rewrite the files; there is no formatter yet. These commands need a
|
|
142
|
+
DuckHaven that offers dashboards; an older one answers with its own not-found error.
|
|
143
|
+
|
|
144
|
+
Changes to the semantic layer are checked against dashboards too. An import,
|
|
145
|
+
`dh semantic purge` or `dh semantic deprecate` that removes something a published dashboard
|
|
146
|
+
reads is refused with exit `5`, listing every dashboard and what it reads, unless you pass
|
|
147
|
+
`--allow-breaking`. `dh semantic import --dry-run` reports what an import would create,
|
|
148
|
+
update and remove, and the dashboards it would break, and changes nothing.
|
|
149
|
+
|
|
150
|
+
## Writing a dashboard
|
|
151
|
+
|
|
152
|
+
`dh dashboard preview FILE` shows a file in DuckHaven while you edit it, without saving it.
|
|
153
|
+
It prints the address of a page that only you can see; open it beside your editor, or pass
|
|
154
|
+
`--open`. Each time you save the file, `dh` sends it, and the page runs it again, with your
|
|
155
|
+
grants. A file that does not load is reported at its line, and the page keeps showing the
|
|
156
|
+
last file that did. A metric that does not exist shows on the page as that widget's error.
|
|
157
|
+
Stop with Ctrl+C.
|
|
158
|
+
|
|
159
|
+
A preview ends two hours after its file last changed or it last ran. A save after that
|
|
160
|
+
starts a new preview, at a new address, which `dh` prints. The page is new in DuckHaven:
|
|
161
|
+
against a server without it, use `--once`. If your browser is signed out, sign in, then open
|
|
162
|
+
the address again, as signing in does not bring you back to it.
|
|
163
|
+
|
|
164
|
+
`--once` runs the file in the terminal instead, once, and prints each widget with its state,
|
|
165
|
+
rows and SQL. It exits as `test` does.
|
|
166
|
+
|
|
167
|
+
### For coding agents
|
|
168
|
+
|
|
169
|
+
An AI coding agent that writes dashboard files can check its own work the same way. Put
|
|
170
|
+
something like this in the repository's `AGENTS.md`, and give the agent a token with the
|
|
171
|
+
`writer` role, which `validate` and `preview` need:
|
|
172
|
+
|
|
173
|
+
```markdown
|
|
174
|
+
## Dashboards
|
|
175
|
+
|
|
176
|
+
Dashboards live in `dashboards/`, one `<name>.dashboard.yml` file each, named after the
|
|
177
|
+
`name` inside it. To write or change one:
|
|
178
|
+
|
|
179
|
+
1. Read the file format: `dh dashboard schema`.
|
|
180
|
+
2. Read what a widget can use: `dh semantic model get <model>` lists its metrics and
|
|
181
|
+
dimensions.
|
|
182
|
+
3. Edit the file.
|
|
183
|
+
4. `dh --format json dashboard validate <file>`, and fix every error at its line.
|
|
184
|
+
5. `dh --format json dashboard preview --once <file>`, and fix every widget whose state is
|
|
185
|
+
`error`. Check that the rows answer what the widget's title says.
|
|
186
|
+
6. Never run `dh dashboard apply` without `--dry-run`, and never publish. CI applies the
|
|
187
|
+
files once a person has reviewed them.
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Exact numbers
|
|
191
|
+
|
|
192
|
+
A DuckHaven with `api_version` 3 or later sends `DECIMAL` values and 64-bit-or-wider integers
|
|
193
|
+
(`BIGINT`, `HUGEINT`, `count(*)`) as strings, because JSON numbers lose precision past about
|
|
194
|
+
16 digits. `dh` prints them exactly as before in table and CSV output. With `--format json`
|
|
195
|
+
they stay strings (`"12.50"`, `"9007199254740993"`), exact. Scripts reading `dh` JSON should
|
|
196
|
+
convert those columns themselves rather than expect JSON numbers.
|
|
197
|
+
|
|
198
|
+
## Documentation
|
|
199
|
+
|
|
200
|
+
- [Command-line quickstart](https://tamasmrtn.github.io/duckhaven/getting-started/cli-quickstart/) — install, sign in,
|
|
201
|
+
run a query
|
|
202
|
+
- [CLI reference](https://tamasmrtn.github.io/duckhaven/reference/cli/) — every command and flag
|
|
203
|
+
|
|
204
|
+
## Licence
|
|
205
|
+
|
|
206
|
+
Apache-2.0. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# duckhaven-cli
|
|
2
|
+
|
|
3
|
+
`dh` — the command-line interface for [DuckHaven](https://github.com/tamasmrtn/duckhaven).
|
|
4
|
+
|
|
5
|
+
It gives analysts, CI pipelines and operators scriptable access to DuckHaven's REST API:
|
|
6
|
+
running SQL, browsing the catalog, publishing dbt lineage and semantic models, applying
|
|
7
|
+
dashboards from git, managing grants, and operating service accounts, users and agents.
|
|
8
|
+
|
|
9
|
+
Like every member of this repo it is a **pure HTTP client of DuckHaven's public REST
|
|
10
|
+
API**, authenticating with a Personal Access Token (`dh_pat_…`). The interactive REPL
|
|
11
|
+
holds a SQL session through
|
|
12
|
+
[`duckhaven-sql-connector`](../duckhaven-sql-connector/README.md); everything else,
|
|
13
|
+
including one-shot `dh sql`, goes over the REST API directly, because the one-shot query
|
|
14
|
+
route works on deployments where SQL sessions are switched off.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
pip install duckhaven-cli
|
|
20
|
+
# or, to get an isolated tool install:
|
|
21
|
+
uv tool install duckhaven-cli
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Getting started
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
dh auth login --host https://duckhaven.example.com
|
|
28
|
+
dh sql -q "select 1"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## The result cache
|
|
32
|
+
|
|
33
|
+
DuckHaven answers a repeated read from its result cache when nothing the query reads has
|
|
34
|
+
changed. `dh sql` says so on stderr when it happens; pass `--no-cache` to run the query
|
|
35
|
+
anyway (also on `dh session open` and `dh session exec`). An owner can switch the cache off
|
|
36
|
+
for a whole workspace with `dh workspace update --no-result-cache`. The interactive REPL
|
|
37
|
+
does not take `--no-cache`: it holds its session through the connector, which has its own
|
|
38
|
+
`use_cache` option, and the shell does not expose it yet.
|
|
39
|
+
|
|
40
|
+
## Newer server features
|
|
41
|
+
|
|
42
|
+
A server that supports them offers: DuckLake catalogs (`dh catalog kinds`,
|
|
43
|
+
`dh catalog create --kind ducklake`, `dh catalog rotate-role`, `dh catalog export`), the
|
|
44
|
+
DuckDB runtimes agents run (`dh agent runtimes`, `--runtime` on
|
|
45
|
+
`dh admin agent elastic-create`), per-agent monitoring over a window or range and the
|
|
46
|
+
queries behind it (`dh admin agent monitoring`, `dh admin agent queries`), applying
|
|
47
|
+
maintenance recommendations (`dh admin maintenance recommendations|apply`), and your
|
|
48
|
+
private editor tabs (`dh worksheet`). Against an older server these commands answer with the
|
|
49
|
+
server's own not-found or validation error.
|
|
50
|
+
|
|
51
|
+
## Dashboards in git
|
|
52
|
+
|
|
53
|
+
A DuckHaven dashboard is one YAML file. Keep each one in a repository as
|
|
54
|
+
`<name>.dashboard.yml`, named after the `name` inside it, review changes in pull
|
|
55
|
+
requests, and let CI apply them. `dh dashboard` is that CI step:
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
# On a pull request: check every file as publishing would, and change nothing.
|
|
59
|
+
dh dashboard apply dashboards/ --dry-run --publish
|
|
60
|
+
|
|
61
|
+
# On main: apply and publish each file, and remove the dashboards whose files were deleted.
|
|
62
|
+
dh dashboard apply dashboards/ --publish --prune --yes \
|
|
63
|
+
--source-id "$GITHUB_REPOSITORY" --source-ref "$GITHUB_SHA"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`apply` sends each file byte for byte, so DuckHaven keeps it as written, comments included,
|
|
67
|
+
and reports a problem at the line its author wrote: `dashboards/sales.dashboard.yml:12:16:
|
|
68
|
+
error: ...`. A dashboard applied this way is managed in git: DuckHaven refuses every other
|
|
69
|
+
change to it until it is detached. Without `--publish` a file becomes the dashboard's draft
|
|
70
|
+
and readers keep the published one; publishing needs a workspace `owner`, but a dry run
|
|
71
|
+
needs only `writer`, so a pull request's check can run with a token that cannot publish. Every
|
|
72
|
+
file is applied and every failure reported, not only the first, and the exit status is the
|
|
73
|
+
first failure's: `5` for a file the server refused, such as `source_changed` (another
|
|
74
|
+
repository manages that dashboard) or `managed_dashboard`.
|
|
75
|
+
|
|
76
|
+
Applying never deletes a dashboard. `--prune` does, for the dashboards applied from
|
|
77
|
+
`--source-id` that no file names any more: it lists them and asks before anything is applied
|
|
78
|
+
(or takes `--yes`), and it deletes nothing when any file failed to apply.
|
|
79
|
+
|
|
80
|
+
The other commands:
|
|
81
|
+
|
|
82
|
+
- `validate` checks files against the workspace without storing anything: a metric that
|
|
83
|
+
does not exist, or a model that is not published.
|
|
84
|
+
- `test PATH...` runs each file once without saving it, with your grants, and reports every
|
|
85
|
+
widget: `ok`, `empty`, `error`, or `no_access` when you may not read what it reads, with
|
|
86
|
+
how long it took to arrive. It exits `6` when a widget fails or cannot be read, so a pull
|
|
87
|
+
request catches a query that checks out but does not run. A widget slower than `--slow`
|
|
88
|
+
seconds (5 by default) is marked slow, which does not fail the run. `--no-cache` runs every
|
|
89
|
+
query rather than answering from the result cache.
|
|
90
|
+
- `impact REF` lists the dashboards that read a model, or a metric or dimension of one
|
|
91
|
+
(`orders.revenue`): what renaming or removing it would touch.
|
|
92
|
+
- `exposures MANIFEST` prints [dbt exposures](https://docs.getdbt.com/docs/build/exposures)
|
|
93
|
+
for the published dashboards that read your dbt project's tables, so dbt's lineage shows
|
|
94
|
+
which dashboards read each model. Keep the file in the project and write it again when
|
|
95
|
+
dashboards change:
|
|
96
|
+
`dh --output models/duckhaven_exposures.yml dashboard exposures target/manifest.json`. Each
|
|
97
|
+
exposure has the dashboard's title, description and link, and depends on the models, seeds,
|
|
98
|
+
snapshots and sources it reads, through its semantic models' datasets or its SQL. A table
|
|
99
|
+
matches a dbt relation when its catalog, schema and name are the relation's database,
|
|
100
|
+
schema and alias, as when DuckHaven imports from dbt. dbt requires an owner:
|
|
101
|
+
`--owner-name` (DuckHaven by default) and `--owner-email`.
|
|
102
|
+
- `rename OLD FILE` renames a dashboard whose file you renamed. Run it once before merging:
|
|
103
|
+
applying a renamed file as usual creates a second dashboard. The old name keeps working in
|
|
104
|
+
links.
|
|
105
|
+
- `adopt NAME DIR` takes over a dashboard made in DuckHaven: it saves the dashboard's file
|
|
106
|
+
into `DIR` and applies it, publishing nothing. Commit the file.
|
|
107
|
+
- `detach`, `publish`, `delete`, `list` and `get`, each one request.
|
|
108
|
+
- `export NAME` prints a dashboard's file and `schema` the JSON Schema for your editor. Both
|
|
109
|
+
print the file itself rather than the usual JSON envelope, so
|
|
110
|
+
`dh dashboard export sales > dashboards/sales.dashboard.yml` works.
|
|
111
|
+
|
|
112
|
+
`dh` does not read or rewrite the files; there is no formatter yet. These commands need a
|
|
113
|
+
DuckHaven that offers dashboards; an older one answers with its own not-found error.
|
|
114
|
+
|
|
115
|
+
Changes to the semantic layer are checked against dashboards too. An import,
|
|
116
|
+
`dh semantic purge` or `dh semantic deprecate` that removes something a published dashboard
|
|
117
|
+
reads is refused with exit `5`, listing every dashboard and what it reads, unless you pass
|
|
118
|
+
`--allow-breaking`. `dh semantic import --dry-run` reports what an import would create,
|
|
119
|
+
update and remove, and the dashboards it would break, and changes nothing.
|
|
120
|
+
|
|
121
|
+
## Writing a dashboard
|
|
122
|
+
|
|
123
|
+
`dh dashboard preview FILE` shows a file in DuckHaven while you edit it, without saving it.
|
|
124
|
+
It prints the address of a page that only you can see; open it beside your editor, or pass
|
|
125
|
+
`--open`. Each time you save the file, `dh` sends it, and the page runs it again, with your
|
|
126
|
+
grants. A file that does not load is reported at its line, and the page keeps showing the
|
|
127
|
+
last file that did. A metric that does not exist shows on the page as that widget's error.
|
|
128
|
+
Stop with Ctrl+C.
|
|
129
|
+
|
|
130
|
+
A preview ends two hours after its file last changed or it last ran. A save after that
|
|
131
|
+
starts a new preview, at a new address, which `dh` prints. The page is new in DuckHaven:
|
|
132
|
+
against a server without it, use `--once`. If your browser is signed out, sign in, then open
|
|
133
|
+
the address again, as signing in does not bring you back to it.
|
|
134
|
+
|
|
135
|
+
`--once` runs the file in the terminal instead, once, and prints each widget with its state,
|
|
136
|
+
rows and SQL. It exits as `test` does.
|
|
137
|
+
|
|
138
|
+
### For coding agents
|
|
139
|
+
|
|
140
|
+
An AI coding agent that writes dashboard files can check its own work the same way. Put
|
|
141
|
+
something like this in the repository's `AGENTS.md`, and give the agent a token with the
|
|
142
|
+
`writer` role, which `validate` and `preview` need:
|
|
143
|
+
|
|
144
|
+
```markdown
|
|
145
|
+
## Dashboards
|
|
146
|
+
|
|
147
|
+
Dashboards live in `dashboards/`, one `<name>.dashboard.yml` file each, named after the
|
|
148
|
+
`name` inside it. To write or change one:
|
|
149
|
+
|
|
150
|
+
1. Read the file format: `dh dashboard schema`.
|
|
151
|
+
2. Read what a widget can use: `dh semantic model get <model>` lists its metrics and
|
|
152
|
+
dimensions.
|
|
153
|
+
3. Edit the file.
|
|
154
|
+
4. `dh --format json dashboard validate <file>`, and fix every error at its line.
|
|
155
|
+
5. `dh --format json dashboard preview --once <file>`, and fix every widget whose state is
|
|
156
|
+
`error`. Check that the rows answer what the widget's title says.
|
|
157
|
+
6. Never run `dh dashboard apply` without `--dry-run`, and never publish. CI applies the
|
|
158
|
+
files once a person has reviewed them.
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Exact numbers
|
|
162
|
+
|
|
163
|
+
A DuckHaven with `api_version` 3 or later sends `DECIMAL` values and 64-bit-or-wider integers
|
|
164
|
+
(`BIGINT`, `HUGEINT`, `count(*)`) as strings, because JSON numbers lose precision past about
|
|
165
|
+
16 digits. `dh` prints them exactly as before in table and CSV output. With `--format json`
|
|
166
|
+
they stay strings (`"12.50"`, `"9007199254740993"`), exact. Scripts reading `dh` JSON should
|
|
167
|
+
convert those columns themselves rather than expect JSON numbers.
|
|
168
|
+
|
|
169
|
+
## Documentation
|
|
170
|
+
|
|
171
|
+
- [Command-line quickstart](https://tamasmrtn.github.io/duckhaven/getting-started/cli-quickstart/) — install, sign in,
|
|
172
|
+
run a query
|
|
173
|
+
- [CLI reference](https://tamasmrtn.github.io/duckhaven/reference/cli/) — every command and flag
|
|
174
|
+
|
|
175
|
+
## Licence
|
|
176
|
+
|
|
177
|
+
Apache-2.0. See [LICENSE](LICENSE).
|
|
@@ -22,7 +22,7 @@ classifiers = [
|
|
|
22
22
|
"Typing :: Typed",
|
|
23
23
|
]
|
|
24
24
|
dependencies = [
|
|
25
|
-
"typer>=0.
|
|
25
|
+
"typer>=0.28.0",
|
|
26
26
|
# click.Abort is caught directly (Ctrl-C/Ctrl-D handling); typer>=0.27 no longer
|
|
27
27
|
# pulls click in as its own dependency, so it must be declared here too.
|
|
28
28
|
"click>=8.5.0",
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.
|
|
22
|
-
__version_tuple__ = version_tuple = (0,
|
|
21
|
+
__version__ = version = '0.5.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 5, 0)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|