steve-cli 0.4.1__tar.gz → 0.5.2__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.
- steve_cli-0.5.2/PKG-INFO +283 -0
- steve_cli-0.5.2/README.md +218 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/pyproject.toml +2 -1
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/cli.py +6 -0
- steve_cli-0.5.2/steve_cli/lineage/adapters/composite.py +12 -0
- steve_cli-0.5.2/steve_cli/lineage/adapters/dataproductregistry.py +42 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/lineage/registry.py +9 -0
- steve_cli-0.5.2/steve_cli/ontology.py +1895 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/storage/trino.py +15 -6
- steve_cli-0.5.2/steve_cli/vkg.py +173 -0
- steve_cli-0.5.2/steve_cli.egg-info/PKG-INFO +283 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli.egg-info/SOURCES.txt +4 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli.egg-info/requires.txt +1 -0
- steve_cli-0.4.1/PKG-INFO +0 -254
- steve_cli-0.4.1/README.md +0 -190
- steve_cli-0.4.1/steve_cli.egg-info/PKG-INFO +0 -254
- {steve_cli-0.4.1 → steve_cli-0.5.2}/setup.cfg +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/__init__.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/decorators/__init__.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/decorators/lineage_job.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/lineage/__init__.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/lineage/adapters/__init__.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/lineage/adapters/logging.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/lineage/adapters/null.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/lineage/adapters/openlineage.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/lineage/collector.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/lineage/port.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/lineage/storage.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/policies/__init__.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/policies/client.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/storage/__init__.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/storage/metadata/__init__.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/storage/metadata/extractors/__init__.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/storage/metadata/extractors/csv.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/storage/metadata/extractors/excel.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/storage/metadata/extractors/generic.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/storage/metadata/extractors/json.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/storage/metadata/extractors/parquet.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/storage/metadata/port.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/storage/metadata/registry.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/storage/parquet.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/storage/protocol.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/storage/s3.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/storage.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/validation/__init__.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/validation/adapters/__init__.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/validation/adapters/great_expectations.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/validation/adapters/null.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/validation/adapters/validoopsie.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/validation/port.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli/validation/registry.py +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli.egg-info/dependency_links.txt +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli.egg-info/entry_points.txt +0 -0
- {steve_cli-0.4.1 → steve_cli-0.5.2}/steve_cli.egg-info/top_level.txt +0 -0
steve_cli-0.5.2/PKG-INFO
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: steve-cli
|
|
3
|
+
Version: 0.5.2
|
|
4
|
+
Summary: A simple CLI tool to run jobs from jobs.yaml with proper environment setup
|
|
5
|
+
Author: Frank
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/7frank/ds-steve-cli
|
|
8
|
+
Project-URL: Repository, https://github.com/7frank/ds-steve-cli
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: boto3>=1.37.38
|
|
19
|
+
Requires-Dist: click>=8.0.0
|
|
20
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
21
|
+
Requires-Dist: pyyaml>=6.0
|
|
22
|
+
Requires-Dist: questionary>=2.0.0
|
|
23
|
+
Requires-Dist: rdflib>=6.0
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
26
|
+
Requires-Dist: black>=22.0; extra == "dev"
|
|
27
|
+
Requires-Dist: isort>=5.0; extra == "dev"
|
|
28
|
+
Requires-Dist: polars>=1.8.2; extra == "dev"
|
|
29
|
+
Requires-Dist: pyarrow>=17.0.0; extra == "dev"
|
|
30
|
+
Requires-Dist: validoopsie>=0.1.0; extra == "dev"
|
|
31
|
+
Requires-Dist: openlineage-python>=1.0.0; extra == "dev"
|
|
32
|
+
Provides-Extra: polars
|
|
33
|
+
Requires-Dist: polars>=1.8.2; extra == "polars"
|
|
34
|
+
Requires-Dist: pyarrow>=17.0.0; extra == "polars"
|
|
35
|
+
Provides-Extra: pandas
|
|
36
|
+
Requires-Dist: pandas>=2.0.3; extra == "pandas"
|
|
37
|
+
Requires-Dist: pyarrow>=17.0.0; extra == "pandas"
|
|
38
|
+
Provides-Extra: lineage
|
|
39
|
+
Requires-Dist: openlineage-python>=1.0.0; extra == "lineage"
|
|
40
|
+
Provides-Extra: validoopsie
|
|
41
|
+
Requires-Dist: validoopsie>=0.1.0; extra == "validoopsie"
|
|
42
|
+
Requires-Dist: polars>=1.8.2; extra == "validoopsie"
|
|
43
|
+
Provides-Extra: great-expectations
|
|
44
|
+
Requires-Dist: great-expectations>=0.18.0; extra == "great-expectations"
|
|
45
|
+
Requires-Dist: pandas>=2.0.3; extra == "great-expectations"
|
|
46
|
+
Provides-Extra: excel
|
|
47
|
+
Requires-Dist: openpyxl>=3.1.0; extra == "excel"
|
|
48
|
+
Provides-Extra: trino
|
|
49
|
+
Requires-Dist: trino>=0.330; extra == "trino"
|
|
50
|
+
Requires-Dist: pyarrow>=17.0.0; extra == "trino"
|
|
51
|
+
Requires-Dist: requests>=2.31.0; extra == "trino"
|
|
52
|
+
Requires-Dist: pyiceberg>=0.7.0; extra == "trino"
|
|
53
|
+
Requires-Dist: s3fs>=2024.1.0; extra == "trino"
|
|
54
|
+
Provides-Extra: visidata
|
|
55
|
+
Requires-Dist: visidata>=3.0; extra == "visidata"
|
|
56
|
+
Provides-Extra: all
|
|
57
|
+
Requires-Dist: openlineage-python>=1.0.0; extra == "all"
|
|
58
|
+
Requires-Dist: pyarrow>=17.0.0; extra == "all"
|
|
59
|
+
Requires-Dist: polars>=1.8.2; extra == "all"
|
|
60
|
+
Requires-Dist: validoopsie>=0.1.0; extra == "all"
|
|
61
|
+
Requires-Dist: great-expectations>=0.18.0; extra == "all"
|
|
62
|
+
Requires-Dist: pandas>=2.0.3; extra == "all"
|
|
63
|
+
Requires-Dist: openpyxl>=3.1.0; extra == "all"
|
|
64
|
+
Requires-Dist: visidata>=3.0; extra == "all"
|
|
65
|
+
|
|
66
|
+
# Steve CLI
|
|
67
|
+
|
|
68
|
+
A CLI tool for the data platform. Commands are organized by concern: general workspace automation, data lakehouse operations, semantic knowledge graphs, and governance.
|
|
69
|
+
|
|
70
|
+
## Installation
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Add to your workspace
|
|
74
|
+
uv add steve-cli
|
|
75
|
+
|
|
76
|
+
# Or pin to a local editable copy (in your workspace pyproject.toml)
|
|
77
|
+
[tool.uv.sources]
|
|
78
|
+
steve-cli = { path = "../../packages/steve-cli", editable = true }
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Upgrade to the latest version:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
steve upgrade
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Platform
|
|
90
|
+
|
|
91
|
+
General-purpose commands for running jobs, managing local development processes, and working with the core infrastructure (object storage, secrets).
|
|
92
|
+
|
|
93
|
+
### `steve jobs` — run automation jobs
|
|
94
|
+
|
|
95
|
+
Runs tasks defined in `jobs.yaml`. Each job declares a command and optional env vars. Designed for data pipeline steps, seeding, bootstrapping, etc.
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
steve jobs # interactive picker
|
|
99
|
+
steve jobs ls # list all jobs with commands and env vars
|
|
100
|
+
steve jobs run <name> # run a job by name
|
|
101
|
+
steve jobs run <name> -f path/to/jobs.yaml
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`jobs.yaml` example:
|
|
105
|
+
|
|
106
|
+
```yaml
|
|
107
|
+
jobs:
|
|
108
|
+
- name: seed-data
|
|
109
|
+
command: ["uv", "run", "src/02_seed_data.py"]
|
|
110
|
+
env:
|
|
111
|
+
BATCH_SIZE: "500"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### `steve apps` — manage long-running apps
|
|
115
|
+
|
|
116
|
+
Starts, stops, and monitors local apps defined in `apps.yaml`. Apps are registered with the auth-proxy so they get a public URL inside the platform.
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
steve apps # interactive picker
|
|
120
|
+
steve apps ls # list apps with status and URLs
|
|
121
|
+
steve apps status # show running/stopped status for all apps
|
|
122
|
+
steve apps start <name>
|
|
123
|
+
steve apps stop <name>
|
|
124
|
+
steve apps restart <name>
|
|
125
|
+
steve apps logs <name> [-n 100]
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### `steve setup env` — decrypt secrets
|
|
129
|
+
|
|
130
|
+
Decrypts SOPS-encrypted `*.enc.env` files in the current directory and writes plaintext `.env` files.
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
steve setup env
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### `steve buckets` — browse S3 storage
|
|
137
|
+
|
|
138
|
+
Interactive browser for S3/MinIO buckets. Detects available buckets from env vars (`BRONZE_ACCESS_KEY`, `SILVER_ACCESS_KEY`, `GOLD_ACCESS_KEY`, or `{WORKSPACE}_ACCESS_KEY`) and shows their contents as a file tree. Files can be opened in visidata.
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
steve buckets
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Data Lakehouse
|
|
147
|
+
|
|
148
|
+
Commands for working with the lakehouse layer: Iceberg tables accessed via Trino.
|
|
149
|
+
|
|
150
|
+
### `steve tables` — browse Iceberg tables via Trino
|
|
151
|
+
|
|
152
|
+
Interactive browser for Iceberg tables. Lists schemas and tables available via Trino, and opens them in visidata for inspection.
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
steve tables
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Requires `TRINO_ENDPOINT` to be set. Install visidata for table preview:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
uv pip install 'steve-cli[visidata]'
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Semantics & Knowledge Graphs
|
|
167
|
+
|
|
168
|
+
Commands for authoring and operating Virtual Knowledge Graphs (VKGs) — semantic layers that expose relational Iceberg data as RDF/SPARQL via Ontop.
|
|
169
|
+
|
|
170
|
+
There are two sides:
|
|
171
|
+
- **`ontology`** — the *author/publish* side: define concepts, register them in the Data Product Registry, compile VKG artifacts
|
|
172
|
+
- **`vkg`** — the *runtime/ops* side: manage named Ontop instances on the VKG control plane, run SPARQL queries
|
|
173
|
+
|
|
174
|
+
### `steve ontology` — author and publish ontologies
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
steve ontology ls # list registered packages in the registry
|
|
178
|
+
steve ontology ls --show-bindings # include binding packages
|
|
179
|
+
steve ontology register <file.yaml> # register a concept or binding YAML
|
|
180
|
+
steve ontology add <uri> # add a dependency URI to ontology.yaml
|
|
181
|
+
steve ontology build # resolve deps + compile → write vkg/ folder locally
|
|
182
|
+
steve ontology compile --root <uri> --binding <uri> # compile and print artifact info
|
|
183
|
+
steve ontology push # compile + upload to VKG control plane (default slot)
|
|
184
|
+
steve ontology push --name <slot> # push to a named VKG slot
|
|
185
|
+
steve ontology push -f ontology-consumer.yaml # use a different manifest file
|
|
186
|
+
steve ontology push --frozen # fail if lock file is absent or stale (like npm ci)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Manifest format (`ontology.yaml`):
|
|
190
|
+
|
|
191
|
+
```yaml
|
|
192
|
+
# Option A: workspace that owns ontology files
|
|
193
|
+
packages:
|
|
194
|
+
- path: ontologies/order_core.yaml
|
|
195
|
+
bindings:
|
|
196
|
+
- path: ontologies/binding_order_iceberg.yaml
|
|
197
|
+
compile:
|
|
198
|
+
root: dp://o/acme/concepts/order/core
|
|
199
|
+
binding: dp://o/acme/bindings/order/iceberg
|
|
200
|
+
|
|
201
|
+
# Option B: consumer workspace (resolves deps from registry, no local files)
|
|
202
|
+
dependencies:
|
|
203
|
+
- dp://o/acme/concepts/party/core
|
|
204
|
+
- dp://o/acme/concepts/order/core
|
|
205
|
+
compile:
|
|
206
|
+
root: dp://o/acme/concepts/order/core
|
|
207
|
+
binding: dp://o/acme/bindings/order/iceberg
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
After `push`, a lock file (`ontology.lock.yaml`) is written with resolved package versions and Iceberg snapshot IDs for audit and future time-travel SQL rewriting.
|
|
211
|
+
|
|
212
|
+
### `steve vkg` — operate VKG instances
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
steve vkg ls # list all slots: status, concept counts, SPARQL URL
|
|
216
|
+
steve vkg status <name> # detailed status for one slot
|
|
217
|
+
steve vkg start <name> # start a stopped slot
|
|
218
|
+
steve vkg stop <name> # stop a running slot (persists desired=stopped)
|
|
219
|
+
steve vkg sparql <name> "<SPARQL query>" # run a SPARQL query and print results as a table
|
|
220
|
+
steve vkg sparql <name> "<query>" --format json
|
|
221
|
+
steve vkg logs <name> [-n 50] # tail the Ontop process log for a slot
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Desired state is persisted per slot — slots with `desired=running` are automatically restored after a pod restart.
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Governance
|
|
229
|
+
|
|
230
|
+
### `steve policies apply` — apply access policies
|
|
231
|
+
|
|
232
|
+
Applies role-based access policies from a YAML file to the Policy Control Plane.
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
steve policies apply # apply from ./policies/access.yaml
|
|
236
|
+
steve policies apply -f custom/path.yaml
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Environment variables
|
|
242
|
+
|
|
243
|
+
| Variable | Used by | Description |
|
|
244
|
+
|---|---|---|
|
|
245
|
+
| `REGISTRY_URL` | `ontology` | Data Product Registry URL (default: `http://localhost:8765`) |
|
|
246
|
+
| `REGISTRY_TOKEN` | `ontology` | Pre-issued JWT for the registry |
|
|
247
|
+
| `REGISTRY_JWT_SECRET` | `ontology` | Secret to mint a JWT (if no token) |
|
|
248
|
+
| `REGISTRY_ORG` | `ontology` | Org claim for minted JWT |
|
|
249
|
+
| `VKG_CONTROL_PLANE_URL` | `ontology push`, `vkg` | Control plane URL (default: `http://localhost:18081`) |
|
|
250
|
+
| `TRINO_ENDPOINT` | `ontology push`, `tables` | Trino HTTP URL for snapshot capture and table browsing |
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## File Metadata Extraction
|
|
255
|
+
|
|
256
|
+
Steve automatically extracts metadata from files read or written via `MetadataRegistry`. The extractor is chosen by file extension.
|
|
257
|
+
|
|
258
|
+
| Extension | Extractor | Requires |
|
|
259
|
+
|---|---|---|
|
|
260
|
+
| `.parquet`, `.pq` | `ParquetExtractor` | `pip install steve-cli[polars]` |
|
|
261
|
+
| `.csv`, `.tsv`, `.txt` | `CsvExtractor` | stdlib only |
|
|
262
|
+
| `.json`, `.jsonl`, `.ndjson` | `JsonExtractor` | stdlib only |
|
|
263
|
+
| `.xlsx`, `.xls`, `.xlsm` | `ExcelExtractor` | `pip install steve-cli[excel]` |
|
|
264
|
+
| anything else | `GenericExtractor` | stdlib only |
|
|
265
|
+
|
|
266
|
+
Custom extractors can be registered at startup:
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
from steve_cli.storage.metadata.port import MetadataExtractorPort, FileMetadata, ColumnMetadata
|
|
270
|
+
from steve_cli.storage.metadata.registry import MetadataRegistry
|
|
271
|
+
|
|
272
|
+
class AvroExtractor(MetadataExtractorPort):
|
|
273
|
+
extensions = (".avro",)
|
|
274
|
+
|
|
275
|
+
def extract(self, data: bytes, path: str) -> FileMetadata:
|
|
276
|
+
import fastavro, io
|
|
277
|
+
reader = fastavro.reader(io.BytesIO(data))
|
|
278
|
+
schema = reader.writer_schema
|
|
279
|
+
columns = [ColumnMetadata(name=f["name"], type=str(f["type"])) for f in schema.get("fields", [])]
|
|
280
|
+
return FileMetadata(format="avro", size_bytes=len(data), rows=len(list(reader)), columns=columns)
|
|
281
|
+
|
|
282
|
+
MetadataRegistry.register("avro", AvroExtractor)
|
|
283
|
+
```
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Steve CLI
|
|
2
|
+
|
|
3
|
+
A CLI tool for the data platform. Commands are organized by concern: general workspace automation, data lakehouse operations, semantic knowledge graphs, and governance.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Add to your workspace
|
|
9
|
+
uv add steve-cli
|
|
10
|
+
|
|
11
|
+
# Or pin to a local editable copy (in your workspace pyproject.toml)
|
|
12
|
+
[tool.uv.sources]
|
|
13
|
+
steve-cli = { path = "../../packages/steve-cli", editable = true }
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Upgrade to the latest version:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
steve upgrade
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Platform
|
|
25
|
+
|
|
26
|
+
General-purpose commands for running jobs, managing local development processes, and working with the core infrastructure (object storage, secrets).
|
|
27
|
+
|
|
28
|
+
### `steve jobs` — run automation jobs
|
|
29
|
+
|
|
30
|
+
Runs tasks defined in `jobs.yaml`. Each job declares a command and optional env vars. Designed for data pipeline steps, seeding, bootstrapping, etc.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
steve jobs # interactive picker
|
|
34
|
+
steve jobs ls # list all jobs with commands and env vars
|
|
35
|
+
steve jobs run <name> # run a job by name
|
|
36
|
+
steve jobs run <name> -f path/to/jobs.yaml
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`jobs.yaml` example:
|
|
40
|
+
|
|
41
|
+
```yaml
|
|
42
|
+
jobs:
|
|
43
|
+
- name: seed-data
|
|
44
|
+
command: ["uv", "run", "src/02_seed_data.py"]
|
|
45
|
+
env:
|
|
46
|
+
BATCH_SIZE: "500"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### `steve apps` — manage long-running apps
|
|
50
|
+
|
|
51
|
+
Starts, stops, and monitors local apps defined in `apps.yaml`. Apps are registered with the auth-proxy so they get a public URL inside the platform.
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
steve apps # interactive picker
|
|
55
|
+
steve apps ls # list apps with status and URLs
|
|
56
|
+
steve apps status # show running/stopped status for all apps
|
|
57
|
+
steve apps start <name>
|
|
58
|
+
steve apps stop <name>
|
|
59
|
+
steve apps restart <name>
|
|
60
|
+
steve apps logs <name> [-n 100]
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### `steve setup env` — decrypt secrets
|
|
64
|
+
|
|
65
|
+
Decrypts SOPS-encrypted `*.enc.env` files in the current directory and writes plaintext `.env` files.
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
steve setup env
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### `steve buckets` — browse S3 storage
|
|
72
|
+
|
|
73
|
+
Interactive browser for S3/MinIO buckets. Detects available buckets from env vars (`BRONZE_ACCESS_KEY`, `SILVER_ACCESS_KEY`, `GOLD_ACCESS_KEY`, or `{WORKSPACE}_ACCESS_KEY`) and shows their contents as a file tree. Files can be opened in visidata.
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
steve buckets
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Data Lakehouse
|
|
82
|
+
|
|
83
|
+
Commands for working with the lakehouse layer: Iceberg tables accessed via Trino.
|
|
84
|
+
|
|
85
|
+
### `steve tables` — browse Iceberg tables via Trino
|
|
86
|
+
|
|
87
|
+
Interactive browser for Iceberg tables. Lists schemas and tables available via Trino, and opens them in visidata for inspection.
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
steve tables
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Requires `TRINO_ENDPOINT` to be set. Install visidata for table preview:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
uv pip install 'steve-cli[visidata]'
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Semantics & Knowledge Graphs
|
|
102
|
+
|
|
103
|
+
Commands for authoring and operating Virtual Knowledge Graphs (VKGs) — semantic layers that expose relational Iceberg data as RDF/SPARQL via Ontop.
|
|
104
|
+
|
|
105
|
+
There are two sides:
|
|
106
|
+
- **`ontology`** — the *author/publish* side: define concepts, register them in the Data Product Registry, compile VKG artifacts
|
|
107
|
+
- **`vkg`** — the *runtime/ops* side: manage named Ontop instances on the VKG control plane, run SPARQL queries
|
|
108
|
+
|
|
109
|
+
### `steve ontology` — author and publish ontologies
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
steve ontology ls # list registered packages in the registry
|
|
113
|
+
steve ontology ls --show-bindings # include binding packages
|
|
114
|
+
steve ontology register <file.yaml> # register a concept or binding YAML
|
|
115
|
+
steve ontology add <uri> # add a dependency URI to ontology.yaml
|
|
116
|
+
steve ontology build # resolve deps + compile → write vkg/ folder locally
|
|
117
|
+
steve ontology compile --root <uri> --binding <uri> # compile and print artifact info
|
|
118
|
+
steve ontology push # compile + upload to VKG control plane (default slot)
|
|
119
|
+
steve ontology push --name <slot> # push to a named VKG slot
|
|
120
|
+
steve ontology push -f ontology-consumer.yaml # use a different manifest file
|
|
121
|
+
steve ontology push --frozen # fail if lock file is absent or stale (like npm ci)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Manifest format (`ontology.yaml`):
|
|
125
|
+
|
|
126
|
+
```yaml
|
|
127
|
+
# Option A: workspace that owns ontology files
|
|
128
|
+
packages:
|
|
129
|
+
- path: ontologies/order_core.yaml
|
|
130
|
+
bindings:
|
|
131
|
+
- path: ontologies/binding_order_iceberg.yaml
|
|
132
|
+
compile:
|
|
133
|
+
root: dp://o/acme/concepts/order/core
|
|
134
|
+
binding: dp://o/acme/bindings/order/iceberg
|
|
135
|
+
|
|
136
|
+
# Option B: consumer workspace (resolves deps from registry, no local files)
|
|
137
|
+
dependencies:
|
|
138
|
+
- dp://o/acme/concepts/party/core
|
|
139
|
+
- dp://o/acme/concepts/order/core
|
|
140
|
+
compile:
|
|
141
|
+
root: dp://o/acme/concepts/order/core
|
|
142
|
+
binding: dp://o/acme/bindings/order/iceberg
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
After `push`, a lock file (`ontology.lock.yaml`) is written with resolved package versions and Iceberg snapshot IDs for audit and future time-travel SQL rewriting.
|
|
146
|
+
|
|
147
|
+
### `steve vkg` — operate VKG instances
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
steve vkg ls # list all slots: status, concept counts, SPARQL URL
|
|
151
|
+
steve vkg status <name> # detailed status for one slot
|
|
152
|
+
steve vkg start <name> # start a stopped slot
|
|
153
|
+
steve vkg stop <name> # stop a running slot (persists desired=stopped)
|
|
154
|
+
steve vkg sparql <name> "<SPARQL query>" # run a SPARQL query and print results as a table
|
|
155
|
+
steve vkg sparql <name> "<query>" --format json
|
|
156
|
+
steve vkg logs <name> [-n 50] # tail the Ontop process log for a slot
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Desired state is persisted per slot — slots with `desired=running` are automatically restored after a pod restart.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Governance
|
|
164
|
+
|
|
165
|
+
### `steve policies apply` — apply access policies
|
|
166
|
+
|
|
167
|
+
Applies role-based access policies from a YAML file to the Policy Control Plane.
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
steve policies apply # apply from ./policies/access.yaml
|
|
171
|
+
steve policies apply -f custom/path.yaml
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Environment variables
|
|
177
|
+
|
|
178
|
+
| Variable | Used by | Description |
|
|
179
|
+
|---|---|---|
|
|
180
|
+
| `REGISTRY_URL` | `ontology` | Data Product Registry URL (default: `http://localhost:8765`) |
|
|
181
|
+
| `REGISTRY_TOKEN` | `ontology` | Pre-issued JWT for the registry |
|
|
182
|
+
| `REGISTRY_JWT_SECRET` | `ontology` | Secret to mint a JWT (if no token) |
|
|
183
|
+
| `REGISTRY_ORG` | `ontology` | Org claim for minted JWT |
|
|
184
|
+
| `VKG_CONTROL_PLANE_URL` | `ontology push`, `vkg` | Control plane URL (default: `http://localhost:18081`) |
|
|
185
|
+
| `TRINO_ENDPOINT` | `ontology push`, `tables` | Trino HTTP URL for snapshot capture and table browsing |
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## File Metadata Extraction
|
|
190
|
+
|
|
191
|
+
Steve automatically extracts metadata from files read or written via `MetadataRegistry`. The extractor is chosen by file extension.
|
|
192
|
+
|
|
193
|
+
| Extension | Extractor | Requires |
|
|
194
|
+
|---|---|---|
|
|
195
|
+
| `.parquet`, `.pq` | `ParquetExtractor` | `pip install steve-cli[polars]` |
|
|
196
|
+
| `.csv`, `.tsv`, `.txt` | `CsvExtractor` | stdlib only |
|
|
197
|
+
| `.json`, `.jsonl`, `.ndjson` | `JsonExtractor` | stdlib only |
|
|
198
|
+
| `.xlsx`, `.xls`, `.xlsm` | `ExcelExtractor` | `pip install steve-cli[excel]` |
|
|
199
|
+
| anything else | `GenericExtractor` | stdlib only |
|
|
200
|
+
|
|
201
|
+
Custom extractors can be registered at startup:
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
from steve_cli.storage.metadata.port import MetadataExtractorPort, FileMetadata, ColumnMetadata
|
|
205
|
+
from steve_cli.storage.metadata.registry import MetadataRegistry
|
|
206
|
+
|
|
207
|
+
class AvroExtractor(MetadataExtractorPort):
|
|
208
|
+
extensions = (".avro",)
|
|
209
|
+
|
|
210
|
+
def extract(self, data: bytes, path: str) -> FileMetadata:
|
|
211
|
+
import fastavro, io
|
|
212
|
+
reader = fastavro.reader(io.BytesIO(data))
|
|
213
|
+
schema = reader.writer_schema
|
|
214
|
+
columns = [ColumnMetadata(name=f["name"], type=str(f["type"])) for f in schema.get("fields", [])]
|
|
215
|
+
return FileMetadata(format="avro", size_bytes=len(data), rows=len(list(reader)), columns=columns)
|
|
216
|
+
|
|
217
|
+
MetadataRegistry.register("avro", AvroExtractor)
|
|
218
|
+
```
|
|
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|
|
5
5
|
|
|
6
6
|
[project]
|
|
7
7
|
name = "steve-cli"
|
|
8
|
-
version = "0.
|
|
8
|
+
version = "0.5.2"
|
|
9
9
|
description = "A simple CLI tool to run jobs from jobs.yaml with proper environment setup"
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
license = {text = "MIT"}
|
|
@@ -26,6 +26,7 @@ dependencies = [
|
|
|
26
26
|
"python-dotenv>=1.0.0",
|
|
27
27
|
"pyyaml>=6.0",
|
|
28
28
|
"questionary>=2.0.0",
|
|
29
|
+
"rdflib>=6.0",
|
|
29
30
|
]
|
|
30
31
|
|
|
31
32
|
[project.optional-dependencies]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from steve_cli.lineage.port import LineageEvent, LineagePort
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CompositeLineageAdapter(LineagePort):
|
|
7
|
+
def __init__(self, adapters: list[LineagePort]):
|
|
8
|
+
self._adapters = adapters
|
|
9
|
+
|
|
10
|
+
def emit(self, event: LineageEvent) -> None:
|
|
11
|
+
for adapter in self._adapters:
|
|
12
|
+
adapter.emit(event)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
from steve_cli.lineage.port import DatasetRef, LineageEvent, LineagePort
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RegistryLineageAdapter(LineagePort):
|
|
14
|
+
def __init__(self):
|
|
15
|
+
self._url = os.getenv("REGISTRY_URL", "http://localhost:8765")
|
|
16
|
+
self._token = os.getenv("REGISTRY_TOKEN", "")
|
|
17
|
+
|
|
18
|
+
def emit(self, event: LineageEvent) -> None:
|
|
19
|
+
if event.state != "COMPLETE":
|
|
20
|
+
return
|
|
21
|
+
for ds in event.outputs:
|
|
22
|
+
self._register(ds)
|
|
23
|
+
|
|
24
|
+
def _register(self, ds: DatasetRef) -> None:
|
|
25
|
+
product = {
|
|
26
|
+
"uri": f"dp://d/{ds.namespace}/{ds.name}",
|
|
27
|
+
"name": ds.name,
|
|
28
|
+
"namespace": ds.namespace,
|
|
29
|
+
"description": "Auto-registered from lineage job",
|
|
30
|
+
}
|
|
31
|
+
headers = {}
|
|
32
|
+
if self._token:
|
|
33
|
+
headers["Authorization"] = f"Bearer {self._token}"
|
|
34
|
+
try:
|
|
35
|
+
requests.post(
|
|
36
|
+
f"{self._url}/api/v1/register",
|
|
37
|
+
json={"product": product},
|
|
38
|
+
headers=headers,
|
|
39
|
+
timeout=10,
|
|
40
|
+
)
|
|
41
|
+
except Exception as exc:
|
|
42
|
+
logger.warning("Failed to register dataset %s/%s in registry: %s", ds.namespace, ds.name, exc)
|
|
@@ -25,6 +25,11 @@ class LineageRegistry:
|
|
|
25
25
|
@classmethod
|
|
26
26
|
def create(cls, name: str | None = None, **kwargs: Any) -> LineagePort:
|
|
27
27
|
provider = name or os.getenv("LINEAGE_PROVIDER", "openlineage")
|
|
28
|
+
if "+" in provider:
|
|
29
|
+
from .adapters.composite import CompositeLineageAdapter
|
|
30
|
+
parts = [p.strip() for p in provider.split("+")]
|
|
31
|
+
adapters = [cls.get(p)(**kwargs) for p in parts]
|
|
32
|
+
return CompositeLineageAdapter(adapters)
|
|
28
33
|
return cls.get(provider)(**kwargs)
|
|
29
34
|
|
|
30
35
|
@classmethod
|
|
@@ -46,5 +51,9 @@ def _register_defaults() -> None:
|
|
|
46
51
|
except ImportError:
|
|
47
52
|
LineageRegistry.register("openlineage", NullLineageAdapter)
|
|
48
53
|
|
|
54
|
+
from .adapters.dataproductregistry import RegistryLineageAdapter
|
|
55
|
+
|
|
56
|
+
LineageRegistry.register("dataproductregistry", RegistryLineageAdapter)
|
|
57
|
+
|
|
49
58
|
|
|
50
59
|
_register_defaults()
|