Add configurable tag for use with auto_tag (#36)

Implements #30, allows setting a (single) tag to override the default of `latest` allows for the woodpecker-ci usecase of setting `next` as the tag for the default branch.

Co-authored-by: Gapodo <gapodo@datenclown.at>
Reviewed-on: https://codeberg.org/woodpecker-plugins/plugin-docker-buildx/pulls/36
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: gapodo <gapodo@geekvoid.net>
Co-committed-by: gapodo <gapodo@geekvoid.net>
This commit is contained in:
gapodo 2022-11-05 03:25:44 +01:00 committed by 6543
parent f10f7b4c7f
commit fce3289743
6 changed files with 192 additions and 64 deletions

View File

@ -127,6 +127,13 @@ func settingsFlags(settings *plugin.Settings) []cli.Flag {
Usage: "generates tag names automatically based on git branch and git tag",
Destination: &settings.Build.TagsAuto,
},
&cli.StringFlag{
Name: "tags.defaultName",
EnvVars: []string{"PLUGIN_DEFAULT_TAG"},
Usage: "allows setting an alternative to `latest` for the auto tag",
Destination: &settings.Build.TagsDefaultName,
Value: "latest",
},
&cli.StringFlag{
Name: "tags.suffix",
EnvVars: []string{"PLUGIN_DEFAULT_SUFFIX", "PLUGIN_AUTO_TAG_SUFFIX"},

View File

@ -99,6 +99,7 @@ It will automatically generate buildkit configuration to use custom CA certifica
| `context` | `.` | sets the path of the build context to use
| `default_tags`/`auto_tag` | `false` | generates tag names automatically based on git branch and git tag, tags supplied via `tags` are additionally added to the auto_tags without suffix
| `default_suffix"`/`auto_tag_suffix`| *none* | generates tag names with the given suffix
| `default_tag` | `latest` | overrides the default tag name used when generating with `auto_tag` enabled
| `label`/`labels` | *none* | sets labels to use for the image in format `<name>=<value>`
| `default_labels`/`auto_labels` | `true` | sets docker image labels based on git information
| `build_args` | *none* | sets custom build arguments for the build

View File

@ -43,28 +43,29 @@ type Login struct {
// Build defines Docker build parameters.
type Build struct {
Remote string // Git remote URL
Ref string // Git commit ref
Branch string // Git repository branch
Dockerfile string // Docker build Dockerfile
Context string // Docker build context
TagsAuto bool // Docker build auto tag
TagsSuffix string // Docker build tags with suffix
Tags cli.StringSlice // Docker build tags
LabelsAuto bool // Docker build auto labels
Labels cli.StringSlice // Docker build labels
Platforms cli.StringSlice // Docker build target platforms
Args cli.StringSlice // Docker build args
ArgsEnv cli.StringSlice // Docker build args from env
Target string // Docker build target
Output string // Docker build output
Pull bool // Docker build pull
CacheFrom cli.StringSlice // Docker build cache-from
Compress bool // Docker build compress
Repo cli.StringSlice // Docker build repository
NoCache bool // Docker build no-cache
AddHost cli.StringSlice // Docker build add-host
Quiet bool // Docker build quiet
Remote string // Git remote URL
Ref string // Git commit ref
Branch string // Git repository branch
Dockerfile string // Docker build Dockerfile
Context string // Docker build context
TagsAuto bool // Docker build auto tag
TagsDefaultName string // Docker build auto tag name override
TagsSuffix string // Docker build tags with suffix
Tags cli.StringSlice // Docker build tags
LabelsAuto bool // Docker build auto labels
Labels cli.StringSlice // Docker build labels
Platforms cli.StringSlice // Docker build target platforms
Args cli.StringSlice // Docker build args
ArgsEnv cli.StringSlice // Docker build args from env
Target string // Docker build target
Output string // Docker build output
Pull bool // Docker build pull
CacheFrom cli.StringSlice // Docker build cache-from
Compress bool // Docker build compress
Repo cli.StringSlice // Docker build repository
NoCache bool // Docker build no-cache
AddHost cli.StringSlice // Docker build add-host
Quiet bool // Docker build quiet
}
// Settings for the Plugin.
@ -110,6 +111,10 @@ func (p *Plugin) Validate() error {
return err
}
if !isSingleTag(p.settings.Build.TagsDefaultName) {
return fmt.Errorf("'%s' is not a valid, single tag", p.settings.Build.TagsDefaultName)
}
// beside the default login all other logins need to set a username and password
for _, l := range p.settings.Logins[1:] {
if l.anonymous() {
@ -126,6 +131,7 @@ func (p *Plugin) Validate() error {
tag, err := DefaultTagSuffix(
p.settings.Build.Ref,
p.settings.Build.TagsSuffix,
p.settings.Build.TagsDefaultName,
)
if err != nil {
logrus.Printf("cannot build docker image for %s, invalid semantic version", p.settings.Build.Ref)

View File

@ -13,10 +13,11 @@ var defaultSettings = Settings{
StoragePath: "/var/lib/docker",
},
Build: Build{
Context: ".",
Tags: *cli.NewStringSlice("latest"),
LabelsAuto: true,
Pull: true,
Context: ".",
Tags: *cli.NewStringSlice("latest"),
TagsDefaultName: "latest",
LabelsAuto: true,
Pull: true,
},
DefaultLogin: Login{
Registry: "https://index.docker.io/v1/",

View File

@ -2,6 +2,7 @@ package plugin
import (
"fmt"
"regexp"
"strings"
"github.com/coreos/go-semver/semver"
@ -9,8 +10,8 @@ import (
// DefaultTagSuffix returns a set of default suggested tags
// based on the commit ref with an attached suffix.
func DefaultTagSuffix(ref, suffix string) ([]string, error) {
tags, err := DefaultTags(ref)
func DefaultTagSuffix(ref, defaultTag, suffix string) ([]string, error) {
tags, err := DefaultTags(ref, defaultTag)
if err != nil {
return nil, err
}
@ -18,7 +19,7 @@ func DefaultTagSuffix(ref, suffix string) ([]string, error) {
return tags, nil
}
for i, tag := range tags {
if tag == "latest" {
if tag == defaultTag {
tags[i] = suffix
} else {
tags[i] = fmt.Sprintf("%s-%s", tag, suffix)
@ -39,14 +40,14 @@ func splitOff(input, delim string) string {
// DefaultTags returns a set of default suggested tags based on
// the commit ref.
func DefaultTags(ref string) ([]string, error) {
func DefaultTags(ref, defaultTag string) ([]string, error) {
if !strings.HasPrefix(ref, "refs/tags/") {
return []string{"latest"}, nil
return []string{defaultTag}, nil
}
v := stripTagPrefix(ref)
version, err := semver.NewVersion(v)
if err != nil {
return []string{"latest"}, err
return []string{defaultTag}, err
}
if version.PreRelease != "" || version.Metadata != "" {
return []string{
@ -91,3 +92,8 @@ func stripTagPrefix(ref string) string {
ref = strings.TrimPrefix(ref, "v")
return ref
}
func isSingleTag(tag string) bool {
// currently only filtering for seperators, this could be improoved...
return !regexp.MustCompile(`[,\s]+`).MatchString(tag) && len(tag) > 0 && len(tag) <= 128
}

View File

@ -25,19 +25,20 @@ func Test_stripTagPrefix(t *testing.T) {
func TestDefaultTags(t *testing.T) {
tests := []struct {
Before string
After []string
DefaultTag string
Before string
After []string
}{
{"", []string{"latest"}},
{"refs/heads/master", []string{"latest"}},
{"refs/tags/0.9.0", []string{"0.9", "0.9.0"}},
{"refs/tags/1.0.0", []string{"1", "1.0", "1.0.0"}},
{"refs/tags/v1.0.0", []string{"1", "1.0", "1.0.0"}},
{"refs/tags/v1.0.0-alpha.1", []string{"1.0.0-alpha.1"}},
{"latest", "", []string{"latest"}},
{"latest", "refs/heads/master", []string{"latest"}},
{"latest", "refs/tags/0.9.0", []string{"0.9", "0.9.0"}},
{"latest", "refs/tags/1.0.0", []string{"1", "1.0", "1.0.0"}},
{"latest", "refs/tags/v1.0.0", []string{"1", "1.0", "1.0.0"}},
{"latest", "refs/tags/v1.0.0-alpha.1", []string{"1.0.0-alpha.1"}},
}
for _, test := range tests {
tags, err := DefaultTags(test.Before)
tags, err := DefaultTags(test.Before, test.DefaultTag)
if err != nil {
t.Error(err)
continue
@ -50,13 +51,22 @@ func TestDefaultTags(t *testing.T) {
}
func TestDefaultTagsError(t *testing.T) {
tests := []string{
"refs/tags/x1.0.0",
"refs/tags/20190203",
tests := []struct {
DefaultTag string
Before string
}{
{
DefaultTag: "latest",
Before: "refs/tags/x1.0.0",
},
{
DefaultTag: "latest",
Before: "refs/tags/20190203",
},
}
for _, test := range tests {
_, err := DefaultTags(test)
_, err := DefaultTags(test.Before, test.DefaultTag)
if err == nil {
t.Errorf("Expect tag error for %s", test)
}
@ -65,30 +75,59 @@ func TestDefaultTagsError(t *testing.T) {
func TestDefaultTagSuffix(t *testing.T) {
tests := []struct {
Before string
Suffix string
After []string
Name string
Before string
Suffix string
After []string
DefaultTag string
}{
// without suffix
{
After: []string{"latest"},
Name: "Default tag without suffix",
DefaultTag: "latest",
After: []string{"latest"},
},
{
Before: "refs/tags/v1.0.0",
Name: "Overridden default tag without suffix",
DefaultTag: "next",
After: []string{"next"},
},
{
Name: "Generate version",
DefaultTag: "latest",
Before: "refs/tags/v1.0.0",
After: []string{
"1",
"1.0",
"1.0.0",
},
},
// with suffix
{
Suffix: "linux-amd64",
After: []string{"linux-amd64"},
Name: "Generate version with overridden default tag",
DefaultTag: "next",
Before: "refs/tags/v1.0.0",
After: []string{
"1",
"1.0",
"1.0.0",
},
},
{
Before: "refs/tags/v1.0.0",
Suffix: "linux-amd64",
Name: "Default tag with suffix (linux-amd64)",
DefaultTag: "latest",
Suffix: "linux-amd64",
After: []string{"linux-amd64"},
},
{
Name: "Overridden default tag with suffix (linux-amd64)",
DefaultTag: "next",
Suffix: "linux-amd64",
After: []string{"linux-amd64"},
},
{
Name: "Generate version with suffix (linux-amd64)",
DefaultTag: "latest",
Before: "refs/tags/v1.0.0",
Suffix: "linux-amd64",
After: []string{
"1-linux-amd64",
"1.0-linux-amd64",
@ -96,12 +135,33 @@ func TestDefaultTagSuffix(t *testing.T) {
},
},
{
Suffix: "nanoserver",
After: []string{"nanoserver"},
Name: "Generate version with suffix (linux-amd64) and overridden default tag (next)",
DefaultTag: "next",
Before: "refs/tags/v1.0.0",
Suffix: "linux-amd64",
After: []string{
"1-linux-amd64",
"1.0-linux-amd64",
"1.0.0-linux-amd64",
},
},
{
Before: "refs/tags/v1.9.2",
Suffix: "nanoserver",
Name: "Default tag with suffix (nanoserver)",
DefaultTag: "latest",
Suffix: "nanoserver",
After: []string{"nanoserver"},
},
{
Name: "Overridden default tag with suffix (nanoserver)",
DefaultTag: "next",
Suffix: "nanoserver",
After: []string{"nanoserver"},
},
{
Name: "Generate version with suffix (nanoserver)",
DefaultTag: "latest",
Before: "refs/tags/v1.9.2",
Suffix: "nanoserver",
After: []string{
"1-nanoserver",
"1.9-nanoserver",
@ -109,8 +169,32 @@ func TestDefaultTagSuffix(t *testing.T) {
},
},
{
Before: "refs/tags/v18.06.0",
Suffix: "nanoserver",
Name: "Generate version with suffix (nanoserver) and overridden default tag (next)",
DefaultTag: "latest",
Before: "refs/tags/v1.9.2",
Suffix: "nanoserver",
After: []string{
"1-nanoserver",
"1.9-nanoserver",
"1.9.2-nanoserver",
},
},
{
Name: "Generate version with suffix (zero-padded version)",
DefaultTag: "latest",
Before: "refs/tags/v18.06.0",
Suffix: "nanoserver",
After: []string{
"18-nanoserver",
"18.06-nanoserver",
"18.06.0-nanoserver",
},
},
{
Name: "Generate version with suffix (zero-padded version) with overridden default tag (next)",
DefaultTag: "next",
Before: "refs/tags/v18.06.0",
Suffix: "nanoserver",
After: []string{
"18-nanoserver",
"18.06-nanoserver",
@ -120,14 +204,14 @@ func TestDefaultTagSuffix(t *testing.T) {
}
for _, test := range tests {
tag, err := DefaultTagSuffix(test.Before, test.Suffix)
tag, err := DefaultTagSuffix(test.Before, test.DefaultTag, test.Suffix)
if err != nil {
t.Error(err)
continue
}
got, want := tag, test.After
if !reflect.DeepEqual(got, want) {
t.Errorf("Got tag %v, want %v", got, want)
t.Errorf("%q. Got tag %v, want %v", test.Name, got, want)
}
}
}
@ -195,3 +279,26 @@ func TestUseDefaultTag(t *testing.T) {
}
}
}
func Test_isSingleTag(t *testing.T) {
tests := []struct {
Tag string
IsValid bool
}{
{"latest", true},
{" latest", false},
{"LaTest__Hi", true},
{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ__.-0123456789", true},
{"_wierd.but-ok1", true},
{"latest ", false},
{"latest,next", false},
// more tests to be added, once the validation is more powerful
}
for _, test := range tests {
valid := isSingleTag(test.Tag)
if valid != test.IsValid {
t.Errorf("Tag verification '%s' tag %v, want %v", test.Tag, valid, test.IsValid)
}
}
}