5. Building Block View
5.1 Level 1: whitebox denver
flowchart TB
cli["denver.py<br/>CLI, config resolution, stage orchestration"]
err["denver_errors.py<br/>DenverError + die()"]
pkg["denver_providers/<br/>registry, Context, one module per provider"]
scripts["conan_scripts/<br/>catalog.py, generate.py"]
tools(["uv · conan · west · docker · git · HTTP · bash"])
cli --> err
pkg --> err
cli -->|imports lazily| pkg
pkg -->|subprocess| tools
pkg -->|subprocess| scripts
scripts --> tools
Building block |
File(s) |
Responsibility |
|---|---|---|
Orchestrator |
|
CLI parsing, config loading and merging, defaults, stage filtering, hooks, running stages, |
Error module |
|
|
Provider package |
|
The registry, |
Conan scripts |
|
Recipe scanning and catalog generation. Not importable, driven only by subprocess |
Examples |
|
Real environments, not framework code |
denver.py imports the provider package lazily, so --help, --version
and completion never pay for importing every provider module.
5.2 Level 2: inside the orchestrator
src/denver.py is one flat module, deliberately free of provider-specific
knowledge apart from one table of default resolvers. What it does for
denver run <env>, in order:
Step |
Function |
What it does |
|---|---|---|
1 |
|
Parse |
2 |
|
Turn |
3 |
|
Follow the |
4 |
|
Config version, |
5 |
|
Section stacking, build the |
6 |
|
Apply |
7 |
|
Hooks, stage env, wrapper relocation or direct run, performance records |
8 |
|
Replace denver’s own process with the final command |
--show-config stops after step 5 and prints. Same function, same result —
that is the point (chapter 4.3).
Details: config resolution.
5.3 Level 2: inside the provider package
Module |
Contents |
|---|---|
|
|
|
|
|
|
|
One provider each |
Details: the provider layer.
5.4 Level 2: Context
Context is the only way a provider touches anything outside itself.
classDiagram
class Context {
+dict env
+dict config
+Path env_dir
+Path cache_dir
+bool force
+bool ci
+bool fast
+int quiet
+bool dry_run
+section(name)
+resolve_path(value)
+venv_dir_for(name)
+run(cmd, ...)
+which(name)
+source(*scripts)
+exec(cmd)
+mkdir(path)
+write_text(path, text)
+rmtree(path)
+set(key, value)
+prepend_path(directory)
+apply_env_map(mapping)
+acquire_lock(wait)
+in_container()
+relocated()
}
class Orchestrator {
denver.py
}
class Provider
Orchestrator ..> Context : builds one per run
Provider ..> Context : its only view of the outside
Context ..> Subprocess : run / exec
Context ..> Filesystem : mkdir / write_text / rmtree
Group |
Members |
|---|---|
Paths |
|
Environment |
|
Config |
|
Processes |
|
Filesystem |
|
Run mode |
|
Concurrency |
|
Everything a --dry-run must intercept, and everything a test must fake,
goes through this one class. No provider imports subprocess or shutil.
5.5 Level 3
The provider layer — registry, lifecycle, each built-in provider, extension providers.
Config resolution — imports, merging, stacking, defaults, overrides.