Skip to content

Commit

Permalink
Add order_by option with support for 'string' ordering
Browse files Browse the repository at this point in the history
add WIP String versioning

add ExtractString test

add note to README

Signed-off-by: cscanlin <[email protected]>
  • Loading branch information
cscanlin-kwh committed Apr 29, 2023
1 parent 319e934 commit 81fe566
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 30 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ version numbers.

* `disable_multipart`: *Optional.* Disable Multipart Upload. useful for S3 compatible providers that do not support multipart upload.

* `order_by`: *default: 'semver'*:
- `'semver'`: The pattern in the regex capture group will be parsed and sorted as a semantic version
- `'string'`: The pattern in the regex capture group will be stored raw and sorted using string sorting rules

### File Names

One of the following two options must be specified:
Expand All @@ -63,7 +67,7 @@ One of the following two options must be specified:
capture group must be specified, with parentheses.

The version extracted from this pattern is used to version the resource.
Semantic versions, or just numbers, are supported. Accordingly, full regular
By default, semantic versions, or just numbers, are supported. If the `order_by: 'string'` option is given, the extracted version will be compared against the other versions as plain strings. Accordingly, full regular
expressions are supported, to specify the capture groups.

The full `regexp` will be matched against the S3 objects as if it was anchored
Expand Down
12 changes: 6 additions & 6 deletions check/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package check
import (
"errors"

"github.com/concourse/s3-resource"
s3resource "github.com/concourse/s3-resource"
"github.com/concourse/s3-resource/versions"
)

Expand Down Expand Up @@ -33,7 +33,7 @@ func (command *Command) checkByRegex(request Request) Response {
extractions := versions.GetBucketFileVersions(command.s3client, request.Source)

if request.Source.InitialPath != "" {
extraction, ok := versions.Extract(request.Source.InitialPath, request.Source.Regexp)
extraction, ok := versions.Extract(request.Source.InitialPath, request.Source.Regexp, request.Source.OrderBy)
if ok {
extractions = append([]versions.Extraction{extraction}, extractions...)
}
Expand All @@ -43,7 +43,7 @@ func (command *Command) checkByRegex(request Request) Response {
return nil
}

lastVersion, matched := versions.Extract(request.Version.Path, request.Source.Regexp)
lastVersion, matched := versions.Extract(request.Version.Path, request.Source.Regexp, request.Source.OrderBy)
if !matched {
return latestVersion(extractions)
} else {
Expand Down Expand Up @@ -98,16 +98,16 @@ func (command *Command) checkByVersionedFile(request Request) Response {

func latestVersion(extractions versions.Extractions) Response {
lastExtraction := extractions[len(extractions)-1]
return []s3resource.Version{{Path: lastExtraction.Path}}
return []s3resource.Version{{Path: lastExtraction.GetPath()}}
}

func newVersions(lastVersion versions.Extraction, extractions versions.Extractions) Response {
response := Response{}

for _, extraction := range extractions {
if extraction.Version.Compare(lastVersion.Version) >= 0 {
if extraction.Compare(lastVersion) >= 0 {
version := s3resource.Version{
Path: extraction.Path,
Path: extraction.GetPath(),
}
response = append(response, version)
}
Expand Down
6 changes: 3 additions & 3 deletions in/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"path/filepath"
"strconv"

"github.com/concourse/s3-resource"
s3resource "github.com/concourse/s3-resource"
"github.com/concourse/s3-resource/versions"
)

Expand Down Expand Up @@ -66,12 +66,12 @@ func (command *Command) Run(destinationDir string, request Request) (Response, e

remotePath = request.Version.Path

extraction, ok := versions.Extract(remotePath, request.Source.Regexp)
extraction, ok := versions.Extract(remotePath, request.Source.Regexp, request.Source.OrderBy)
if !ok {
return Response{}, fmt.Errorf("regex does not match provided version: %#v", request.Version)
}

versionNumber = extraction.VersionNumber
versionNumber = extraction.GetVersionNumber()

isInitialVersion = request.Source.InitialPath != "" && request.Version.Path == request.Source.InitialPath
} else {
Expand Down
4 changes: 4 additions & 0 deletions models.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type Source struct {
InitialContentText string `json:"initial_content_text"`
InitialContentBinary string `json:"initial_content_binary"`
DisableMultipart bool `json:"disable_multipart"`
OrderBy string `json:"order_by"`
}

func (source Source) IsValid() (bool, string) {
Expand All @@ -41,6 +42,9 @@ func (source Source) IsValid() (bool, string) {
if source.InitialContentText != "" && source.InitialContentBinary != "" {
return false, "please use intial_content_text or initial_content_binary but not both"
}
if source.OrderBy != "" && source.OrderBy != "string" && source.OrderBy != "semver" {
return false, "please use either 'string' or 'semver' as argument for `order_by`"
}

hasInitialContent := source.InitialContentText != "" || source.InitialContentBinary != ""
if hasInitialContent && source.InitialVersion == "" && source.InitialPath == "" {
Expand Down
105 changes: 92 additions & 13 deletions versions/versions.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ import (
"github.com/cppforlife/go-semi-semantic/version"
)

func sliceIndex(haystack []string, needle string) int {
for i, element := range haystack {
if element == needle {
return i
}
}

return -1
}

func MatchUnanchored(paths []string, pattern string) ([]string, error) {
matched := []string{}

Expand All @@ -28,13 +38,13 @@ func MatchUnanchored(paths []string, pattern string) ([]string, error) {
return matched, nil
}

func Extract(path string, pattern string) (Extraction, bool) {
func GetMatch(path string, pattern string) (string, bool) {
compiled := regexp.MustCompile(pattern)
matches := compiled.FindStringSubmatch(path)

var match string
if len(matches) < 2 { // whole string and match
return Extraction{}, false
return "", false
} else if len(matches) == 2 {
match = matches[1]
} else if len(matches) > 2 { // many matches
Expand All @@ -47,29 +57,58 @@ func Extract(path string, pattern string) (Extraction, bool) {
match = matches[1]
}
}
return match, true
}

func Extract(path string, pattern string, order_by string) (Extraction, bool) {
if order_by == "string" {
return ExtractString(path, pattern)
} else {
return ExtractSemver(path, pattern)
}
}
func ExtractSemver(path string, pattern string) (SemverExtraction, bool) {

match, ok := GetMatch(path, pattern)

if !ok {
return SemverExtraction{}, false
}

ver, err := version.NewVersionFromString(match)
if err != nil {
panic("version number was not valid: " + err.Error())
}

extraction := Extraction{
extraction := SemverExtraction{
Path: path,
Version: ver,
VersionNumber: match,
}

return extraction, true
return extraction, ok
}
func ExtractString(path string, pattern string) (StringExtraction, bool) {

func sliceIndex(haystack []string, needle string) int {
for i, element := range haystack {
if element == needle {
return i
}
match, ok := GetMatch(path, pattern)

if !ok {
return StringExtraction{}, false
}

return -1
extraction := StringExtraction{
Path: path,
VersionNumber: match,
}

return extraction, ok
}

type Extraction interface {
Compare(other Extraction) int
GetPath() string
GetVersion() version.Version
GetVersionNumber() string
}

type Extractions []Extraction
Expand All @@ -79,14 +118,14 @@ func (e Extractions) Len() int {
}

func (e Extractions) Less(i int, j int) bool {
return e[i].Version.IsLt(e[j].Version)
return e[i].Compare(e[j]) == -1
}

func (e Extractions) Swap(i int, j int) {
e[i], e[j] = e[j], e[i]
}

type Extraction struct {
type SemverExtraction struct {
// path to s3 object in bucket
Path string

Expand All @@ -97,6 +136,46 @@ type Extraction struct {
VersionNumber string
}

func (s SemverExtraction) Compare(other Extraction) int {
return s.Version.Compare(other.GetVersion())
}

func (s SemverExtraction) GetPath() string {
return s.Path
}

func (s SemverExtraction) GetVersion() version.Version {
return s.Version
}

func (s SemverExtraction) GetVersionNumber() string {
return s.VersionNumber
}

type StringExtraction struct {
// path to s3 object in bucket
Path string

// the raw version match
VersionNumber string
}

func (s StringExtraction) Compare(other Extraction) int {
return strings.Compare(s.VersionNumber, other.GetVersionNumber())
}

func (s StringExtraction) GetPath() string {
return s.Path
}

func (s StringExtraction) GetVersion() version.Version {
panic("StringExtraction does not have a parsed Version")
}

func (s StringExtraction) GetVersionNumber() string {
return s.VersionNumber
}

// GetMatchingPathsFromBucket gets all the paths in the S3 bucket `bucketName` which match all the sections of `regex`
//
// `regex` is a forward-slash (`/`) delimited list of regular expressions that
Expand Down Expand Up @@ -187,7 +266,7 @@ func GetBucketFileVersions(client s3resource.S3Client, source s3resource.Source)

var extractions = make(Extractions, 0, len(matchingPaths))
for _, path := range matchingPaths {
extraction, ok := Extract(path, regex)
extraction, ok := Extract(path, regex, source.OrderBy)

if ok {
extractions = append(extractions, extraction)
Expand Down
Loading

0 comments on commit 81fe566

Please sign in to comment.