Implement git tag semver filter

This commit is contained in:
stefanprodan 2020-04-07 13:22:55 +03:00
parent 98901f2909
commit 62350a944b
5 changed files with 96 additions and 2 deletions

View File

@ -37,6 +37,10 @@ type GitRepositorySpec struct {
// The git tag to checkout, takes precedence over branch.
// +optional
Tag string `json:"tag"`
// The git tag semver expression, takes precedence over tag.
// +optional
SemVer string `json:"semver"`
}
// GitRepositoryStatus defines the observed state of GitRepository

View File

@ -55,6 +55,9 @@ spec:
interval:
description: The interval at which to check for repository updates.
type: string
semver:
description: The git tag semver expression, takes precedence over tag.
type: string
tag:
description: The git tag to checkout, takes precedence over branch.
type: string

View File

@ -23,10 +23,10 @@ import (
"os"
"os/exec"
"path/filepath"
"sigs.k8s.io/controller-runtime/pkg/event"
"strings"
"time"
"github.com/blang/semver"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-logr/logr"
@ -35,6 +35,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/predicate"
sourcerv1 "github.com/fluxcd/sourcer/api/v1alpha1"
@ -141,6 +142,7 @@ func (r *GitRepositoryReconciler) sync(gr sourcerv1.GitRepository) (sourcerv1.Re
refName = plumbing.NewTagReferenceName(gr.Spec.Tag)
}
// create tmp dir
dir, err := ioutil.TempDir("", gr.Name)
if err != nil {
ex := fmt.Errorf("tmp dir error %w", err)
@ -159,6 +161,7 @@ func (r *GitRepositoryReconciler) sync(gr sourcerv1.GitRepository) (sourcerv1.Re
Depth: 2,
ReferenceName: refName,
SingleBranch: true,
Tags: git.AllTags,
})
if err != nil {
ex := fmt.Errorf("git clone error %w", err)
@ -170,6 +173,86 @@ func (r *GitRepositoryReconciler) sync(gr sourcerv1.GitRepository) (sourcerv1.Re
}, "", ex
}
// checkout tag based on semver expression
if gr.Spec.SemVer != "" {
rng, err := semver.ParseRange(gr.Spec.SemVer)
if err != nil {
ex := fmt.Errorf("semver parse range error %w", err)
return sourcerv1.RepositoryCondition{
Type: sourcerv1.RepositoryConditionReady,
Status: corev1.ConditionFalse,
Reason: "GitCloneFailed",
Message: ex.Error(),
}, "", ex
}
repoTags, err := repo.Tags()
if err != nil {
ex := fmt.Errorf("git list tags error %w", err)
return sourcerv1.RepositoryCondition{
Type: sourcerv1.RepositoryConditionReady,
Status: corev1.ConditionFalse,
Reason: "GitCloneFailed",
Message: ex.Error(),
}, "", ex
}
tags := make(map[string]string)
_ = repoTags.ForEach(func(t *plumbing.Reference) error {
tags[t.Name().Short()] = t.Strings()[1]
return nil
})
svTags := make(map[string]string)
svers := []semver.Version{}
for tag, _ := range tags {
v, _ := semver.ParseTolerant(tag)
if rng(v) {
svers = append(svers, v)
svTags[v.String()] = tag
}
}
if len(svers) > 0 {
semver.Sort(svers)
v := svers[len(svers)-1]
t := svTags[v.String()]
commit := tags[t]
w, err := repo.Worktree()
if err != nil {
ex := fmt.Errorf("git worktree error %w", err)
return sourcerv1.RepositoryCondition{
Type: sourcerv1.RepositoryConditionReady,
Status: corev1.ConditionFalse,
Reason: "GitCheckoutFailed",
Message: ex.Error(),
}, "", ex
}
err = w.Checkout(&git.CheckoutOptions{
Hash: plumbing.NewHash(commit),
})
if err != nil {
ex := fmt.Errorf("git checkout error %w", err)
return sourcerv1.RepositoryCondition{
Type: sourcerv1.RepositoryConditionReady,
Status: corev1.ConditionFalse,
Reason: "GitCheckoutFailed",
Message: ex.Error(),
}, "", ex
}
} else {
ex := fmt.Errorf("no match found for semver %s", gr.Spec.SemVer)
return sourcerv1.RepositoryCondition{
Type: sourcerv1.RepositoryConditionReady,
Status: corev1.ConditionFalse,
Reason: "GitCheckoutFailed",
Message: ex.Error(),
}, "", ex
}
}
// read commit hash
ref, err := repo.Head()
if err != nil {
@ -233,7 +316,9 @@ func (r *GitRepositoryReconciler) sync(gr sourcerv1.GitRepository) (sourcerv1.Re
func (r *GitRepositoryReconciler) shouldResetStatus(gr sourcerv1.GitRepository) bool {
resetStatus := false
if gr.Status.Artifacts != "" {
if _, err := os.Stat(filepath.Join(r.StoragePath, gr.Status.Artifacts)); err != nil {
pathParts := strings.Split(gr.Status.Artifacts, "/")
path := fmt.Sprintf("repositories/%s-%s/%s", gr.Name, gr.Namespace, pathParts[len(pathParts)-1])
if _, err := os.Stat(filepath.Join(r.StoragePath, path)); err != nil {
resetStatus = true
}
}

1
go.mod
View File

@ -3,6 +3,7 @@ module github.com/fluxcd/sourcer
go 1.13
require (
github.com/blang/semver v3.5.0+incompatible
github.com/go-git/go-git/v5 v5.0.0
github.com/go-logr/logr v0.1.0
github.com/onsi/ginkgo v1.11.0

1
go.sum
View File

@ -37,6 +37,7 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/blang/semver v3.5.0+incompatible h1:CGxCgetQ64DKk7rdZ++Vfnb1+ogGNnB17OJKJXD2Cfs=
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=