diff --git a/docs/advanced-usage/registry-management.md b/docs/advanced-usage/registry-management.md index 808b1390e..8e2eef8f6 100644 --- a/docs/advanced-usage/registry-management.md +++ b/docs/advanced-usage/registry-management.md @@ -135,3 +135,34 @@ dokku registry:set node-js-app push-on-release # globally dokku registry:set --global push-on-release ``` + +### Push extra tags + +To push the image on release with extra tags, set the `push-extra-tags` to a comma-separated list of tags via the `registry:set` command. The default value for this property is empty. Setting the property will result in the image being tagged with extra tags every release. + +```shell +# multiple-tags +dokku registry:set node-js-app push-extra-tags foo,bar +``` +The `push-extra-tags` can be set to a single tag too. + +```shell +# single tag +dokku registry:set node-js-app push-extra-tags foo +``` + +This property can be set for a single app or globally via the `--global` flag. When set globally, the app-specific value will always overide the global value. The default global value for this property is `false`. + +```shell +dokku registry:set --global push-extra-tags foo,bar +``` + +Setting the property value to an empty string will reset the value to the system default. Resetting the value can be done per app or globally. + +```shell +# per-app +dokku registry:set node-js-app push-extra-tags + +# globally +dokku registry:set --global push-extra-tags +``` diff --git a/plugins/registry/functions.go b/plugins/registry/functions.go index d965486ee..7c2882c62 100644 --- a/plugins/registry/functions.go +++ b/plugins/registry/functions.go @@ -79,6 +79,14 @@ func incrementTagVersion(appName string) (int, error) { return version, nil } +func getRegistryPushExtraTagsForApp(appName string) string { + value := common.PropertyGet("registry", appName, "push-extra-tags") + if value == "" { + value = common.PropertyGet("registry", "--global", "push-extra-tags") + } + return value +} + func pushToRegistry(appName string, tag int, imageID string, imageRepo string) error { common.LogVerboseQuiet("Retrieving image info for app") @@ -98,9 +106,27 @@ func pushToRegistry(appName string, tag int, imageID string, imageRepo string) e return errors.New("Unable to tag image") } - // For the future, we should also add the ability to create the remote repository - // This is only really important for registries that do not support creation on push - // Examples include AWS and Quay.io + extraTags := getRegistryPushExtraTagsForApp(appName) + if extraTags != "" { + extraTagsArray := strings.Split(extraTags, ",") + for _, extraTag := range extraTagsArray { + extraTagImage := fmt.Sprintf("%s%s:%s", registryServer, imageRepo, extraTag) + common.LogVerboseQuiet(fmt.Sprintf("Tagging %s as %s in registry format", imageRepo, extraTag)) + if !dockerTag(imageID, extraTagImage) { + return errors.New(fmt.Sprintf("Unable to tag image as %s", extraTag)) + } + defer func() { + common.LogVerboseQuiet(fmt.Sprintf("Untagging extra tag %s", extraTag)) + if err := common.RemoveImages([]string{extraTagImage}); err != nil { + common.LogWarn(fmt.Sprintf("Unable to untag extra tag %s", extraTag, err.Error())) + } + }() + common.LogVerboseQuiet(fmt.Sprintf("Pushing %s", extraTagImage)) + if !dockerPush(extraTagImage) { + return errors.New(fmt.Sprintf("Unable to push image with %s tag", extraTag)) + } + } + } common.LogVerboseQuiet(fmt.Sprintf("Pushing %s", fullImage)) if !dockerPush(fullImage) { diff --git a/plugins/registry/registry.go b/plugins/registry/registry.go index abea76773..420960bd3 100644 --- a/plugins/registry/registry.go +++ b/plugins/registry/registry.go @@ -6,6 +6,7 @@ var ( "image-repo": "", "push-on-release": "false", "server": "", + "push-extra-tags": "", } // GlobalProperties is a map of all valid global registry properties @@ -13,5 +14,6 @@ var ( "image-repo-template": true, "push-on-release": true, "server": true, + "push-extra-tags": true, } ) diff --git a/plugins/registry/report.go b/plugins/registry/report.go index 710719f32..9d1fe3251 100644 --- a/plugins/registry/report.go +++ b/plugins/registry/report.go @@ -23,6 +23,7 @@ func ReportSingleApp(appName string, format string, infoFlag string) error { "--registry-global-image-repo-template": reportGlobalImageRepoTemplate, "--registry-server": reportServer, "--registry-tag-version": reportTagVersion, + "--registry-push-extra-tags": reportPushExtraTags, } flagKeys := []string{} @@ -96,3 +97,7 @@ func reportTagVersion(appName string) string { tagVersion := common.PropertyGet("registry", appName, "tag-version") return strings.TrimSpace(tagVersion) } + +func reportPushExtraTags(appName string) string { + return common.PropertyGet("registry", appName, "push-extra-tags") +} diff --git a/tests/unit/registry.bats b/tests/unit/registry.bats index 403e68f9d..321c7bf31 100644 --- a/tests/unit/registry.bats +++ b/tests/unit/registry.bats @@ -294,3 +294,30 @@ teardown() { echo "status: $status" assert_success } + +@test "(registry) registry:set push-extra-tags" { + if [[ -z "$DOCKERHUB_USERNAME" ]] || [[ -z "$DOCKERHUB_TOKEN" ]]; then + skip "skipping due to missing docker.io credentials DOCKERHUB_USERNAME:DOCKERHUB_TOKEN" + fi + + run /bin/bash -c "dokku registry:set $TEST_APP push-on-release true" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku registry:set $TEST_APP image-repo dokku/test-app" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku registry:set $TEST_APP push-extra-tags foo" + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success + assert_output_contains "dokku/test-app:foo" +}