Skip to content

feat: support cooldown for Golang module#8966

Draft
olblak wants to merge 13 commits into
updatecli:mainfrom
olblak:issue/7866/1
Draft

feat: support cooldown for Golang module#8966
olblak wants to merge 13 commits into
updatecli:mainfrom
olblak:issue/7866/1

Conversation

@olblak

@olblak olblak commented May 24, 2026

Copy link
Copy Markdown
Member

Related to #7866
Fix #8997

Implement the ability to filter out version newer or older than a specific date.
This pull request adds support for Golang module.

Here is an example of a source definition:

sources:
  default:
    kind: golang/module
    spec:
      module: github.com/updatecli/udash
      versionfilter:
        kind: semver
      age:
        #maximum: 1y
        minimum: 1y

Test

To test this pull request, you can run the following commands:

cd pkg/plugins/utils/age/
go test

Or test manually the example above.

Additional Information

Checklist

  • I have updated the documentation via pull request in website repository.

Tradeoff

Potential improvement

@olblak olblak added the enhancement New feature or request label May 24, 2026
@olblak olblak marked this pull request as draft May 24, 2026 17:47
@olblak olblak changed the title Issue/7866/1 feat: support cooldown for Golang module May 24, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for an age filter (with minimum / maximum duration constraints) to Golang module and Golang language source/condition plugins, so users can implement "cooldown" semantics that exclude releases that are too recent or too old. A new shared pkg/plugins/utils/age package introduces the spec, validation, duration parsing (with h/d/w/mo/y units), and IsOlderThan / IsNewerThan helpers. For the Golang language plugin, when age filtering is enabled, version dates are obtained by cloning the upstream Go git repository and using committer dates.

Changes:

  • Introduce reusable age.Spec (parser, validator, comparator) plus tests.
  • Wire age filtering into go/module source and condition via Go proxy @v/<ver>.info metadata.
  • Wire age filtering into go/language source/condition with a new git-tag-based release-date lookup (age.go).

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
pkg/plugins/utils/age/main.go New shared age filter Spec with duration parsing and comparison helpers
pkg/plugins/utils/age/main_test.go Unit tests for parser, Validate, IsOlderThan, IsNewerThan, IsZero
pkg/plugins/resources/go/module/spec.go Adds Age field and documents per-mode compatibility
pkg/plugins/resources/go/module/main.go Validates Age spec in constructor; includes it in ReportConfig
pkg/plugins/resources/go/module/version.go Filters proxy versions/pseudo versions by release age; extracts versionInfo struct
pkg/plugins/resources/go/module/condition.go Branches on Age to additionally check release date for Version
pkg/plugins/resources/go/module/source_test.go Adds age-based source test cases
pkg/plugins/resources/go/module/condition_test.go Adds age-based condition test cases
pkg/plugins/resources/go/language/spec.go Adds Age field with documentation
pkg/plugins/resources/go/language/main.go Includes Age in ReportConfig
pkg/plugins/resources/go/language/source.go Selects between proxy and git-based version lookup based on Age
pkg/plugins/resources/go/language/condition.go Same selection logic for condition path
pkg/plugins/resources/go/language/age.go New git-clone-based tag enumeration to obtain release dates
pkg/plugins/resources/go/language/source_test.go Adds age-based source tests
pkg/plugins/resources/go/language/condition_test.go Adds age-based condition tests

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pkg/plugins/resources/go/language/source.go Outdated
Comment thread pkg/plugins/resources/go/language/age.go Outdated
Comment thread pkg/plugins/resources/go/module/version.go Outdated
Comment thread pkg/plugins/utils/age/main_test.go
Comment thread pkg/plugins/resources/go/module/version.go
Comment thread pkg/plugins/resources/go/module/condition.go
Comment thread pkg/plugins/resources/go/module/version.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 6 comments.

Comment thread pkg/plugins/resources/go/language/source.go
Comment thread pkg/plugins/resources/go/module/condition.go Outdated
Comment thread pkg/plugins/resources/go/module/condition.go
Comment thread pkg/plugins/resources/go/module/version.go
Comment thread pkg/plugins/utils/age/main.go
Comment thread pkg/plugins/resources/go/language/age.go Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 8 comments.

Comment thread pkg/plugins/resources/go/module/version.go
Comment on lines +55 to +58
if g.Spec.Age.Minimum != "" && g.Spec.Age.IsOlderThan(releaseDate, nil) {
logrus.Debugf("ignoring version %q from proxy %q because its age is below %q (released on %s)\n", versionToCheck, proxy, g.Spec.Age.Minimum, releaseDate)
continue
}
Comment thread pkg/plugins/resources/go/module/spec.go
// * source
//
VersionFilter version.Filter `yaml:",omitempty"`
// age defines the minimum age of a release to be considered valid. It accepts a duration string (e.g., "24h", "7d").
Comment on lines +82 to +120
// IsOlderThan returns true if the version is older than the date period.
func (a Spec) IsOlderThan(t time.Time, since *time.Time) bool {
if a.Minimum == "" {
return false
}

if since == nil {
now := time.Now()
since = &now
}

duration, err := parseReleaseAge(a.Minimum)
if err != nil {
logrus.Errorf("invalid MinimumReleaseAge %q: %q\n", a.Minimum, err)
return false
}

return since.Sub(t) < duration
}

// IsNewerThan returns true if the version is newer than the date period.
func (a Spec) IsNewerThan(t time.Time, since *time.Time) bool {
if a.Maximum == "" {
return false
}

if since == nil {
now := time.Now()
since = &now
}

duration, err := parseReleaseAge(a.Maximum)
if err != nil {
logrus.Errorf("invalid MaximumReleaseAge %q: %q\n", a.Maximum, err)
return false
}

return since.Sub(t) > duration
}
Comment on lines +143 to +173
// Sanitize versions by filtering out versions that are too recent based on the MinimumReleaseAge filter
if !releaseAge.IsZero() {
sanitizedVersions := []string{}

for v := range versions {
getVersionInfo, err := getVersionInfoFromProxy(ctx, client, proxy, module, versions[v])
if err != nil {
logrus.Debugf("ignoring version %q from proxy %q due to %q\n", versions[v], proxy, err)
continue
}

releaseDate, err := time.Parse(time.RFC3339, getVersionInfo.Time)
if err != nil {
logrus.Debugf("ignoring version %q from proxy %q due to invalid release date format: %q\n", versions[v], proxy, err)
continue
}

if releaseAge.Minimum != "" && releaseAge.IsOlderThan(releaseDate, nil) {
logrus.Debugf("ignoring version %q from proxy %q because its age is below %q (released on %s)\n", versions[v], proxy, releaseAge.Minimum, releaseDate)
continue
}

if releaseAge.Maximum != "" && releaseAge.IsNewerThan(releaseDate, nil) {
logrus.Debugf("ignoring version %q from proxy %q because its age is above %q (released on %s)\n", versions[v], proxy, releaseAge.Maximum, releaseDate)
continue
}

sanitizedVersions = append(sanitizedVersions, versions[v])
}
versions = sanitizedVersions
}
Comment on lines +34 to +58
repo, err := git.PlainClone(workingDir, false, &git.CloneOptions{
URL: GolangGitRepository,
Depth: 1,
Tags: git.AllTags,
})

if err != nil {
if err != git.ErrRepositoryAlreadyExists {
return nil, err
}

repo, err = git.PlainOpen(workingDir)
if err != nil {
return nil, err
}
}

err = repo.Fetch(&git.FetchOptions{
Tags: git.AllTags,
Force: true,
Depth: 1,
})
if err != nil && err != git.NoErrAlreadyUpToDate {
return nil, err
}
Comment on lines +42 to +80
func parseReleaseAge(releaseAge string) (time.Duration, error) {
releaseAge = strings.TrimSpace(releaseAge)

if strings.HasSuffix(releaseAge, "d") {
daysStr := strings.TrimSuffix(releaseAge, "d")
dayInt, err := strconv.Atoi(daysStr)
if err != nil {
return 0, err
}
return time.ParseDuration(fmt.Sprintf("%dh", 24*dayInt))
}

if strings.HasSuffix(releaseAge, "w") {
weeksStr := strings.TrimSuffix(releaseAge, "w")
weekInt, err := strconv.Atoi(weeksStr)
if err != nil {
return 0, err
}
return time.ParseDuration(fmt.Sprintf("%dh", 24*7*weekInt))
}
if strings.HasSuffix(releaseAge, "y") {
yearsStr := strings.TrimSuffix(releaseAge, "y")
yearInt, err := strconv.Atoi(yearsStr)
if err != nil {
return 0, err
}
return time.ParseDuration(fmt.Sprintf("%dh", 24*365*yearInt))
}
if strings.HasSuffix(releaseAge, "mo") {
monthsStr := strings.TrimSuffix(releaseAge, "mo")
monthInt, err := strconv.Atoi(monthsStr)
if err != nil {
return 0, err
}
return time.ParseDuration(fmt.Sprintf("%dh", 24*365*monthInt/12))
}

return time.ParseDuration(releaseAge)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Golang Autodiscovery

2 participants