Skip to content

Commit

Permalink
Merge pull request #77 from jleben/sort-by-time
Browse files Browse the repository at this point in the history
Optionally sort releases by time rather than by version
  • Loading branch information
vito authored Feb 6, 2019
2 parents b5cdd2c + 9553b61 commit 0447360
Show file tree
Hide file tree
Showing 8 changed files with 448 additions and 143 deletions.
29 changes: 24 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ Fetches and creates versioned GitHub resources.
group is used as the release version; otherwise, the entire matching substring
is used as the version.

* `order_by`: *Optional. One of [`version`, `time`]. Default `version`.*
Selects whether to order releases by version (as extracted by `tag_filter`)
or by time. See `check` behavior described below for details.

### Example

``` yaml
Expand Down Expand Up @@ -94,14 +98,29 @@ To set a custom tag filter:
### `check`: Check for released versions.

Releases are listed and sorted by their tag, using
[semver](http://semver.org) semantics if possible. If `version` is specified, `check` returns releases from the specified version on. Otherwise, `check` returns the latest release.
Lists releases, sorted either by their version or time, depending on the `order_by` source option.

When sorting by version, the version is extracted from the git tag using the `tag_filter` source option.
Versions are compared using [semver](http://semver.org) semantics if possible.

When sorting by time and a release is published, it uses the publication time, otherwise it uses the creation time.

The returned list contains an object of the following format for each release (with timestamp in the RFC3339 format):

```
{
"id": "12345",
"tag": "v1.2.3",
"timestamp": "2006-01-02T15:04:05.999999999Z"
}
```

When `check` is given such an object as the `version` parameter, it returns releases from the specified version or time on.
Otherwise it returns the release with the latest version or time.

### `in`: Fetch assets from a release.

Fetches artifacts from the given release version. If the version is not
specified, the latest version is chosen using [semver](http://semver.org)
semantics.
Fetches artifacts from the requested release.

Also creates the following files:

Expand Down
148 changes: 102 additions & 46 deletions check_command.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package resource

import (
"sort"
"strconv"

"github.com/google/go-github/github"

"github.com/cppforlife/go-semi-semantic/version"
"github.com/google/go-github/github"
"sort"
)

type CheckCommand struct {
Expand All @@ -19,6 +16,30 @@ func NewCheckCommand(github GitHub) *CheckCommand {
}
}

func SortByVersion(releases []*github.RepositoryRelease, versionParser *versionParser) {
sort.Slice(releases, func(i, j int) bool {
first, err := version.NewVersionFromString(versionParser.parse(*releases[i].TagName))
if err != nil {
return true
}

second, err := version.NewVersionFromString(versionParser.parse(*releases[j].TagName))
if err != nil {
return false
}

return first.IsLt(second)
})
}

func SortByTimestamp(releases []*github.RepositoryRelease) {
sort.Slice(releases, func(i, j int) bool {
a := releases[i]
b := releases[j]
return getTimestamp(a).Before(getTimestamp(b))
})
}

func (c *CheckCommand) Run(request CheckRequest) ([]Version, error) {
releases, err := c.github.ListReleases()
if err != nil {
Expand All @@ -29,6 +50,11 @@ func (c *CheckCommand) Run(request CheckRequest) ([]Version, error) {
return []Version{}, nil
}

orderByTime := false
if request.Source.OrderBy == "time" {
orderByTime = true
}

var filteredReleases []*github.RepositoryRelease

versionParser, err := newVersionParser(request.Source.TagFilter)
Expand All @@ -37,6 +63,7 @@ func (c *CheckCommand) Run(request CheckRequest) ([]Version, error) {
}

for _, release := range releases {

if request.Source.Drafts != *release.Draft {
continue
}
Expand All @@ -48,33 +75,49 @@ func (c *CheckCommand) Run(request CheckRequest) ([]Version, error) {
continue
}

if release.TagName == nil {
continue
}
if _, err := version.NewVersionFromString(versionParser.parse(*release.TagName)); err != nil {
continue
if orderByTime {
// We won't do anything with the tags, so just make sure the filter matches the tag.
var tag string
if release.TagName != nil {
tag = *release.TagName
}
if !versionParser.re.MatchString(tag) {
continue
}
// We don't expect any releases with a missing (zero) timestamp,
// but we skip those just in case, since the data type includes them
if getTimestamp(release).IsZero() {
continue
}
} else {
// We will sort by versions parsed out of tags, so make sure we parse successfully.
if release.TagName == nil {
continue
}
if _, err := version.NewVersionFromString(versionParser.parse(*release.TagName)); err != nil {
continue
}
}

filteredReleases = append(filteredReleases, release)
}

sort.Slice(filteredReleases, func(i, j int) bool {
first, err := version.NewVersionFromString(versionParser.parse(*filteredReleases[i].TagName))
if err != nil {
return true
}

second, err := version.NewVersionFromString(versionParser.parse(*filteredReleases[j].TagName))
if err != nil {
return false
}

return first.IsLt(second)
})
// If there are no valid releases, output an empty list.

if len(filteredReleases) == 0 {
return []Version{}, nil
}

// Sort releases by time or by version

if orderByTime {
SortByTimestamp(filteredReleases)
} else {
SortByVersion(filteredReleases, &versionParser)
}

// If request has no version, output the latest release

latestRelease := filteredReleases[len(filteredReleases)-1]

if (request.Version == Version{}) {
Expand All @@ -83,36 +126,49 @@ func (c *CheckCommand) Run(request CheckRequest) ([]Version, error) {
}, nil
}

if *latestRelease.TagName == request.Version.Tag {
return []Version{}, nil
}
// Find first release equal or later than the current version

upToLatest := false
reversedVersions := []Version{}

for _, release := range filteredReleases {
if !upToLatest {
if *release.Draft || *release.Prerelease {
id := *release.ID
upToLatest = request.Version.ID == strconv.Itoa(id)
} else {
version := *release.TagName
upToLatest = request.Version.Tag == version
}
}
var firstIncludedReleaseIndex int = -1

if upToLatest {
reversedVersions = append(reversedVersions, versionFromRelease(release))
if orderByTime {
// Only search if request has a timestamp
if !request.Version.Timestamp.IsZero() {
firstIncludedReleaseIndex = sort.Search(len(filteredReleases), func(i int) bool {
release := filteredReleases[i]
return !getTimestamp(release).Before(request.Version.Timestamp)
})
}
} else {
requestVersion, err := version.NewVersionFromString(versionParser.parse(request.Version.Tag))
if err == nil {
firstIncludedReleaseIndex = sort.Search(len(filteredReleases), func(i int) bool {
release := filteredReleases[i]
releaseVersion, err := version.NewVersionFromString(versionParser.parse(*release.TagName))
if err != nil {
return false
}
return !releaseVersion.IsLt(requestVersion)
})
}
}

if !upToLatest {
// current version was removed; start over from latest
reversedVersions = append(
reversedVersions,
// Output all releases equal or later than the current version,
// or just the latest release if there are no such releases.

outputVersions := []Version{}

if firstIncludedReleaseIndex >= 0 && firstIncludedReleaseIndex < len(filteredReleases) {
// Found first release >= current version, so output this and all the following release versions
for i := firstIncludedReleaseIndex; i < len(filteredReleases); i++ {
outputVersions = append(outputVersions, versionFromRelease(filteredReleases[i]))
}
} else {
// No release >= current version, so output the latest release version
outputVersions = append(
outputVersions,
versionFromRelease(filteredReleases[len(filteredReleases)-1]),
)
}

return reversedVersions, nil
return outputVersions, nil
}
Loading

0 comments on commit 0447360

Please sign in to comment.