- stamp add: create .stamp/*.md changeset files (interactive + --no-interactive) - stamp status: show pending stamps and projected next versions (plain + --json) - stamp version: consume stamps, bump semver, update CHANGELOGs, commit - stamp publish: create git tags, GitHub/Gitea releases, upload artifacts - stamp comment: post/update idempotent PR comments via GitHub/Gitea API - Supports monorepos via stamp.toml [[projects]] blocks - Changeset files support YAML (---) and TOML (+++) frontmatter - Semver + pre-release support (alpha, beta, rc) - Examples and workflow documentation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
124 lines
3.3 KiB
Go
124 lines
3.3 KiB
Go
package semver
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
goSemver "github.com/Masterminds/semver/v3"
|
|
|
|
"github.com/thokra/stamp/internal/changeset"
|
|
)
|
|
|
|
// Bump computes the next version given the current version string and a bump type.
|
|
// preID is the pre-release identifier (e.g. "alpha", "beta", "rc") used for pre-* bumps.
|
|
func Bump(current string, bump changeset.BumpType, preID string) (string, error) {
|
|
v, err := goSemver.NewVersion(current)
|
|
if err != nil {
|
|
return "", fmt.Errorf("invalid version %q: %w", current, err)
|
|
}
|
|
|
|
if preID == "" {
|
|
preID = "0"
|
|
}
|
|
|
|
switch bump {
|
|
case changeset.BumpMajor:
|
|
next := v.IncMajor()
|
|
return next.Original(), nil
|
|
|
|
case changeset.BumpMinor:
|
|
next := v.IncMinor()
|
|
return next.Original(), nil
|
|
|
|
case changeset.BumpPatch:
|
|
next := v.IncPatch()
|
|
return next.Original(), nil
|
|
|
|
case changeset.BumpPreMajor:
|
|
next := v.IncMajor()
|
|
pre, err := goSemver.NewVersion(fmt.Sprintf("%d.%d.%d-%s.0", next.Major(), next.Minor(), next.Patch(), preID))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return pre.Original(), nil
|
|
|
|
case changeset.BumpPreMinor:
|
|
next := v.IncMinor()
|
|
pre, err := goSemver.NewVersion(fmt.Sprintf("%d.%d.%d-%s.0", next.Major(), next.Minor(), next.Patch(), preID))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return pre.Original(), nil
|
|
|
|
case changeset.BumpPrePatch:
|
|
next := v.IncPatch()
|
|
pre, err := goSemver.NewVersion(fmt.Sprintf("%d.%d.%d-%s.0", next.Major(), next.Minor(), next.Patch(), preID))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return pre.Original(), nil
|
|
|
|
case changeset.BumpPreRelease:
|
|
// If already a pre-release with matching identifier, increment the pre-release number.
|
|
// Otherwise start a fresh pre-release on current patch.
|
|
preStr := v.Prerelease()
|
|
if preStr != "" && strings.HasPrefix(preStr, preID+".") {
|
|
parts := strings.SplitN(preStr, ".", 2)
|
|
var num int
|
|
fmt.Sscanf(parts[1], "%d", &num)
|
|
next, err := goSemver.NewVersion(fmt.Sprintf("%d.%d.%d-%s.%d",
|
|
v.Major(), v.Minor(), v.Patch(), preID, num+1))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return next.Original(), nil
|
|
}
|
|
// Start a new pre-release on next patch.
|
|
patch := v.IncPatch()
|
|
next, err := goSemver.NewVersion(fmt.Sprintf("%d.%d.%d-%s.0",
|
|
patch.Major(), patch.Minor(), patch.Patch(), preID))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return next.Original(), nil
|
|
|
|
default:
|
|
return "", fmt.Errorf("unknown bump type %q", bump)
|
|
}
|
|
}
|
|
|
|
// HighestBump returns the highest-priority bump type from a set of bumps.
|
|
// Priority: major > minor > patch > premajor > preminor > prepatch > prerelease.
|
|
func HighestBump(bumps []changeset.BumpType) changeset.BumpType {
|
|
priority := map[changeset.BumpType]int{
|
|
changeset.BumpMajor: 6,
|
|
changeset.BumpMinor: 5,
|
|
changeset.BumpPatch: 4,
|
|
changeset.BumpPreMajor: 3,
|
|
changeset.BumpPreMinor: 2,
|
|
changeset.BumpPrePatch: 1,
|
|
changeset.BumpPreRelease: 0,
|
|
}
|
|
|
|
best := changeset.BumpPreRelease
|
|
bestP := -1
|
|
for _, b := range bumps {
|
|
if p, ok := priority[b]; ok && p > bestP {
|
|
best = b
|
|
bestP = p
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
// ProjectBumps aggregates bump types per project from a list of changesets.
|
|
func ProjectBumps(changesets []*changeset.Changeset) map[string][]changeset.BumpType {
|
|
result := map[string][]changeset.BumpType{}
|
|
for _, cs := range changesets {
|
|
for project, bump := range cs.Bumps {
|
|
result[project] = append(result[project], bump)
|
|
}
|
|
}
|
|
return result
|
|
}
|