Documentation
¶
Overview ¶
Package changeset reads and writes the .changeset/<name>.md files that drive monorel releases.
File format:
--- "package-a": minor "package-b": patch --- Body text becomes the changelog entry for these packages.
The frontmatter is YAML; the body is markdown that monorel passes through verbatim into per-package CHANGELOG.md sections. Files live in .changeset/<name>.md at the repo root; .changeset/pre.json is pre-release-mode state and is not a changeset.
Index ¶
Examples ¶
Constants ¶
const PreStateCurrentSchemaVersion = 1
PreStateCurrentSchemaVersion is the version this build of monorel writes to .changeset/pre.json. Bump (and add a migration in LoadPreState) when the on-disk shape changes incompatibly.
const PreStateFilename = "pre.json"
PreStateFilename is the basename of the on-disk state file.
Variables ¶
This section is empty.
Functions ¶
func IsReserved ¶
IsReserved reports whether name is a reserved (non-changeset) filename inside the changeset directory. Walkers should skip reserved names rather than try to parse them as changesets.
func IsValidName ¶
IsValidName reports whether name is a plausible changeset filename: non-empty, lowercase letters / digits / hyphens, no slashes, no leading/trailing/consecutive hyphens.
We don't enforce the two-word format (users may rename files manually); the rule is "what's safe to use as a filename and readable in PR titles."
func RandomName ¶
func RandomName() string
RandomName returns a "<adjective>-<noun>" filename suitable for a new changeset. Uses crypto/rand so concurrent invocations don't collide on a shared seed.
func RemovePreState ¶
Remove deletes .changeset/pre.json. Returns nil if the file does not exist (idempotent).
Types ¶
type Changeset ¶
type Changeset struct {
// Name is the filename without the ".md" extension and without
// the ".changeset/" directory prefix. Used to delete the file
// after a release consumes it, and as a stable identifier in
// logs and PR bodies.
Name string
// Bumps maps package names (matching keys in monorel.toml's
// [packages] table) to the requested bump level for this
// changeset. Always non-empty for a valid changeset.
Bumps map[string]semver.BumpLevel
// Body is the markdown text after the frontmatter. Trimmed of
// leading/trailing whitespace. May be empty (parser tolerates
// it; release writes a placeholder line in that case).
Body string
}
Changeset is one parsed .changeset/<name>.md file.
func LoadAll ¶
LoadAll reads every changeset file under dir (excluding reserved names) and returns them sorted by name. Missing dir is not an error: it just yields zero changesets.
func Parse ¶
Parse reads a single changeset from r. name is the changeset's filename minus ".md".
Errors are returned for: missing or malformed frontmatter, YAML parse failures, empty bumps map, and unknown bump levels.
Example ¶
ExampleParse reads one .changeset/<name>.md file's contents and returns the structured frontmatter + body. The name argument is the filename minus the .md suffix; it propagates into Changeset.Name so callers can correlate parse results with their on-disk source.
package main
import (
"fmt"
"strings"
"monorel.disaresta.com/changeset"
)
func main() {
src := `---
"transports/zerolog": minor
"go.loglayer.dev": patch
---
Adds Lazy() helper for deferred field evaluation. Pass-through
fix in the root.
`
cs, err := changeset.Parse(strings.NewReader(src), "quick-otter")
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println("name:", cs.Name)
for _, pkg := range cs.PackageNames() {
fmt.Printf("- %s: %s\n", pkg, cs.Bumps[pkg])
}
fmt.Println("body:", strings.SplitN(cs.Body, "\n", 2)[0])
}
Output: name: quick-otter - go.loglayer.dev: patch - transports/zerolog: minor body: Adds Lazy() helper for deferred field evaluation. Pass-through
func (*Changeset) PackageNames ¶
PackageNames returns the package names this changeset affects in lexicographic order.
func (*Changeset) Write ¶
Write serializes a changeset back to the .changeset/<name>.md format. Bumps are emitted in lexicographic order so file content is deterministic across runs.
func (*Changeset) WriteFile ¶
WriteFile is a convenience wrapper that writes the changeset to dir/<name>.md, creating dir if it doesn't exist.
Example ¶
ExampleChangeset_WriteFile demonstrates the round trip: construct a Changeset, write it under .changeset/<name>.md, and parse it back. This is the pattern an authoring tool (IDE plugin, custom `monorel add` wrapper, etc.) would follow to produce changesets programmatically.
package main
import (
"fmt"
"os"
"path/filepath"
"monorel.disaresta.com/changeset"
"monorel.disaresta.com/semver"
)
func main() {
dir, _ := os.MkdirTemp("", "monorel-changesets")
defer os.RemoveAll(dir)
cs := &changeset.Changeset{
Name: "quick-otter",
Bumps: map[string]semver.BumpLevel{
"transports/zerolog": semver.Minor,
},
Body: "Adds Lazy() helper.",
}
if err := cs.WriteFile(dir); err != nil {
fmt.Println("write error:", err)
return
}
// Round-trip: read the file back via LoadAll.
loaded, err := changeset.LoadAll(dir)
if err != nil {
fmt.Println("load error:", err)
return
}
for _, c := range loaded {
fmt.Printf("%s: %d package(s), body=%q\n", c.Name, len(c.Bumps), c.Body)
}
// Verify the file landed where expected.
if _, err := os.Stat(filepath.Join(dir, "quick-otter.md")); err != nil {
fmt.Println("file missing:", err)
}
}
Output: quick-otter: 1 package(s), body="Adds Lazy() helper."
type PreState ¶
type PreState struct {
// SchemaVersion identifies the on-disk format. Currently 1.
// Older files written before this field existed deserialize as
// 0 and are treated as version 1 (backward-compatible). Newer
// versions (greater than [PreStateCurrentSchemaVersion]) are
// rejected with a clear error so a stale monorel binary can't
// silently misread a future format.
SchemaVersion int `json:"schemaVersion,omitempty"`
// Mode is always "pre" while the state file exists. Reserved
// for forward-compatibility with future modes (e.g. "exit"
// rendering the file inert without deleting it).
Mode string `json:"mode"`
// Channel is the pre-release identifier ("rc", "beta", "alpha",
// or any non-empty SemVer-2.0-compatible label). Used as the
// suffix prefix: -<channel>.<counter>.
Channel string `json:"channel"`
// Counters tracks the next pre-release counter per package. A
// missing entry means counter 0 for the next release.
Counters map[string]int `json:"counters,omitempty"`
}
PreState is the on-disk representation of pre-release mode. Lives at .changeset/pre.json when monorel is in a pre-release window.
While in pre-release mode, monorel release appends "-<channel>.N" to each released package's version and increments that package's counter. Exiting pre-release mode (monorel pre exit) deletes the file; the next stable release bumps from the last *stable* tag, applying every changeset accumulated since.
func LoadPreState ¶
LoadPreState reads .changeset/pre.json. Returns (nil, nil) if the file does not exist (the common, "not in pre-release mode" case).
func (*PreState) CounterFor ¶
CounterFor returns the next pre-release counter for the given package, defaulting to 0 if no entry exists.
func (*PreState) IncrementCounter ¶
IncrementCounter raises the counter for pkg by one and returns the value before the increment (i.e. the value to USE for the release that triggered the increment).
Initializes the Counters map if nil so that callers don't have to.