From bc5acce55e4845481a87c382983b896d414f4ba6 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 27 May 2026 03:23:14 -0400 Subject: [PATCH] fix: promote registry image-repo-template to app + global The registry plugin accepted `registry:set image-repo-template ` and persisted the per-app file, but only ever read the global value when rendering the image repository, so per-app overrides were silently dropped. The property now follows the same per-app to global to default fallback chain used by `push-on-release` and `server`, and `registry:report` exposes the raw per-app value alongside the existing global and computed flags. The `push-extra-tags` report flags were extended for the same reason: the runtime already honored per-app and global values, but the report only surfaced the raw per-app entry. --- docs/advanced-usage/registry-management.md | 22 +++- plugins/registry/functions.go | 11 +- plugins/registry/registry.go | 9 +- plugins/registry/report.go | 30 ++++- tests/unit/registry.bats | 130 ++++++++++++++++++++- 5 files changed, 186 insertions(+), 16 deletions(-) diff --git a/docs/advanced-usage/registry-management.md b/docs/advanced-usage/registry-management.md index 66aff2da2..01ace63a9 100644 --- a/docs/advanced-usage/registry-management.md +++ b/docs/advanced-usage/registry-management.md @@ -146,13 +146,27 @@ dokku registry:set node-js-app image-repo ### Templating the image repository name -Instead of setting the image repository name on a per-app basis, it can be set via a template globally with the `image-repo-template` property: +Instead of setting the image repository name on a per-app basis, it can be set via a template with the `image-repo-template` property. The property can be set globally or for a specific app, with the per-app value overriding the global value when both are present: ```shell +# globally dokku registry:set --global image-repo-template "my-awesome-prefix/{{ .AppName }}" + +# per-app +dokku registry:set node-js-app image-repo-template "my-awesome-prefix/{{ .AppName }}-prod" ``` -Dokku uses a Golang template and has access to the `AppName` variable as shown above. +Dokku uses a Golang template and has access to the `AppName` variable as shown above. The per-app `image-repo` property always takes precedence over the rendered template when both are set. + +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 image-repo-template + +# globally +dokku registry:set --global image-repo-template +``` ### Pushing images on build @@ -220,7 +234,7 @@ dokku registry:set --global push-extra-tags | Property | Scope | Default | Report flags | Description | |---|---|---|---|---| | `image-repo` | app only | `dokku/` | `--registry-image-repo`, `--registry-computed-image-repo` | Repository name used when pushing the app's image (overrides the global template) | -| `image-repo-template` | global only | none | `--registry-global-image-repo-template`, `--registry-computed-image-repo-template` | Go template used to compute the per-app image repository when `image-repo` is unset | -| `push-extra-tags` | app + global | none | `--registry-push-extra-tags` | Comma-separated list of additional tags pushed alongside the deploy tag | +| `image-repo-template` | app + global | none | `--registry-image-repo-template`, `--registry-global-image-repo-template`, `--registry-computed-image-repo-template` | Go template used to compute the per-app image repository when `image-repo` is unset | +| `push-extra-tags` | app + global | none | `--registry-push-extra-tags`, `--registry-global-push-extra-tags`, `--registry-computed-push-extra-tags` | Comma-separated list of additional tags pushed alongside the deploy tag | | `push-on-release` | app + global | `false` | `--registry-push-on-release`, `--registry-global-push-on-release`, `--registry-computed-push-on-release` | When `true`, pushes the image to the registry on every successful build | | `server` | app + global | none | `--registry-server`, `--registry-global-server`, `--registry-computed-server` | Registry server host (e.g. `ghcr.io`) used when pushing images | diff --git a/plugins/registry/functions.go b/plugins/registry/functions.go index 8ef4d442f..bad7f668b 100644 --- a/plugins/registry/functions.go +++ b/plugins/registry/functions.go @@ -63,7 +63,10 @@ func GetDockerConfigArgs(appName string) []string { } func getImageRepoFromTemplate(appName string) (string, error) { - imageRepoTemplate := common.PropertyGet("registry", "--global", "image-repo-template") + imageRepoTemplate := strings.TrimSpace(reportImageRepoTemplate(appName)) + if imageRepoTemplate == "" { + imageRepoTemplate = reportGlobalImageRepoTemplate(appName) + } if imageRepoTemplate == "" { return "", nil } @@ -131,11 +134,7 @@ func incrementTagVersion(appName string) (int, error) { } 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 + return reportComputedPushExtraTags(appName) } func pushToRegistry(appName string, tag int, imageID string, imageRepo string) error { diff --git a/plugins/registry/registry.go b/plugins/registry/registry.go index 420960bd3..34c56432a 100644 --- a/plugins/registry/registry.go +++ b/plugins/registry/registry.go @@ -3,10 +3,11 @@ package registry var ( // DefaultProperties is a map of all valid registry properties with corresponding default property values DefaultProperties = map[string]string{ - "image-repo": "", - "push-on-release": "false", - "server": "", - "push-extra-tags": "", + "image-repo": "", + "image-repo-template": "", + "push-on-release": "false", + "server": "", + "push-extra-tags": "", } // GlobalProperties is a map of all valid global registry properties diff --git a/plugins/registry/report.go b/plugins/registry/report.go index 1071b78c7..c0cce6e42 100644 --- a/plugins/registry/report.go +++ b/plugins/registry/report.go @@ -23,6 +23,8 @@ func ReportSingleApp(appName string, format string, infoFlag string) error { "--registry-global-server": reportGlobalServer, "--registry-computed-image-repo-template": reportComputedImageRepoTemplate, "--registry-global-image-repo-template": reportGlobalImageRepoTemplate, + "--registry-computed-push-extra-tags": reportComputedPushExtraTags, + "--registry-global-push-extra-tags": reportGlobalPushExtraTags, } } else { flags = map[string]common.ReportFunc{ @@ -35,9 +37,12 @@ func ReportSingleApp(appName string, format string, infoFlag string) error { "--registry-global-server": reportGlobalServer, "--registry-computed-image-repo-template": reportComputedImageRepoTemplate, "--registry-global-image-repo-template": reportGlobalImageRepoTemplate, + "--registry-image-repo-template": reportImageRepoTemplate, "--registry-server": reportServer, "--registry-tag-version": reportTagVersion, "--registry-push-extra-tags": reportPushExtraTags, + "--registry-global-push-extra-tags": reportGlobalPushExtraTags, + "--registry-computed-push-extra-tags": reportComputedPushExtraTags, } } @@ -96,12 +101,20 @@ func reportComputedServer(appName string) string { return strings.TrimSpace(server) } +func reportImageRepoTemplate(appName string) string { + return common.PropertyGet("registry", appName, "image-repo-template") +} + func reportGlobalImageRepoTemplate(appName string) string { return common.PropertyGet("registry", "--global", "image-repo-template") } func reportComputedImageRepoTemplate(appName string) string { - return reportGlobalImageRepoTemplate(appName) + value := strings.TrimSpace(reportImageRepoTemplate(appName)) + if value == "" { + value = reportGlobalImageRepoTemplate(appName) + } + return value } func reportGlobalServer(appName string) string { @@ -120,3 +133,18 @@ func reportTagVersion(appName string) string { func reportPushExtraTags(appName string) string { return common.PropertyGet("registry", appName, "push-extra-tags") } + +func reportGlobalPushExtraTags(appName string) string { + return common.PropertyGet("registry", "--global", "push-extra-tags") +} + +func reportComputedPushExtraTags(appName string) string { + value := strings.TrimSpace(reportPushExtraTags(appName)) + if value == "" { + value = reportGlobalPushExtraTags(appName) + } + if value == "" { + value = DefaultProperties["push-extra-tags"] + } + return value +} diff --git a/tests/unit/registry.bats b/tests/unit/registry.bats index a31189f2d..4603a63e6 100644 --- a/tests/unit/registry.bats +++ b/tests/unit/registry.bats @@ -181,6 +181,83 @@ teardown() { assert_success } +@test "(registry:report) image-repo-template raw vs computed vs global" { + run /bin/bash -c "dokku registry:set --global image-repo-template" + assert_success + + run /bin/bash -c "dokku registry:report $TEST_APP --format json | jq -r '.\"registry-image-repo-template\"'" + assert_success + assert_output "" + + run /bin/bash -c "dokku registry:report $TEST_APP --format json | jq -r '.\"registry-global-image-repo-template\"'" + assert_success + assert_output "" + + run /bin/bash -c "dokku registry:report $TEST_APP --format json | jq -r '.\"registry-computed-image-repo-template\"'" + assert_success + assert_output "" + + run /bin/bash -c "dokku registry:set --global image-repo-template 'global-prefix/{{ .AppName }}'" + assert_success + + run /bin/bash -c "dokku registry:report $TEST_APP --format json | jq -r '.\"registry-image-repo-template\"'" + assert_success + assert_output "" + + run /bin/bash -c "dokku registry:report $TEST_APP --format json | jq -r '.\"registry-global-image-repo-template\"'" + assert_success + assert_output 'global-prefix/{{ .AppName }}' + + run /bin/bash -c "dokku registry:report $TEST_APP --format json | jq -r '.\"registry-computed-image-repo-template\"'" + assert_success + assert_output 'global-prefix/{{ .AppName }}' + + # The global template renders into the computed image-repo. + run /bin/bash -c "dokku registry:report $TEST_APP --format json | jq -r '.\"registry-computed-image-repo\"'" + assert_success + assert_output "global-prefix/$TEST_APP" + + # Per-app template overrides the global template. + run /bin/bash -c "dokku registry:set $TEST_APP image-repo-template 'app-prefix/{{ .AppName }}-prod'" + assert_success + + run /bin/bash -c "dokku registry:report $TEST_APP --format json | jq -r '.\"registry-image-repo-template\"'" + assert_success + assert_output 'app-prefix/{{ .AppName }}-prod' + + run /bin/bash -c "dokku registry:report $TEST_APP --format json | jq -r '.\"registry-global-image-repo-template\"'" + assert_success + assert_output 'global-prefix/{{ .AppName }}' + + run /bin/bash -c "dokku registry:report $TEST_APP --format json | jq -r '.\"registry-computed-image-repo-template\"'" + assert_success + assert_output 'app-prefix/{{ .AppName }}-prod' + + # The per-app template flows through to the computed image-repo. + run /bin/bash -c "dokku registry:report $TEST_APP --format json | jq -r '.\"registry-computed-image-repo\"'" + assert_success + assert_output "app-prefix/$TEST_APP-prod" + + # Unsetting the per-app template restores the global template behavior. + run /bin/bash -c "dokku registry:set $TEST_APP image-repo-template" + assert_success + + run /bin/bash -c "dokku registry:report $TEST_APP --format json | jq -r '.\"registry-image-repo-template\"'" + assert_success + assert_output "" + + run /bin/bash -c "dokku registry:report $TEST_APP --format json | jq -r '.\"registry-computed-image-repo-template\"'" + assert_success + assert_output 'global-prefix/{{ .AppName }}' + + run /bin/bash -c "dokku registry:report $TEST_APP --format json | jq -r '.\"registry-computed-image-repo\"'" + assert_success + assert_output "global-prefix/$TEST_APP" + + run /bin/bash -c "dokku registry:set --global image-repo-template" + assert_success +} + @test "(registry:report) push-on-release raw vs computed vs global" { run /bin/bash -c "dokku registry:set --global push-on-release" assert_success @@ -233,6 +310,8 @@ teardown() { assert_success run /bin/bash -c "dokku registry:set --global image-repo-template" assert_success + run /bin/bash -c "dokku registry:set --global push-extra-tags" + assert_success run /bin/bash -c "dokku registry:report --global --format json | jq -r '.\"registry-global-push-on-release\"'" assert_success @@ -258,12 +337,22 @@ teardown() { assert_success assert_output "" + run /bin/bash -c "dokku registry:report --global --format json | jq -r '.\"registry-global-push-extra-tags\"'" + assert_success + assert_output "" + + run /bin/bash -c "dokku registry:report --global --format json | jq -r '.\"registry-computed-push-extra-tags\"'" + assert_success + assert_output "" + run /bin/bash -c "dokku registry:set --global push-on-release true" assert_success run /bin/bash -c "dokku registry:set --global server ghcr.io" assert_success run /bin/bash -c "dokku registry:set --global image-repo-template 'dokku/{{ APP }}'" assert_success + run /bin/bash -c "dokku registry:set --global push-extra-tags latest,stable" + assert_success run /bin/bash -c "dokku registry:report --global --format json | jq -r '.\"registry-global-push-on-release\"'" assert_success @@ -289,19 +378,51 @@ teardown() { assert_success assert_output 'dokku/{{ APP }}' + run /bin/bash -c "dokku registry:report --global --format json | jq -r '.\"registry-global-push-extra-tags\"'" + assert_success + assert_output "latest,stable" + + run /bin/bash -c "dokku registry:report --global --format json | jq -r '.\"registry-computed-push-extra-tags\"'" + assert_success + assert_output "latest,stable" + run /bin/bash -c "dokku registry:set --global push-on-release" assert_success run /bin/bash -c "dokku registry:set --global server" assert_success run /bin/bash -c "dokku registry:set --global image-repo-template" assert_success + run /bin/bash -c "dokku registry:set --global push-extra-tags" + assert_success } -@test "(registry:report) push-extra-tags raw" { +@test "(registry:report) push-extra-tags raw vs computed vs global" { + run /bin/bash -c "dokku registry:set --global push-extra-tags" + assert_success + run /bin/bash -c "dokku registry:report $TEST_APP --format json | jq -r '.\"registry-push-extra-tags\"'" assert_success assert_output "" + run /bin/bash -c "dokku registry:report $TEST_APP --format json | jq -r '.\"registry-global-push-extra-tags\"'" + assert_success + assert_output "" + + run /bin/bash -c "dokku registry:report $TEST_APP --format json | jq -r '.\"registry-computed-push-extra-tags\"'" + assert_success + assert_output "" + + run /bin/bash -c "dokku registry:set --global push-extra-tags global-tag" + assert_success + + run /bin/bash -c "dokku registry:report $TEST_APP --format json | jq -r '.\"registry-global-push-extra-tags\"'" + assert_success + assert_output "global-tag" + + run /bin/bash -c "dokku registry:report $TEST_APP --format json | jq -r '.\"registry-computed-push-extra-tags\"'" + assert_success + assert_output "global-tag" + run /bin/bash -c "dokku registry:set $TEST_APP push-extra-tags release,stable" assert_success @@ -309,8 +430,15 @@ teardown() { assert_success assert_output "release,stable" + run /bin/bash -c "dokku registry:report $TEST_APP --format json | jq -r '.\"registry-computed-push-extra-tags\"'" + assert_success + assert_output "release,stable" + run /bin/bash -c "dokku registry:set $TEST_APP push-extra-tags" assert_success + + run /bin/bash -c "dokku registry:set --global push-extra-tags" + assert_success } @test "(registry) registry:set server" {