Merge pull request #8498 from dokku/migrate-env-vars-to-properties

Migrate environment variables to plugin properties
This commit is contained in:
Jose Diaz-Gonzalez
2026-04-26 04:05:59 -04:00
committed by GitHub
53 changed files with 648 additions and 508 deletions

View File

@@ -606,37 +606,12 @@ func GetRunningImageTag(appName string, imageTag string) (string, error) {
// GetDokkuAppShell returns the shell for a given app
func GetDokkuAppShell(appName string) string {
shell := "/bin/bash"
globalShell := ""
appShell := ""
ctx := context.Background()
errs, ctx := errgroup.WithContext(ctx)
errs.Go(func() error {
results, _ := CallPlugnTriggerWithContext(ctx, PlugnTriggerInput{
Trigger: "config-get-global",
Args: []string{"DOKKU_APP_SHELL"},
})
globalShell = results.StdoutContents()
return nil
})
errs.Go(func() error {
results, _ := CallPlugnTriggerWithContext(ctx, PlugnTriggerInput{
Trigger: "config-get",
Args: []string{appName, "DOKKU_APP_SHELL"},
})
appShell = results.StdoutContents()
return nil
})
errs.Wait()
appShell := PropertyGet("scheduler", appName, "shell")
if appShell != "" {
shell = appShell
} else if globalShell != "" {
shell = globalShell
return appShell
}
return shell
return PropertyGetDefault("scheduler", "--global", "shell", "/bin/bash")
}
// DokkuApps returns a list of all local apps

View File

@@ -390,23 +390,17 @@ func DockerBin() string {
func DockerCleanup(appName string, forceCleanup bool) error {
if !forceCleanup {
skipCleanup := false
if appName != "" {
triggerName := "config-get"
triggerArgs := []string{appName, "DOKKU_SKIP_CLEANUP"}
if appName == "--global" {
triggerName = "config-get-global"
triggerArgs = []string{"DOKKU_SKIP_CLEANUP"}
}
results, _ := CallPlugnTrigger(PlugnTriggerInput{
Trigger: triggerName,
Args: triggerArgs,
})
if results.StdoutContents() == "true" {
if appName != "" && appName != "--global" {
if PropertyGet("builder", appName, "skip-cleanup") == "true" {
skipCleanup = true
}
}
if !skipCleanup && PropertyGet("builder", "--global", "skip-cleanup") == "true" {
skipCleanup = true
}
// Fallback for .dokkurc compatibility
if skipCleanup || os.Getenv("DOKKU_SKIP_CLEANUP") == "true" {
LogInfo1("DOKKU_SKIP_CLEANUP set. Skipping dokku cleanup")
return nil

View File

@@ -632,8 +632,8 @@ release_and_deploy() {
if verify_image "$IMAGE"; then
IMAGE_SOURCE_TYPE="$(get_image_builder_type "$IMAGE" "$APP")"
local DOKKU_APP_SKIP_DEPLOY="$(config_get "$APP" DOKKU_SKIP_DEPLOY || true)"
local DOKKU_GLOBAL_SKIP_DEPLOY="$(config_get --global DOKKU_SKIP_DEPLOY || true)"
local DOKKU_APP_SKIP_DEPLOY="$(fn-plugin-property-get-default "ps" "$APP" "skip-deploy" "")"
local DOKKU_GLOBAL_SKIP_DEPLOY="$(fn-plugin-property-get-default "ps" "--global" "skip-deploy" "")"
local DOKKU_SKIP_DEPLOY=${DOKKU_APP_SKIP_DEPLOY:="$DOKKU_GLOBAL_SKIP_DEPLOY"}
@@ -917,7 +917,7 @@ suppress_output() {
}
fn-migrate-config-to-property() {
declare desc="migrates deprecated config variables to property counterpart"
declare desc="[deprecated] migrates deprecated config variables to property counterpart. Use 'prop migrate-config-to-property' instead."
declare PLUGIN="$1" KEY="$2" CONFIG_VAR="$3" GLOBAL_CONFIG_VAR="$4"
# todo: refactor to remove config_unset usage

View File

@@ -548,6 +548,121 @@ func PropertySetupApp(pluginName string, appName string) error {
return nil
}
// MigrateConfigEntry describes a single config-to-property migration
type MigrateConfigEntry struct {
// ConfigVar is the per-app environment variable name (e.g. "DOKKU_APP_PROXY_TYPE")
ConfigVar string
// GlobalConfigVar is the global environment variable name (e.g. "DOKKU_PROXY_TYPE"), empty if none
GlobalConfigVar string
// Property is the target property name (e.g. "type")
Property string
// Transform is an optional value transformation function applied before writing
Transform func(value string) string
// ListProperty indicates the value should be written as a list property via PropertyListWrite
ListProperty bool
}
// MigrateConfigToProperties migrates config variables to properties for a given plugin
// across all apps and optionally globally. It is idempotent: if the property already
// exists, the migration is skipped for that app/entry.
func MigrateConfigToProperties(pluginName string, entries []MigrateConfigEntry) error {
apps, err := UnfilteredDokkuApps()
if err != nil && !errors.Is(err, NoAppsExist) {
return nil
}
for _, entry := range entries {
if entry.GlobalConfigVar != "" {
if err := migrateConfigEntry(pluginName, "--global", entry.GlobalConfigVar, entry); err != nil {
return err
}
}
for _, appName := range apps {
if entry.ConfigVar == "" {
continue
}
if err := migrateConfigEntry(pluginName, appName, entry.ConfigVar, entry); err != nil {
return err
}
}
}
return nil
}
// migrateConfigEntry migrates a single config variable to a property for a given app
func migrateConfigEntry(pluginName string, appName string, configVar string, entry MigrateConfigEntry) error {
if entry.ListProperty {
if exists, _ := PropertyListLength(pluginName, appName, entry.Property); exists > 0 {
return nil
}
} else if PropertyExists(pluginName, appName, entry.Property) {
return nil
}
triggerName := "config-get"
triggerArgs := []string{appName, configVar}
if appName == "--global" {
triggerName = "config-get-global"
triggerArgs = []string{configVar}
}
results, _ := CallPlugnTrigger(PlugnTriggerInput{
Trigger: triggerName,
Args: triggerArgs,
})
value := results.StdoutContents()
if value == "" {
return nil
}
if entry.Transform != nil {
value = entry.Transform(value)
}
if appName == "--global" {
LogInfo1(fmt.Sprintf("Migrating deprecated global %s to %s %s property. Use 'dokku %s:set --global %s <value>' to manage this going forward.", configVar, pluginName, entry.Property, pluginName, entry.Property))
} else {
LogInfo1(fmt.Sprintf("Migrating deprecated %s to %s %s property for %s. Use 'dokku %s:set %s %s <value>' to manage this going forward.", configVar, pluginName, entry.Property, appName, pluginName, appName, entry.Property))
}
if entry.ListProperty {
values := strings.Split(value, " ")
if err := PropertyListWrite(pluginName, appName, entry.Property, values); err != nil {
return err
}
} else {
if err := PropertyWrite(pluginName, appName, entry.Property, value); err != nil {
return err
}
}
unsetTrigger := "config-unset"
unsetArgs := []string{appName, configVar}
if appName == "--global" {
unsetArgs = []string{"--global", configVar}
}
_, err := CallPlugnTrigger(PlugnTriggerInput{
Trigger: unsetTrigger,
Args: unsetArgs,
Env: map[string]string{
"DOKKU_QUIET_OUTPUT": "1",
},
})
if err != nil {
LogWarn(err.Error())
}
return nil
}
func getPropertyPath(pluginName string, appName string, property string) string {
pluginAppConfigRoot := getPluginAppPropertyPath(pluginName, appName)
return filepath.Join(pluginAppConfigRoot, property)

View File

@@ -192,6 +192,76 @@ func main() {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}
case "migrate-config-to-property":
property := flag.Arg(2)
configVar := flag.Arg(3)
globalConfigVar := ""
transformName := ""
listProperty := false
for i := 4; i < flag.NArg(); i++ {
arg := flag.Arg(i)
switch {
case arg == "--list":
listProperty = true
case arg == "--global-config-var" && i+1 < flag.NArg():
i++
globalConfigVar = flag.Arg(i)
case arg == "--transform" && i+1 < flag.NArg():
i++
transformName = flag.Arg(i)
}
}
var transform func(string) string
switch transformName {
case "restore-bool":
transform = func(value string) string {
if value == "0" {
return "false"
}
return "true"
}
case "non-empty-to-true":
transform = func(value string) string {
if value != "" {
return "true"
}
return value
}
case "skip-all-checks-to-disabled":
transform = func(value string) string {
if value == "true" {
return "_all_"
}
return ""
}
case "checks-enabled-to-skipped":
transform = func(value string) string {
if value == "0" {
return "_all_"
}
return ""
}
case "":
// no transform
default:
fmt.Fprintf(os.Stderr, "Unknown transform: %s\n", transformName)
os.Exit(1)
}
entry := common.MigrateConfigEntry{
ConfigVar: configVar,
GlobalConfigVar: globalConfigVar,
Property: property,
Transform: transform,
ListProperty: listProperty,
}
if err := common.MigrateConfigToProperties(pluginName, []common.MigrateConfigEntry{entry}); err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}
case "set":
appName := flag.Arg(2)
property := flag.Arg(3)