From e2842ce86bec583165eb09b422f16114221a3d77 Mon Sep 17 00:00:00 2001 From: Ravi Kotecha Date: Mon, 20 Jan 2025 17:58:28 +0000 Subject: [PATCH 1/6] Implement support for calver as an additional update-strategy --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/argoproj-labs/argocd-image-updater?shareId=XXXX-XXXX-XXXX-XXXX). Signed-off-by: Ravi Kotecha --- docs/basics/update-strategies.md | 51 +++++++++++++++++++++++++++ registry-scanner/pkg/image/options.go | 2 ++ registry-scanner/pkg/image/version.go | 6 ++++ 3 files changed, 59 insertions(+) diff --git a/docs/basics/update-strategies.md b/docs/basics/update-strategies.md index d549c0d8..eee56e7f 100644 --- a/docs/basics/update-strategies.md +++ b/docs/basics/update-strategies.md @@ -18,6 +18,7 @@ The following update strategies are currently supported: * [latest/newest-build](#strategy-latest) - Update to the most recently built image found in a registry * [digest](#strategy-digest) - Update to the latest version of a given version (tag), using the tag's SHA digest * [name/alphabetical](#strategy-name) - Sorts tags alphabetically and update to the one with the highest cardinality +* [calver](#strategy-calver) - Update to the latest version of an image considering calendar versioning constraints !!!warning "Renamed image update strategies" The `latest` strategy has been renamed to `newest-build`, and `name` strategy has been renamed to `alphabetical`. @@ -292,3 +293,53 @@ argocd-image-updater.argoproj.io/myimage.allow-tags: regexp:^[0-9]{4}-[0-9]{2}-[ would only consider tags that match a given regular expression for update. In this case, only tags matching a date specification of `YYYY-MM-DD` would be considered for update. + +### calver - Update to calendar versions + +Strategy name: `calver` + +Basic configuration: + +```yaml +argocd-image-updater.argoproj.io/image-list: some/image[:] +argocd-image-updater.argoproj.io/.update-strategy: calver +``` + +The `calver` strategy allows you to track & update images which use tags that +follow the +[calendar versioning scheme](https://calver.org). Tag names must contain calver +compatible identifiers in the format `YYYY.MM.DD`, where `YYYY`, `MM`, and `DD` must be +whole numbers. + +This will allow you to update to the latest version of an image within a given +year, month, or day, or just to the latest version that is tagged with a valid +calendar version identifier. + +To tell Argo CD Image Updater which versions are allowed, simply give a calver +version as a constraint in the `image-list` annotation. For example, to allow +updates to the latest version within the `2023.08` month, use + +``` +argocd-image-updater.argoproj.io/image-list: some/image:2023.08.x +``` + +The above example would update to any new tag pushed to the registry matching +this constraint, e.g. `2023.08.15`, `2023.08.30` etc, but not to a new month +(e.g. `2023.09`). + +Likewise, to allow updates to any month within the year `2023`, +use + +```yaml +argocd-image-updater.argoproj.io/image-list: some/image:2023.x +``` + +The above example would update to any new tag pushed to the registry matching +this constraint, e.g. `2023.08.15`, `2023.09.01`, `2023.12.31` etc, but not to a new year +(e.g. `2024`). + +If no version constraint is specified in the list of allowed images, Argo CD +Image Updater will pick the highest version number found in the registry. + +Argo CD Image Updater will omit any tags from your registry that do not match +a calendar version when using the `calver` update strategy. diff --git a/registry-scanner/pkg/image/options.go b/registry-scanner/pkg/image/options.go index 308569c4..b4c01fa9 100644 --- a/registry-scanner/pkg/image/options.go +++ b/registry-scanner/pkg/image/options.go @@ -112,6 +112,8 @@ func (img *ContainerImage) ParseUpdateStrategy(val string) UpdateStrategy { return StrategyAlphabetical case "digest": return StrategyDigest + case "calver": + return StrategyCalVer default: logCtx.Warnf("Unknown sort option %s -- using semver", val) return StrategySemVer diff --git a/registry-scanner/pkg/image/version.go b/registry-scanner/pkg/image/version.go index 97437bd5..cccf9838 100644 --- a/registry-scanner/pkg/image/version.go +++ b/registry-scanner/pkg/image/version.go @@ -22,6 +22,8 @@ const ( StrategyAlphabetical UpdateStrategy = 2 // VersionSortDigest uses latest digest of an image StrategyDigest UpdateStrategy = 3 + // VersionSortCalVer sorts tags using calendar versioning + StrategyCalVer UpdateStrategy = 4 ) func (us UpdateStrategy) String() string { @@ -34,6 +36,8 @@ func (us UpdateStrategy) String() string { return "alphabetical" case StrategyDigest: return "digest" + case StrategyCalVer: + return "calver" } return "unknown" @@ -93,6 +97,8 @@ func (img *ContainerImage) GetNewestVersionFromTags(vc *VersionConstraint, tagLi availableTags = tagList.SortByDate() case StrategyDigest: availableTags = tagList.SortAlphabetically() + case StrategyCalVer: + availableTags = tagList.SortByCalVer() } considerTags := tag.SortableImageTagList{} From 1d8b17e1deb518fa53bcebafa51f5b4380093ac2 Mon Sep 17 00:00:00 2001 From: Ravi Kotecha Date: Tue, 21 Jan 2025 11:23:46 +0000 Subject: [PATCH 2/6] add calver dep Signed-off-by: Ravi Kotecha --- registry-scanner/go.mod | 2 ++ registry-scanner/go.sum | 4 ++++ registry-scanner/pkg/tag/tag.go | 24 ++++++++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/registry-scanner/go.mod b/registry-scanner/go.mod index 862c9326..7cac3325 100644 --- a/registry-scanner/go.mod +++ b/registry-scanner/go.mod @@ -6,6 +6,7 @@ require ( github.com/Masterminds/semver/v3 v3.3.1 github.com/argoproj/pkg v0.13.7-0.20230627120311-a4dd357b057e github.com/distribution/distribution/v3 v3.0.0-20230722181636-7b502560cad4 + github.com/k1LoW/calver v0.7.3 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.0 github.com/patrickmn/go-cache v2.1.0+incompatible @@ -55,6 +56,7 @@ require ( github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.48.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect + github.com/snabb/isoweek v1.0.3 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/x448/float16 v0.8.4 // indirect diff --git a/registry-scanner/go.sum b/registry-scanner/go.sum index 2c211589..a6cb101c 100644 --- a/registry-scanner/go.sum +++ b/registry-scanner/go.sum @@ -156,6 +156,8 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/k1LoW/calver v0.7.3 h1:i05crMZqiIgkswcv0esE7DBi+QBpIr1BP/3PgV5HAmg= +github.com/k1LoW/calver v0.7.3/go.mod h1:Djp3yuoeRnIxwWjLOwY14lgcha5YnWYggABhNhSxHl4= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= @@ -284,6 +286,8 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/snabb/isoweek v1.0.3 h1:BwEULUhj7UToLLa7FivDTLzA4y1epTYkLhnn31huBRs= +github.com/snabb/isoweek v1.0.3/go.mod h1:J5hJfY1CG56xmKCC/4XfoaWZcOiB+qntmyKEDATSnlw= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= diff --git a/registry-scanner/pkg/tag/tag.go b/registry-scanner/pkg/tag/tag.go index 26cc2157..33ab17a5 100644 --- a/registry-scanner/pkg/tag/tag.go +++ b/registry-scanner/pkg/tag/tag.go @@ -9,6 +9,7 @@ import ( "github.com/argoproj-labs/argocd-image-updater/registry-scanner/pkg/log" "github.com/Masterminds/semver/v3" + "github.com/k1LoW/calver" ) // ImageTag is a representation of an image tag with metadata @@ -172,6 +173,29 @@ func (il ImageTagList) SortBySemVer() SortableImageTagList { return sil } +func (il ImageTagList) SortByCalVer() SortableImageTagList { + il.lock.RLock() + defer il.lock.RUnlock() + sil := make(SortableImageTagList, 0, len(il.items)) + calvers := make([]calver.Calver, 0, len(il.items)) + for _, v := range il.items { + cv, err := calver.Parse(v.TagName) + if err != nil { + // Fallback to alphabetical order if parsing fails + sil = append(sil, v) + } else { + calvers = append(calvers, cv) + } + } + sort.Slice(calvers, func(i, j int) bool { + return calvers[i].String() < calvers[j].String() + }) + for _, cv := range calvers { + sil = append(sil, il.items[cv.String()]) + } + return sil +} + // Should only be used in a method that holds a lock on the ImageTagList func (il ImageTagList) unlockedContains(tag *ImageTag) bool { if _, ok := il.items[tag.TagName]; ok { From 840c1b4fe44a3018ef47a2612dbb88cf8d4eff52 Mon Sep 17 00:00:00 2001 From: Ravi Kotecha Date: Tue, 21 Jan 2025 11:26:25 +0000 Subject: [PATCH 3/6] use native calver sort Signed-off-by: Ravi Kotecha --- registry-scanner/pkg/tag/tag.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/registry-scanner/pkg/tag/tag.go b/registry-scanner/pkg/tag/tag.go index 33ab17a5..4ae68dce 100644 --- a/registry-scanner/pkg/tag/tag.go +++ b/registry-scanner/pkg/tag/tag.go @@ -177,7 +177,8 @@ func (il ImageTagList) SortByCalVer() SortableImageTagList { il.lock.RLock() defer il.lock.RUnlock() sil := make(SortableImageTagList, 0, len(il.items)) - calvers := make([]calver.Calver, 0, len(il.items)) + calvers := make(calver.Calvers, 0, len(il.items)) + for _, v := range il.items { cv, err := calver.Parse(v.TagName) if err != nil { @@ -187,9 +188,7 @@ func (il ImageTagList) SortByCalVer() SortableImageTagList { calvers = append(calvers, cv) } } - sort.Slice(calvers, func(i, j int) bool { - return calvers[i].String() < calvers[j].String() - }) + calvers.Sort() for _, cv := range calvers { sil = append(sil, il.items[cv.String()]) } From 72ac18bfae8fc525751c61cc8af889c74b9cdaf9 Mon Sep 17 00:00:00 2001 From: Ravi Kotecha Date: Thu, 23 Jan 2025 13:23:02 +0000 Subject: [PATCH 4/6] handle calvar format through allow-tags Signed-off-by: Ravi Kotecha --- registry-scanner/pkg/image/matchfunc.go | 11 +++++++++++ registry-scanner/pkg/image/options.go | 2 ++ registry-scanner/pkg/image/version.go | 8 +++++++- registry-scanner/pkg/image/version_test.go | 9 +++++++++ registry-scanner/pkg/tag/tag.go | 4 ++-- 5 files changed, 31 insertions(+), 3 deletions(-) diff --git a/registry-scanner/pkg/image/matchfunc.go b/registry-scanner/pkg/image/matchfunc.go index 036e6fb0..32eb93b5 100644 --- a/registry-scanner/pkg/image/matchfunc.go +++ b/registry-scanner/pkg/image/matchfunc.go @@ -4,6 +4,7 @@ import ( "regexp" "github.com/argoproj-labs/argocd-image-updater/registry-scanner/pkg/log" + "github.com/k1LoW/calver" ) // MatchFuncAny matches any pattern, i.e. always returns true @@ -25,3 +26,13 @@ func MatchFuncRegexp(tagName string, args interface{}) bool { } return pattern.Match([]byte(tagName)) } + +// MatchFuncCalVer checks if a tag matches the specified CalVer layout +func MatchFuncCalVer(tagName string, args interface{}) bool { + layoutStr, ok := args.(string) + if !ok { + return false + } + _, err := calver.Parse(layoutStr, tagName) + return err == nil +} diff --git a/registry-scanner/pkg/image/options.go b/registry-scanner/pkg/image/options.go index b4c01fa9..76dcd0f5 100644 --- a/registry-scanner/pkg/image/options.go +++ b/registry-scanner/pkg/image/options.go @@ -175,6 +175,8 @@ func (img *ContainerImage) ParseMatchfunc(val string) (MatchFuncFn, interface{}) return MatchFuncNone, nil } return MatchFuncRegexp, re + case "calver": + return MatchFuncCalVer, opt[1] default: logCtx.Warnf("Unknown match function: %s", opt[0]) return MatchFuncNone, nil diff --git a/registry-scanner/pkg/image/version.go b/registry-scanner/pkg/image/version.go index cccf9838..80720003 100644 --- a/registry-scanner/pkg/image/version.go +++ b/registry-scanner/pkg/image/version.go @@ -1,6 +1,7 @@ package image import ( + "fmt" "path/filepath" "github.com/argoproj-labs/argocd-image-updater/registry-scanner/pkg/log" @@ -98,7 +99,12 @@ func (img *ContainerImage) GetNewestVersionFromTags(vc *VersionConstraint, tagLi case StrategyDigest: availableTags = tagList.SortAlphabetically() case StrategyCalVer: - availableTags = tagList.SortByCalVer() + layout, ok := vc.MatchArgs.(string) + if !ok { + logCtx.Errorf("calver layout not specified in allow-tags annotation") + return nil, fmt.Errorf("calver layout not specified in allow-tags annotation") + } + availableTags = tagList.SortByCalVer(layout) } considerTags := tag.SortableImageTagList{} diff --git a/registry-scanner/pkg/image/version_test.go b/registry-scanner/pkg/image/version_test.go index 4c1fe2cd..0ccb0dfa 100644 --- a/registry-scanner/pkg/image/version_test.go +++ b/registry-scanner/pkg/image/version_test.go @@ -76,6 +76,15 @@ func Test_LatestVersion(t *testing.T) { assert.Nil(t, newTag) }) + t.Run("Find the latest version with a calver constraint that is valid", func(t *testing.T) { + tagList := newImageTagList([]string{"2021.01.01", "2022.02.02", "2023.05.01", "2025.01.25"}) + img := NewFromIdentifier("jannfis/test:2021.01.01") + vc := VersionConstraint{Constraint: "2022.01.01", Strategy: StrategyCalVer, MatchArgs: "YYYY.MM.DD"} + newTag, err := img.GetNewestVersionFromTags(&vc, tagList) + assert.NoError(t, err) + assert.NotNil(t, newTag) + }) + t.Run("Find the latest version with no tags", func(t *testing.T) { tagList := newImageTagList([]string{}) img := NewFromIdentifier("jannfis/test:1.0") diff --git a/registry-scanner/pkg/tag/tag.go b/registry-scanner/pkg/tag/tag.go index 4ae68dce..0c64dd4f 100644 --- a/registry-scanner/pkg/tag/tag.go +++ b/registry-scanner/pkg/tag/tag.go @@ -173,14 +173,14 @@ func (il ImageTagList) SortBySemVer() SortableImageTagList { return sil } -func (il ImageTagList) SortByCalVer() SortableImageTagList { +func (il ImageTagList) SortByCalVer(layout string) SortableImageTagList { il.lock.RLock() defer il.lock.RUnlock() sil := make(SortableImageTagList, 0, len(il.items)) calvers := make(calver.Calvers, 0, len(il.items)) for _, v := range il.items { - cv, err := calver.Parse(v.TagName) + cv, err := calver.Parse(layout, v.TagName) if err != nil { // Fallback to alphabetical order if parsing fails sil = append(sil, v) From 77baf9519be05c9f3dac9e5648a68fa4be0f463b Mon Sep 17 00:00:00 2001 From: Ravi Kotecha Date: Thu, 23 Jan 2025 14:28:19 +0000 Subject: [PATCH 5/6] add more tests for calver strategy Signed-off-by: Ravi Kotecha --- registry-scanner/pkg/image/version_test.go | 64 ++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/registry-scanner/pkg/image/version_test.go b/registry-scanner/pkg/image/version_test.go index 0ccb0dfa..25352e9f 100644 --- a/registry-scanner/pkg/image/version_test.go +++ b/registry-scanner/pkg/image/version_test.go @@ -83,6 +83,65 @@ func Test_LatestVersion(t *testing.T) { newTag, err := img.GetNewestVersionFromTags(&vc, tagList) assert.NoError(t, err) assert.NotNil(t, newTag) + assert.Equal(t, "2025.01.25", newTag.TagName) + }) + + t.Run("Find latest version with YYYY.MM calver format", func(t *testing.T) { + tagList := newImageTagList([]string{"2021.01", "2022.02", "2023.05", "2025.01"}) + img := NewFromIdentifier("jannfis/test:2021.01") + vc := VersionConstraint{Constraint: "2022.01", Strategy: StrategyCalVer, MatchArgs: "YYYY.MM"} + newTag, err := img.GetNewestVersionFromTags(&vc, tagList) + assert.NoError(t, err) + assert.NotNil(t, newTag) + assert.Equal(t, "2025.01", newTag.TagName) + }) + + t.Run("Find latest version with YY.MM.DD calver format", func(t *testing.T) { + tagList := newImageTagList([]string{"21.01.01", "22.02.02", "23.05.01", "25.01.25"}) + img := NewFromIdentifier("jannfis/test:21.01.01") + vc := VersionConstraint{Constraint: "22.01.01", Strategy: StrategyCalVer, MatchArgs: "YY.MM.DD"} + newTag, err := img.GetNewestVersionFromTags(&vc, tagList) + assert.NoError(t, err) + assert.NotNil(t, newTag) + assert.Equal(t, "25.01.25", newTag.TagName) + }) + + t.Run("Invalid calver format should return error", func(t *testing.T) { + tagList := newImageTagList([]string{"2021.01.01", "2022.02.02"}) + img := NewFromIdentifier("jannfis/test:2021.01.01") + vc := VersionConstraint{Constraint: "2022.01.01", Strategy: StrategyCalVer, MatchArgs: "invalid-format"} + newTag, err := img.GetNewestVersionFromTags(&vc, tagList) + assert.Error(t, err) + assert.Nil(t, newTag) + }) + + t.Run("Tags not matching calver format should be ignored", func(t *testing.T) { + tagList := newImageTagList([]string{"2021.01.01", "invalid", "2023.05.01", "not-a-date"}) + img := NewFromIdentifier("jannfis/test:2021.01.01") + vc := VersionConstraint{Constraint: "2022.01.01", Strategy: StrategyCalVer, MatchArgs: "YYYY.MM.DD"} + newTag, err := img.GetNewestVersionFromTags(&vc, tagList) + assert.NoError(t, err) + assert.NotNil(t, newTag) + assert.Equal(t, "2023.05.01", newTag.TagName) + }) + + t.Run("Empty tag list with calver should return nil", func(t *testing.T) { + tagList := newImageTagList([]string{}) + img := NewFromIdentifier("jannfis/test:2021.01.01") + vc := VersionConstraint{Constraint: "2022.01.01", Strategy: StrategyCalVer, MatchArgs: "YYYY.MM.DD"} + newTag, err := img.GetNewestVersionFromTags(&vc, tagList) + assert.NoError(t, err) + assert.Nil(t, newTag) + }) + + t.Run("Missing constraint with calver should use current date", func(t *testing.T) { + tagList := newImageTagList([]string{"2021.01.01", "2022.02.02", "2023.05.01"}) + img := NewFromIdentifier("jannfis/test:2021.01.01") + vc := VersionConstraint{Strategy: StrategyCalVer, MatchArgs: "YYYY.MM.DD"} + newTag, err := img.GetNewestVersionFromTags(&vc, tagList) + assert.NoError(t, err) + assert.NotNil(t, newTag) + assert.Equal(t, "2023.05.01", newTag.TagName) }) t.Run("Find the latest version with no tags", func(t *testing.T) { @@ -149,6 +208,7 @@ func Test_UpdateStrategy_String(t *testing.T) { {"StrategyNewestBuild", StrategyNewestBuild, "newest-build"}, {"StrategyAlphabetical", StrategyAlphabetical, "alphabetical"}, {"StrategyDigest", StrategyDigest, "digest"}, + {"StrategyCalVer", StrategyCalVer, "calver"}, {"unknown", UpdateStrategy(-1), "unknown"}, } for _, tt := range tests { @@ -180,6 +240,7 @@ func Test_UpdateStrategy_IsCacheable(t *testing.T) { assert.True(t, StrategySemVer.IsCacheable()) assert.True(t, StrategyNewestBuild.IsCacheable()) assert.True(t, StrategyAlphabetical.IsCacheable()) + assert.True(t, StrategyCalVer.IsCacheable()) assert.False(t, StrategyDigest.IsCacheable()) } @@ -187,6 +248,7 @@ func Test_UpdateStrategy_NeedsMetadata(t *testing.T) { assert.False(t, StrategySemVer.NeedsMetadata()) assert.True(t, StrategyNewestBuild.NeedsMetadata()) assert.False(t, StrategyAlphabetical.NeedsMetadata()) + assert.False(t, StrategyCalVer.NeedsMetadata()) assert.False(t, StrategyDigest.NeedsMetadata()) } @@ -194,6 +256,7 @@ func Test_UpdateStrategy_NeedsVersionConstraint(t *testing.T) { assert.False(t, StrategySemVer.NeedsVersionConstraint()) assert.False(t, StrategyNewestBuild.NeedsVersionConstraint()) assert.False(t, StrategyAlphabetical.NeedsVersionConstraint()) + assert.True(t, StrategyCalVer.NeedsVersionConstraint()) assert.True(t, StrategyDigest.NeedsVersionConstraint()) } @@ -201,5 +264,6 @@ func Test_UpdateStrategy_WantsOnlyConstraintTag(t *testing.T) { assert.False(t, StrategySemVer.WantsOnlyConstraintTag()) assert.False(t, StrategyNewestBuild.WantsOnlyConstraintTag()) assert.False(t, StrategyAlphabetical.WantsOnlyConstraintTag()) + assert.False(t, StrategyCalVer.WantsOnlyConstraintTag()) assert.True(t, StrategyDigest.WantsOnlyConstraintTag()) } From e2d56993a50ef8abf8e615206fbb21fc933eef47 Mon Sep 17 00:00:00 2001 From: Ravi Kotecha Date: Thu, 23 Jan 2025 15:48:47 +0000 Subject: [PATCH 6/6] add docs for calver update strategy make clear that format must be supplied Signed-off-by: Ravi Kotecha --- docs/basics/update-strategies.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/basics/update-strategies.md b/docs/basics/update-strategies.md index eee56e7f..1856130c 100644 --- a/docs/basics/update-strategies.md +++ b/docs/basics/update-strategies.md @@ -303,8 +303,15 @@ Basic configuration: ```yaml argocd-image-updater.argoproj.io/image-list: some/image[:] argocd-image-updater.argoproj.io/.update-strategy: calver +argocd-image-updater.argoproj.io/.allow-tags: calver:YYYY.0M.MICRO ``` +!!! note "CalVer Format Specification" + The `calver` strategy requires defining the version format using the [calver layout syntax](https://github.com/k1LoW/calver). Common patterns include: + - `YYYY.0M.MICRO` for year.month.counter (e.g. 2023.08.1) + - `YY.MM.MICRO` for 2-digit year.month.counter (e.g. 23.8.5) + - `YYYY.MM.DD` for date-based versions (e.g. 2023.08.15) + The `calver` strategy allows you to track & update images which use tags that follow the [calendar versioning scheme](https://calver.org). Tag names must contain calver