semver

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 2, 2026 License: EUPL-1.2 Imports: 9 Imported by: 0

Documentation

Overview

Package semver implements parsing of [Semantic Versions].

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrInvalidVersion = errors.New("invalid version")

ErrInvalidVersion is returned by Parse and ParseStrict when the given version number is invalid.

Functions

This section is empty.

Types

type Strict

type Strict struct {
	Version
}

Strict is a Version that unmarshals using ParseStrict instead of Parse.

func (*Strict) UnmarshalText

func (v *Strict) UnmarshalText(text []byte) error

UnmarshalText decodes a Semantic Version from its text-representation using ParseStrict. The text must be strictly valid SemVer.

It implements encoding.TextUnmarshaler.

Example
package main

import (
	"encoding/json"
	"fmt"

	"go.hofstra.dev/semver"
)

func main() {
	var v semver.Strict

	_ = json.Unmarshal([]byte(`"1.2.3-rc.1"`), &v)
	fmt.Println(v)
}
Output:
1.2.3-rc.1

type Version

type Version struct {
	// Major version. Increments for backwards incompatible changes.
	// Use [Version.NextMajor] to increment.
	Major int

	// Minor version. Increments for new (backwards compatible) features.
	// Use [Version.NextMinor] to increment.
	Minor int

	// Patch version. Increments for backwards compatible bug-fixes.
	// Use [Version.NextPatch] to increment.
	Patch int

	// Pre version. Indicates that the version is unstable.
	// Use [Version.WithPreRelease] to set it to a sanitized value.
	Pre string

	// Build metadata. Informational only, and ignored for most operations.
	// Use [Version.WithBuild] to set it ito a sanitized value.
	Build string
}

A Version represents a Semantic Version. Use New, Version.WithPreRelease and Version.WithBuild to ensure that the field values are valid.

See the Semantic Version Specification for details on how these fields can be used.

It implements encoding.TextMarshaler, encoding.TextUnmarshaler, fmt.Stringer and slog.LogValuer.

func Build

func Build() Version

Build returns the version of the main module corresponding to the debug.BuildInfo. It returns a zero version when go module support is disabled or the main version is invalid.

Example
package main

import (
	"fmt"

	"go.hofstra.dev/semver"
)

func main() {
	fmt.Println(semver.Build())
}
Output:
0.0.0+devel

func ForModule

func ForModule(module debug.Module) Version

ForModule returns the version for the given module. It returns the zero version if the module version is invalid.

Example
package main

import (
	"fmt"
	"runtime/debug"

	"go.hofstra.dev/semver"
)

func main() {
	v := semver.ForModule(debug.Module{
		Path:    "golang.org/x/exp",
		Version: "v0.0.0-20260312153236-7ab1446f8b90",
	})
	fmt.Println(v)

	v = semver.ForModule(debug.Module{
		Path:    "golang.org/x/sync",
		Version: "v0.20.0",
	})
	fmt.Println(v)

	v = semver.ForModule(debug.Module{
		Path:    "main",
		Version: "(devel)",
	})
	fmt.Println(v)

}
Output:
0.0.0-20260312153236-7ab1446f8b90
0.20.0
0.0.0+devel

func Go

func Go() Version

Go returns the version of the Go toolchain that built the binary. It may return the zero Version for a non-stable Go release.

Example
package main

import (
	"fmt"

	"go.hofstra.dev/semver"
)

func main() {
	fmt.Println(semver.Go())
}

func MustParse

func MustParse(s string) Version

MustParse parses given version string using Parse. It panics where Parse returns an error.

Example
package main

import (
	"fmt"

	"go.hofstra.dev/semver"
)

func main() {
	v := semver.MustParse("v1.0.0-rc.1+build.1")
	fmt.Println(v)
}
Output:
1.0.0-rc.1+build.1

func MustParseStrict

func MustParseStrict(s string) Version

MustParseStrict parses given version string using ParseStrict. It panics where ParseStrict returns an error.

Example
package main

import (
	"fmt"

	"go.hofstra.dev/semver"
)

func main() {
	v := semver.MustParseStrict("1.0.0-rc.1+build.1")
	fmt.Println(v)
}
Output:
1.0.0-rc.1+build.1

func New

func New(major, minor, patch int) Version

New returns a Version with the given major, minor and patch versions. It panics if one of the given versions is negative.

func Parse

func Parse(s string) (v Version, err error)

Parse parses the given string as a Semantic Version. It does not strictly validate that the given version is valid, as it allows for several things that are not valid SemVer:

  • It strips any prefix consisting of 'a-z', 'A-Z' and '_'.
  • It allows for partial versions like '1' and '1.2'.

Use ParseStrict to only allow fully valid semantic versions.

Example
package main

import (
	"fmt"

	"go.hofstra.dev/semver"
)

func main() {
	versions := []string{
		// These versions are valid semver.
		"1.2.3",
		"1.0.0-rc.1+build.1",

		// These versions are not valid semver,
		// but are accepted by Parse, but not by ParseStrict.
		"v1.2.3",
		"version1.2.3",
		"go1.27",
		"release_1.2.3",
	}

	for _, s := range versions {
		v, _ := semver.Parse(s)

		fmt.Printf("%-18s %#v\n", s, v)
	}
}
Output:
1.2.3              semver.Version{Major:1, Minor:2, Patch:3, Pre:"", Build:""}
1.0.0-rc.1+build.1 semver.Version{Major:1, Minor:0, Patch:0, Pre:"rc.1", Build:"build.1"}
v1.2.3             semver.Version{Major:1, Minor:2, Patch:3, Pre:"", Build:""}
version1.2.3       semver.Version{Major:1, Minor:2, Patch:3, Pre:"", Build:""}
go1.27             semver.Version{Major:1, Minor:27, Patch:0, Pre:"", Build:""}
release_1.2.3      semver.Version{Major:1, Minor:2, Patch:3, Pre:"", Build:""}

func ParseStrict

func ParseStrict(s string) (v Version, err error)

ParseStrict parses the given string as a Semantic Version. It must be strictly valid according to the SemVer specification.

Example
package main

import (
	"fmt"

	"go.hofstra.dev/semver"
)

func main() {
	// valid semver
	v, _ := semver.ParseStrict("1.0.0-rc.1+build.1")
	fmt.Println(v)

	// not valid semver
	_, err := semver.ParseStrict("v1.2.3")
	fmt.Println(err)

}
Output:
1.0.0-rc.1+build.1
invalid version: major: invalid number: "v"

func (Version) Compare

func (v Version) Compare(w Version) int

Compare compares the version v with w according the precedence rules in the SemVer spec. It returns -1 if v is before w, +1 if v is after w, and 0 if they are equal.

Example
package main

import (
	"fmt"
	"slices"

	"go.hofstra.dev/semver"
)

func main() {
	versions := []semver.Version{
		semver.MustParse("1.0.0-alpha.1"),
		semver.MustParse("1.0.0-rc.1"),
		semver.MustParse("1.0.0-beta"),
		semver.MustParse("1.0.0-alpha.beta"),
		semver.MustParse("1.0.0"),
		semver.MustParse("1.0.0-beta.11"),
		semver.MustParse("1.0.0-beta.11"),
		semver.MustParse("1.0.0-alpha"),
		semver.MustParse("1.0.0-beta.2"),
		semver.MustParse("2.0.0"),
		semver.MustParse("1.2.3"),
		semver.MustParse("1.2.3"),
		semver.MustParse("1.2.0"),
	}

	// sort the versions using the compare method
	slices.SortFunc(versions, semver.Version.Compare)

	for _, version := range versions {
		fmt.Println(version)
	}
}
Output:
1.0.0-alpha
1.0.0-alpha.1
1.0.0-alpha.beta
1.0.0-beta
1.0.0-beta.2
1.0.0-beta.11
1.0.0-beta.11
1.0.0-rc.1
1.0.0
1.2.0
1.2.3
1.2.3
2.0.0

func (Version) Equal

func (v Version) Equal(w Version) bool

Equal returns true if v is equal to w according the precedence rules in the SemVer spec. Use v == w for exact comparison.

Example
package main

import (
	"fmt"

	"go.hofstra.dev/semver"
)

func main() {
	a := semver.MustParse("1.0.0-rc.1+build.1")
	b := semver.MustParse("1.0.0-rc.1+build.2")
	c := semver.MustParse("1.0.0-rc.2+build.1")

	fmt.Printf("%s eq %s -> %t\n", a, b, a.Equal(b))
	fmt.Printf("%s eq %s -> %t\n", a, c, a.Equal(c))
}
Output:
1.0.0-rc.1+build.1 eq 1.0.0-rc.1+build.2 -> true
1.0.0-rc.1+build.1 eq 1.0.0-rc.2+build.1 -> false

func (Version) Go

func (v Version) Go() string

Go returns v as a Go module version string: a semantic version with a v prefix. This is equivalent to Version.WithPrefix("v"). See module version numbering for details.

Example
package main

import (
	"fmt"

	"go.hofstra.dev/semver"
)

func main() {
	v := semver.New(1, 2, 3)
	fmt.Println(v.Go())
}
Output:
v1.2.3

func (Version) IsZero

func (v Version) IsZero() bool

IsZero returns true if the version is equivalent to the zero value. The version's build metadata is ignored.

Example
package main

import (
	"fmt"

	"go.hofstra.dev/semver"
)

func main() {
	versions := []semver.Version{
		semver.MustParse("v0.0.0-20170915032832-14c0d48ead0c"),
		semver.MustParse("v0.0.0+meta"),
		semver.MustParse("v2.0.0"),
	}

	for _, v := range versions {
		fmt.Printf("%s is zero: %t\n", v, v.IsZero())
	}
}
Output:
0.0.0-20170915032832-14c0d48ead0c is zero: false
0.0.0+meta is zero: true
2.0.0 is zero: false

func (Version) LogValue

func (v Version) LogValue() slog.Value

LogValue returns the version for logging.

It implements slog.LogValuer.

Example
package main

import (
	"log/slog"
	"os"

	"go.hofstra.dev/semver"
)

func main() {
	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
		// Remove the timestamps from the logging so this example has consistent output.
		ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
			if a.Key == slog.TimeKey && len(groups) == 0 {
				return slog.Attr{}
			}

			return a
		},
	}))

	logger.Info("Running",
		"application", "my-app",
		"version", semver.New(1, 2, 3))
}
Output:
level=INFO msg=Running application=my-app version=1.2.3

func (Version) MajorMinor

func (v Version) MajorMinor() string

MajorMinor returns the major/minor version, eg: 1.2 for 1.2.3.

Example
package main

import (
	"fmt"

	"go.hofstra.dev/semver"
)

func main() {
	v := semver.New(1, 2, 3).WithPreRelease("rc", 1)

	fmt.Println(v.MajorMinor())
}
Output:
1.2

func (Version) MarshalText

func (v Version) MarshalText() (text []byte, err error)

MarshalText encodes the version to its text-representation.

It implements encoding.TextMarshaler.

Example
package main

import (
	"encoding/json"
	"os"

	"go.hofstra.dev/semver"
)

func main() {
	v := semver.New(1, 2, 3).WithPreRelease("rc", 1)

	_ = json.NewEncoder(os.Stdout).Encode(v)
}
Output:
"1.2.3-rc.1"

func (Version) NextMajor

func (v Version) NextMajor() Version

NextMajor returns the next major version, with all other fields set to the zero value.

Example
package main

import (
	"fmt"

	"go.hofstra.dev/semver"
)

func main() {
	v := semver.New(1, 2, 3).WithPreRelease("rc", 1)

	fmt.Println(v.NextMajor())
}
Output:
2.0.0

func (Version) NextMinor

func (v Version) NextMinor() Version

NextMinor returns the next minor version, with all other fields except for the major version set to the zero value.

Example
package main

import (
	"fmt"

	"go.hofstra.dev/semver"
)

func main() {
	v := semver.New(1, 2, 3).WithPreRelease("rc", 1)

	fmt.Println(v.NextMinor())
}
Output:
1.3.0

func (Version) NextPatch

func (v Version) NextPatch() Version

NextPatch returns the next patch version, with the pre-release version and build metadata set to the zero value.

Example
package main

import (
	"fmt"

	"go.hofstra.dev/semver"
)

func main() {
	v := semver.New(1, 2, 3).WithPreRelease("rc", 1)

	fmt.Println(v.NextPatch())
}
Output:
1.2.4

func (Version) NextPreRelease

func (v Version) NextPreRelease(kind string) Version

NextPreRelease returns the next pre-release version (starting at 1) for the kind of pre-release (alpha, beta, rc, etc). It removes all other pre-release information.

Example
package main

import (
	"fmt"

	"go.hofstra.dev/semver"
)

func main() {
	v := semver.New(1, 2, 3).NextPreRelease("rc")
	fmt.Println(v)

	v = v.NextPreRelease("rc")
	fmt.Println(v)

}
Output:
1.2.3-rc.1
1.2.3-rc.2

func (Version) PreReleaseVersion

func (v Version) PreReleaseVersion() (kind string, n int, found bool)

PreReleaseVersion returns the pre-release version number and the kind of pre-release for the first pre-release component. Other pre-release components are ignored.

Example
package main

import (
	"fmt"

	"go.hofstra.dev/semver"
)

func main() {
	versions := []semver.Version{
		semver.MustParse("1.0.0-alpha"),
		semver.MustParse("1.0.0-alpha.1"),
		semver.MustParse("1.0.0-alpha.beta"),
		semver.MustParse("1.0.0-beta"),
		semver.MustParse("1.0.0-beta.2"),
		semver.MustParse("1.0.0-beta.11"),
		semver.MustParse("1.0.0-beta.11"),
		semver.MustParse("1.0.0-rc.1"),
		semver.MustParse("1.0.0"),
	}

	for _, v := range versions {
		kind, n, found := v.PreReleaseVersion()

		fmt.Printf("%-5s %2d %t\n", kind, n, found)
	}
}
Output:
alpha  0 true
alpha  1 true
       0 false
beta   0 true
beta   2 true
beta  11 true
beta  11 true
rc     1 true
       0 false

func (Version) Release

func (v Version) Release() Version

Release returns the version without pre-release or build identifiers.

Example
package main

import (
	"fmt"

	"go.hofstra.dev/semver"
)

func main() {
	v := semver.New(1, 2, 3).WithPreRelease("rc", 1)

	fmt.Println(v.Release())
}
Output:
1.2.3

func (Version) String

func (v Version) String() string

String returns the string representation of v.

It implements fmt.Stringer.

func (*Version) UnmarshalText

func (v *Version) UnmarshalText(text []byte) error

UnmarshalText decodes a Semantic Version from its text-representation using Parse. The text does not have to be strictly valid SemVer.

It implements encoding.TextUnmarshaler.

Example
package main

import (
	"encoding/json"
	"fmt"

	"go.hofstra.dev/semver"
)

func main() {
	var v semver.Version

	_ = json.Unmarshal([]byte(`"1.2.3-rc.1"`), &v)
	fmt.Println(v)
}
Output:
1.2.3-rc.1

func (Version) WithBuild

func (v Version) WithBuild(parts ...string) Version

WithBuild returns the version with the build metadata set to contain the given parts (string or int). It sanitizes parts in order to ensure valid build metadata as follows:

  • Invalid characters are escaped as '-'.
  • Empty parts ("") are omitted.
Example
package main

import (
	"fmt"

	"go.hofstra.dev/semver"
)

func main() {
	v := semver.New(1, 2, 3)

	// set the build info, note that empty, emoji and '.' are not valid
	v = v.WithBuild("a", "", "b", "💔", "c.d")

	fmt.Println(v)
}
Output:
1.2.3+a.b.-.c-d

func (Version) WithPreRelease

func (v Version) WithPreRelease(parts ...any) Version

WithPreRelease returns the version with the pre-release version set to contain the given parts (string or int). It sanitizes parts in order to ensure a valid pre-release version as follows:

  • Invalid characters are escaped as '-'.
  • Empty parts ("") are omitted.
  • The leading zero is removed from numbers (e.g. "0123" becomes "123").
Example
package main

import (
	"fmt"

	"go.hofstra.dev/semver"
)

func main() {
	v := semver.New(1, 2, 3)

	// set the pre-release version, note that empty, leading zeroes, emoji and '.' are not valid
	v = v.WithPreRelease("a", "", "01", 2, "💔", "c.d")

	fmt.Println(v)
}
Output:
1.2.3-a.1.2.-.c-d

func (Version) WithPrefix

func (v Version) WithPrefix(prefix string) string

WithPrefix returns the string representation of v prepended with the given prefix.

Example
package main

import (
	"fmt"

	"go.hofstra.dev/semver"
)

func main() {
	v := semver.New(1, 2, 3).WithPreRelease("rc", 1)

	fmt.Println(v.WithPrefix("version"))
}
Output:
version1.2.3-rc.1

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL