composable-data-stack 0.4.0__py3-none-any.whl
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.
- cli/__init__.py +1 -0
- cli/diagnostics.py +13 -0
- cli/graph.py +58 -0
- cli/image_updates.py +326 -0
- cli/image_verification.py +484 -0
- cli/loader.py +180 -0
- cli/main.py +1656 -0
- cli/overlay.py +239 -0
- cli/planner.py +618 -0
- cli/preflight.py +418 -0
- cli/renderer.py +791 -0
- cli/resolver.py +28 -0
- cli/resources/__init__.py +1 -0
- cli/resources/rule-schema.json +274 -0
- cli/resources/rule-set.json +919 -0
- cli/secrets.py +169 -0
- cli/security.py +768 -0
- cli/security_common.py +41 -0
- cli/state.py +112 -0
- cli/up_runner.py +257 -0
- cli/validator.py +570 -0
- composable_data_stack-0.4.0.dist-info/METADATA +872 -0
- composable_data_stack-0.4.0.dist-info/RECORD +27 -0
- composable_data_stack-0.4.0.dist-info/WHEEL +5 -0
- composable_data_stack-0.4.0.dist-info/entry_points.txt +2 -0
- composable_data_stack-0.4.0.dist-info/licenses/LICENSE +201 -0
- composable_data_stack-0.4.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,872 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: composable-data-stack
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: CLI for validating and composing composable data stack profiles
|
|
5
|
+
Author: Ronald Hensbergen
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/RonaldHensbergen/composable-data-stack
|
|
8
|
+
Project-URL: Documentation, https://github.com/RonaldHensbergen/composable-data-stack/tree/main/docs
|
|
9
|
+
Project-URL: Issues, https://github.com/RonaldHensbergen/composable-data-stack/issues
|
|
10
|
+
Project-URL: Repository, https://github.com/RonaldHensbergen/composable-data-stack
|
|
11
|
+
Keywords: data-platform,data-stack,docker-compose,infrastructure-as-code
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
19
|
+
Classifier: Topic :: System :: Systems Administration
|
|
20
|
+
Requires-Python: >=3.14
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: PyYAML>=6.0
|
|
24
|
+
Requires-Dist: jsonschema>=4.22.0
|
|
25
|
+
Requires-Dist: jinja2>=3.0
|
|
26
|
+
Requires-Dist: packaging>=23.0
|
|
27
|
+
Requires-Dist: rich>=15.0
|
|
28
|
+
Provides-Extra: completion
|
|
29
|
+
Requires-Dist: argcomplete<4,>=3.0.0; extra == "completion"
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: coverage>=7.6; extra == "dev"
|
|
32
|
+
Requires-Dist: bandit>=1.7; extra == "dev"
|
|
33
|
+
Requires-Dist: pip-audit>=2.7; extra == "dev"
|
|
34
|
+
Requires-Dist: ruff==0.16.2; extra == "dev"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# 🚀 Composable Data Stack (CDS)
|
|
38
|
+
|
|
39
|
+
> **Terraform for data platforms.**
|
|
40
|
+
> Build, validate, secure, and evolve data stacks using modular components and explicit contracts.
|
|
41
|
+
|
|
42
|
+

|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 🧠 What Is CDS (In 1 Minute)
|
|
47
|
+
|
|
48
|
+
Composable Data Stack (CDS) is a framework for defining and assembling data platforms from reusable modules such as orchestrators, warehouses, BI tools, and secrets providers.
|
|
49
|
+
|
|
50
|
+
## 🤝 Get Involved
|
|
51
|
+
|
|
52
|
+
- **Star and follow** on GitHub: [RonaldHensbergen/composable-data-stack](https://github.com/RonaldHensbergen/composable-data-stack)
|
|
53
|
+
- **Contribute**: open a discussion, file an issue, or send a PR to help shape CDS
|
|
54
|
+
- **Proof it**: if you run it in a real workflow, share your feedback — good or bad
|
|
55
|
+
|
|
56
|
+
> **Note:** Development helper tools are located in the `tools/` directory (git-ignored). See `tools/pr-cli/README.md` for PR creation scripts.
|
|
57
|
+
|
|
58
|
+
Instead of hardcoding integrations or relying on fragile pipelines, CDS introduces:
|
|
59
|
+
|
|
60
|
+
- 🔧 **Modules**: reusable components (Dagster, Postgres, Superset)
|
|
61
|
+
- 🔗 **Contracts**: explicit interfaces between components
|
|
62
|
+
- 🧩 **Profiles**: fully composed, runnable stacks
|
|
63
|
+
|
|
64
|
+
Think of it as Infrastructure as Code, but for data platforms.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## ⚡ Why CDS
|
|
69
|
+
|
|
70
|
+
Modern data platforms force a trade-off:
|
|
71
|
+
|
|
72
|
+
|Approach|Problem|
|
|
73
|
+
|---|---|
|
|
74
|
+
|Monolithic stack|Rigid, hard to evolve|
|
|
75
|
+
|Custom pipelines|Flexible but fragile and inconsistent|
|
|
76
|
+
|
|
77
|
+
CDS gives you the best of both:
|
|
78
|
+
|
|
79
|
+
- composability without chaos
|
|
80
|
+
- flexibility with guarantees
|
|
81
|
+
- modularity with structure
|
|
82
|
+
- no vendor lock-in by design
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 🎯 When To Use CDS
|
|
87
|
+
|
|
88
|
+
Use CDS if you:
|
|
89
|
+
|
|
90
|
+
- want to swap tools (Airflow ↔ Dagster, Superset ↔ Metabase)
|
|
91
|
+
- need reproducible environments across dev, CI, and prod
|
|
92
|
+
- are building a platform for multiple teams
|
|
93
|
+
- want contract-driven integration instead of implicit coupling
|
|
94
|
+
|
|
95
|
+
CDS may be overkill if:
|
|
96
|
+
|
|
97
|
+
- you only run a single-tool stack
|
|
98
|
+
- you do not need interchangeable components
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 🏗️ Example
|
|
103
|
+
|
|
104
|
+
The `local-dagster-postgres-superset` profile defines:
|
|
105
|
+
|
|
106
|
+
- Dagster -> orchestration
|
|
107
|
+
- Postgres -> storage
|
|
108
|
+
- Superset -> BI
|
|
109
|
+
|
|
110
|
+
### What CDS Does
|
|
111
|
+
|
|
112
|
+
1. Validates module definitions
|
|
113
|
+
2. Resolves contract bindings
|
|
114
|
+
3. Checks compatibility and security constraints
|
|
115
|
+
4. Produces a fully wired stack definition
|
|
116
|
+
|
|
117
|
+
`cds plan` resolves the full dependency graph before runtime configuration is generated, ensuring all module interactions are valid and predictable.
|
|
118
|
+
|
|
119
|
+
You can replace components without changing system behavior:
|
|
120
|
+
|
|
121
|
+
```text
|
|
122
|
+
Dagster -> Airflow
|
|
123
|
+
Superset -> Metabase
|
|
124
|
+
Postgres -> MariaDB
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 🗺️ Architecture Overview
|
|
130
|
+
|
|
131
|
+
CDS wires modules through **contracts**, not direct dependencies. This section has two levels: a high-level picture of what gets wired together ([Overview](#overview)), and a detailed look at what happens when you run a CDS command ([Internal Flow](#internal-flow)).
|
|
132
|
+
|
|
133
|
+
### Overview
|
|
134
|
+
|
|
135
|
+
Below, `local-dagster-postgres-superset` wires Dagster to Postgres to Superset through contracts:
|
|
136
|
+
|
|
137
|
+
```mermaid
|
|
138
|
+
---
|
|
139
|
+
flowchart TD
|
|
140
|
+
Dagster[Dagster]
|
|
141
|
+
Postgres[(Postgres)]
|
|
142
|
+
Superset[Superset]
|
|
143
|
+
|
|
144
|
+
Dagster -->|transformation-runner| Postgres
|
|
145
|
+
Postgres -->|warehouse-query| Superset
|
|
146
|
+
|
|
147
|
+
classDef tool stroke:#818cf8,fill:#eef2ff
|
|
148
|
+
classDef database stroke:#2dd4bf,fill:#f0fdfa
|
|
149
|
+
classDef viz stroke:#a78bfa,fill:#f5f3ff
|
|
150
|
+
|
|
151
|
+
class Dagster tool
|
|
152
|
+
class Postgres database
|
|
153
|
+
class Superset viz
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Internal Flow
|
|
157
|
+
|
|
158
|
+
CDS splits into two phases: **compile-time**, where `cds` itself validates, resolves, and renders a plain `docker-compose.yaml`; and **runtime**, where the real `docker compose` binary builds and starts containers from that file. CDS never runs containers itself.
|
|
159
|
+
|
|
160
|
+
`cds test` runs the full compile-time pipeline in order — **validate → security → plan → render**. A `validate` (or `plan`) failure skips all downstream stages, but a **security** failure does not skip `plan`/`render`: those stages still run so `cds test` reports every stage's status in one pass, while the overall command still exits non-zero if any stage failed. `cds up` runs the same pipeline **minus security** (`validate → plan → render`), then hands off to `docker compose build`/`docker compose up`. See the [CLI table](#️-cli) below for exactly what each command runs.
|
|
161
|
+
|
|
162
|
+
- **Validate** checks profile shape, module configs, dependencies, secret refs, contract bindings, and outputs.
|
|
163
|
+
- **Security** (`cds test` only) runs rule-based checks against modules and resolved secrets; a failing check is reported but does not prevent `plan`/`render` from also running.
|
|
164
|
+
- **Plan** resolves contract bindings and substitutes secrets and defaults.
|
|
165
|
+
- **Render** generates the final `docker-compose.yaml`, with secret values as `${CDS_VAR}` placeholders; never the raw value.
|
|
166
|
+
- **Runtime** (`cds up` only): `docker compose build` (skippable with `--no-build`), then `docker compose up --detach`, then a live `cds state` view that polls until the stack settles (skippable with `--detach`). Build/up/log output is persisted to a log file (`.cds/logs/` by default). Docker Compose, not CDS, resolves `${CDS_VAR}` placeholders from a `.env` file (see `cds init`) and starts the containers.
|
|
167
|
+
|
|
168
|
+
Passing `validate → security → plan → render` proves the profile *compiles*
|
|
169
|
+
correctly — it is not proof that the resulting stack *runs* correctly. For the
|
|
170
|
+
broader success criteria used to certify a profile (booting the rendered
|
|
171
|
+
stack, service health, an end-to-end pipeline run, and downstream data
|
|
172
|
+
consumption in Superset), see
|
|
173
|
+
[docs/profile-testing/test-plan.md](docs/profile-testing/test-plan.md) and
|
|
174
|
+
[docs/profile-testing/failure-path-and-ci.md](docs/profile-testing/failure-path-and-ci.md).
|
|
175
|
+
|
|
176
|
+
```mermaid
|
|
177
|
+
---
|
|
178
|
+
flowchart TD
|
|
179
|
+
subgraph compile["Compile-time (cds)"]
|
|
180
|
+
direction TB
|
|
181
|
+
Profile[/profile.yaml/]
|
|
182
|
+
Validate[Validate]
|
|
183
|
+
Security["Security checks<br/>(cds test only)"]
|
|
184
|
+
Plan[Plan]
|
|
185
|
+
Render[Render]
|
|
186
|
+
Compose[/docker-compose.yaml/]
|
|
187
|
+
Stop1((stops here))
|
|
188
|
+
|
|
189
|
+
Profile --> Validate
|
|
190
|
+
Validate -->|structural + contract checks| Security
|
|
191
|
+
Security -->|rule-based checks| Plan
|
|
192
|
+
Plan -->|resolve + substitute| Render
|
|
193
|
+
Render --> Compose
|
|
194
|
+
|
|
195
|
+
Validate -.->|E020, E041, E042, E081| Stop1
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
subgraph runtime["Runtime (docker compose, cds up only)"]
|
|
199
|
+
direction TB
|
|
200
|
+
Build["docker compose build<br/>(skip with --no-build)"]
|
|
201
|
+
Up["docker compose up"]
|
|
202
|
+
Env[(".env file")]
|
|
203
|
+
Containers["running containers,<br/>real secret values injected"]
|
|
204
|
+
|
|
205
|
+
Build --> Up
|
|
206
|
+
Env -.->|resolves CDS_VAR| Up
|
|
207
|
+
Up --> Containers
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
Compose --> Build
|
|
211
|
+
|
|
212
|
+
classDef stage stroke:#818cf8,fill:#eef2ff
|
|
213
|
+
classDef artifact stroke:#2dd4bf,fill:#f0fdfa
|
|
214
|
+
classDef stop stroke:#f87171,fill:#fef2f2,stroke-dasharray: 3 3
|
|
215
|
+
classDef runtimeNode stroke:#a78bfa,fill:#f5f3ff
|
|
216
|
+
|
|
217
|
+
class Validate,Security,Plan,Render stage
|
|
218
|
+
class Profile,Compose artifact
|
|
219
|
+
class Stop1 stop
|
|
220
|
+
class Build,Up,Env,Containers runtimeNode
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
This mirrors the [`cds` command table](#️-cli) below: `validate`, `plan`, and `render` are each callable on their own; `security` only runs as part of `cds test`, not `cds up`. Module and contract definitions follow the [Contract-First](#contract-first) design principle, so most of what "Validate" and "Plan" check comes directly from `module.yaml` and `profile.yaml`.
|
|
224
|
+
|
|
225
|
+
**See also:** [Security](#-security) for what the security stage checks, [Troubleshooting](#️-troubleshooting) for what each error code means and how to fix it, and [docs/architecture.md § Secrets and contract resolution](docs/architecture.md#secrets-and-contract-resolution) for a deeper look at how `secrets.*` and `contractRef` values actually resolve.
|
|
226
|
+
|
|
227
|
+
## 🔐 Security
|
|
228
|
+
|
|
229
|
+
CDS includes built-in security validation to prevent unsafe configurations before a stack is deployed.
|
|
230
|
+
|
|
231
|
+
The `cds security` checks analyze profiles and modules for common risks such as:
|
|
232
|
+
|
|
233
|
+
- weak or default passwords
|
|
234
|
+
- missing secret configurations
|
|
235
|
+
- insecure service exposure
|
|
236
|
+
- unsafe defaults in module configuration
|
|
237
|
+
- incomplete contract bindings that may leak data
|
|
238
|
+
|
|
239
|
+
Security checks run as part of validation and can be extended with custom rules.
|
|
240
|
+
|
|
241
|
+
### Example
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
cds security local-dagster-postgres-superset
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## 📦 What You Get
|
|
250
|
+
|
|
251
|
+
When you run CDS:
|
|
252
|
+
|
|
253
|
+
- validated module graph
|
|
254
|
+
- resolved contract bindings
|
|
255
|
+
- dependency-aware execution plan
|
|
256
|
+
- generated Docker Compose configuration
|
|
257
|
+
- reproducible stack definition
|
|
258
|
+
|
|
259
|
+
This allows you to go from a declarative profile to a runnable local data stack.
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## ✅ Prerequisites
|
|
264
|
+
|
|
265
|
+
CDS has separate compile-time and runtime requirements.
|
|
266
|
+
|
|
267
|
+
### Validate, Plan, And Render
|
|
268
|
+
|
|
269
|
+
The commands `cds validate`, `cds security`, `cds plan`, and `cds render`
|
|
270
|
+
do not require Docker. Install:
|
|
271
|
+
|
|
272
|
+
- Python 3.14 or newer, including `pip` and `venv`
|
|
273
|
+
- Git when cloning and installing CDS from source
|
|
274
|
+
- Internet access for the initial source and Python dependency downloads
|
|
275
|
+
|
|
276
|
+
### Build And Run A Stack
|
|
277
|
+
|
|
278
|
+
The `cds up` command and generated local profiles additionally require:
|
|
279
|
+
|
|
280
|
+
- Docker Engine (Linux) or Docker Desktop (macOS and Windows)
|
|
281
|
+
- Docker Compose v2, invoked as `docker compose`
|
|
282
|
+
- A running Docker daemon accessible to the current user
|
|
283
|
+
- Internet access for the initial container image pulls and builds, unless all
|
|
284
|
+
required artifacts are already cached
|
|
285
|
+
- Free host ports required by the selected profile
|
|
286
|
+
- Write access to the checkout for `.env`, `docker-compose.yml`, and workdir
|
|
287
|
+
data
|
|
288
|
+
|
|
289
|
+
Docker Desktop on Windows must use the WSL 2 backend. See the
|
|
290
|
+
[support policy](docs/support-policy.md) for supported operating systems and
|
|
291
|
+
runtime versions. For platform-specific setup from an empty machine, follow
|
|
292
|
+
the [complete installation guide](docs/installation.md).
|
|
293
|
+
|
|
294
|
+
For the complete example stack, allocate at least 8 GB of memory to Docker and
|
|
295
|
+
keep at least 10 GB of disk space free for images, build layers, volumes, and
|
|
296
|
+
logs. Larger workloads require additional resources.
|
|
297
|
+
|
|
298
|
+
### Preflight Check
|
|
299
|
+
|
|
300
|
+
Linux/macOS:
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
python3 --version
|
|
304
|
+
git --version
|
|
305
|
+
docker --version
|
|
306
|
+
docker compose version
|
|
307
|
+
docker info >/dev/null
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
Windows PowerShell:
|
|
311
|
+
|
|
312
|
+
```powershell
|
|
313
|
+
py --version
|
|
314
|
+
git --version
|
|
315
|
+
docker --version
|
|
316
|
+
docker compose version
|
|
317
|
+
docker info | Out-Null
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Python must report version 3.14 or newer, Docker Compose must report v2, and
|
|
321
|
+
`docker info` must complete without a daemon or permission error. Git is
|
|
322
|
+
optional when CDS is installed from a package rather than source.
|
|
323
|
+
|
|
324
|
+
Tools such as `make`, Node.js, and `pre-commit` are contributor tooling and are
|
|
325
|
+
not required to run CDS profiles.
|
|
326
|
+
|
|
327
|
+
## 🚀 Quickstart
|
|
328
|
+
|
|
329
|
+
### CLI package preview
|
|
330
|
+
|
|
331
|
+
The Python distribution contains the `cds` command and its built-in security
|
|
332
|
+
rules. After production PyPI publishing is enabled, install the isolated CLI
|
|
333
|
+
with:
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
pipx install composable-data-stack
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
The package does not yet download profiles, modules, image build contexts, or
|
|
340
|
+
runtime workdirs. Continue with a repository checkout for the reference stack,
|
|
341
|
+
or configure external project assets with `CDS_PROFILE_PATH` and
|
|
342
|
+
`CDS_MODULE_PATH`. See [Packaging](docs/packaging.md) for the current TestPyPI
|
|
343
|
+
flow.
|
|
344
|
+
|
|
345
|
+
### 1. Clone
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
git clone https://github.com/RonaldHensbergen/composable-data-stack.git
|
|
349
|
+
cd composable-data-stack
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### 2. Setup Environment
|
|
353
|
+
|
|
354
|
+
Linux/macOS:
|
|
355
|
+
|
|
356
|
+
```bash
|
|
357
|
+
python3 -m venv .venv
|
|
358
|
+
source .venv/bin/activate
|
|
359
|
+
pip install -e .
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
Windows PowerShell:
|
|
363
|
+
|
|
364
|
+
```powershell
|
|
365
|
+
py -m venv .venv
|
|
366
|
+
.\.venv\Scripts\Activate.ps1
|
|
367
|
+
python -m pip install -e .
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Windows CMD:
|
|
371
|
+
|
|
372
|
+
```bat
|
|
373
|
+
py -m venv .venv
|
|
374
|
+
.venv\Scripts\activate.bat
|
|
375
|
+
python -m pip install -e .
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
If PowerShell blocks the activation script, run
|
|
379
|
+
`Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass` in the same
|
|
380
|
+
terminal session and activate the environment again.
|
|
381
|
+
|
|
382
|
+
### 3. Configure Environment
|
|
383
|
+
|
|
384
|
+
```bash
|
|
385
|
+
cds init local-dagster-postgres-superset
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
Set:
|
|
389
|
+
|
|
390
|
+
```text
|
|
391
|
+
CDS_ANALYTICS_DB_NAME
|
|
392
|
+
CDS_ANALYTICS_DB_USER
|
|
393
|
+
CDS_DAGSTER_DB_NAME
|
|
394
|
+
CDS_DAGSTER_DB_USER
|
|
395
|
+
CDS_SUPERSET_DB_NAME
|
|
396
|
+
CDS_SUPERSET_DB_USER
|
|
397
|
+
CDS_POSTGRES_SUPERUSER_PASSWORD
|
|
398
|
+
CDS_ANALYTICS_DB_PASSWORD
|
|
399
|
+
CDS_DAGSTER_DB_PASSWORD
|
|
400
|
+
CDS_SUPERSET_DB_PASSWORD
|
|
401
|
+
CDS_SUPERSET_SECRET_KEY
|
|
402
|
+
CDS_SUPERSET_ADMIN_PASSWORD
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### 4. Validate A Stack
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
cds validate local-dagster-postgres-superset
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
Expected output:
|
|
412
|
+
|
|
413
|
+
```text
|
|
414
|
+
Profile is valid.
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### 5. Check Runtime Prerequisites
|
|
418
|
+
|
|
419
|
+
```bash
|
|
420
|
+
cds preflight local-dagster-postgres-superset
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
This checks the runtime CLI and daemon, Compose support, required environment
|
|
424
|
+
values, and declared host ports without starting services. `cds init` only
|
|
425
|
+
creates configuration, while `cds validate` checks profile structure.
|
|
426
|
+
|
|
427
|
+
### 6. Run Security Checks
|
|
428
|
+
|
|
429
|
+
```bash
|
|
430
|
+
cds security local-dagster-postgres-superset
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### 7. Generate A Plan
|
|
434
|
+
|
|
435
|
+
```bash
|
|
436
|
+
cds plan local-dagster-postgres-superset
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
This resolves:
|
|
440
|
+
|
|
441
|
+
- module dependencies
|
|
442
|
+
- contract bindings
|
|
443
|
+
- execution order
|
|
444
|
+
|
|
445
|
+
### 8. Render The Stack
|
|
446
|
+
|
|
447
|
+
```bash
|
|
448
|
+
cds render local-dagster-postgres-superset
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
By default, this writes `docker-compose.yml` to the project root.
|
|
452
|
+
|
|
453
|
+
Use a custom location when needed:
|
|
454
|
+
|
|
455
|
+
```bash
|
|
456
|
+
cds render local-dagster-postgres-superset --output build/docker-compose.yml
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
This generates:
|
|
460
|
+
|
|
461
|
+
- docker-compose.yml
|
|
462
|
+
- service definitions
|
|
463
|
+
- fully wired module configuration
|
|
464
|
+
|
|
465
|
+
### 9. Run The Stack
|
|
466
|
+
|
|
467
|
+
```bash
|
|
468
|
+
cds up local-dagster-postgres-superset
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
This runs `validate` → `plan` → `render` → `docker compose build` → `docker compose up` in one step.
|
|
472
|
+
|
|
473
|
+
`docker compose build` output is streamed to the terminal (grouped by image) and also persisted to a log file (default: `.cds/logs/up-<profile>-<timestamp>.log`; override with `--log-file`). `docker compose up` always runs detached: in the default mode its output goes only to the log file while the terminal shows the live state view; with `--detach` it is streamed to the terminal like the build output. Once the stack starts, `cds up` polls `docker compose ps` and redraws the same grouped/colored view as `cds state` until every service is healthy/running/exited or a timeout is hit (default 180s; override with `--timeout`; disable colored labels with `--no-color`). Container logs from that point on go only to the log file, not the terminal, since the terminal is showing the live state view. `cds up` exits `0` once the stack settles, or `1` on timeout or an unhealthy service.
|
|
474
|
+
|
|
475
|
+
Add `--detach` (or `-d`) to skip the live state view and return as soon as the stack starts, e.g. for scripting or CI:
|
|
476
|
+
|
|
477
|
+
```bash
|
|
478
|
+
cds up local-dagster-postgres-superset --detach
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
Use `--no-build` to skip the build step when images are already available:
|
|
482
|
+
|
|
483
|
+
```bash
|
|
484
|
+
cds up local-dagster-postgres-superset --no-build
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### 9. Persistent Incoming Data Folder For Dagster
|
|
488
|
+
|
|
489
|
+
The Dagster module mounts a host directory into the containers so incoming files survive reboots.
|
|
490
|
+
|
|
491
|
+
- Host path: `workdirs/shared-data/incoming`
|
|
492
|
+
- Container path: `/app/data/cds/incoming`
|
|
493
|
+
|
|
494
|
+
Dagster includes a sensor that detects new files in `/app/data/cds/incoming` and runs a pickup job.
|
|
495
|
+
Picked files are moved to:
|
|
496
|
+
|
|
497
|
+
- Host path: `workdirs/shared-data/processed`
|
|
498
|
+
- Container path: `/app/data/cds/processed`
|
|
499
|
+
|
|
500
|
+
Create the directories once if they do not exist:
|
|
501
|
+
|
|
502
|
+
```bash
|
|
503
|
+
mkdir -p workdirs/shared-data/incoming workdirs/shared-data/processed
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
---
|
|
507
|
+
|
|
508
|
+
## 🧩 Core Concepts
|
|
509
|
+
|
|
510
|
+
### Modules
|
|
511
|
+
|
|
512
|
+
Reusable building blocks:
|
|
513
|
+
|
|
514
|
+
- orchestration (Dagster, Airflow)
|
|
515
|
+
- warehouse (Postgres, MariaDB)
|
|
516
|
+
- BI (Superset, Metabase)
|
|
517
|
+
- secrets (env, vault)
|
|
518
|
+
|
|
519
|
+
Structure:
|
|
520
|
+
|
|
521
|
+
```text
|
|
522
|
+
modules/<category>/<name>/
|
|
523
|
+
├── module.yaml
|
|
524
|
+
├── defaults.yaml
|
|
525
|
+
├── compose.yaml
|
|
526
|
+
├── scripts/
|
|
527
|
+
└── tests/
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### Contracts
|
|
531
|
+
|
|
532
|
+
Contracts define how modules interact.
|
|
533
|
+
|
|
534
|
+
Examples:
|
|
535
|
+
|
|
536
|
+
|Contract|Purpose|
|
|
537
|
+
|---|---|
|
|
538
|
+
|sql-database|database interface|
|
|
539
|
+
|http-service|service exposure|
|
|
540
|
+
|secrets-provider|secret resolution|
|
|
541
|
+
|
|
542
|
+
Example binding:
|
|
543
|
+
|
|
544
|
+
```text
|
|
545
|
+
dagster.database -> postgres.sql-database
|
|
546
|
+
superset.database -> postgres.sql-database
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
No implicit dependencies. Everything is explicit.
|
|
550
|
+
|
|
551
|
+
### Profiles
|
|
552
|
+
|
|
553
|
+
Profiles define supported stacks:
|
|
554
|
+
|
|
555
|
+
```text
|
|
556
|
+
local-dagster-postgres-superset
|
|
557
|
+
local-airflow-postgres-superset
|
|
558
|
+
integration-airflow-postgres-dbt
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
Structure:
|
|
562
|
+
|
|
563
|
+
```text
|
|
564
|
+
profiles/[profile]/
|
|
565
|
+
├── profile.yaml
|
|
566
|
+
├── values.yaml
|
|
567
|
+
└── README.md
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
### Environment Overlays
|
|
571
|
+
|
|
572
|
+
A profile can be promoted from local development through staging to
|
|
573
|
+
production without duplicating its configuration. Add an `environments/`
|
|
574
|
+
directory next to `profile.yaml` containing one YAML file per environment;
|
|
575
|
+
each file is merged over the base profile when `--environment <name>` is
|
|
576
|
+
passed to a profile-consuming command:
|
|
577
|
+
|
|
578
|
+
```text
|
|
579
|
+
profiles/[profile]/
|
|
580
|
+
├── profile.yaml
|
|
581
|
+
└── environments/
|
|
582
|
+
├── dev.yaml
|
|
583
|
+
└── prod.yaml
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
An overlay only needs to declare the values that differ from the base
|
|
587
|
+
profile. Module entries are merged by their stable `id` (not array
|
|
588
|
+
position), so an overlay can override just one field of one module:
|
|
589
|
+
|
|
590
|
+
```yaml
|
|
591
|
+
# profiles/[profile]/environments/prod.yaml
|
|
592
|
+
metadata:
|
|
593
|
+
environment: production # promotes the profile's security classification
|
|
594
|
+
|
|
595
|
+
spec:
|
|
596
|
+
modules:
|
|
597
|
+
- id: postgres
|
|
598
|
+
config:
|
|
599
|
+
storage:
|
|
600
|
+
size: 20Gi
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
```bash
|
|
604
|
+
cds validate my-profile --environment prod
|
|
605
|
+
cds plan my-profile --environment prod
|
|
606
|
+
cds test my-profile --environment prod
|
|
607
|
+
cds up my-profile --environment prod
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
`local-dagster-postgres-superset` ships a working `dev`/`prod` example under
|
|
611
|
+
`profiles/local-dagster-postgres-superset/environments/` you can inspect or
|
|
612
|
+
copy.
|
|
613
|
+
|
|
614
|
+
Setting `metadata.environment` in an overlay also changes which security
|
|
615
|
+
policy `cds security`/`cds test` applies (see [Security](#-security)) —
|
|
616
|
+
promoting to `production` enables stricter checks, so a profile that passes
|
|
617
|
+
locally may report new findings once resolved with `--environment prod`.
|
|
618
|
+
That's expected: it surfaces settings that are fine for local development but
|
|
619
|
+
unsafe to carry into production.
|
|
620
|
+
|
|
621
|
+
Compare what an overlay actually changes with `cds diff`, without ever
|
|
622
|
+
printing a secret value (profiles only ever hold secret *references*, never
|
|
623
|
+
resolved values):
|
|
624
|
+
|
|
625
|
+
```bash
|
|
626
|
+
cds diff my-profile --from dev --to prod
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
```text
|
|
630
|
+
Differences from 'dev' to 'prod':
|
|
631
|
+
|
|
632
|
+
~ metadata.environment: "development" -> "production"
|
|
633
|
+
~ spec.modules[postgres].config.storage.size: "2Gi" -> "20Gi"
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
Commands without `--environment` are unaffected — they behave exactly as
|
|
637
|
+
before and never look for an `environments/` directory.
|
|
638
|
+
|
|
639
|
+
---
|
|
640
|
+
|
|
641
|
+
## ⚙️ CLI
|
|
642
|
+
|
|
643
|
+
|Command|Description|
|
|
644
|
+
|---|---|
|
|
645
|
+
|cds init [profile]|Generate a project `.env` template from profile secret definitions|
|
|
646
|
+
|cds validate [profile]|Validate modules and contracts|
|
|
647
|
+
|cds preflight [profile]|Check runtime tools, required environment values, and host ports without starting services|
|
|
648
|
+
|cds plan [profile]|Resolve dependencies and generate an execution plan|
|
|
649
|
+
|cds render [profile]|Generate Docker Compose configuration from a resolved plan|
|
|
650
|
+
|cds up [profile]|Validate, plan, render, build, and start services with docker compose; logs output to a file and shows a live `cds state` view until the stack settles (use `--no-build` to skip build, `--detach` to skip the live view, `--log-file`/`--timeout`/`--no-color` to override defaults)|
|
|
651
|
+
|cds state [profile]|Show running service status grouped by health (use `--no-color` to disable colored labels)|
|
|
652
|
+
|cds test [profile]|One-shot smoke validation: validate, security, plan, and render|
|
|
653
|
+
|cds security [profile]|Run rule-based security validation on a profile|
|
|
654
|
+
|cds diff [profile] --from \<env\> --to \<env\>|Show effective configuration differences between two environment overlays, secrets never included|
|
|
655
|
+
|cds use [profile] [--clear]|Save (show/clear) a default profile so it doesn't have to be passed to other commands|
|
|
656
|
+
|cds completion \<bash\|zsh\|powershell\>|Print shell setup instructions for tab-completion|
|
|
657
|
+
|
|
658
|
+
`init`, `validate`, `preflight`, `plan`, `render`, `up`, `test`, and `security`
|
|
659
|
+
all accept `--environment <name>` (or `-e <name>`) to merge
|
|
660
|
+
`environments/<name>.yaml` over the base profile before resolving; see
|
|
661
|
+
[Environment Overlays](#environment-overlays).
|
|
662
|
+
|
|
663
|
+
`[profile]` accepts:
|
|
664
|
+
|
|
665
|
+
| Form | Example |
|
|
666
|
+
| ---- | ------- |
|
|
667
|
+
| Profile name | `local-dagster-postgres-superset` |
|
|
668
|
+
| Path to a `profile.yaml` file | `profiles/local-dagster-postgres-superset/profile.yaml` |
|
|
669
|
+
| Path to a profiles root directory | `profiles/` |
|
|
670
|
+
|
|
671
|
+
When `[profile]` is omitted, resolution falls back in order to: `CDS_PROFILE_PATH` if set (accepts the same three forms), then the default profile saved via `cds use <profile>`, then the single profile under `profiles/` if there is exactly one. An explicitly-set env var takes precedence over the persisted `cds use` default, matching common CLI convention (env vars are per-invocation and reflect the current session more reliably than a saved, gitignored default that's easy to forget about).
|
|
672
|
+
|
|
673
|
+
To view the full list of options for any command, use the `--help` flag:
|
|
674
|
+
|
|
675
|
+
```bash
|
|
676
|
+
cds --help
|
|
677
|
+
cds validate --help
|
|
678
|
+
cds plan --help
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
---
|
|
682
|
+
|
|
683
|
+
## 🪟 Windows Task Runner
|
|
684
|
+
|
|
685
|
+
Windows contributors without `make` can use `Makefile.ps1`, a PowerShell equivalent covering the core developer tasks:
|
|
686
|
+
|
|
687
|
+
```powershell
|
|
688
|
+
# Install in editable mode
|
|
689
|
+
.\Makefile.ps1 install
|
|
690
|
+
|
|
691
|
+
# Validate the default profile
|
|
692
|
+
.\Makefile.ps1 validate
|
|
693
|
+
|
|
694
|
+
# Validate a specific profile
|
|
695
|
+
.\Makefile.ps1 validate-profile -P profiles/local-dagster-postgres-superset/profile.yaml
|
|
696
|
+
|
|
697
|
+
# Build distribution packages
|
|
698
|
+
.\Makefile.ps1 package
|
|
699
|
+
|
|
700
|
+
# List available targets
|
|
701
|
+
.\Makefile.ps1 help
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
This does not replace the Linux/macOS `Makefile`, both exist side by side. Windows users can still install `make` via WSL or Chocolatey if they prefer the original workflow. `lint` and `docker-build` are not ported here. Run `yamllint .` and `npx markdownlint-cli` directly, or use `pre-commit` if it is set up in this repo. Docker Desktop's `docker build` works the same on Windows as it does elsewhere.
|
|
705
|
+
|
|
706
|
+
---
|
|
707
|
+
|
|
708
|
+
## 🛠️ Troubleshooting
|
|
709
|
+
|
|
710
|
+
Common errors from `cds validate`, `cds plan`, and `cds render`, and how to fix them.
|
|
711
|
+
|
|
712
|
+
| Error | Cause | Fix |
|
|
713
|
+
| --- | --- | --- |
|
|
714
|
+
| `[E020] ... YAML file not found: <path>` | The profile identifier or file path passed to `cds validate`, `cds plan`, or `cds render <profile>` doesn't resolve to an existing YAML file. | Run `cds list profiles` to see valid identifiers. Set `CDS_PROFILE_PATH` to a profile name, a `profile.yaml` file path, or a profiles root directory. |
|
|
715
|
+
| `[E081] ... Required secret "CDS_X_PASSWORD" not found in environment` | A secret marked `required: true` in the profile's `spec.secrets.values` is missing from the shell environment or the `.env` file in the current working directory. | Run `cds init <profile>` to generate `.env` in the project root, set the missing `CDS_*` variable, or export it directly before running the command. |
|
|
716
|
+
| `[E041] ... Contract ref "x.y" points to unknown module "x"` | A `consumes` binding's `contractRef` refers to a module ID that isn't defined in the profile. | Check `spec.modules` for the correct module `id`, and confirm the contract ref follows `<module-id>.<contract-name>`. |
|
|
717
|
+
| `[E041] ... but it does not provide "<contract-name>"` | The referenced module exists, but its `spec.provides` list doesn't expose that contract name. | Check the producing module's `module.yaml` for the contracts it actually provides, and fix the consumer's `contractRef` to match. |
|
|
718
|
+
| `[E042] ... Contract kind mismatch` | The consumer expects one contract kind (e.g. `sql-database`) but the producer exposes a different kind. | Point the binding at a module that provides the expected contract kind, or update the consumer's expected kind if the mismatch is intentional. |
|
|
719
|
+
|
|
720
|
+
All diagnostics print with their error code and YAML path (e.g. `spec.modules[1].config`), so search the profile file for that path to find the exact line to fix.
|
|
721
|
+
|
|
722
|
+
---
|
|
723
|
+
|
|
724
|
+
## 🔄 Workflow
|
|
725
|
+
|
|
726
|
+
```text
|
|
727
|
+
1. cds validate -> check module definitions
|
|
728
|
+
2. cds security -> detect unsafe configurations
|
|
729
|
+
3. cds plan -> resolve dependencies and bindings
|
|
730
|
+
4. cds render -> generate Docker Compose stack
|
|
731
|
+
5. cds up -> start services
|
|
732
|
+
6. cds test -> one-shot validate + security + plan + render smoke check
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
---
|
|
736
|
+
|
|
737
|
+
## 📂 Repository Structure
|
|
738
|
+
|
|
739
|
+
```text
|
|
740
|
+
.
|
|
741
|
+
├── cli/
|
|
742
|
+
├── modules/
|
|
743
|
+
│ ├── bi/
|
|
744
|
+
│ ├── orchestration/
|
|
745
|
+
│ ├── secrets/
|
|
746
|
+
│ └── warehouse/
|
|
747
|
+
├── profiles/
|
|
748
|
+
├── docs/
|
|
749
|
+
├── pyproject.toml
|
|
750
|
+
└── Makefile
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
---
|
|
754
|
+
|
|
755
|
+
## 🧱 Design Principles
|
|
756
|
+
|
|
757
|
+
### Contract-First
|
|
758
|
+
|
|
759
|
+
Modules declare:
|
|
760
|
+
|
|
761
|
+
- what they provide
|
|
762
|
+
- what they require
|
|
763
|
+
- configuration inputs
|
|
764
|
+
- health checks
|
|
765
|
+
- lifecycle hooks
|
|
766
|
+
|
|
767
|
+
### Profile-Driven
|
|
768
|
+
|
|
769
|
+
Profiles define supported stacks.
|
|
770
|
+
The profile is the unit of support, not individual modules.
|
|
771
|
+
|
|
772
|
+
### Zero Hidden Coupling
|
|
773
|
+
|
|
774
|
+
- no implicit environment variables
|
|
775
|
+
- no cross-module assumptions
|
|
776
|
+
- no shared mutable state
|
|
777
|
+
|
|
778
|
+
All interactions happen through explicit contracts.
|
|
779
|
+
|
|
780
|
+
### Security By Default
|
|
781
|
+
|
|
782
|
+
CDS validates configurations before runtime, ensuring that:
|
|
783
|
+
|
|
784
|
+
- weak credentials are detected early
|
|
785
|
+
- secrets are properly configured
|
|
786
|
+
- services are not unintentionally exposed
|
|
787
|
+
|
|
788
|
+
Security is part of platform composition, not an afterthought.
|
|
789
|
+
|
|
790
|
+
### One Model, Multiple Environments
|
|
791
|
+
|
|
792
|
+
The same composition model applies across:
|
|
793
|
+
|
|
794
|
+
- local development
|
|
795
|
+
- CI environments
|
|
796
|
+
- production
|
|
797
|
+
|
|
798
|
+
Only runtime packaging differs.
|
|
799
|
+
|
|
800
|
+
---
|
|
801
|
+
|
|
802
|
+
## 📊 Comparison
|
|
803
|
+
|
|
804
|
+
|Capability|Monolith|Custom pipelines|CDS|
|
|
805
|
+
|---|---|---|---|
|
|
806
|
+
|Swap components|❌|⚠️|✅|
|
|
807
|
+
|Reuse modules|❌|❌|✅|
|
|
808
|
+
|Explicit contracts|❌|❌|✅|
|
|
809
|
+
|Reproducibility|⚠️|⚠️|✅|
|
|
810
|
+
|Security validation|❌|❌|✅|
|
|
811
|
+
|Vendor lock-in|✅|⚠️|❌|
|
|
812
|
+
|
|
813
|
+
---
|
|
814
|
+
|
|
815
|
+
## 📌 Status
|
|
816
|
+
|
|
817
|
+
MVP ready:
|
|
818
|
+
|
|
819
|
+
- module validation
|
|
820
|
+
- contract resolution
|
|
821
|
+
- security checks
|
|
822
|
+
- profile composition
|
|
823
|
+
- Docker Compose rendering
|
|
824
|
+
|
|
825
|
+
Next:
|
|
826
|
+
|
|
827
|
+
- runtime orchestration
|
|
828
|
+
- Kubernetes support
|
|
829
|
+
- advanced secret providers
|
|
830
|
+
- stack bootstrap and health checks
|
|
831
|
+
|
|
832
|
+
See [docs/roadmap.md](docs/roadmap.md) for milestones and detailed status.
|
|
833
|
+
See [docs/support-policy.md](docs/support-policy.md) for OS support policy and platform-specific limitations.
|
|
834
|
+
|
|
835
|
+
---
|
|
836
|
+
|
|
837
|
+
## 🤝 Contributing
|
|
838
|
+
|
|
839
|
+
Contributions are welcome.
|
|
840
|
+
|
|
841
|
+
Please read these first:
|
|
842
|
+
|
|
843
|
+
- [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
844
|
+
- [docs/maintainer-merge-policy.md](docs/maintainer-merge-policy.md)
|
|
845
|
+
- [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)
|
|
846
|
+
- [SECURITY.md](SECURITY.md)
|
|
847
|
+
- [SUPPORT.md](SUPPORT.md)
|
|
848
|
+
- [CHANGELOG.md](CHANGELOG.md)
|
|
849
|
+
- [RELEASE.md](RELEASE.md)
|
|
850
|
+
|
|
851
|
+
Good first contributions:
|
|
852
|
+
|
|
853
|
+
- adding new modules
|
|
854
|
+
- improving profile examples
|
|
855
|
+
- extending contract definitions
|
|
856
|
+
- adding validation or security rules
|
|
857
|
+
|
|
858
|
+
---
|
|
859
|
+
|
|
860
|
+
## 📖 Documentation
|
|
861
|
+
|
|
862
|
+
- [Quickstart](README.md#-quickstart) — get running in 5 minutes
|
|
863
|
+
- [From Docker Compose to CDS Profile](docs/from-docker-to-cds-profile.md) — complete transformation guide
|
|
864
|
+
- [Architecture](docs/architecture.md) — design and core concepts
|
|
865
|
+
- [Modules](docs/modules.md) — how to structure reusable components
|
|
866
|
+
- [Roadmap](docs/roadmap.md) — planned features and milestones
|
|
867
|
+
|
|
868
|
+
---
|
|
869
|
+
|
|
870
|
+
## 📜 License
|
|
871
|
+
|
|
872
|
+
See `LICENSE`.
|