Skip to main content

Architecture

The build is a straight pipeline. Every stage is a plain module with a readable role; nothing is hidden behind frameworks.

schema.dbml ──▶ model/parser ─┐
mapping.yaml ─▶ model/mapping_loader ─┼─▶ analysis/ validator,
release.yaml ─▶ model/release ────────┘ ordering, typemap, phases,
permissions, styles
┌────────────────────────┘

generators/ jsgen · rollbackgen · assessgen ·
demogen · manifestgen · reportgen (one artifact family each)


bundle.emit_bundle() — the ONE emission sequence:
stale clearing, INDEX.md, checksums.txt, provenance

Repository map

One module per concern, grouped into layer packages that mirror the pipeline; the packaging spine sits at the package root:

LayerModulesResponsibility
model/parser · mapping_loader · releaseParse DBML, the mapping YAML (+ enums/retention), release.yaml into typed objects
analysis/validator · ordering · typemap · phases · permissions · stylesBuild-time rules (fail-closed), dependency ordering, SP type/formatter/permission projections
generators/jsgen · rollbackgen · assessgen · demogen · manifestgen · reportgenEach renders one artifact family from model + analysis
rootbundle · templating · cli · extensionThe one emission sequence (emit_bundle), stale clearing, INDEX/checksums, the shared Jinja env, the CLI, the extension protocol

Data flows strictly downward — model knows nothing of analysis, analysis nothing of generators — and the root modules orchestrate. The generated API reference documents each module's public surface, organised the same way.

Templates mirror the layout

templates/*.js.j2 are the pasteable entry-point scripts; templates/_*.js.j2 are shared partials (provenance header, site guard, HTTP transport, write headers, cached digest) included by all of them; templates/deploy/_*.js.j2 are deploy.js's phase bodies. Each template opens with a contract comment — extracted verbatim into the template reference.

A shared partial exists only when every including script needs it identically (identity, guard, digest, HTTP transport). Phase and domain logic stays with its phase — see the development philosophy for why that line is drawn where it is.

The phases manifest

analysis/phases.py is the single source of phase truth. Group and step numbers derive from position in DEPLOY_GROUPS; add or move a step and every consumer renumbers automatically — deploy.js banners, [Phase X.Y] error tags, the manifest's phase references, and test expectations. Reference steps by name or key, never by number.

The extension protocol

Organisation-specific behaviour (identity seeding, classification projection, per-site policy) stays out of the core. Implement DeploymentExtension (hooks: expand_column, seed_lists, extra_validators, manifest_extras) in your own package and register it under the dbml_sharepoint.extensions entry-point group:

[project.entry-points."dbml_sharepoint.extensions"]
my_org = "my_org_deployer.extension:MyOrgExtension"

The core CLI resolves --extension my_org; or ship your own thin CLI that composes the core pipeline programmatically — clear_generated → validate → manifest → emit_bundle — rather than re-implementing emission. The composition points are public functions; see the bundle and cli API pages.