db: treat Go module pseudo-versions as dev builds
Untagged main-sha builds inherit a Go module pseudo-version from
runtime/debug.BuildInfo (vX.Y.Z-<timestamp>-<hash>). isDev only
filtered "", "dev", and "(devel)", so the pseudo-version was stored
in database_versions and the next real release tripped the
multi-minor upgrade guard:
headscale version v0.29.0-beta.1 cannot be used with a database
last used by v0.0.0-20260520093041-e4e742c776ee, upgrading more
than one minor version at a time is not supported
Add pseudoVersionTime, a regex + time.Parse predicate covering all
three Go pseudo-version forms (v0.0.0 base, pre-release ancestor,
release ancestor), and delegate from isDev. The dev gate at
db.go:790 already prevents pseudo-versions from being written, so
already-poisoned databases self-heal on the next real-release start.
Fixes #3281
This commit is contained in:
parent
4483fd0cad
commit
77ba225cdb
2 changed files with 242 additions and 7 deletions
|
|
@ -3,6 +3,7 @@ package db
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
|
@ -139,10 +140,49 @@ func setDatabaseVersion(db *gorm.DB, version string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// pseudoVersionTimeLayout is Go's pseudo-version timestamp layout
|
||||
// (golang.org/ref/mod#pseudo-versions): UTC yyyymmddhhmmss.
|
||||
const pseudoVersionTimeLayout = "20060102150405"
|
||||
|
||||
// pseudoVersionSuffix matches the trailing "<sep><14 digits>-<12
|
||||
// lowercase hex>" of a Go module pseudo-version. The base form
|
||||
// (vX.0.0-<date>-<hash>) uses "-" before the timestamp; the
|
||||
// pre-release-ancestor and release-ancestor forms
|
||||
// (vX.Y.Z-pre.0.<date>-<hash> and vX.Y.(Z+1)-0.<date>-<hash>) use "."
|
||||
// because the digit-only "0" marker precedes the timestamp.
|
||||
var pseudoVersionSuffix = regexp.MustCompile(`[-.](\d{14})-[0-9a-f]{12}$`)
|
||||
|
||||
// pseudoVersionTime returns the embedded commit time when v is a
|
||||
// syntactically and semantically valid Go module pseudo-version. The
|
||||
// timestamp must parse as a real UTC time; lookalikes with malformed
|
||||
// dates (e.g. month 13, day 30 in February) are rejected.
|
||||
func pseudoVersionTime(v string) (time.Time, bool) {
|
||||
m := pseudoVersionSuffix.FindStringSubmatch(v)
|
||||
if m == nil {
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
t, err := time.Parse(pseudoVersionTimeLayout, m[1])
|
||||
if err != nil {
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
return t, true
|
||||
}
|
||||
|
||||
// isDev reports whether a version string represents a development build
|
||||
// that should skip version checking.
|
||||
// that should skip version checking. Go module pseudo-versions (used by
|
||||
// untagged main-sha builds, where runtime/debug.BuildInfo falls back to
|
||||
// vX.Y.Z-<timestamp>-<commit>) are treated as dev to avoid poisoning
|
||||
// database_versions with synthetic baselines.
|
||||
func isDev(version string) bool {
|
||||
return version == "" || version == "dev" || version == "(devel)"
|
||||
if version == "" || version == "dev" || version == "(devel)" {
|
||||
return true
|
||||
}
|
||||
|
||||
_, ok := pseudoVersionTime(version)
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
// checkVersionUpgradePath verifies that the running headscale version
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue