|
1 | | -<div align="center"> |
2 | | - |
3 | 1 | # OTF Release |
4 | 2 |
|
5 | | -</div> |
6 | | - |
7 | | -<div align="right"> |
8 | | - |
9 | | -Part of Open Tech Foundation ecosystem. |
10 | | - |
11 | | -</div> |
| 3 | +> Manual-bump, changelog-aware release CLI for polyglot monorepos. |
12 | 4 |
|
13 | | -> Curated-changelog, manual-bump release CLI for polyglot monorepos. |
| 5 | +`otf-release` is a single Rust binary that helps a repo move from curated release notes to a |
| 6 | +release PR, then to CI-driven publishing. It supports npm workspaces, Cargo workspaces, and |
| 7 | +generic manifest-based packages through a committed `release.toml`. |
14 | 8 |
|
15 | | -A single-binary release tool for polyglot monorepos. You write your release notes in each package's `[Unreleased]` changelog section and pick the bumps — `otf-release` handles the rest: dependency-aware version cascades, topological publishing across multiple ecosystems (**npm, cargo, generic/JSR**), and a matrix-gated cross-platform GitHub release via a single generated `release.yml`. |
| 9 | +The core rule is simple: humans choose what to release and how much to bump; the tool handles |
| 10 | +dependency cascades, manifest edits, changelog updates, tags, publishing order, and generated |
| 11 | +GitHub workflows. |
16 | 12 |
|
17 | | -Unlike commit-driven tools, your hand-written notes are the strict source of truth. |
| 13 | +## ⚙️ Installation |
18 | 14 |
|
19 | | -## Installation |
| 15 | +**macOS / Linux** |
20 | 16 |
|
21 | | -You can easily install `otf-release` using our automated installation scripts: |
22 | | - |
23 | | -**macOS / Linux:** |
24 | 17 | ```bash |
25 | 18 | curl -fsSL https://raw.githubusercontent.com/Open-Tech-Foundation/release/main/install.sh | bash |
26 | 19 | ``` |
27 | 20 |
|
28 | | -**Windows (PowerShell):** |
| 21 | +**Windows PowerShell** |
| 22 | + |
29 | 23 | ```powershell |
30 | 24 | irm https://raw.githubusercontent.com/Open-Tech-Foundation/release/main/install.ps1 | iex |
31 | 25 | ``` |
32 | 26 |
|
33 | | -Alternatively, you can compile from source using Cargo: |
| 27 | +**From source** |
| 28 | + |
34 | 29 | ```bash |
35 | 30 | cargo install --git https://github.com/Open-Tech-Foundation/release |
36 | 31 | ``` |
37 | 32 |
|
38 | | -## Commands |
39 | | - |
40 | | -| Command | Usage | Description | |
41 | | -|---------|-------|-------------| |
42 | | -| **`init`** | `otf-release init` | Interactive setup: configure ecosystems, build matrices, and artifacts. Generates `release.toml` and `release.yml`. | |
43 | | -| **`version`** | `otf-release version` | Interactive local release: choose bumps, cascade dependencies, write changelogs, and automatically open a Release PR. | |
44 | | -| **`publish`** | `otf-release publish` | Non-interactive CI flow: publishes changed packages in topological order, attaching staged build artifacts. | |
45 | | -| **`config`** | `otf-release config` | Interactively edit your `release.toml` file without manually typing out OS architecture strings or workflow targets. | |
46 | | -| **`snapshot`** | `otf-release snapshot` | Non-interactive CI flow: completely automates an ephemeral snapshot release powered by a short git hash (e.g. `1.0.0-snapshot.a1b2c3d`) | |
47 | | -| **`self-update`** | `otf-release self-update` | Updates your local `otf-release` binary to the latest version published on GitHub Releases. | |
48 | | -| **`upgrade`** | `otf-release upgrade` | Upgrades your local `release.toml` and regenerates your CI pipeline to match the latest CLI version features. | |
49 | | - |
50 | | -## Workflow |
| 33 | +## 🧭 Command Surface |
| 34 | + |
| 35 | +| Command | Status | What it does | |
| 36 | +| --- | --- | --- | |
| 37 | +| `otf-release init` | ✅ Supported | Interactive setup. Writes `release.toml`, `.github/workflows/release.yml`, and `.github/workflows/snapshot.yml`. | |
| 38 | +| `otf-release version` | ✅ Supported | Interactive local release flow. Select packages, choose bumps, cascade dependents, update manifests/changelogs, push a release branch, and open a PR. | |
| 39 | +| `otf-release publish` | ✅ Supported | CI-oriented publish flow. Publishes in dependency order, skips already-published versions, creates `name@version` tags, and creates package releases from notes. | |
| 40 | +| `otf-release snapshot` | 🧪 Experimental | Creates hash-based prerelease versions such as `1.2.3-snapshot.a1b2c3d` and publishes them from CI. | |
| 41 | +| `otf-release config` | ◐ Partial | Interactive editor for common `release.toml` fields such as hooks, enabled ecosystems, and build targets. | |
| 42 | +| `otf-release upgrade` | ◐ Partial | Regenerates `release.yml` from the current `release.toml`. | |
| 43 | +| `otf-release self-update` | ✅ Supported | Checks GitHub Releases and reruns the install script when a newer CLI version exists. | |
| 44 | + |
| 45 | +## ✅ Feature Matrix |
| 46 | + |
| 47 | +| Area | Supported now | Notes | |
| 48 | +| --- | --- | --- | |
| 49 | +| npm adapter | ✅ | Discovers npm workspaces, preserves dependency range operators, resolves `workspace:*`, checks `npm view`, publishes with `npm publish --access public --no-workspaces`. | |
| 50 | +| Cargo adapter | ✅ | Discovers Cargo workspaces, supports concrete crate versions and `version.workspace = true`, updates path dependency versions, checks `cargo info`, publishes with `cargo publish -p`. | |
| 51 | +| Generic adapter | ✅ | Versions a configured manifest field and optionally runs a configured publish command for registries such as JSR. Idempotency is tag-based. | |
| 52 | +| Polyglot versioning | ✅ | `version` runs as one release transaction across all enabled adapters. | |
| 53 | +| Polyglot publishing | ✅ | `publish` loops enabled adapters and publishes each ecosystem in dependency order. | |
| 54 | +| Dependency cascades | ✅ | Adapter-owned rules. npm peer dependencies mirror the dependency bump; normal deps patch dependents. Cargo/generic dependents patch. | |
| 55 | +| Private packages/apps | ✅ | Never versioned or published; internal ranges are still updated so apps remain buildable. | |
| 56 | +| Curated changelog mode | ✅ | Uses each package's `[Unreleased]` section as the release-note source. | |
| 57 | +| Generated changelog mode | ✅ | Builds notes from git commit messages since the last package tag and prepends generated notes to `CHANGELOG.md`. | |
| 58 | +| Prereleases | ✅ | Supports stable bumps, channel entry (`alpha`, `beta`, `rc`), channel iteration, channel switching, and graduation to stable. | |
| 59 | +| Build-only packages | ✅ | CI can build artifacts and attach them to a GitHub Release instead of publishing to a registry. | |
| 60 | +| Lifecycle hooks | ✅ | `pre_version`, `post_version`, `pre_publish`, and `post_publish` run from `release.toml`. | |
| 61 | +| GitHub workflow generation | ✅ | Generates release and snapshot workflows from `release.toml`; intended as editable scaffolds. | |
| 62 | +| Git providers | GitHub only | Config has a `provider` field, but only GitHub PR/release behavior is implemented. | |
| 63 | + |
| 64 | +## ⚠️ Known Gaps |
| 65 | + |
| 66 | +| Gap | Impact | |
| 67 | +| --- | --- | |
| 68 | +| `--first-release` is not wired through the version flow yet. | First-release ergonomics are still stricter than the CLI help implies. | |
| 69 | +| `publish` lifecycle hooks run per adapter, not once per full command. | Polyglot repos may run global `pre_publish` / `post_publish` hooks more than intended. | |
| 70 | +| `snapshot` is experimental. | Multi-adapter semantics, generated notes, rollback expectations, and workflow polish need more hardening. | |
| 71 | +| Generated `release.yml` still needs stronger validation. | It is useful scaffolding, but complex monorepos may need hand edits. | |
| 72 | +| Build-only GitHub releases use `vX.Y.Z` while package publish tags use `name@X.Y.Z`. | Independent build-only packages can be awkward when multiple packages release at different versions. | |
| 73 | +| `config` does not edit every `release.toml` field. | Users still need to hand-edit package mode, commands, artifacts, generic fields, provider, snapshot tag, and changelog strategy. | |
| 74 | +| Generic manifest parsing is intentionally simple. | Works for common JSON/TOML-style version fields, but is not a full structured parser. | |
| 75 | +| Only GitHub is implemented. | GitLab, Bitbucket, Gitea, and Codeberg are future work. | |
| 76 | + |
| 77 | +## 🔁 Release Flow |
51 | 78 |
|
52 | 79 | ```mermaid |
53 | 80 | flowchart TD |
54 | | - Init["1️⃣ <b>Init</b><br/>Run <code>otf-release init</code> once to generate configs & CI"] |
55 | | - Curate["2️⃣ <b>Curate</b><br/>Write <code>[Unreleased]</code> notes as you develop"] |
56 | | - PreVersion{"🪝 pre_version"} |
57 | | - Version["3️⃣ <b>Version</b><br/>Run <code>otf-release version</code> to bump & open PR"] |
58 | | - PostVersion{"🪝 post_version"} |
59 | | - Merge["4️⃣ <b>Merge</b><br/>Review & merge the Release PR to <code>main</code>"] |
60 | | - PrePublish{"🪝 pre_publish"} |
61 | | - Publish["5️⃣ <b>Publish</b><br/>CI auto-compiles & publishes artifacts natively"] |
62 | | - PostPublish{"🪝 post_publish"} |
63 | | -
|
64 | | - Init --> Curate --> PreVersion --> Version --> PostVersion --> Merge --> PrePublish --> Publish --> PostPublish |
65 | | -``` |
66 | | - |
67 | | -## Changelog Strategies |
68 | | - |
69 | | -During `otf-release init`, you can select how you want to manage your release notes via the `changelog_strategy` configuration. |
70 | | - |
71 | | -- **Curated (Default):** You manually write release notes inside an `[Unreleased]` section within each package's `CHANGELOG.md` as you develop. `otf-release` acts as a strict curator, consuming these exact notes for the release. |
72 | | -- **Generated:** `otf-release` automatically reads the git commit history since the last package tag and generates a list of commit messages. These generated notes are used in the Release PR and automatically prepended to the `CHANGELOG.md` upon release. |
73 | | - |
74 | | -## Pre-releases |
75 | | - |
76 | | -When running `otf-release version`, the interactive prompt will first ask you to select a release channel. By default, it uses the **stable** channel. If you select an alternative channel (e.g. `alpha`, `beta`, `rc`), `otf-release` will automatically compute valid semantic pre-release versions for your bumps. |
77 | | - |
78 | | -For example, choosing a `minor` bump on the `beta` channel will transition `1.0.0` into `1.1.0-beta.0`. Once on a pre-release channel, you can select the new `prerelease` bump option to iterate tags (e.g., `beta.0` → `beta.1`). |
79 | | - |
80 | | -## Snapshot Releases |
81 | | - |
82 | | -To avoid polluting your changelog with every single CI run, you can configure an automated `snapshot` workflow. During `otf-release init`, the wizard will ask you for a snapshot tag (like `snapshot`, `canary`, or `edge`). |
83 | | - |
84 | | -This will automatically scaffold a `.github/workflows/snapshot.yml` that triggers on `main` branch pushes. It runs `otf-release snapshot`, which generates short-hash ephemeral versions (like `1.0.0-canary.a1b2c3d`) and automatically bumps your ecosystem boundaries and pushes to registries without touching your tags or PRs. |
85 | | - |
86 | | -## Git Hosting Providers |
87 | | - |
88 | | -During `otf-release init`, you will be prompted to select your Git hosting provider. This determines the format of the Release PRs, release links, and CI workflows generated by the CLI. |
89 | | - |
90 | | -Currently, **GitHub** is fully supported as the default provider. Support for GitLab, Bitbucket, Gitea, and Codeberg is planned and will be available in future releases. Your selection is saved as the `provider` field in `release.toml`. |
91 | | - |
92 | | -## Lifecycle Hooks |
93 | | - |
94 | | -You can define custom shell scripts to run at critical stages of the release process by editing your `release.toml` file. These hooks are executed across all operating systems automatically using your native shell (`sh` on Unix, `powershell` on Windows). |
95 | | - |
96 | | -```toml |
97 | | -[hooks] |
98 | | -# Runs before the interactive version prompt starts (e.g. to validate repo state) |
99 | | -pre_version = ["npm run lint", "node scripts/validate.js"] |
100 | | - |
101 | | -# Runs after versions and changelogs are updated, but BEFORE they are committed |
102 | | -post_version = ["python3 scripts/sync-docs.py"] |
103 | | - |
104 | | -# Runs in CI before the publish loop begins |
105 | | -pre_publish = ["npm run test"] |
106 | | - |
107 | | -# Runs in CI after everything is successfully published |
108 | | -post_publish = ["curl -X POST ..."] |
| 81 | + Init["⚙️ Init<br/>Generate release.toml and workflows"] |
| 82 | + Curate["📝 Prepare notes<br/>Curated CHANGELOG or generated commits"] |
| 83 | + PreVersion{"pre_version hooks"} |
| 84 | + Version["🏷️ Version<br/>Choose bumps, cascade, update files"] |
| 85 | + PostVersion{"post_version hooks"} |
| 86 | + PR["🔍 Release PR<br/>Review and merge"] |
| 87 | + PrePublish{"pre_publish hooks"} |
| 88 | + Publish["🚀 Publish<br/>CI publishes registries and/or artifacts"] |
| 89 | + PostPublish{"post_publish hooks"} |
| 90 | +
|
| 91 | + Init --> Curate --> PreVersion --> Version --> PostVersion --> PR --> PrePublish --> Publish --> PostPublish |
109 | 92 | ``` |
110 | 93 |
|
111 | | -## License |
| 94 | +## 📄 License |
112 | 95 |
|
113 | | -MIT License. See [LICENSE](LICENSE) for details. |
| 96 | +MIT. See [LICENSE](LICENSE). |
0 commit comments