feat: derive recommended version from changelog; no version file required
- RecommendedTag now reads the current version from the most recent released section heading in the changelog (## [x.y.z] - ...) when no --version-file flag is given, removing the dependency on a separate version file for recommendation. - When the changelog contains no prior releases, the base version defaults to 0.0.0, so the first recommended tag is v1.0.0 (or higher depending on unreleased content). - Prepare creates the release-version file if it does not already exist, so new repositories do not need to pre-seed it. - Add tests covering changelog-based version resolution, first-release default, and automatic file creation. - Update README and changelog unreleased section to document the new behaviour.
This commit is contained in:
@@ -15,6 +15,8 @@ const (
|
||||
defaultChangelog = "changelog.md"
|
||||
)
|
||||
|
||||
var releasedSectionRe = regexp.MustCompile(`(?m)^## \[(\d+\.\d+\.\d+)\] - `)
|
||||
|
||||
type Options struct {
|
||||
VersionFile string
|
||||
VersionPattern string
|
||||
@@ -66,9 +68,22 @@ func RecommendedTag(rootDir string, options Options) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
currentVersion, err := readCurrentVersion(rootDir, resolved)
|
||||
if err != nil {
|
||||
return "", err
|
||||
var currentVersion string
|
||||
if options.VersionFile != "" {
|
||||
currentVersion, err = readCurrentVersion(rootDir, resolved)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
version, found, err := readLatestChangelogVersion(rootDir, resolved.Changelog)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !found {
|
||||
currentVersion = "0.0.0"
|
||||
} else {
|
||||
currentVersion = version
|
||||
}
|
||||
}
|
||||
|
||||
unreleasedBody, err := readUnreleasedBody(rootDir, resolved.Changelog)
|
||||
@@ -146,6 +161,9 @@ func updateVersionFile(rootDir, version string, options resolvedOptions) error {
|
||||
path := filepath.Join(rootDir, options.VersionFile)
|
||||
contents, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return os.WriteFile(path, []byte(version+"\n"), 0o644)
|
||||
}
|
||||
return fmt.Errorf("read version file: %w", err)
|
||||
}
|
||||
|
||||
@@ -244,6 +262,20 @@ func readChangelogState(rootDir, changelogPath string) (string, string, int, int
|
||||
return unreleasedBody, text, afterHeader, nextSectionStart, path, nil
|
||||
}
|
||||
|
||||
func readLatestChangelogVersion(rootDir, changelogPath string) (string, bool, error) {
|
||||
path := filepath.Join(rootDir, changelogPath)
|
||||
contents, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return "", false, fmt.Errorf("read changelog: %w", err)
|
||||
}
|
||||
|
||||
match := releasedSectionRe.FindStringSubmatch(string(contents))
|
||||
if match == nil {
|
||||
return "", false, nil
|
||||
}
|
||||
return match[1], true, nil
|
||||
}
|
||||
|
||||
func parseSemver(version string) (semver, error) {
|
||||
parts := strings.Split(strings.TrimSpace(version), ".")
|
||||
if len(parts) != 3 {
|
||||
|
||||
Reference in New Issue
Block a user