Skip to content

Latest commit

 

History

History
98 lines (78 loc) · 4.01 KB

File metadata and controls

98 lines (78 loc) · 4.01 KB

Architecture

otf-release is a single static binary (Rust) split into a registry-agnostic core and one or more adapters. The core orchestrates a release; an adapter knows how one ecosystem reads manifests, formats version ranges, talks to a registry, and publishes.

Design rules

  1. Core never reads a manifest. No package.json, no Cargo.toml. The core only ever talks to an Adapter. This is what keeps it polyglot.
  2. The adapter owns ecosystem policy, including the cascade rule (dependent_bump) and range syntax (format_range) — not a shared config file.
  3. One committed config. release.toml (written by init) records which adapters are enabled and the per-package build steps — it is the source of truth that version, publish, and the generated release.yml all derive from. Everything else is read from disk (manifests, changelogs, .artifacts/) and the registry/git (tags); no other state is persisted between runs.
  4. Bumps are chosen by a human. Release notes can be curated from [Unreleased] sections or generated from commit history, depending on release.toml.

Crate layout

Cargo.toml                      # workspace
crates/
  core/      opentf-release-core         # ecosystem-agnostic orchestration (lib)
    src/
      lib.rs
      adapter.rs    # Adapter trait + domain types (Pkg, Bump, DepKind, InternalDep)
      graph.rs      # dependency graph: topo sort + bump cascade engine
      changelog.rs  # Keep a Changelog parse/rewrite
      preflight.rs  # strict compliance gate
      summary.rs    # confirmation / dry-run rendering
      version.rs    # `version` command orchestration
      publish.rs    # `publish` command orchestration
      init.rs       # `release.yml` generator
  adapters/  opentf-release-adapters     # registry adapters (lib)
    src/
      lib.rs
      npm/          # npm workspace adapter
      cargo.rs      # Cargo workspace adapter
      generic.rs    # manifest + user-command adapter
  cli/       opentf-release              # binary `otf-release` (clap)
    src/
      main.rs

Dependency direction

cli ──▶ core ◀── adapters
  └───────────────▶ adapters
  • core defines the Adapter trait and all domain types. It depends on nothing internal.
  • adapters depends on core (it implements the trait).
  • cli depends on both: it constructs the concrete adapters and hands them to the core command functions as &dyn Adapter.

This direction means a new adapter can usually be added without touching core.

Domain types

Defined in crates/core/src/adapter.rs:

  • Pkg — a discovered package normalized to ecosystem-agnostic terms (name, version, manifest/changelog paths, publishable flag, internal deps).
  • BumpPatch < Minor < Major, ordered so max() picks the strongest bump when a package is hit by several cascade paths.
  • DepKindDep | PeerDep | DevDep (adapter-specific set; npm-flavored in v1).
  • InternalDep — an edge to another package in the same monorepo, with its declared range.

Data flow

version (local)

discover ─▶ preflight ─▶ prompt ─▶ cascade ─▶ summary/confirm
   ─▶ branch ─▶ apply (versions, ranges, changelogs) ─▶ lockfile
   ─▶ commit ─▶ push ─▶ open PR

publish (CI)

discover ─▶ filter (publishable & !is_published) ─▶ topo sort
   ─▶ for each: resolve_workspace_links ─▶ publish ─▶ tag + GH Release
   (halt on first failure; re-run resumes forward)

See commands/version.md and commands/publish.md for the step-by-step contracts.

Why a workspace (not one crate)

Splitting core from adapters enforces rule #1 at the compiler level: core literally cannot depend on the npm adapter, so it cannot reach into a package.json by accident. The cli crate is the only place that names a concrete adapter.