Library API
monorel ships a Go library API alongside the CLI. The following pure-function packages are public and SemVer-committed from v1.0.0 onward:
| Package | Purpose | Entry points |
|---|---|---|
config | Parse and validate monorel.toml. | Load, Config.Validate, IsKnownProvider |
changeset | Read and write .changeset/*.md files. | LoadAll, Parse, Changeset.WriteFile, RandomName |
plan | Pure-function planner: config + changesets + tags + pre-state → release plan. | Plan |
semver | Bump-level abstraction, version application, initial-release rules. | Apply, InitialFromBump, Max, ParseBumpLevel, IsValid, Compare |
validate | Fault-tolerant static checks against a config + changeset directory. | Run, HasErrors, HasWarnings |
changelog | Keep-a-Changelog renderer with non-destructive insertion. | Insert, WriteFile, Today |
doctor | Repository state diagnostics the planner can't catch (e.g. revived changesets). | Run, Finding, Severity, GitLog |
Module path: monorel.disaresta.com. Install:
go get monorel.disaresta.com@latestFull GoDoc with runnable examples lives on pkg.go.dev.
When to reach for the library
- Custom orchestrator. You're running monorel under a different CI system, or you want to modify the always-open release PR pattern. Use
plan.Planto compute the next-release plan and drive your own publish step. - IDE / editor plugin. You want "is this changeset frontmatter valid?" or "does this package key exist in monorel.toml?" feedback. Use
changeset.Parse+validate.Run. - Repo audit tool. You want to surface monorel-shaped state across many repos. Use
config.Load+validate.Run. - Custom changeset authoring. A bot or workflow that produces
.changeset/*.mdfiles programmatically. Usechangeset.Changeset+Changeset.WriteFile. - Repo health diagnostics. A pre-merge or pre-release CI step that fails closed when a previously-shipped changeset is back on disk (the stale-branch + squash-merge revival pattern). Use
doctor.Runwith aGitLogadapter over your existing git library.
doctor
The doctor package wraps a small, extensible check-runner. Today it ships a single check, revived-changeset, that catches a common failure mode: when a contributor branches from main BEFORE a release commit and squash-merges later, GitHub re-introduces the changeset files the release commit deleted. The next release plan re-ships the same content under a new version.
package main
import (
"fmt"
"os/exec"
"sort"
"strings"
"monorel.disaresta.com/doctor"
)
func main() {
findings, err := doctor.Run(doctor.Options{
RepoDir: ".",
GitLog: shellGitLog("."),
})
if err != nil {
panic(err)
}
for _, f := range findings {
fmt.Printf("%s [%s] %s: %s\n", f.Severity, f.CheckName, f.Path, f.Message)
}
}
// shellGitLog adapts `git log` to doctor.GitLog. Returns every file
// path deleted by a commit whose message contains messageGrep as a
// case-sensitive literal substring, deduped and sorted.
func shellGitLog(dir string) doctor.GitLog {
return func(messageGrep string) ([]string, error) {
cmd := exec.Command("git", "log",
"--diff-filter=D", "--name-only", "--format=",
"--fixed-strings", "--grep="+messageGrep)
cmd.Dir = dir
out, err := cmd.Output()
if err != nil {
return nil, err
}
seen := map[string]struct{}{}
var paths []string
for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
if line == "" {
continue
}
if _, dup := seen[line]; dup {
continue
}
seen[line] = struct{}{}
paths = append(paths, line)
}
sort.Strings(paths)
return paths, nil
}
}doctor.GitLog is a function value, not an interface, so callers can adapt any git library (go-git, libgit2 bindings, an in-memory cache) without conforming to a struct shape. The os/exec form above is the simplest adapter; replace it with a call into your existing git library if you have one. The contract: func(messageGrep string) (deletedPaths []string, err error) returning every path deleted by a commit whose subject or body contains the literal substring.
What's NOT public
These packages stay in internal/ deliberately and are not part of the SemVer commitment:
| Internal package | Why it stays private |
|---|---|
release | Writes files, creates commits, creates tags. Promoting locks side-effect ordering. |
orchestrator | Provider-coupled (calls provider.Client); not useful without it. |
provider | Host-abstraction interface; promoting locks every interface change as breaking. |
git | Shell-out implementation detail. The doctor package's GitLog function-value seam is how callers get at git history without depending on it. |
cli | Cobra wiring. The library is the API; the CLI is one consumer. |
Side-effect-bearing packages should never be public commitments — every refactor would be a SemVer break.
Stability
From v1.0.0 onward, the public packages are SemVer-committed. Additive changes (new types, new functions, new fields on existing types) are minor bumps; breaking changes (renames, removals, signature changes, field type changes) are major bumps that ship as monorel.disaresta.com/v2/... per Go module convention.
Quick example
package main
import (
"fmt"
"log"
"monorel.disaresta.com/changeset"
"monorel.disaresta.com/config"
"monorel.disaresta.com/plan"
)
func main() {
cfg, err := config.Load("monorel.toml")
if err != nil {
log.Fatal(err)
}
cs, err := changeset.LoadAll(".changeset")
if err != nil {
log.Fatal(err)
}
// In a real tool, fetch tags from git or your provider's API. Here we use
// a hardcoded list for illustration.
tags := []string{"v1.6.1", "transports/zerolog/v1.6.1"}
p, err := plan.Plan(cfg, cs, tags, nil)
if err != nil {
log.Fatal(err)
}
for _, r := range p.Releases {
fmt.Printf("%s: %s -> %s (tag %s)\n", r.Name, r.From, r.To, r.Tag)
}
}plan.Plan is a pure function: same inputs, same output, no I/O. Drop in test fixtures or an in-memory tag list and the planner runs unchanged.
