feat(releaser): release v1.5.0 — Node.js, multi-module Maven, configurable bump rules
- Add internal/node package: reads/writes package.json version (single or multi-path via node.package_json / node.package_jsons) - Add maven.pom_paths support: update multiple pom.xml files in one release commit; pom_paths overrides pom_path; --pom flag clears pom_paths - Add git.bump_rules config: per-type control of which version component bumps (breaking/feat/fix accept "patch" or "minor"); wired through version.Next() as a new sixth parameter - Extract injectable function vars (absPath, gitAllCommits, gitCommitsSince, gitCommitFiles) to enable error-path testing without interfaces - Achieve 100% per-package statement coverage across all 12 packages Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,18 +6,38 @@ import (
|
||||
"git.k3nny.fr/releaser/internal/commits"
|
||||
)
|
||||
|
||||
// BumpLevel controls which version component is incremented.
|
||||
type BumpLevel int
|
||||
|
||||
const (
|
||||
BumpPatch BumpLevel = iota
|
||||
BumpMinor
|
||||
)
|
||||
|
||||
// Next computes the next version string (without tag prefix, e.g. "1.2.4").
|
||||
// currentPatch is -1 when no tag exists yet (first release will be X.Y.0).
|
||||
// releasable is the set of commit types that trigger a bump; nil defaults to all three.
|
||||
// bumpRules maps each type to its bump level; nil defaults to BumpPatch for all.
|
||||
// Returns ("", false) when there are no releasable commits.
|
||||
func Next(major, minor, currentPatch int, types []commits.Type, releasable map[commits.Type]bool) (string, bool) {
|
||||
func Next(major, minor, currentPatch int, types []commits.Type, releasable map[commits.Type]bool, bumpRules map[commits.Type]BumpLevel) (string, bool) {
|
||||
if releasable == nil {
|
||||
releasable = commits.ReleasableSet(nil)
|
||||
}
|
||||
found := false
|
||||
useMinor := false
|
||||
for _, t := range types {
|
||||
if releasable[t] {
|
||||
return fmt.Sprintf("%d.%d.%d", major, minor, currentPatch+1), true
|
||||
found = true
|
||||
if bumpRules[t] == BumpMinor {
|
||||
useMinor = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
if !found {
|
||||
return "", false
|
||||
}
|
||||
if useMinor {
|
||||
return fmt.Sprintf("%d.%d.0", major, minor+1), true
|
||||
}
|
||||
return fmt.Sprintf("%d.%d.%d", major, minor, currentPatch+1), true
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ func TestNext(t *testing.T) {
|
||||
wantOk: true,
|
||||
},
|
||||
{
|
||||
desc: "breaking change still bumps patch on release branch",
|
||||
desc: "breaking change still bumps patch by default",
|
||||
major: 2, minor: 0, currentPatch: 1,
|
||||
types: []commits.Type{commits.TypeBreaking},
|
||||
want: "2.0.2",
|
||||
@@ -61,7 +61,7 @@ func TestNext(t *testing.T) {
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.desc, func(t *testing.T) {
|
||||
got, ok := Next(c.major, c.minor, c.currentPatch, c.types, nil)
|
||||
got, ok := Next(c.major, c.minor, c.currentPatch, c.types, nil, nil)
|
||||
if ok != c.wantOk {
|
||||
t.Errorf("ok=%v, want %v", ok, c.wantOk)
|
||||
}
|
||||
@@ -71,3 +71,73 @@ func TestNext(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNextMinorBump(t *testing.T) {
|
||||
rules := map[commits.Type]BumpLevel{
|
||||
commits.TypeBreaking: BumpMinor,
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
major, minor int
|
||||
currentPatch int
|
||||
types []commits.Type
|
||||
want string
|
||||
}{
|
||||
{
|
||||
desc: "breaking → minor bump",
|
||||
major: 1, minor: 2, currentPatch: 3,
|
||||
types: []commits.Type{commits.TypeBreaking},
|
||||
want: "1.3.0",
|
||||
},
|
||||
{
|
||||
desc: "feat (patch rule) with breaking (minor rule) → minor wins",
|
||||
major: 1, minor: 2, currentPatch: 3,
|
||||
types: []commits.Type{commits.TypeFeat, commits.TypeBreaking},
|
||||
want: "1.3.0",
|
||||
},
|
||||
{
|
||||
desc: "fix only — no minor rule → patch bump",
|
||||
major: 1, minor: 2, currentPatch: 3,
|
||||
types: []commits.Type{commits.TypeFix},
|
||||
want: "1.2.4",
|
||||
},
|
||||
{
|
||||
desc: "first release with minor bump",
|
||||
major: 2, minor: 0, currentPatch: -1,
|
||||
types: []commits.Type{commits.TypeBreaking},
|
||||
want: "2.1.0",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.desc, func(t *testing.T) {
|
||||
got, ok := Next(c.major, c.minor, c.currentPatch, c.types, nil, rules)
|
||||
if !ok {
|
||||
t.Fatal("expected ok=true")
|
||||
}
|
||||
if got != c.want {
|
||||
t.Errorf("got %q, want %q", got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNextAllMinorRules(t *testing.T) {
|
||||
rules := map[commits.Type]BumpLevel{
|
||||
commits.TypeBreaking: BumpMinor,
|
||||
commits.TypeFeat: BumpMinor,
|
||||
commits.TypeFix: BumpMinor,
|
||||
}
|
||||
got, ok := Next(1, 4, 2, []commits.Type{commits.TypeFix}, nil, rules)
|
||||
if !ok || got != "1.5.0" {
|
||||
t.Errorf("got %q ok=%v, want 1.5.0 true", got, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNextNilBumpRulesDefaultsToPatch(t *testing.T) {
|
||||
got, ok := Next(1, 2, 5, []commits.Type{commits.TypeBreaking}, nil, nil)
|
||||
if !ok || got != "1.2.6" {
|
||||
t.Errorf("got %q ok=%v, want 1.2.6 true", got, ok)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user