Port the auto tag feature from docker plugin (#36)
* Port the auto tag feature from https://plugins.drone.io/drone-plugins/drone-docker The logic is forked from https://github.com/drone-plugins/drone-docker code base with necessary modification. I've tested it e2e for DockerHub on my Drone server via this plugin image https://hub.docker.com/repository/docker/15cm/drone-kaniko, for both tag pushes and commit pushes. With this change the .drone.yml in this repo should work as intended. Other changes: - Rename the existing "auto tag" flags/code to "expand tag" for a less misleading naming. - ATTENTION: make a breaking change to set default value of "--tags" to empty. Rationale is to expect most users to use the auto tagging feature. When power users want to specify tags, they should always explicitly set tags instead of being surprised by the default "latest" tag. * Change how --auto-tag flag works with other flags The --auto-tag has to be a breaking change. This commit limit the breaking impact to the users who enable the flag. Behaviors of flag combination after this commit: * --auto-tag=false: No changes. * --auto-tag=false,--expand-tag=true,tags=1.0.0: * Old behavior: Should not happen. --expand-tag didn't exist. * New Behavior: Build with [1,1.0,1.0.0] tags. * --auto-tag=true * Old behavior: Build with the "latest" tag. * New behavior: Build with auto detected tags. Abort if auto detection failed. * --auto-tag=true,tags=latest: same as "--auto-tag=true". * --auto-tag=true,tags=1.0.0: * Old behavior: Build with [1,1.0,1.0.0] tags. * New behavior: Abort the build with an error message. * --auto-tag=true,--expand-tag=true,tags=1.0.0: Abort the build with an error message. Also added a test for the integration of the BUILD struct and the tagger package, which is used by kaniko.go. * Update readme to note that expand-tag and auto-tag don't support artifacts
This commit is contained in:
parent
bebe65a6c2
commit
de43f3afb6
53
README.md
53
README.md
@ -39,6 +39,7 @@ docker build \
|
||||
```
|
||||
|
||||
## Usage
|
||||
### Manual Tagging
|
||||
|
||||
```console
|
||||
docker run --rm \
|
||||
@ -52,14 +53,14 @@ docker run --rm \
|
||||
plugins/kaniko:linux-amd64
|
||||
```
|
||||
|
||||
### Automatic Tagging
|
||||
With expanded tagging enabled, semantic versions can be passed to PLUGIN_TAGS directly for expansion.
|
||||
|
||||
With auto tagging enabled, semantic versions can be passed to PLUGIN_TAGS directly for expansion:
|
||||
**Note**: this feature only works for build labels. Artifact labels are not supported.
|
||||
|
||||
```console
|
||||
docker run --rm \
|
||||
-e PLUGIN_TAGS=v1.2.3,latest \
|
||||
-e PLUGIN_AUTO_TAG=true \
|
||||
-e PLUGIN_EXPAND_TAGS=true \
|
||||
-v $(pwd):/drone \
|
||||
-w /drone \
|
||||
plugins/kaniko:linux-amd64
|
||||
@ -72,14 +73,58 @@ PLUGIN_TAGS=1,1.2,1.2.3,latest
|
||||
|
||||
This allows for passing `$DRONE_TAG` directly as a tag for repos that use [semver](https://semver.org) tags.
|
||||
|
||||
To avoid confusion between repo tags and image tags, `PLUGIN_AUTO_TAG` also recognizes a semantic version
|
||||
To avoid confusion between repo tags and image tags, `PLUGIN_EXPAND_TAGS` also recognizes a semantic version
|
||||
without the `v` prefix. As such, the following is also equivalent to the above:
|
||||
|
||||
```console
|
||||
docker run --rm \
|
||||
-e PLUGIN_TAGS=1.2.3,latest \
|
||||
-e PLUGIN_EXPAND_TAGS=true \
|
||||
-v $(pwd):/drone \
|
||||
-w /drone \
|
||||
plugins/kaniko:linux-amd64
|
||||
```
|
||||
|
||||
### Auto Tagging
|
||||
The [auto tag feature](https://plugins.drone.io/drone-plugins/drone-docker/** of docker plugin is also supported.
|
||||
|
||||
When auto tagging is enabled, if any of the case is matched below, a docker build will be pushed with auto generated tags. Otherwise the docker build will be skipped.
|
||||
|
||||
**Note**: this feature only works for build labels. Artifact labels are not supported.
|
||||
|
||||
#### Git Tag Push:
|
||||
|
||||
```console
|
||||
docker run --rm \
|
||||
-e DRONE_COMMIT_REF=refs/tags/v1.2.3 \
|
||||
-e PLUGIN_REPO=foo/bar \
|
||||
-e PLUGIN_USERNAME=foo \
|
||||
-e PLUGIN_PASSWORD=bar \
|
||||
-e PLUGIN_AUTO_TAG=true \
|
||||
-v $(pwd):/drone \
|
||||
-w /drone \
|
||||
plugins/kaniko:linux-amd64
|
||||
```
|
||||
|
||||
Tags to push:
|
||||
- 1.2.3
|
||||
- 1.2
|
||||
- 1
|
||||
|
||||
#### Git Commit Push in default branch:
|
||||
|
||||
```console
|
||||
docker run --rm \
|
||||
-e DRONE_COMMIT_REF=refs/heads/master \
|
||||
-e DRONE_REPO_BRANCH=main \
|
||||
-e PLUGIN_REPO=foo/bar \
|
||||
-e PLUGIN_USERNAME=foo \
|
||||
-e PLUGIN_PASSWORD=bar \
|
||||
-e PLUGIN_AUTO_TAG=true \
|
||||
-v $(pwd):/drone \
|
||||
-w /drone \
|
||||
plugins/kaniko:linux-amd64
|
||||
```
|
||||
|
||||
Tags to push:
|
||||
- latest
|
||||
|
@ -58,6 +58,16 @@ func main() {
|
||||
Value: ".",
|
||||
EnvVar: "PLUGIN_CONTEXT",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "drone-commit-ref",
|
||||
Usage: "git commit ref passed by Drone",
|
||||
EnvVar: "DRONE_COMMIT_REF",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "drone-repo-branch",
|
||||
Usage: "git repository default branch passed by Drone",
|
||||
EnvVar: "DRONE_REPO_BRANCH",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "tags",
|
||||
Usage: "build tags",
|
||||
@ -66,10 +76,20 @@ func main() {
|
||||
FilePath: ".tags",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "auto_tag",
|
||||
Name: "expand-tag",
|
||||
Usage: "enable for semver tagging",
|
||||
EnvVar: "PLUGIN_EXPAND_TAG",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "auto-tag",
|
||||
Usage: "enable auto generation of build tags",
|
||||
EnvVar: "PLUGIN_AUTO_TAG",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "auto-tag-suffix",
|
||||
Usage: "the suffix of auto build tags",
|
||||
EnvVar: "PLUGIN_AUTO_TAG_SUFFIX",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "args",
|
||||
Usage: "build args",
|
||||
@ -171,23 +191,27 @@ func run(c *cli.Context) error {
|
||||
|
||||
plugin := kaniko.Plugin{
|
||||
Build: kaniko.Build{
|
||||
Dockerfile: c.String("dockerfile"),
|
||||
Context: c.String("context"),
|
||||
Tags: c.StringSlice("tags"),
|
||||
AutoTag: c.Bool("auto_tag"),
|
||||
Args: c.StringSlice("args"),
|
||||
Target: c.String("target"),
|
||||
Repo: c.String("repo"),
|
||||
Labels: c.StringSlice("custom-labels"),
|
||||
SkipTlsVerify: c.Bool("skip-tls-verify"),
|
||||
SnapshotMode: c.String("snapshot-mode"),
|
||||
EnableCache: c.Bool("enable-cache"),
|
||||
CacheRepo: c.String("cache-repo"),
|
||||
CacheTTL: c.Int("cache-ttl"),
|
||||
DigestFile: defaultDigestFile,
|
||||
NoPush: noPush,
|
||||
Verbosity: c.String("verbosity"),
|
||||
Platform: c.String("platform"),
|
||||
DroneCommitRef: c.String("drone-commit-ref"),
|
||||
DroneRepoBranch: c.String("drone-repo-branch"),
|
||||
Dockerfile: c.String("dockerfile"),
|
||||
Context: c.String("context"),
|
||||
Tags: c.StringSlice("tags"),
|
||||
AutoTag: c.Bool("auto-tag"),
|
||||
AutoTagSuffix: c.String("auto-tag-suffix"),
|
||||
ExpandTag: c.Bool("expand-tag"),
|
||||
Args: c.StringSlice("args"),
|
||||
Target: c.String("target"),
|
||||
Repo: c.String("repo"),
|
||||
Labels: c.StringSlice("custom-labels"),
|
||||
SkipTlsVerify: c.Bool("skip-tls-verify"),
|
||||
SnapshotMode: c.String("snapshot-mode"),
|
||||
EnableCache: c.Bool("enable-cache"),
|
||||
CacheRepo: c.String("cache-repo"),
|
||||
CacheTTL: c.Int("cache-ttl"),
|
||||
DigestFile: defaultDigestFile,
|
||||
NoPush: noPush,
|
||||
Verbosity: c.String("verbosity"),
|
||||
Platform: c.String("platform"),
|
||||
},
|
||||
Artifact: kaniko.Artifact{
|
||||
Tags: c.StringSlice("tags"),
|
||||
@ -238,7 +262,7 @@ func buildRepo(registry, repo string) string {
|
||||
// No custom registry, just return the repo name
|
||||
return repo
|
||||
}
|
||||
if strings.HasPrefix(repo, registry + "/") {
|
||||
if strings.HasPrefix(repo, registry+"/") {
|
||||
// Repo already includes the registry prefix
|
||||
// For backward compatibility, we won't add the prefix again.
|
||||
return repo
|
||||
|
@ -71,6 +71,16 @@ func main() {
|
||||
Value: ".",
|
||||
EnvVar: "PLUGIN_CONTEXT",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "drone-commit-ref",
|
||||
Usage: "git commit ref passed by Drone",
|
||||
EnvVar: "DRONE_COMMIT_REF",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "drone-repo-branch",
|
||||
Usage: "git repository default branch passed by Drone",
|
||||
EnvVar: "DRONE_REPO_BRANCH",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "tags",
|
||||
Usage: "build tags",
|
||||
@ -79,10 +89,20 @@ func main() {
|
||||
FilePath: ".tags",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "auto_tag",
|
||||
Name: "expand-tag",
|
||||
Usage: "enable for semver tagging",
|
||||
EnvVar: "PLUGIN_EXPAND_TAG",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "auto-tag",
|
||||
Usage: "enable auto generation of build tags",
|
||||
EnvVar: "PLUGIN_AUTO_TAG",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "auto-tag-suffix",
|
||||
Usage: "the suffix of auto build tags",
|
||||
EnvVar: "PLUGIN_AUTO_TAG_SUFFIX",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "args",
|
||||
Usage: "build args",
|
||||
@ -242,22 +262,26 @@ func run(c *cli.Context) error {
|
||||
|
||||
plugin := kaniko.Plugin{
|
||||
Build: kaniko.Build{
|
||||
Dockerfile: c.String("dockerfile"),
|
||||
Context: c.String("context"),
|
||||
Tags: c.StringSlice("tags"),
|
||||
AutoTag: c.Bool("auto_tag"),
|
||||
Args: c.StringSlice("args"),
|
||||
Target: c.String("target"),
|
||||
Repo: fmt.Sprintf("%s/%s", c.String("registry"), c.String("repo")),
|
||||
Labels: c.StringSlice("custom-labels"),
|
||||
SnapshotMode: c.String("snapshot-mode"),
|
||||
EnableCache: c.Bool("enable-cache"),
|
||||
CacheRepo: fmt.Sprintf("%s/%s", c.String("registry"), c.String("cache-repo")),
|
||||
CacheTTL: c.Int("cache-ttl"),
|
||||
DigestFile: defaultDigestFile,
|
||||
NoPush: noPush,
|
||||
Verbosity: c.String("verbosity"),
|
||||
Platform: c.String("platform"),
|
||||
DroneCommitRef: c.String("drone-commit-ref"),
|
||||
DroneRepoBranch: c.String("drone-repo-branch"),
|
||||
Dockerfile: c.String("dockerfile"),
|
||||
Context: c.String("context"),
|
||||
Tags: c.StringSlice("tags"),
|
||||
AutoTag: c.Bool("auto-tag"),
|
||||
AutoTagSuffix: c.String("auto-tag-suffix"),
|
||||
ExpandTag: c.Bool("expand-tag"),
|
||||
Args: c.StringSlice("args"),
|
||||
Target: c.String("target"),
|
||||
Repo: fmt.Sprintf("%s/%s", c.String("registry"), c.String("repo")),
|
||||
Labels: c.StringSlice("custom-labels"),
|
||||
SnapshotMode: c.String("snapshot-mode"),
|
||||
EnableCache: c.Bool("enable-cache"),
|
||||
CacheRepo: fmt.Sprintf("%s/%s", c.String("registry"), c.String("cache-repo")),
|
||||
CacheTTL: c.Int("cache-ttl"),
|
||||
DigestFile: defaultDigestFile,
|
||||
NoPush: noPush,
|
||||
Verbosity: c.String("verbosity"),
|
||||
Platform: c.String("platform"),
|
||||
},
|
||||
Artifact: kaniko.Artifact{
|
||||
Tags: c.StringSlice("tags"),
|
||||
|
@ -52,6 +52,16 @@ func main() {
|
||||
Value: ".",
|
||||
EnvVar: "PLUGIN_CONTEXT",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "drone-commit-ref",
|
||||
Usage: "git commit ref passed by Drone",
|
||||
EnvVar: "DRONE_COMMIT_REF",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "drone-repo-branch",
|
||||
Usage: "git repository default branch passed by Drone",
|
||||
EnvVar: "DRONE_REPO_BRANCH",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "tags",
|
||||
Usage: "build tags",
|
||||
@ -60,10 +70,20 @@ func main() {
|
||||
FilePath: ".tags",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "auto_tag",
|
||||
Name: "expand-tag",
|
||||
Usage: "enable for semver tagging",
|
||||
EnvVar: "PLUGIN_EXPAND_TAG",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "auto-tag",
|
||||
Usage: "enable auto generation of build tags",
|
||||
EnvVar: "PLUGIN_AUTO_TAG",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "auto-tag-suffix",
|
||||
Usage: "the suffix of auto build tags",
|
||||
EnvVar: "PLUGIN_AUTO_TAG_SUFFIX",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "args",
|
||||
Usage: "build args",
|
||||
@ -157,22 +177,26 @@ func run(c *cli.Context) error {
|
||||
|
||||
plugin := kaniko.Plugin{
|
||||
Build: kaniko.Build{
|
||||
Dockerfile: c.String("dockerfile"),
|
||||
Context: c.String("context"),
|
||||
Tags: c.StringSlice("tags"),
|
||||
AutoTag: c.Bool("auto_tag"),
|
||||
Args: c.StringSlice("args"),
|
||||
Target: c.String("target"),
|
||||
Repo: fmt.Sprintf("%s/%s", c.String("registry"), c.String("repo")),
|
||||
Labels: c.StringSlice("custom-labels"),
|
||||
SnapshotMode: c.String("snapshot-mode"),
|
||||
EnableCache: c.Bool("enable-cache"),
|
||||
CacheRepo: fmt.Sprintf("%s/%s", c.String("registry"), c.String("cache-repo")),
|
||||
CacheTTL: c.Int("cache-ttl"),
|
||||
DigestFile: defaultDigestFile,
|
||||
NoPush: noPush,
|
||||
Verbosity: c.String("verbosity"),
|
||||
Platform: c.String("platform"),
|
||||
DroneCommitRef: c.String("drone-commit-ref"),
|
||||
DroneRepoBranch: c.String("drone-repo-branch"),
|
||||
Dockerfile: c.String("dockerfile"),
|
||||
Context: c.String("context"),
|
||||
Tags: c.StringSlice("tags"),
|
||||
AutoTag: c.Bool("auto-tag"),
|
||||
AutoTagSuffix: c.String("auto-tag-suffix"),
|
||||
ExpandTag: c.Bool("expand-tag"),
|
||||
Args: c.StringSlice("args"),
|
||||
Target: c.String("target"),
|
||||
Repo: fmt.Sprintf("%s/%s", c.String("registry"), c.String("repo")),
|
||||
Labels: c.StringSlice("custom-labels"),
|
||||
SnapshotMode: c.String("snapshot-mode"),
|
||||
EnableCache: c.Bool("enable-cache"),
|
||||
CacheRepo: fmt.Sprintf("%s/%s", c.String("registry"), c.String("cache-repo")),
|
||||
CacheTTL: c.Int("cache-ttl"),
|
||||
DigestFile: defaultDigestFile,
|
||||
NoPush: noPush,
|
||||
Verbosity: c.String("verbosity"),
|
||||
Platform: c.String("platform"),
|
||||
},
|
||||
Artifact: kaniko.Artifact{
|
||||
Tags: c.StringSlice("tags"),
|
||||
|
1
go.mod
1
go.mod
@ -6,6 +6,7 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/service/ecr v1.4.3
|
||||
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.4.3
|
||||
github.com/aws/smithy-go v1.7.0
|
||||
github.com/coreos/go-semver v0.3.0
|
||||
github.com/google/go-cmp v0.5.6
|
||||
github.com/joho/godotenv v1.3.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
|
2
go.sum
2
go.sum
@ -21,6 +21,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.6.2 h1:l504GWCoQi1Pk68vSUFGLmDIEMzRf
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.6.2/go.mod h1:RBhoMJB8yFToaCnbe0jNq5Dcdy0jp6LhHqg55rjClkM=
|
||||
github.com/aws/smithy-go v1.7.0 h1:+cLHMRrDZvQ4wk+KuQ9yH6eEg6KZEJ9RI2IkDqnygCg=
|
||||
github.com/aws/smithy-go v1.7.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
|
||||
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
83
kaniko.go
83
kaniko.go
@ -8,29 +8,34 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/drone/drone-kaniko/pkg/artifact"
|
||||
"github.com/drone/drone-kaniko/pkg/tagger"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
type (
|
||||
// Build defines Docker build parameters.
|
||||
Build struct {
|
||||
Dockerfile string // Docker build Dockerfile
|
||||
Context string // Docker build context
|
||||
Tags []string // Docker build tags
|
||||
AutoTag bool // Set this to create semver-tagged labels
|
||||
Args []string // Docker build args
|
||||
Target string // Docker build target
|
||||
Repo string // Docker build repository
|
||||
Labels []string // Label map
|
||||
SkipTlsVerify bool // Docker skip tls certificate verify for registry
|
||||
SnapshotMode string // Kaniko snapshot mode
|
||||
EnableCache bool // Whether to enable kaniko cache
|
||||
CacheRepo string // Remote repository that will be used to store cached layers
|
||||
CacheTTL int // Cache timeout in hours
|
||||
DigestFile string // Digest file location
|
||||
NoPush bool // Set this flag if you only want to build the image, without pushing to a registry
|
||||
Verbosity string // Log level
|
||||
Platform string // Allows to build with another default platform than the host, similarly to docker build --platform
|
||||
DroneCommitRef string // Drone git commit reference
|
||||
DroneRepoBranch string // Drone repo branch
|
||||
Dockerfile string // Docker build Dockerfile
|
||||
Context string // Docker build context
|
||||
Tags []string // Docker build tags
|
||||
AutoTag bool // Set this to auto detect tags from git commits and semver-tagged labels
|
||||
AutoTagSuffix string // Suffix to append to the auto detect tags
|
||||
ExpandTag bool // Set this to expand the `Tags` into semver-tagged labels
|
||||
Args []string // Docker build args
|
||||
Target string // Docker build target
|
||||
Repo string // Docker build repository
|
||||
Labels []string // Label map
|
||||
SkipTlsVerify bool // Docker skip tls certificate verify for registry
|
||||
SnapshotMode string // Kaniko snapshot mode
|
||||
EnableCache bool // Whether to enable kaniko cache
|
||||
CacheRepo string // Remote repository that will be used to store cached layers
|
||||
CacheTTL int // Cache timeout in hours
|
||||
DigestFile string // Digest file location
|
||||
NoPush bool // Set this flag if you only want to build the image, without pushing to a registry
|
||||
Verbosity string // Log level
|
||||
Platform string // Allows to build with another default platform than the host, similarly to docker build --platform
|
||||
}
|
||||
|
||||
// Artifact defines content of artifact file
|
||||
@ -49,7 +54,7 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
// labelsForTag returns the labels to use for the given tag, subject to the value of AutoTag.
|
||||
// labelsForTag returns the labels to use for the given tag, subject to the value of ExpandTag.
|
||||
//
|
||||
// Build information (e.g. +linux_amd64) is carried through to all labels.
|
||||
// Pre-release information (e.g. -rc1) suppresses major and major+minor auto-labels.
|
||||
@ -66,8 +71,8 @@ func (b Build) labelsForTag(tag string) (labels []string) {
|
||||
semverTag = withV
|
||||
}
|
||||
|
||||
// Pass through tags if auto-tag is not set, or if the tag is not a semantic version
|
||||
if !b.AutoTag || !semver.IsValid(semverTag) {
|
||||
// Pass through tags if expand-tag is not set, or if the tag is not a semantic version
|
||||
if !b.ExpandTag || !semver.IsValid(semverTag) {
|
||||
return []string{tag}
|
||||
}
|
||||
tag = semverTag
|
||||
@ -90,6 +95,30 @@ func (b Build) labelsForTag(tag string) (labels []string) {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the auto detected tags. See the AutoTag section of
|
||||
// https://plugins.drone.io/drone-plugins/drone-docker/ for more info.
|
||||
func (b Build) AutoTags() (tags []string, err error) {
|
||||
if len(b.Tags) > 1 || len(b.Tags) == 1 && b.Tags[0] != "latest" {
|
||||
err = fmt.Errorf("The auto-tag flag does not work with user provided tags %s", b.Tags)
|
||||
return
|
||||
}
|
||||
// We have tried the best to prevent enabling auto-tag and passing in
|
||||
// user specified at the same time. Starts to auto detect tags.
|
||||
// Note: passing in a "latest" tag with auto-tag enabled won't trigger the
|
||||
// early returns above, because we cannot tell if the tag is provided by
|
||||
// the default value of by the users.
|
||||
commitRef := b.DroneCommitRef
|
||||
if !tagger.UseAutoTag(commitRef, b.DroneRepoBranch) {
|
||||
err = fmt.Errorf("Could not auto detect the tag. Skipping automated docker build for commit %s", commitRef)
|
||||
return
|
||||
}
|
||||
tags, err = tagger.AutoTagsSuffix(commitRef, b.AutoTagSuffix)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Invalid semantic version when auto detecting the tag. Skipping automated docker build for %s.", commitRef)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Exec executes the plugin step
|
||||
func (p Plugin) Exec() error {
|
||||
if !p.Build.NoPush && p.Build.Repo == "" {
|
||||
@ -100,6 +129,18 @@ func (p Plugin) Exec() error {
|
||||
return fmt.Errorf("dockerfile does not exist at path: %s", p.Build.Dockerfile)
|
||||
}
|
||||
|
||||
var tags = p.Build.Tags
|
||||
if p.Build.AutoTag && p.Build.ExpandTag {
|
||||
return fmt.Errorf("The auto-tag flag conflicts with the expand-tag flag")
|
||||
}
|
||||
if p.Build.AutoTag {
|
||||
var err error
|
||||
tags, err = p.Build.AutoTags()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
cmdArgs := []string{
|
||||
fmt.Sprintf("--dockerfile=%s", p.Build.Dockerfile),
|
||||
fmt.Sprintf("--context=dir://%s", p.Build.Context),
|
||||
@ -107,7 +148,7 @@ func (p Plugin) Exec() error {
|
||||
|
||||
// Set the destination repository
|
||||
if !p.Build.NoPush {
|
||||
for _, tag := range p.Build.Tags {
|
||||
for _, tag := range tags {
|
||||
for _, label := range p.Build.labelsForTag(tag) {
|
||||
cmdArgs = append(cmdArgs, fmt.Sprintf("--destination=%s:%s", p.Build.Repo, label))
|
||||
}
|
||||
|
137
kaniko_test.go
137
kaniko_test.go
@ -8,67 +8,134 @@ import (
|
||||
|
||||
func TestBuild_labelsForTag(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
tag string
|
||||
autoTags []string
|
||||
name string
|
||||
tag string
|
||||
expandTags []string
|
||||
}{
|
||||
{
|
||||
name: "semver",
|
||||
tag: "v1.2.3",
|
||||
autoTags: []string{"1", "1.2", "1.2.3"},
|
||||
name: "semver",
|
||||
tag: "v1.2.3",
|
||||
expandTags: []string{"1", "1.2", "1.2.3"},
|
||||
},
|
||||
{
|
||||
name: "no_patch",
|
||||
tag: "v1.2",
|
||||
autoTags: []string{"1", "1.2", "1.2.0"},
|
||||
name: "no_patch",
|
||||
tag: "v1.2",
|
||||
expandTags: []string{"1", "1.2", "1.2.0"},
|
||||
},
|
||||
{
|
||||
name: "only_major",
|
||||
tag: "v1",
|
||||
autoTags: []string{"1", "1.0", "1.0.0"},
|
||||
name: "only_major",
|
||||
tag: "v1",
|
||||
expandTags: []string{"1", "1.0", "1.0.0"},
|
||||
},
|
||||
{
|
||||
name: "full_with_build",
|
||||
tag: "v1.2.3+build-info",
|
||||
autoTags: []string{"1+build-info", "1.2+build-info", "1.2.3+build-info"},
|
||||
name: "full_with_build",
|
||||
tag: "v1.2.3+build-info",
|
||||
expandTags: []string{"1+build-info", "1.2+build-info", "1.2.3+build-info"},
|
||||
},
|
||||
{
|
||||
name: "build_with_underscores",
|
||||
tag: "v1.2.3+linux_amd64",
|
||||
autoTags: []string{"1+linux-amd64", "1.2+linux-amd64", "1.2.3+linux-amd64"},
|
||||
name: "build_with_underscores",
|
||||
tag: "v1.2.3+linux_amd64",
|
||||
expandTags: []string{"1+linux-amd64", "1.2+linux-amd64", "1.2.3+linux-amd64"},
|
||||
},
|
||||
{
|
||||
name: "prerelease",
|
||||
tag: "v1.2.3-rc1",
|
||||
autoTags: []string{"1.2.3-rc1"},
|
||||
name: "prerelease",
|
||||
tag: "v1.2.3-rc1",
|
||||
expandTags: []string{"1.2.3-rc1"},
|
||||
},
|
||||
{
|
||||
name: "prerelease_with_build",
|
||||
tag: "v1.2.3-rc1+bld",
|
||||
autoTags: []string{"1.2.3-rc1+bld"},
|
||||
name: "prerelease_with_build",
|
||||
tag: "v1.2.3-rc1+bld",
|
||||
expandTags: []string{"1.2.3-rc1+bld"},
|
||||
},
|
||||
{
|
||||
name: "invalid_build",
|
||||
tag: "v1+bld", // can only include build detail with all three elements
|
||||
autoTags: []string{"v1+bld"},
|
||||
name: "invalid_build",
|
||||
tag: "v1+bld", // can only include build detail with all three elements
|
||||
expandTags: []string{"v1+bld"},
|
||||
},
|
||||
{
|
||||
name: "accidental_non_semver",
|
||||
tag: "1.2.3",
|
||||
autoTags: []string{"1", "1.2", "1.2.3"},
|
||||
name: "accidental_non_semver",
|
||||
tag: "1.2.3",
|
||||
expandTags: []string{"1", "1.2", "1.2.3"},
|
||||
},
|
||||
{
|
||||
name: "non_semver",
|
||||
tag: "latest",
|
||||
autoTags: []string{"latest"},
|
||||
name: "non_semver",
|
||||
tag: "latest",
|
||||
expandTags: []string{"latest"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tags := Build{AutoTag: true}.labelsForTag(tt.tag)
|
||||
if got, want := tags, tt.autoTags; !cmp.Equal(got, want) {
|
||||
tags := Build{ExpandTag: true}.labelsForTag(tt.tag)
|
||||
if got, want := tags, tt.expandTags; !cmp.Equal(got, want) {
|
||||
t.Errorf("tagsFor(%q) = %q, want %q", tt.tag, got, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuild_AutoTags(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
repoBranch string
|
||||
commitRef string
|
||||
autoTagSuffix string
|
||||
expectedTags []string
|
||||
}{
|
||||
{
|
||||
name: "commit push",
|
||||
repoBranch: "master",
|
||||
commitRef: "refs/heads/master",
|
||||
autoTagSuffix: "",
|
||||
expectedTags: []string{"latest"},
|
||||
},
|
||||
{
|
||||
name: "tag push",
|
||||
repoBranch: "master",
|
||||
commitRef: "refs/tags/v1.0.0",
|
||||
autoTagSuffix: "",
|
||||
expectedTags: []string{
|
||||
"1",
|
||||
"1.0",
|
||||
"1.0.0",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "tag push",
|
||||
repoBranch: "master",
|
||||
commitRef: "refs/tags/v1.0.0",
|
||||
autoTagSuffix: "linux-amd64",
|
||||
expectedTags: []string{
|
||||
"1-linux-amd64",
|
||||
"1.0-linux-amd64",
|
||||
"1.0.0-linux-amd64",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
b := Build{DroneCommitRef: tt.commitRef, DroneRepoBranch: tt.repoBranch, AutoTag: true}
|
||||
if tt.autoTagSuffix != "" {
|
||||
b.AutoTagSuffix = tt.autoTagSuffix
|
||||
}
|
||||
tags, err := b.AutoTags()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected err %q", err)
|
||||
}
|
||||
if got, want := tags, tt.expectedTags; !cmp.Equal(got, want) {
|
||||
t.Errorf("auto detected tags = %q, wanted = %q", got, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
t.Run("flag conflict", func(t *testing.T) {
|
||||
b := Build{
|
||||
DroneCommitRef: "refs/tags/v1.0.0",
|
||||
DroneRepoBranch: "master",
|
||||
AutoTag: true,
|
||||
Tags: []string{"v1"},
|
||||
}
|
||||
_, err := b.AutoTags()
|
||||
if err == nil {
|
||||
t.Errorf("Expect flag conflict error")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
95
pkg/tagger/tagger.go
Normal file
95
pkg/tagger/tagger.go
Normal file
@ -0,0 +1,95 @@
|
||||
// A fork of https://github.com/drone-plugins/drone-docker/blob/master/tags.go
|
||||
|
||||
package tagger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/go-semver/semver"
|
||||
)
|
||||
|
||||
// AutoTagsSuffix returns a set of default suggested tags
|
||||
// based on the commit ref with an attached suffix.
|
||||
func AutoTagsSuffix(ref, suffix string) ([]string, error) {
|
||||
tags, err := AutoTags(ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(suffix) == 0 {
|
||||
return tags, nil
|
||||
}
|
||||
for i, tag := range tags {
|
||||
if tag == "latest" {
|
||||
tags[i] = suffix
|
||||
} else {
|
||||
tags[i] = fmt.Sprintf("%s-%s", tag, suffix)
|
||||
}
|
||||
}
|
||||
return tags, nil
|
||||
}
|
||||
|
||||
func splitOff(input string, delim string) string {
|
||||
parts := strings.SplitN(input, delim, 2)
|
||||
|
||||
if len(parts) == 2 {
|
||||
return parts[0]
|
||||
}
|
||||
|
||||
return input
|
||||
}
|
||||
|
||||
// AutoTags returns a set of default suggested tags based on
|
||||
// the commit ref.
|
||||
func AutoTags(ref string) ([]string, error) {
|
||||
if !strings.HasPrefix(ref, "refs/tags/") {
|
||||
return []string{"latest"}, nil
|
||||
}
|
||||
v := stripTagPrefix(ref)
|
||||
version, err := semver.NewVersion(v)
|
||||
if err != nil {
|
||||
return []string{"latest"}, err
|
||||
}
|
||||
if version.PreRelease != "" || version.Metadata != "" {
|
||||
return []string{
|
||||
version.String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
v = stripTagPrefix(ref)
|
||||
v = splitOff(splitOff(v, "+"), "-")
|
||||
dotParts := strings.SplitN(v, ".", 3)
|
||||
|
||||
if version.Major == 0 {
|
||||
return []string{
|
||||
fmt.Sprintf("%0*d.%0*d", len(dotParts[0]), version.Major, len(dotParts[1]), version.Minor),
|
||||
fmt.Sprintf("%0*d.%0*d.%0*d", len(dotParts[0]), version.Major, len(dotParts[1]), version.Minor, len(dotParts[2]), version.Patch),
|
||||
}, nil
|
||||
}
|
||||
return []string{
|
||||
fmt.Sprintf("%0*d", len(dotParts[0]), version.Major),
|
||||
fmt.Sprintf("%0*d.%0*d", len(dotParts[0]), version.Major, len(dotParts[1]), version.Minor),
|
||||
fmt.Sprintf("%0*d.%0*d.%0*d", len(dotParts[0]), version.Major, len(dotParts[1]), version.Minor, len(dotParts[2]), version.Patch),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UseAutoTag for keep only default branch for latest tag.
|
||||
func UseAutoTag(ref, defaultBranch string) bool {
|
||||
if strings.HasPrefix(ref, "refs/tags/") {
|
||||
return true
|
||||
}
|
||||
if stripHeadPrefix(ref) == defaultBranch {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func stripHeadPrefix(ref string) string {
|
||||
return strings.TrimPrefix(ref, "refs/heads/")
|
||||
}
|
||||
|
||||
func stripTagPrefix(ref string) string {
|
||||
ref = strings.TrimPrefix(ref, "refs/tags/")
|
||||
ref = strings.TrimPrefix(ref, "v")
|
||||
return ref
|
||||
}
|
199
pkg/tagger/tagger_test.go
Normal file
199
pkg/tagger/tagger_test.go
Normal file
@ -0,0 +1,199 @@
|
||||
// A fork of https://github.com/drone-plugins/drone-docker/blob/master/tags_test.go
|
||||
|
||||
package tagger
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_stripTagPrefix(t *testing.T) {
|
||||
var tests = []struct {
|
||||
Before string
|
||||
After string
|
||||
}{
|
||||
{"refs/tags/1.0.0", "1.0.0"},
|
||||
{"refs/tags/v1.0.0", "1.0.0"},
|
||||
{"v1.0.0", "1.0.0"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
got, want := stripTagPrefix(test.Before), test.After
|
||||
if got != want {
|
||||
t.Errorf("Got tag %s, want %s", got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAutoTags(t *testing.T) {
|
||||
var tests = []struct {
|
||||
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"}},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
tags, err := AutoTags(test.Before)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
got, want := tags, test.After
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("Got tag %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAutoTagsError(t *testing.T) {
|
||||
var tests = []string{
|
||||
"refs/tags/x1.0.0",
|
||||
"refs/tags/20190203",
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
_, err := AutoTags(test)
|
||||
if err == nil {
|
||||
t.Errorf("Expect tag error for %s", test)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAutoTagsSuffix(t *testing.T) {
|
||||
var tests = []struct {
|
||||
Before string
|
||||
Suffix string
|
||||
After []string
|
||||
}{
|
||||
// without suffix
|
||||
{
|
||||
After: []string{"latest"},
|
||||
},
|
||||
{
|
||||
Before: "refs/tags/v1.0.0",
|
||||
After: []string{
|
||||
"1",
|
||||
"1.0",
|
||||
"1.0.0",
|
||||
},
|
||||
},
|
||||
// with suffix
|
||||
{
|
||||
Suffix: "linux-amd64",
|
||||
After: []string{"linux-amd64"},
|
||||
},
|
||||
{
|
||||
Before: "refs/tags/v1.0.0",
|
||||
Suffix: "linux-amd64",
|
||||
After: []string{
|
||||
"1-linux-amd64",
|
||||
"1.0-linux-amd64",
|
||||
"1.0.0-linux-amd64",
|
||||
},
|
||||
},
|
||||
{
|
||||
Suffix: "nanoserver",
|
||||
After: []string{"nanoserver"},
|
||||
},
|
||||
{
|
||||
Before: "refs/tags/v1.9.2",
|
||||
Suffix: "nanoserver",
|
||||
After: []string{
|
||||
"1-nanoserver",
|
||||
"1.9-nanoserver",
|
||||
"1.9.2-nanoserver",
|
||||
},
|
||||
},
|
||||
{
|
||||
Before: "refs/tags/v18.06.0",
|
||||
Suffix: "nanoserver",
|
||||
After: []string{
|
||||
"18-nanoserver",
|
||||
"18.06-nanoserver",
|
||||
"18.06.0-nanoserver",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
tag, err := AutoTagsSuffix(test.Before, 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_stripHeadPrefix(t *testing.T) {
|
||||
type args struct {
|
||||
ref string
|
||||
}
|
||||
tests := []struct {
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{
|
||||
args: args{
|
||||
ref: "refs/heads/master",
|
||||
},
|
||||
want: "master",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
if got := stripHeadPrefix(tt.args.ref); got != tt.want {
|
||||
t.Errorf("stripHeadPrefix() = %v, want %v", got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUseAutoTag(t *testing.T) {
|
||||
type args struct {
|
||||
ref string
|
||||
defaultBranch string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "latest tag for default branch",
|
||||
args: args{
|
||||
ref: "refs/heads/master",
|
||||
defaultBranch: "master",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "build from tags",
|
||||
args: args{
|
||||
ref: "refs/tags/v1.0.0",
|
||||
defaultBranch: "master",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "skip build for not default branch",
|
||||
args: args{
|
||||
ref: "refs/heads/develop",
|
||||
defaultBranch: "master",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
if got := UseAutoTag(tt.args.ref, tt.args.defaultBranch); got != tt.want {
|
||||
t.Errorf("%q. UseAutoTag() = %v, want %v", tt.name, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user