From 8240096ddac6fae789da2148442a8371143d2215 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Tue, 10 Mar 2026 15:37:49 -0400 Subject: [PATCH 001/546] feat: upgrade ingress-nginx chart from 4.10.0 to 4.14.4 --- plugins/scheduler-k3s/scheduler_k3s.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/scheduler-k3s/scheduler_k3s.go b/plugins/scheduler-k3s/scheduler_k3s.go index 190571c5f..2d7cd5df8 100644 --- a/plugins/scheduler-k3s/scheduler_k3s.go +++ b/plugins/scheduler-k3s/scheduler_k3s.go @@ -116,7 +116,7 @@ var HelmCharts = []HelmChart{ Namespace: "ingress-nginx", ReleaseName: "ingress-nginx", RepoURL: "https://kubernetes.github.io/ingress-nginx", - Version: "4.10.0", + Version: "4.14.4", }, { ChartPath: "keda", From 2b0f96dc1abbac33e2319712acae8d298874d160 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Tue, 10 Mar 2026 15:38:33 -0400 Subject: [PATCH 002/546] feat: upgrade vector chart from 0.42.0 to 0.50.0 --- plugins/scheduler-k3s/scheduler_k3s.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/scheduler-k3s/scheduler_k3s.go b/plugins/scheduler-k3s/scheduler_k3s.go index 190571c5f..c97b3d4c2 100644 --- a/plugins/scheduler-k3s/scheduler_k3s.go +++ b/plugins/scheduler-k3s/scheduler_k3s.go @@ -140,7 +140,7 @@ var HelmCharts = []HelmChart{ Namespace: "vector", ReleaseName: "vector", RepoURL: "https://helm.vector.dev", - Version: "0.42.0", + Version: "0.50.0", }, } From 5a787af3ae5e5858a6c2e6809f4ce94d0f8ede03 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 23 Nov 2025 01:39:14 -0500 Subject: [PATCH 003/546] feat: add support for specifying buildpacks via app.json The order of detection is: - buildpacks:set - app.json - .buildpacks - auto-detected from codebase Closes #7102 --- plugins/app-json/appjson.go | 9 +++++++++ plugins/buildpacks/functions.go | 33 +++++++++++++++++++++++++++++++++ plugins/buildpacks/go.mod | 6 ++++++ plugins/buildpacks/go.sum | 6 ++++++ plugins/buildpacks/triggers.go | 11 +++++++++-- plugins/scheduler-k3s/go.mod | 16 ++++++++-------- 6 files changed, 71 insertions(+), 10 deletions(-) diff --git a/plugins/app-json/appjson.go b/plugins/app-json/appjson.go index 3133330c1..ddb045a11 100644 --- a/plugins/app-json/appjson.go +++ b/plugins/app-json/appjson.go @@ -26,6 +26,9 @@ var ( // AppJSON is a struct that represents an app.json file as understood by Dokku type AppJSON struct { + // Buildpacks is a list of buildpacks to use for the app + Buildpacks []Buildpack `json:"buildpacks"` + // Cron is a list of cron tasks to execute Cron []CronTask `json:"cron"` @@ -51,6 +54,12 @@ type AppJSON struct { } `json:"scripts"` } +// Buildpack is a struct that represents a single buildpack +type Buildpack struct { + // URL is the URL of the buildpack + URL string `json:"url"` +} + // CronTask is a struct that represents a single cron task from an app.json file type CronTask struct { // Command is the command to execute diff --git a/plugins/buildpacks/functions.go b/plugins/buildpacks/functions.go index 47acbbc78..37d8e9515 100644 --- a/plugins/buildpacks/functions.go +++ b/plugins/buildpacks/functions.go @@ -8,9 +8,38 @@ import ( "regexp" "strings" + appjson "github.com/dokku/dokku/plugins/app-json" "github.com/dokku/dokku/plugins/common" ) +// getBuildpacks returns the buildpacks for a given app +// the order of application is: +// 1. .buildpacks: if it exists, use it +// 2. app.json: if it exists and has buildpacks, use them +// 3. buildpack properties: if they exist, use them +// 4. buildpack detection +func getBuildpacks(appName string) ([]string, error) { + buildpacks, err := common.PropertyListGet("buildpacks", appName, "buildpacks") + if err != nil { + return buildpacks, err + } + + appJSON, err := appjson.GetAppJSON(appName) + if err != nil { + return buildpacks, err + } + + if len(appJSON.Buildpacks) == 0 { + return buildpacks, nil + } + + for _, b := range appJSON.Buildpacks { + buildpacks = append(buildpacks, b.URL) + } + + return buildpacks, nil +} + func rewriteBuildpacksFile(sourceWorkDir string) error { buildpacksPath := filepath.Join(sourceWorkDir, ".buildpacks") if !common.FileExists(buildpacksPath) { @@ -27,6 +56,10 @@ func rewriteBuildpacksFile(sourceWorkDir string) error { continue } + if strings.HasPrefix(buildpack, "#") { + continue + } + buildpack, err = validBuildpackURL(buildpack) if err != nil { return fmt.Errorf("Unable to parse .buildpacks file, line %d: %s", i, err) diff --git a/plugins/buildpacks/go.mod b/plugins/buildpacks/go.mod index 54905d796..ae4000145 100644 --- a/plugins/buildpacks/go.mod +++ b/plugins/buildpacks/go.mod @@ -3,6 +3,7 @@ module github.com/dokku/dokku/plugins/buildpacks go 1.25.5 require ( + github.com/dokku/dokku/plugins/app-json v0.0.0-00010101000000-000000000000 github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 github.com/spf13/pflag v1.0.10 ) @@ -12,6 +13,7 @@ require ( github.com/fatih/color v1.19.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/kr/fs v0.1.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -21,9 +23,13 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pkg/sftp v1.13.10 // indirect github.com/ryanuber/columnize v2.1.2+incompatible // indirect + github.com/tailscale/hujson v0.0.0-20250605163823-992244df8c5a // indirect golang.org/x/crypto v0.50.0 // indirect golang.org/x/sync v0.20.0 // indirect golang.org/x/sys v0.43.0 // indirect + k8s.io/utils v0.0.0-20240102154912-e7106e64919e // indirect ) +replace github.com/dokku/dokku/plugins/app-json => ../app-json + replace github.com/dokku/dokku/plugins/common => ../common diff --git a/plugins/buildpacks/go.sum b/plugins/buildpacks/go.sum index c97fc9665..1db1549b6 100644 --- a/plugins/buildpacks/go.sum +++ b/plugins/buildpacks/go.sum @@ -10,6 +10,8 @@ github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/U github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= @@ -36,6 +38,8 @@ github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tailscale/hujson v0.0.0-20250605163823-992244df8c5a h1:a6TNDN9CgG+cYjaeN8l2mc4kSz2iMiCDQxPEyltUV/I= +github.com/tailscale/hujson v0.0.0-20250605163823-992244df8c5a/go.mod h1:EbW0wDK/qEUYI0A5bqq0C2kF8JTQwWONmGDBbzsxxHo= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= @@ -53,3 +57,5 @@ golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ= +k8s.io/utils v0.0.0-20240102154912-e7106e64919e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= diff --git a/plugins/buildpacks/triggers.go b/plugins/buildpacks/triggers.go index 37f1c33d7..fce9ecd5d 100644 --- a/plugins/buildpacks/triggers.go +++ b/plugins/buildpacks/triggers.go @@ -74,7 +74,7 @@ func TriggerPostDelete(appName string) error { // TriggerPostExtract writes a .buildpacks file into the app func TriggerPostExtract(appName string, sourceWorkDir string) error { - buildpacks, err := common.PropertyListGet("buildpacks", appName, "buildpacks") + buildpacks, err := getBuildpacks(appName) if err != nil { return nil } @@ -91,13 +91,20 @@ func TriggerPostExtract(appName string, sourceWorkDir string) error { w := bufio.NewWriter(file) for _, buildpack := range buildpacks { + buildpack, err = validBuildpackURL(buildpack) + if err != nil { + return err + } + fmt.Fprintln(w, buildpack) } if err = w.Flush(); err != nil { return fmt.Errorf("Error writing .buildpacks file: %s", err.Error()) } - file.Chmod(0600) + if err = file.Chmod(0600); err != nil { + return fmt.Errorf("Error setting .buildpacks file permissions: %s", err.Error()) + } return nil } diff --git a/plugins/scheduler-k3s/go.mod b/plugins/scheduler-k3s/go.mod index d7868e783..f19ee7996 100644 --- a/plugins/scheduler-k3s/go.mod +++ b/plugins/scheduler-k3s/go.mod @@ -5,14 +5,14 @@ go 1.25.5 require ( github.com/Masterminds/semver/v3 v3.4.0 github.com/cert-manager/cert-manager v1.20.2 - github.com/dokku/dokku/plugins/app-json v0.0.0-20250618161309-8d0c35f1333c - github.com/dokku/dokku/plugins/common v0.0.0-20250618161309-8d0c35f1333c - github.com/dokku/dokku/plugins/config v0.0.0-20250618161309-8d0c35f1333c - github.com/dokku/dokku/plugins/cron v0.0.0-20250618161309-8d0c35f1333c - github.com/dokku/dokku/plugins/docker-options v0.0.0-20250618161309-8d0c35f1333c - github.com/dokku/dokku/plugins/logs v0.0.0-20250618161309-8d0c35f1333c - github.com/dokku/dokku/plugins/nginx-vhosts v0.0.0-20250618161309-8d0c35f1333c - github.com/dokku/dokku/plugins/registry v0.0.0-20250618161309-8d0c35f1333c + github.com/dokku/dokku/plugins/app-json v0.0.0-00010101000000-000000000000 + github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 + github.com/dokku/dokku/plugins/config v0.0.0-00010101000000-000000000000 + github.com/dokku/dokku/plugins/cron v0.0.0-00010101000000-000000000000 + github.com/dokku/dokku/plugins/docker-options v0.0.0-00010101000000-000000000000 + github.com/dokku/dokku/plugins/logs v0.0.0-00010101000000-000000000000 + github.com/dokku/dokku/plugins/nginx-vhosts v0.0.0-00010101000000-000000000000 + github.com/dokku/dokku/plugins/registry v0.0.0-00010101000000-000000000000 github.com/fatih/color v1.19.0 github.com/fluxcd/pkg/kustomize v1.29.0 github.com/go-openapi/jsonpointer v0.23.0 From 44dbbe6a3473b21d9beb6e6d313c2fbab9ab80d2 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Fri, 28 Nov 2025 02:31:13 -0500 Subject: [PATCH 004/546] fix: return buildpacks specified by the buildpacks plugin --- plugins/buildpacks/functions.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/buildpacks/functions.go b/plugins/buildpacks/functions.go index 37d8e9515..7037cf516 100644 --- a/plugins/buildpacks/functions.go +++ b/plugins/buildpacks/functions.go @@ -24,6 +24,10 @@ func getBuildpacks(appName string) ([]string, error) { return buildpacks, err } + if len(buildpacks) > 0 { + return buildpacks, nil + } + appJSON, err := appjson.GetAppJSON(appName) if err != nil { return buildpacks, err From b0b11bcf263df643ee70bb8dc602da31882d4f0a Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 18 Apr 2026 05:43:44 -0400 Subject: [PATCH 005/546] fix: remove buildpacks from dockerfile test app and add tests for app.json buildpacks The buildpacks entry in tests/apps/dockerfile/app.json caused the post-extract trigger to write a .buildpacks file during Dockerfile-based deploys, breaking builder detection. Remove the entry and add Go unit tests for getBuildpacks/validBuildpackURL, a bats integration test for deploying with app.json buildpacks, and documentation for the new feature. --- docs/appendices/file-formats/app-json.md | 19 +++ .../builders/buildpack-management.md | 18 +++ plugins/buildpacks/buildpacks_test.go | 141 ++++++++++++++++++ plugins/buildpacks/go.mod | 5 + plugins/buildpacks/go.sum | 1 + tests.mk | 4 +- tests/apps/dockerfile/app.json | 5 - tests/unit/buildpacks.bats | 27 ++++ 8 files changed, 214 insertions(+), 6 deletions(-) create mode 100644 plugins/buildpacks/buildpacks_test.go diff --git a/docs/appendices/file-formats/app-json.md b/docs/appendices/file-formats/app-json.md index 53924fabc..6532c93b2 100644 --- a/docs/appendices/file-formats/app-json.md +++ b/docs/appendices/file-formats/app-json.md @@ -5,6 +5,25 @@ > [!IMPORTANT] > While the `app.json` format used by Dokku is based on the one [supported by Heroku](https://devcenter.heroku.com/articles/app-json-schema), not all Heroku functionality is supported by Dokku. +## Buildpacks + +```json +{ + "buildpacks": [ + { + "url": "heroku/python" + }, + { + "url": "https://github.com/heroku/heroku-buildpack-nodejs.git" + } + ] +} +``` + +(list, optional) A list of buildpacks to use when deploying the app. Each entry is an object with a `url` property. Buildpacks specified via the `buildpacks:add` or `buildpacks:set` commands take precedence over those specified in `app.json`. Buildpack URLs may use the shorthand format (e.g., `heroku/python`) which will be expanded to the full GitHub URL. + +- `url`: (string, required) The URL or shorthand reference of the buildpack. + ## Cron ```json diff --git a/docs/deployment/builders/buildpack-management.md b/docs/deployment/builders/buildpack-management.md index c577e26c9..a2b876b9f 100644 --- a/docs/deployment/builders/buildpack-management.md +++ b/docs/deployment/builders/buildpack-management.md @@ -130,6 +130,24 @@ This will use the latest commit on the `master` branch of the specified buildpac dokku buildpacks:set node-js-app https://github.com/heroku/heroku-buildpack-nodejs#v87 ``` +### Specifying buildpacks via app.json + +Buildpacks can also be specified in the `app.json` file at the root of your repository: + +```json +{ + "buildpacks": [ + { + "url": "heroku/python" + } + ] +} +``` + +Buildpacks configured via `buildpacks:add` or `buildpacks:set` commands take precedence over those specified in `app.json`. If no buildpacks are configured via commands and `app.json` contains a `buildpacks` entry, those buildpacks will be used. + +Shorthand buildpack references (e.g., `heroku/python`) are expanded to full GitHub URLs automatically. + ### Displaying buildpack reports for an app You can get a report about the app's buildpacks status using the `buildpacks:report` command: diff --git a/plugins/buildpacks/buildpacks_test.go b/plugins/buildpacks/buildpacks_test.go new file mode 100644 index 000000000..30fe99dae --- /dev/null +++ b/plugins/buildpacks/buildpacks_test.go @@ -0,0 +1,141 @@ +package buildpacks + +import ( + "os" + "path/filepath" + "testing" + + . "github.com/onsi/gomega" +) + +func setupTestEnvironment(t *testing.T) string { + t.Helper() + tmpDir, err := os.MkdirTemp("", "buildpacks-test") + if err != nil { + t.Fatal(err) + } + + os.Setenv("DOKKU_LIB_ROOT", tmpDir) + os.Setenv("PLUGIN_PATH", "/var/lib/dokku/plugins") + os.Setenv("PLUGIN_ENABLED_PATH", "/var/lib/dokku/plugins/enabled") + + return tmpDir +} + +func teardownTestEnvironment(tmpDir string) { + os.RemoveAll(tmpDir) +} + +func TestValidBuildpackURL(t *testing.T) { + RegisterTestingT(t) + + url, err := validBuildpackURL("") + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("Must specify a buildpack")) + + url, err = validBuildpackURL("heroku/nodejs") + Expect(err).NotTo(HaveOccurred()) + Expect(url).To(Equal("https://github.com/heroku/heroku-buildpack-nodejs.git")) + + url, err = validBuildpackURL("heroku/python") + Expect(err).NotTo(HaveOccurred()) + Expect(url).To(Equal("https://github.com/heroku/heroku-buildpack-python.git")) + + url, err = validBuildpackURL("heroku-community/apt") + Expect(err).NotTo(HaveOccurred()) + Expect(url).To(Equal("https://github.com/heroku/heroku-buildpack-apt.git")) + + url, err = validBuildpackURL("https://github.com/heroku/heroku-buildpack-nodejs.git") + Expect(err).NotTo(HaveOccurred()) + Expect(url).To(Equal("https://github.com/heroku/heroku-buildpack-nodejs.git")) + + url, err = validBuildpackURL("https://github.com/heroku/heroku-buildpack-nodejs") + Expect(err).NotTo(HaveOccurred()) + Expect(url).To(Equal("https://github.com/heroku/heroku-buildpack-nodejs")) + + url, err = validBuildpackURL("git://github.com/heroku/heroku-buildpack-nodejs.git") + Expect(err).NotTo(HaveOccurred()) + Expect(url).To(Equal("git://github.com/heroku/heroku-buildpack-nodejs.git")) + + _, err = validBuildpackURL("nodejs") + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("Invalid buildpack specified")) + + _, err = validBuildpackURL("/nodejs") + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("Invalid buildpack specified")) +} + +func TestGetBuildpacksEmpty(t *testing.T) { + RegisterTestingT(t) + tmpDir := setupTestEnvironment(t) + defer teardownTestEnvironment(tmpDir) + + buildpacks, err := getBuildpacks("test-app") + Expect(err).NotTo(HaveOccurred()) + Expect(buildpacks).To(BeEmpty()) +} + +func TestGetBuildpacksFromProperties(t *testing.T) { + RegisterTestingT(t) + tmpDir := setupTestEnvironment(t) + defer teardownTestEnvironment(tmpDir) + + propDir := filepath.Join(tmpDir, "config", "buildpacks", "test-app") + Expect(os.MkdirAll(propDir, 0755)).To(Succeed()) + Expect(os.WriteFile(filepath.Join(propDir, "buildpacks"), []byte("https://github.com/heroku/heroku-buildpack-nodejs.git\nhttps://github.com/heroku/heroku-buildpack-python.git\n"), 0644)).To(Succeed()) + + buildpacks, err := getBuildpacks("test-app") + Expect(err).NotTo(HaveOccurred()) + Expect(buildpacks).To(HaveLen(2)) + Expect(buildpacks[0]).To(Equal("https://github.com/heroku/heroku-buildpack-nodejs.git")) + Expect(buildpacks[1]).To(Equal("https://github.com/heroku/heroku-buildpack-python.git")) +} + +func TestGetBuildpacksFromAppJSON(t *testing.T) { + RegisterTestingT(t) + tmpDir := setupTestEnvironment(t) + defer teardownTestEnvironment(tmpDir) + + appJSONDir := filepath.Join(tmpDir, "data", "app-json", "test-app") + Expect(os.MkdirAll(appJSONDir, 0755)).To(Succeed()) + Expect(os.WriteFile(filepath.Join(appJSONDir, "app.json"), []byte(`{"buildpacks": [{"url": "https://github.com/heroku/heroku-buildpack-nodejs.git"}]}`), 0644)).To(Succeed()) + + buildpacks, err := getBuildpacks("test-app") + Expect(err).NotTo(HaveOccurred()) + Expect(buildpacks).To(HaveLen(1)) + Expect(buildpacks[0]).To(Equal("https://github.com/heroku/heroku-buildpack-nodejs.git")) +} + +func TestGetBuildpacksPropertiesOverrideAppJSON(t *testing.T) { + RegisterTestingT(t) + tmpDir := setupTestEnvironment(t) + defer teardownTestEnvironment(tmpDir) + + propDir := filepath.Join(tmpDir, "config", "buildpacks", "test-app") + Expect(os.MkdirAll(propDir, 0755)).To(Succeed()) + Expect(os.WriteFile(filepath.Join(propDir, "buildpacks"), []byte("https://github.com/heroku/heroku-buildpack-ruby.git\n"), 0644)).To(Succeed()) + + appJSONDir := filepath.Join(tmpDir, "data", "app-json", "test-app") + Expect(os.MkdirAll(appJSONDir, 0755)).To(Succeed()) + Expect(os.WriteFile(filepath.Join(appJSONDir, "app.json"), []byte(`{"buildpacks": [{"url": "https://github.com/heroku/heroku-buildpack-nodejs.git"}]}`), 0644)).To(Succeed()) + + buildpacks, err := getBuildpacks("test-app") + Expect(err).NotTo(HaveOccurred()) + Expect(buildpacks).To(HaveLen(1)) + Expect(buildpacks[0]).To(Equal("https://github.com/heroku/heroku-buildpack-ruby.git")) +} + +func TestGetBuildpacksAppJSONEmptyArray(t *testing.T) { + RegisterTestingT(t) + tmpDir := setupTestEnvironment(t) + defer teardownTestEnvironment(tmpDir) + + appJSONDir := filepath.Join(tmpDir, "data", "app-json", "test-app") + Expect(os.MkdirAll(appJSONDir, 0755)).To(Succeed()) + Expect(os.WriteFile(filepath.Join(appJSONDir, "app.json"), []byte(`{"buildpacks": []}`), 0644)).To(Succeed()) + + buildpacks, err := getBuildpacks("test-app") + Expect(err).NotTo(HaveOccurred()) + Expect(buildpacks).To(BeEmpty()) +} diff --git a/plugins/buildpacks/go.mod b/plugins/buildpacks/go.mod index ae4000145..5afa00299 100644 --- a/plugins/buildpacks/go.mod +++ b/plugins/buildpacks/go.mod @@ -11,6 +11,7 @@ require ( require ( github.com/alexellis/go-execute/v2 v2.2.1 // indirect github.com/fatih/color v1.19.0 // indirect + github.com/google/go-cmp v0.7.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect @@ -18,15 +19,19 @@ require ( github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/melbahja/goph v1.5.0 // indirect + github.com/onsi/gomega v1.39.1 // indirect github.com/otiai10/copy v1.14.1 // indirect github.com/otiai10/mint v1.6.3 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pkg/sftp v1.13.10 // indirect github.com/ryanuber/columnize v2.1.2+incompatible // indirect github.com/tailscale/hujson v0.0.0-20250605163823-992244df8c5a // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.50.0 // indirect + golang.org/x/net v0.52.0 // indirect golang.org/x/sync v0.20.0 // indirect golang.org/x/sys v0.43.0 // indirect + golang.org/x/text v0.36.0 // indirect k8s.io/utils v0.0.0-20240102154912-e7106e64919e // indirect ) diff --git a/plugins/buildpacks/go.sum b/plugins/buildpacks/go.sum index 1db1549b6..957619840 100644 --- a/plugins/buildpacks/go.sum +++ b/plugins/buildpacks/go.sum @@ -55,6 +55,7 @@ golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY= golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY= golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ= diff --git a/tests.mk b/tests.mk index c31d42db0..d13240a98 100644 --- a/tests.mk +++ b/tests.mk @@ -149,6 +149,7 @@ ci-go-coverage: @$(MAKE) ci-go-coverage-plugin PLUGIN_NAME=common @$(MAKE) ci-go-coverage-plugin PLUGIN_NAME=config @$(MAKE) ci-go-coverage-plugin PLUGIN_NAME=network + @$(MAKE) ci-go-coverage-plugin PLUGIN_NAME=buildpacks ci-go-coverage-plugin: mkdir -p test-results/coverage @@ -177,9 +178,10 @@ go-tests: @$(MAKE) go-test-plugin PLUGIN_NAME=common @$(MAKE) go-test-plugin PLUGIN_NAME=config @$(MAKE) go-test-plugin PLUGIN_NAME=network + @$(MAKE) go-test-plugin PLUGIN_NAME=buildpacks go-test-plugin: - cd plugins/$(PLUGIN_NAME) && go get github.com/onsi/gomega && DOKKU_ROOT=/home/dokku go test -v -p 1 -race -mod=readonly || exit $$? + cd plugins/$(PLUGIN_NAME) && go get github.com/onsi/gomega && DOKKU_ROOT=/home/dokku DOKKU_LIB_ROOT=/var/lib/dokku go test -v -p 1 -race -mod=readonly || exit $$? go-test-plugin-in-docker: @echo running go unit tests... diff --git a/tests/apps/dockerfile/app.json b/tests/apps/dockerfile/app.json index bd0d70165..39127109e 100644 --- a/tests/apps/dockerfile/app.json +++ b/tests/apps/dockerfile/app.json @@ -3,11 +3,6 @@ "dokku-postgres", "dokku-redis" ], - "buildpacks": [ - { - "url": "https://github.com/heroku/heroku-buildpack-nodejs" - } - ], "description": "Used in dokku's test suite", "env": { "SECRET_TOKEN": { diff --git a/tests/unit/buildpacks.bats b/tests/unit/buildpacks.bats index e4d24fb39..59e9b4438 100644 --- a/tests/unit/buildpacks.bats +++ b/tests/unit/buildpacks.bats @@ -336,6 +336,33 @@ teardown() { assert_output_contains "heroku-buildpack-python" } +@test "(buildpacks) deploy with buildpacks from app.json" { + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP template_buildpacks_appjson + echo "output: $output" + echo "status: $status" + assert_success + assert_output_contains "heroku-buildpack-python" +} + +template_buildpacks_appjson() { + local APP="$1" + local APP_REPO_DIR="$2" + [[ -z "$APP" ]] && local APP="$TEST_APP" + echo "injecting app.json with buildpacks -> $APP_REPO_DIR/app.json" + cat <"$APP_REPO_DIR/app.json" +{ + "buildpacks": [ + { + "url": "heroku/python" + } + ] +} +EOF + + echo "injecting requirements.txt -> $APP_REPO_DIR/requirements.txt" + touch "$APP_REPO_DIR/requirements.txt" +} + template_buildpacks_cleanup() { local APP="$1" local APP_REPO_DIR="$2" From 08596c9648af38bb87c5822538a30519e63fb2b5 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 18 Apr 2026 20:29:05 -0400 Subject: [PATCH 006/546] fix: skip app.json buildpacks for image-based deploys When deploying via git:from-image, the source image may contain an app.json with buildpacks entries that are not relevant. Check for the source-image property and skip app.json buildpacks when set. --- plugins/buildpacks/functions.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/buildpacks/functions.go b/plugins/buildpacks/functions.go index 7037cf516..89beebc3a 100644 --- a/plugins/buildpacks/functions.go +++ b/plugins/buildpacks/functions.go @@ -28,6 +28,14 @@ func getBuildpacks(appName string) ([]string, error) { return buildpacks, nil } + results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ + Trigger: "git-get-property", + Args: []string{appName, "source-image"}, + }) + if results.StdoutContents() != "" { + return buildpacks, nil + } + appJSON, err := appjson.GetAppJSON(appName) if err != nil { return buildpacks, err From 2993fa800ce228cb31186a5bbdd367a4e3b23c8a Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Fri, 24 Apr 2026 02:10:30 -0400 Subject: [PATCH 007/546] feat(nginx): generate 502 config for apps without web listeners When an app has no web listeners (not yet deployed, no web process type, or web processes not running), generate a minimal nginx config that returns 502 Bad Gateway instead of having no config at all. This ensures domains resolve, monitoring tools detect non-200 status codes, and SSL certificate provisioning tools like letsencrypt can function. --- docs/networking/proxies/nginx.md | 15 +++ plugins/nginx-vhosts/functions | 81 +++++++++++++-- plugins/nginx-vhosts/post-create | 21 ++++ plugins/nginx-vhosts/proxy-build-config | 2 +- .../nginx-vhosts/templates/nginx.conf.sigil | 12 +++ tests/unit/nginx-vhosts_13.bats | 98 +++++++++++++++++++ 6 files changed, 218 insertions(+), 11 deletions(-) create mode 100644 plugins/nginx-vhosts/post-create create mode 100644 tests/unit/nginx-vhosts_13.bats diff --git a/docs/networking/proxies/nginx.md b/docs/networking/proxies/nginx.md index 330f5fa0f..8ca02d0f6 100644 --- a/docs/networking/proxies/nginx.md +++ b/docs/networking/proxies/nginx.md @@ -27,6 +27,21 @@ Nginx will proxy the requests in a [round-robin balancing fashion](http://nginx. > [!NOTE] > Due to how the plugin is implemented, if an app successfully starts up `web` containers but fails to deploy some other containers, nginx may eventually stop routing requests. Users should revert their code in these cases, or manually trigger `dokku proxy:build-config $APP` in order to ensure requests route to the new web containers. +### Nginx Configuration for Undeployed Apps + +> [!IMPORTANT] +> New as of 0.37.0 + +When an app is created but not yet deployed, has no `web` process type, or has no running web processes, Dokku generates a minimal nginx configuration that returns `502 Bad Gateway` responses. This ensures that: + +- The app's domain resolves and returns a non-200 status code, allowing monitoring tools to detect the issue. +- SSL certificate provisioning tools such as letsencrypt can function, as an nginx server is listening on the domain. +- The `nginx.conf.d/` include directory is available for plugin customization. + +The 502 error page includes auto-retry JavaScript that will automatically reload the page when the application becomes available. + +Once the app is deployed with running `web` processes, the placeholder configuration is automatically replaced with the full proxy configuration. + ### Starting nginx > [!IMPORTANT] diff --git a/plugins/nginx-vhosts/functions b/plugins/nginx-vhosts/functions index 0f81141c4..dd5fdf453 100755 --- a/plugins/nginx-vhosts/functions +++ b/plugins/nginx-vhosts/functions @@ -306,9 +306,19 @@ nginx_build_config() { local PROXY_BUSY_BUFFERS_SIZE="$(fn-nginx-computed-proxy-busy-buffers-size "$APP")" local PROXY_KEEPALIVE="$(fn-nginx-computed-proxy-keepalive "$APP")" - if [[ -z "$DOKKU_APP_LISTENERS" ]]; then + local NGINX_BIND_ADDRESS_IP4="$(fn-nginx-computed-bind-address-ipv4 "$APP")" + local NGINX_BIND_ADDRESS_IP6="$(fn-nginx-computed-bind-address-ipv6 "$APP")" + local NGINX_UNDERSCORE_IN_HEADERS="$(fn-nginx-computed-underscore-in-headers "$APP")" + local PROXY_X_FORWARDED_FOR="$(fn-nginx-computed-x-forwarded-for-value "$APP")" + local PROXY_X_FORWARDED_PORT="$(fn-nginx-computed-x-forwarded-port-value "$APP")" + local PROXY_X_FORWARDED_PROTO="$(fn-nginx-computed-x-forwarded-proto-value "$APP")" + local PROXY_X_FORWARDED_SSL="$(fn-nginx-computed-x-forwarded-ssl "$APP")" + + if [[ -z "$DOKKU_APP_LISTENERS" ]] && [[ -z "$PASSED_LISTEN_IP_PORT" ]]; then dokku_log_warn_quiet "No web listeners specified for $APP" - elif (is_deployed "$APP"); then + fi + + if [[ -n "$DOKKU_APP_LISTENERS" ]] && (is_deployed "$APP"); then if [[ "$(plugn trigger network-get-static-listeners "$APP" "web")" == "" ]]; then local IMAGE_TAG=$(get_running_image_tag "$APP") local IMAGE=$(get_deploying_app_image_name "$APP" "$IMAGE_TAG" 2>/dev/null) @@ -329,14 +339,6 @@ nginx_build_config() { local NGINX_TEMPLATE_SOURCE="app-supplied" fi - local NGINX_BIND_ADDRESS_IP4="$(fn-nginx-computed-bind-address-ipv4 "$APP")" - local NGINX_BIND_ADDRESS_IP6="$(fn-nginx-computed-bind-address-ipv6 "$APP")" - local NGINX_UNDERSCORE_IN_HEADERS="$(fn-nginx-computed-underscore-in-headers "$APP")" - local PROXY_X_FORWARDED_FOR="$(fn-nginx-computed-x-forwarded-for-value "$APP")" - local PROXY_X_FORWARDED_PORT="$(fn-nginx-computed-x-forwarded-port-value "$APP")" - local PROXY_X_FORWARDED_PROTO="$(fn-nginx-computed-x-forwarded-proto-value "$APP")" - local PROXY_X_FORWARDED_SSL="$(fn-nginx-computed-x-forwarded-ssl "$APP")" - eval "$(config_export app "$APP")" local SIGIL_PARAMS=(-f "$NGINX_TEMPLATE" APP="$APP" DOKKU_ROOT="$DOKKU_ROOT" NOSSL_SERVER_NAME="$NOSSL_SERVER_NAME" @@ -413,6 +415,65 @@ nginx_build_config() { plugn trigger nginx-pre-reload "$APP" "$DOKKU_APP_LISTEN_PORT" "$DOKKU_APP_LISTEN_IP" + dokku_log_verbose "Reloading nginx" + validate_nginx && restart_nginx >/dev/null + elif [[ -z "$DOKKU_APP_LISTENERS" ]] && [[ -z "$PASSED_LISTEN_IP_PORT" ]]; then + local NGINX_BUILD_CONFIG_TMP_WORK_DIR=$(mktemp -d "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX") + local NGINX_CONF=$(mktemp --tmpdir="${NGINX_BUILD_CONFIG_TMP_WORK_DIR}" "nginx.conf.XXXXXX") + trap "rm -rf '$NGINX_CONF' '$NGINX_BUILD_CONFIG_TMP_WORK_DIR' >/dev/null" RETURN INT TERM EXIT + + local SIGIL_PARAMS=(-f "$NGINX_TEMPLATE" APP="$APP" DOKKU_ROOT="$DOKKU_ROOT" + NOSSL_SERVER_NAME="$NOSSL_SERVER_NAME" + DOKKU_APP_LISTENERS="" + DOKKU_APP_WEB_LISTENERS="" + DOKKU_LIB_ROOT="$DOKKU_LIB_ROOT" + PASSED_LISTEN_IP_PORT="" + SPDY_SUPPORTED="$SPDY_SUPPORTED" + TLS13_SUPPORTED="$TLS13_SUPPORTED" + HTTP2_SUPPORTED="$HTTP2_SUPPORTED" + NGINX_LOG_ROOT="$NGINX_LOG_ROOT" + NGINX_ACCESS_LOG_FORMAT="$NGINX_ACCESS_LOG_FORMAT" + NGINX_ACCESS_LOG_PATH="$NGINX_ACCESS_LOG_PATH" + NGINX_ERROR_LOG_PATH="$NGINX_ERROR_LOG_PATH" + NGINX_BIND_ADDRESS_IP4="$NGINX_BIND_ADDRESS_IP4" + NGINX_BIND_ADDRESS_IP6="$NGINX_BIND_ADDRESS_IP6" + HTTP2_DIRECTIVE_SUPPORTED="$HTTP2_DIRECTIVE_SUPPORTED" + HTTP2_PUSH_SUPPORTED="$HTTP2_PUSH_SUPPORTED" + GRPC_SUPPORTED="$GRPC_SUPPORTED" + DOKKU_APP_LISTEN_PORT="" DOKKU_APP_LISTEN_IP="" + APP_SSL_PATH="$APP_SSL_PATH" SSL_INUSE="$SSL_INUSE" SSL_SERVER_NAME="$SSL_SERVER_NAME" + CLIENT_BODY_TIMEOUT="$CLIENT_BODY_TIMEOUT" + CLIENT_HEADER_TIMEOUT="$CLIENT_HEADER_TIMEOUT" + CLIENT_MAX_BODY_SIZE="$CLIENT_MAX_BODY_SIZE" + KEEPALIVE_TIMEOUT="$KEEPALIVE_TIMEOUT" + LINGERING_TIMEOUT="$LINGERING_TIMEOUT" + PROXY_CONNECT_TIMEOUT="$PROXY_CONNECT_TIMEOUT" + PROXY_READ_TIMEOUT="$PROXY_READ_TIMEOUT" + PROXY_SEND_TIMEOUT="$PROXY_SEND_TIMEOUT" + SEND_TIMEOUT="$SEND_TIMEOUT" + PROXY_BUFFER_SIZE="$PROXY_BUFFER_SIZE" + PROXY_BUFFERING="$PROXY_BUFFERING" + PROXY_BUFFERS="$PROXY_BUFFERS" + PROXY_BUSY_BUFFERS_SIZE="$PROXY_BUSY_BUFFERS_SIZE" + PROXY_KEEPALIVE="$PROXY_KEEPALIVE" + NGINX_UNDERSCORE_IN_HEADERS="$NGINX_UNDERSCORE_IN_HEADERS" + NGINX_PORT="$PROXY_PORT" NGINX_SSL_PORT="$PROXY_SSL_PORT" + PROXY_PORT="$PROXY_PORT" PROXY_SSL_PORT="$PROXY_SSL_PORT" + PROXY_PORT_MAP="$PROXY_PORT_MAP" PROXY_UPSTREAM_PORTS="$PROXY_UPSTREAM_PORTS" + PROXY_X_FORWARDED_FOR="$PROXY_X_FORWARDED_FOR" + PROXY_X_FORWARDED_PORT="$PROXY_X_FORWARDED_PORT" + PROXY_X_FORWARDED_PROTO="$PROXY_X_FORWARDED_PROTO" + PROXY_X_FORWARDED_SSL="$PROXY_X_FORWARDED_SSL") + + xargs -i echo "-----> Configuring {}...(using $NGINX_TEMPLATE_SOURCE template)" <<<"$(echo "${SSL_VHOSTS}" "${NONSSL_VHOSTS}" | tr ' ' '\n' | sort -u)" + sigil "${SIGIL_PARAMS[@]}" | cat -s >"$NGINX_CONF" + + dokku_log_info1 "Creating $SCHEME nginx.conf" + mkdir -p "$DOKKU_ROOT/$APP/nginx.conf.d" + mv "$NGINX_CONF" "$DOKKU_ROOT/$APP/nginx.conf" + + plugn trigger nginx-pre-reload "$APP" "$DOKKU_APP_LISTEN_PORT" "$DOKKU_APP_LISTEN_IP" + dokku_log_verbose "Reloading nginx" validate_nginx && restart_nginx >/dev/null fi diff --git a/plugins/nginx-vhosts/post-create b/plugins/nginx-vhosts/post-create new file mode 100644 index 000000000..161dd046e --- /dev/null +++ b/plugins/nginx-vhosts/post-create @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" +source "$PLUGIN_AVAILABLE_PATH/nginx-vhosts/functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +trigger-nginx-vhosts-post-create() { + declare desc="nginx-vhosts post-create trigger" + declare trigger="post-create" + declare APP="$1" + + fn-plugin-property-setup-app "nginx" "$APP" + + if [[ "$(plugn trigger proxy-type "$APP")" != "nginx" ]]; then + return + fi + + nginx_build_config "$APP" +} + +trigger-nginx-vhosts-post-create "$@" diff --git a/plugins/nginx-vhosts/proxy-build-config b/plugins/nginx-vhosts/proxy-build-config index c9c330e6c..ea95b757f 100755 --- a/plugins/nginx-vhosts/proxy-build-config +++ b/plugins/nginx-vhosts/proxy-build-config @@ -26,7 +26,7 @@ trigger-nginx-vhosts-proxy-build-config() { rm -f "${DOKKU_LIB_ROOT}/data/nginx-vhosts/app-$APP/nginx.conf.sigil" fi else - nginx_clear_config "$APP" + nginx_build_config "$APP" fi } diff --git a/plugins/nginx-vhosts/templates/nginx.conf.sigil b/plugins/nginx-vhosts/templates/nginx.conf.sigil index a7ec0e382..d30b8fe9a 100644 --- a/plugins/nginx-vhosts/templates/nginx.conf.sigil +++ b/plugins/nginx-vhosts/templates/nginx.conf.sigil @@ -26,6 +26,7 @@ server { } {{ else }} location / { +{{ if $.DOKKU_APP_WEB_LISTENERS }} gzip on; gzip_min_length 1100; @@ -51,6 +52,9 @@ server { proxy_set_header X-Forwarded-Proto {{ $.PROXY_X_FORWARDED_PROTO }}; proxy_set_header X-Request-Start $msec; {{ if $.PROXY_X_FORWARDED_SSL }}proxy_set_header X-Forwarded-Ssl {{ $.PROXY_X_FORWARDED_SSL }};{{ end }} +{{ else }} + return 502; +{{ end }} } client_max_body_size {{ $.CLIENT_MAX_BODY_SIZE }}; @@ -103,6 +107,7 @@ server { send_timeout {{ $.SEND_TIMEOUT }}; location / { +{{ if $.DOKKU_APP_WEB_LISTENERS }} gzip on; gzip_min_length 1100; @@ -129,6 +134,9 @@ server { proxy_set_header X-Forwarded-Proto {{ $.PROXY_X_FORWARDED_PROTO }}; proxy_set_header X-Request-Start $msec; {{ if $.PROXY_X_FORWARDED_SSL }}proxy_set_header X-Forwarded-Ssl {{ $.PROXY_X_FORWARDED_SSL }};{{ end }} +{{ else }} + return 502; +{{ end }} } client_max_body_size {{ $.CLIENT_MAX_BODY_SIZE }}; @@ -159,6 +167,7 @@ server { include {{ $.DOKKU_ROOT }}/{{ $.APP }}/nginx.conf.d/*.conf; } {{ else if eq $scheme "grpc"}} +{{ if $.DOKKU_APP_WEB_LISTENERS }} server { {{ if eq $.HTTP2_DIRECTIVE_SUPPORTED "true" }} listen [{{ $.NGINX_BIND_ADDRESS_IP6 }}]:{{ $listen_port }}; @@ -179,7 +188,9 @@ server { client_max_body_size {{ $.CLIENT_MAX_BODY_SIZE }}; include {{ $.DOKKU_ROOT }}/{{ $.APP }}/nginx.conf.d/*.conf; } +{{ end }} {{ else if eq $scheme "grpcs"}} +{{ if $.DOKKU_APP_WEB_LISTENERS }} server { {{ if eq $.HTTP2_DIRECTIVE_SUPPORTED "true" }} listen [{{ $.NGINX_BIND_ADDRESS_IP6 }}]:{{ $listen_port }} ssl; @@ -208,6 +219,7 @@ server { } {{ end }} {{ end }} +{{ end }} {{ if $.DOKKU_APP_WEB_LISTENERS }} {{ range $upstream_port := $.PROXY_UPSTREAM_PORTS | split " " }} diff --git a/tests/unit/nginx-vhosts_13.bats b/tests/unit/nginx-vhosts_13.bats new file mode 100644 index 000000000..049e1f46c --- /dev/null +++ b/tests/unit/nginx-vhosts_13.bats @@ -0,0 +1,98 @@ +#!/usr/bin/env bats + +load test_helper + +setup() { + global_setup + [[ -f "$DOKKU_ROOT/VHOST" ]] && cp -fp "$DOKKU_ROOT/VHOST" "$DOKKU_ROOT/VHOST.bak" +} + +teardown() { + destroy_app + [[ -f "$DOKKU_ROOT/VHOST.bak" ]] && mv "$DOKKU_ROOT/VHOST.bak" "$DOKKU_ROOT/VHOST" && chown dokku:dokku "$DOKKU_ROOT/VHOST" + global_teardown +} + +@test "(nginx-vhosts) proxy:build-config generates 502 config for undeployed app" { + run create_app + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "test -f $DOKKU_ROOT/$TEST_APP/nginx.conf" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku nginx:show-config $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + assert_output_contains "return 502" -1 +} + +@test "(nginx-vhosts) proxy:build-config undeployed app returns 502" { + run create_app + echo "output: $output" + echo "status: $status" + assert_success + + assert_http_localhost_response "http" "${TEST_APP}.${DOKKU_DOMAIN}" "80" "/" "" "502" +} + +@test "(nginx-vhosts) proxy:build-config 502 config replaced after deploy" { + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku nginx:show-config $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + assert_output_contains "return 502" 0 + assert_output_contains "proxy_pass" -1 + + assert_http_success "http://${TEST_APP}.${DOKKU_DOMAIN}" +} + +@test "(nginx-vhosts) proxy:build-config domains:add updates 502 config" { + run create_app + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku domains:add $TEST_APP custom.${DOKKU_DOMAIN}" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku nginx:show-config $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + assert_output_contains "custom.${DOKKU_DOMAIN}" -1 + assert_output_contains "return 502" -1 +} + +@test "(nginx-vhosts) proxy:build-config no nginx.conf when proxy disabled" { + run create_app + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "test -f $DOKKU_ROOT/$TEST_APP/nginx.conf" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku proxy:disable $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "test -f $DOKKU_ROOT/$TEST_APP/nginx.conf" + echo "output: $output" + echo "status: $status" + assert_failure +} From 5bf82d258512249d70ed08ed3264b4dea427ca87 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Fri, 24 Apr 2026 02:40:27 -0400 Subject: [PATCH 008/546] docs: add note about when the new feature lands --- docs/networking/proxies/nginx.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/networking/proxies/nginx.md b/docs/networking/proxies/nginx.md index 8ca02d0f6..9e6c63d56 100644 --- a/docs/networking/proxies/nginx.md +++ b/docs/networking/proxies/nginx.md @@ -30,7 +30,7 @@ Nginx will proxy the requests in a [round-robin balancing fashion](http://nginx. ### Nginx Configuration for Undeployed Apps > [!IMPORTANT] -> New as of 0.37.0 +> New as of 0.38.0 When an app is created but not yet deployed, has no `web` process type, or has no running web processes, Dokku generates a minimal nginx configuration that returns `502 Bad Gateway` responses. This ensures that: From 20f2100d467acf8d6303ed7fc7d2c8c6429a5b71 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Fri, 24 Apr 2026 02:54:06 -0400 Subject: [PATCH 009/546] docs: add 0.38.0 migration guide with nginx.conf.sigil update notes Custom nginx.conf.sigil templates that reference DOKKU_APP_WEB_LISTENERS may now receive an empty value when rendered for apps without running web processes. The migration guide documents how to handle this with a conditional in the template. --- docs/README.md | 1 + docs/appendices/0.38.0-migration-guide.md | 15 +++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 docs/appendices/0.38.0-migration-guide.md diff --git a/docs/README.md b/docs/README.md index 08ce6096d..b4e6d5c81 100644 --- a/docs/README.md +++ b/docs/README.md @@ -121,6 +121,7 @@ This documentation covers the installation, configuration, and usage of Dokku - ### Migration Guides +- [0.38.0](appendices/0.38.0-migration-guide.md) - [0.37.0](appendices/0.37.0-migration-guide.md) - [0.36.0](appendices/0.36.0-migration-guide.md) - [0.35.0](appendices/0.35.0-migration-guide.md) diff --git a/docs/appendices/0.38.0-migration-guide.md b/docs/appendices/0.38.0-migration-guide.md new file mode 100644 index 000000000..e547325ec --- /dev/null +++ b/docs/appendices/0.38.0-migration-guide.md @@ -0,0 +1,15 @@ +# 0.38.0 Migration Guide + +## Changes + +- Dokku now generates a minimal nginx configuration for apps without running `web` processes (undeployed apps, apps with no `web` process type, or apps with stopped web processes). This configuration returns `502 Bad Gateway` responses, ensuring the app's domain resolves and monitoring tools can detect non-200 status codes. The configuration is automatically replaced with the full proxy configuration once the app is deployed with running `web` processes. See the [nginx documentation](/docs/networking/proxies/nginx.md#nginx-configuration-for-undeployed-apps) for more details. +- Users with custom `nginx.conf.sigil` templates that reference `DOKKU_APP_WEB_LISTENERS` should be aware that this variable may now be empty when the template is rendered for apps without running web processes. Custom templates should handle this case gracefully, for example by using a conditional to serve an error page instead of proxying: + ``` + location / { + {{ if $.DOKKU_APP_WEB_LISTENERS }} + proxy_pass http://{{ $.APP }}-{{ $upstream_port }}; + {{ else }} + return 502; + {{ end }} + } + ``` From 7529d8cb081efd5f6ca708d29b9474a77443df2a Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Fri, 24 Apr 2026 03:04:04 -0400 Subject: [PATCH 010/546] refactor(nginx): deduplicate sigil params in nginx_build_config Merge the deployed and empty-listeners branches into a single flow that shares the SIGIL_PARAMS array, temp dir setup, sigil rendering, and nginx reload. Branch only for deployed-specific logic: image verification, custom template lookup, per-process listeners, and HSTS. --- plugins/nginx-vhosts/functions | 137 +++++++++++---------------------- 1 file changed, 47 insertions(+), 90 deletions(-) diff --git a/plugins/nginx-vhosts/functions b/plugins/nginx-vhosts/functions index dd5fdf453..8ff9386f6 100755 --- a/plugins/nginx-vhosts/functions +++ b/plugins/nginx-vhosts/functions @@ -318,28 +318,38 @@ nginx_build_config() { dokku_log_warn_quiet "No web listeners specified for $APP" fi + local IS_DEPLOYED_WITH_LISTENERS=false if [[ -n "$DOKKU_APP_LISTENERS" ]] && (is_deployed "$APP"); then - if [[ "$(plugn trigger network-get-static-listeners "$APP" "web")" == "" ]]; then - local IMAGE_TAG=$(get_running_image_tag "$APP") - local IMAGE=$(get_deploying_app_image_name "$APP" "$IMAGE_TAG" 2>/dev/null) - if ! verify_image "$IMAGE" 2>/dev/null; then - dokku_log_fail "Missing image for app" + IS_DEPLOYED_WITH_LISTENERS=true + fi + + if [[ "$IS_DEPLOYED_WITH_LISTENERS" == "true" ]] || { [[ -z "$DOKKU_APP_LISTENERS" ]] && [[ -z "$PASSED_LISTEN_IP_PORT" ]]; }; then + if [[ "$IS_DEPLOYED_WITH_LISTENERS" == "true" ]]; then + if [[ "$(plugn trigger network-get-static-listeners "$APP" "web")" == "" ]]; then + local IMAGE_TAG=$(get_running_image_tag "$APP") + local IMAGE=$(get_deploying_app_image_name "$APP" "$IMAGE_TAG" 2>/dev/null) + if ! verify_image "$IMAGE" 2>/dev/null; then + dokku_log_fail "Missing image for app" + fi fi fi local NGINX_BUILD_CONFIG_TMP_WORK_DIR=$(mktemp -d "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX") local NGINX_CONF=$(mktemp --tmpdir="${NGINX_BUILD_CONFIG_TMP_WORK_DIR}" "nginx.conf.XXXXXX") - local CUSTOM_NGINX_TEMPLATE="$NGINX_BUILD_CONFIG_TMP_WORK_DIR/$NGINX_TEMPLATE_NAME" trap "rm -rf '$NGINX_CONF' '$NGINX_BUILD_CONFIG_TMP_WORK_DIR' >/dev/null" RETURN INT TERM EXIT - get_custom_nginx_template "$APP" "$CUSTOM_NGINX_TEMPLATE" 2>/dev/null - if [[ -f "$CUSTOM_NGINX_TEMPLATE" ]]; then - dokku_log_info1 'Overriding default nginx.conf with detected nginx.conf.sigil' - local NGINX_TEMPLATE="$CUSTOM_NGINX_TEMPLATE" - local NGINX_TEMPLATE_SOURCE="app-supplied" + if [[ "$IS_DEPLOYED_WITH_LISTENERS" == "true" ]]; then + local CUSTOM_NGINX_TEMPLATE="$NGINX_BUILD_CONFIG_TMP_WORK_DIR/$NGINX_TEMPLATE_NAME" + get_custom_nginx_template "$APP" "$CUSTOM_NGINX_TEMPLATE" 2>/dev/null + if [[ -f "$CUSTOM_NGINX_TEMPLATE" ]]; then + dokku_log_info1 'Overriding default nginx.conf with detected nginx.conf.sigil' + local NGINX_TEMPLATE="$CUSTOM_NGINX_TEMPLATE" + local NGINX_TEMPLATE_SOURCE="app-supplied" + fi + + eval "$(config_export app "$APP")" fi - eval "$(config_export app "$APP")" local SIGIL_PARAMS=(-f "$NGINX_TEMPLATE" APP="$APP" DOKKU_ROOT="$DOKKU_ROOT" NOSSL_SERVER_NAME="$NOSSL_SERVER_NAME" # Deprecated: Remove this after a few versions @@ -384,87 +394,30 @@ nginx_build_config() { PROXY_X_FORWARDED_PROTO="$PROXY_X_FORWARDED_PROTO" PROXY_X_FORWARDED_SSL="$PROXY_X_FORWARDED_SSL") - while read -r line || [[ -n "$line" ]]; do - PROC_TYPE=${line%%=*} - LISTENERS="$(plugn trigger network-get-listeners "$APP" "$PROC_TYPE" | xargs)" - UPP_PROC_TYPE="${PROC_TYPE^^}" - UPP_PROC_TYPE="${UPP_PROC_TYPE//-/_}" - SIGIL_PARAMS+=("DOKKU_APP_${UPP_PROC_TYPE}_LISTENERS=$LISTENERS") - done < <(plugn trigger ps-current-scale "$APP") + if [[ "$IS_DEPLOYED_WITH_LISTENERS" == "true" ]]; then + while read -r line || [[ -n "$line" ]]; do + PROC_TYPE=${line%%=*} + LISTENERS="$(plugn trigger network-get-listeners "$APP" "$PROC_TYPE" | xargs)" + UPP_PROC_TYPE="${PROC_TYPE^^}" + UPP_PROC_TYPE="${UPP_PROC_TYPE//-/_}" + SIGIL_PARAMS+=("DOKKU_APP_${UPP_PROC_TYPE}_LISTENERS=$LISTENERS") + done < <(plugn trigger ps-current-scale "$APP") - if grep DOKKU_APP_LISTENERS "$NGINX_TEMPLATE"; then - dokku_log_warn "Deprecated: Usage of DOKKU_APP_LISTENERS within nginx.conf.sigil templates is deprecated in favor of DOKKU_APP_WEB_LISTENERS" + if grep DOKKU_APP_LISTENERS "$NGINX_TEMPLATE"; then + dokku_log_warn "Deprecated: Usage of DOKKU_APP_LISTENERS within nginx.conf.sigil templates is deprecated in favor of DOKKU_APP_WEB_LISTENERS" + fi + + if grep NGINX_SSL_PORT "$NGINX_TEMPLATE"; then + dokku_log_warn "Deprecated: Usage of NGINX_SSL_PORT within nginx.conf.sigil templates is deprecated in favor of PROXY_SSL_PORT" + fi + + if grep NGINX_PORT "$NGINX_TEMPLATE"; then + dokku_log_warn "Deprecated: Usage of NGINX_PORT within nginx.conf.sigil templates is deprecated in favor of PROXY_PORT" + fi + else + SIGIL_PARAMS+=(DOKKU_APP_WEB_LISTENERS="") fi - if grep NGINX_SSL_PORT "$NGINX_TEMPLATE"; then - dokku_log_warn "Deprecated: Usage of NGINX_SSL_PORT within nginx.conf.sigil templates is deprecated in favor of PROXY_SSL_PORT" - fi - - if grep NGINX_PORT "$NGINX_TEMPLATE"; then - dokku_log_warn "Deprecated: Usage of NGINX_PORT within nginx.conf.sigil templates is deprecated in favor of PROXY_PORT" - fi - - # execute sigil template processing - xargs -i echo "-----> Configuring {}...(using $NGINX_TEMPLATE_SOURCE template)" <<<"$(echo "${SSL_VHOSTS}" "${NONSSL_VHOSTS}" | tr ' ' '\n' | sort -u)" - sigil "${SIGIL_PARAMS[@]}" | cat -s >"$NGINX_CONF" - - dokku_log_info1 "Creating $SCHEME nginx.conf" - mv "$NGINX_CONF" "$DOKKU_ROOT/$APP/nginx.conf" - - fn-nginx-vhosts-manage-hsts "$APP" "$SSL_INUSE" - - plugn trigger nginx-pre-reload "$APP" "$DOKKU_APP_LISTEN_PORT" "$DOKKU_APP_LISTEN_IP" - - dokku_log_verbose "Reloading nginx" - validate_nginx && restart_nginx >/dev/null - elif [[ -z "$DOKKU_APP_LISTENERS" ]] && [[ -z "$PASSED_LISTEN_IP_PORT" ]]; then - local NGINX_BUILD_CONFIG_TMP_WORK_DIR=$(mktemp -d "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX") - local NGINX_CONF=$(mktemp --tmpdir="${NGINX_BUILD_CONFIG_TMP_WORK_DIR}" "nginx.conf.XXXXXX") - trap "rm -rf '$NGINX_CONF' '$NGINX_BUILD_CONFIG_TMP_WORK_DIR' >/dev/null" RETURN INT TERM EXIT - - local SIGIL_PARAMS=(-f "$NGINX_TEMPLATE" APP="$APP" DOKKU_ROOT="$DOKKU_ROOT" - NOSSL_SERVER_NAME="$NOSSL_SERVER_NAME" - DOKKU_APP_LISTENERS="" - DOKKU_APP_WEB_LISTENERS="" - DOKKU_LIB_ROOT="$DOKKU_LIB_ROOT" - PASSED_LISTEN_IP_PORT="" - SPDY_SUPPORTED="$SPDY_SUPPORTED" - TLS13_SUPPORTED="$TLS13_SUPPORTED" - HTTP2_SUPPORTED="$HTTP2_SUPPORTED" - NGINX_LOG_ROOT="$NGINX_LOG_ROOT" - NGINX_ACCESS_LOG_FORMAT="$NGINX_ACCESS_LOG_FORMAT" - NGINX_ACCESS_LOG_PATH="$NGINX_ACCESS_LOG_PATH" - NGINX_ERROR_LOG_PATH="$NGINX_ERROR_LOG_PATH" - NGINX_BIND_ADDRESS_IP4="$NGINX_BIND_ADDRESS_IP4" - NGINX_BIND_ADDRESS_IP6="$NGINX_BIND_ADDRESS_IP6" - HTTP2_DIRECTIVE_SUPPORTED="$HTTP2_DIRECTIVE_SUPPORTED" - HTTP2_PUSH_SUPPORTED="$HTTP2_PUSH_SUPPORTED" - GRPC_SUPPORTED="$GRPC_SUPPORTED" - DOKKU_APP_LISTEN_PORT="" DOKKU_APP_LISTEN_IP="" - APP_SSL_PATH="$APP_SSL_PATH" SSL_INUSE="$SSL_INUSE" SSL_SERVER_NAME="$SSL_SERVER_NAME" - CLIENT_BODY_TIMEOUT="$CLIENT_BODY_TIMEOUT" - CLIENT_HEADER_TIMEOUT="$CLIENT_HEADER_TIMEOUT" - CLIENT_MAX_BODY_SIZE="$CLIENT_MAX_BODY_SIZE" - KEEPALIVE_TIMEOUT="$KEEPALIVE_TIMEOUT" - LINGERING_TIMEOUT="$LINGERING_TIMEOUT" - PROXY_CONNECT_TIMEOUT="$PROXY_CONNECT_TIMEOUT" - PROXY_READ_TIMEOUT="$PROXY_READ_TIMEOUT" - PROXY_SEND_TIMEOUT="$PROXY_SEND_TIMEOUT" - SEND_TIMEOUT="$SEND_TIMEOUT" - PROXY_BUFFER_SIZE="$PROXY_BUFFER_SIZE" - PROXY_BUFFERING="$PROXY_BUFFERING" - PROXY_BUFFERS="$PROXY_BUFFERS" - PROXY_BUSY_BUFFERS_SIZE="$PROXY_BUSY_BUFFERS_SIZE" - PROXY_KEEPALIVE="$PROXY_KEEPALIVE" - NGINX_UNDERSCORE_IN_HEADERS="$NGINX_UNDERSCORE_IN_HEADERS" - NGINX_PORT="$PROXY_PORT" NGINX_SSL_PORT="$PROXY_SSL_PORT" - PROXY_PORT="$PROXY_PORT" PROXY_SSL_PORT="$PROXY_SSL_PORT" - PROXY_PORT_MAP="$PROXY_PORT_MAP" PROXY_UPSTREAM_PORTS="$PROXY_UPSTREAM_PORTS" - PROXY_X_FORWARDED_FOR="$PROXY_X_FORWARDED_FOR" - PROXY_X_FORWARDED_PORT="$PROXY_X_FORWARDED_PORT" - PROXY_X_FORWARDED_PROTO="$PROXY_X_FORWARDED_PROTO" - PROXY_X_FORWARDED_SSL="$PROXY_X_FORWARDED_SSL") - xargs -i echo "-----> Configuring {}...(using $NGINX_TEMPLATE_SOURCE template)" <<<"$(echo "${SSL_VHOSTS}" "${NONSSL_VHOSTS}" | tr ' ' '\n' | sort -u)" sigil "${SIGIL_PARAMS[@]}" | cat -s >"$NGINX_CONF" @@ -472,6 +425,10 @@ nginx_build_config() { mkdir -p "$DOKKU_ROOT/$APP/nginx.conf.d" mv "$NGINX_CONF" "$DOKKU_ROOT/$APP/nginx.conf" + if [[ "$IS_DEPLOYED_WITH_LISTENERS" == "true" ]]; then + fn-nginx-vhosts-manage-hsts "$APP" "$SSL_INUSE" + fi + plugn trigger nginx-pre-reload "$APP" "$DOKKU_APP_LISTEN_PORT" "$DOKKU_APP_LISTEN_IP" dokku_log_verbose "Reloading nginx" From fc7ddf34ba151101b70a91d682fa0aff38dadbf3 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Fri, 24 Apr 2026 03:57:22 -0400 Subject: [PATCH 011/546] fix(nginx): make post-create executable and guard empty port maps The post-create trigger was not executable so plugn skipped it. Also skip generating the 502 config when PROXY_PORT_MAP is empty (e.g. after ports:clear) to avoid a sigil template index error. --- plugins/nginx-vhosts/functions | 2 +- plugins/nginx-vhosts/post-create | 0 2 files changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 plugins/nginx-vhosts/post-create diff --git a/plugins/nginx-vhosts/functions b/plugins/nginx-vhosts/functions index 8ff9386f6..152bec39f 100755 --- a/plugins/nginx-vhosts/functions +++ b/plugins/nginx-vhosts/functions @@ -323,7 +323,7 @@ nginx_build_config() { IS_DEPLOYED_WITH_LISTENERS=true fi - if [[ "$IS_DEPLOYED_WITH_LISTENERS" == "true" ]] || { [[ -z "$DOKKU_APP_LISTENERS" ]] && [[ -z "$PASSED_LISTEN_IP_PORT" ]]; }; then + if [[ "$IS_DEPLOYED_WITH_LISTENERS" == "true" ]] || { [[ -z "$DOKKU_APP_LISTENERS" ]] && [[ -z "$PASSED_LISTEN_IP_PORT" ]] && [[ -n "$PROXY_PORT_MAP" ]]; }; then if [[ "$IS_DEPLOYED_WITH_LISTENERS" == "true" ]]; then if [[ "$(plugn trigger network-get-static-listeners "$APP" "web")" == "" ]]; then local IMAGE_TAG=$(get_running_image_tag "$APP") diff --git a/plugins/nginx-vhosts/post-create b/plugins/nginx-vhosts/post-create old mode 100644 new mode 100755 From dcce09f19f55cc88bcc2575e0676aa4273543fa8 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Fri, 24 Apr 2026 04:14:49 -0400 Subject: [PATCH 012/546] fix(nginx): skip post-create config when nginx is stopped When nginx is stopped (e.g. during traefik/caddy tests), the post-create trigger should not attempt to build and reload the nginx config as the service is not running. --- plugins/nginx-vhosts/post-create | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/nginx-vhosts/post-create b/plugins/nginx-vhosts/post-create index 161dd046e..04f15e915 100755 --- a/plugins/nginx-vhosts/post-create +++ b/plugins/nginx-vhosts/post-create @@ -15,6 +15,10 @@ trigger-nginx-vhosts-post-create() { return fi + if [[ "$(fn-plugin-property-get "nginx" "--global" "proxy-status")" == "stopped" ]]; then + return + fi + nginx_build_config "$APP" } From ddf470282e9d8c59c76b203c789f76b7f669e12e Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Fri, 24 Apr 2026 05:02:49 -0400 Subject: [PATCH 013/546] fix(tests): update config and nginx tests for post-create side effects The config:show and config:export tests now include DOKKU_PROXY_PORT in expected output since ports-configure runs at app creation time. Add sleep before HTTP assertion in nginx-vhosts_13 test to allow nginx async reload to complete. --- tests/unit/config.bats | 10 +++++----- tests/unit/nginx-vhosts_13.bats | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/unit/config.bats b/tests/unit/config.bats index e9bfb1fce..af758b96a 100644 --- a/tests/unit/config.bats +++ b/tests/unit/config.bats @@ -506,7 +506,7 @@ teardown() { run /bin/bash -c "dokku --app $TEST_APP config:show" echo "output: $output" echo "status: $status" - assert_output "=====> $TEST_APP env vars"$'\nBKEY: true\naKey: true\nbKey: true\nzKey: true' + assert_output "=====> $TEST_APP env vars"$'\nBKEY: true\nDOKKU_PROXY_PORT: 80\naKey: true\nbKey: true\nzKey: true' } @test "(config) config:export" { @@ -518,20 +518,20 @@ teardown() { run /bin/bash -c "dokku config:export --format docker-args $TEST_APP" echo "output: $output" echo "status: $status" - assert_output "--env=BKEY='true' --env=aKey='true' --env=bKey='true' --env=zKey='true'" + assert_output "--env=BKEY='true' --env=DOKKU_PROXY_PORT='80' --env=aKey='true' --env=bKey='true' --env=zKey='true'" run /bin/bash -c "dokku config:export --format shell $TEST_APP" echo "output: $output" echo "status: $status" - assert_output "BKEY='true' aKey='true' bKey='true' zKey='true' " + assert_output "BKEY='true' DOKKU_PROXY_PORT='80' aKey='true' bKey='true' zKey='true' " run /bin/bash -c "dokku config:export --format json $TEST_APP" echo "output: $output" echo "status: $status" - assert_output '{"BKEY":"true","aKey":"true","bKey":"true","zKey":"true"}' + assert_output '{"BKEY":"true","DOKKU_PROXY_PORT":"80","aKey":"true","bKey":"true","zKey":"true"}' run /bin/bash -c "dokku config:export --format json-list $TEST_APP" echo "output: $output" echo "status: $status" - assert_output '[{"name":"BKEY","value":"true"},{"name":"aKey","value":"true"},{"name":"bKey","value":"true"},{"name":"zKey","value":"true"}]' + assert_output '[{"name":"BKEY","value":"true"},{"name":"DOKKU_PROXY_PORT","value":"80"},{"name":"aKey","value":"true"},{"name":"bKey","value":"true"},{"name":"zKey","value":"true"}]' } diff --git a/tests/unit/nginx-vhosts_13.bats b/tests/unit/nginx-vhosts_13.bats index 049e1f46c..37845e6fc 100644 --- a/tests/unit/nginx-vhosts_13.bats +++ b/tests/unit/nginx-vhosts_13.bats @@ -36,6 +36,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success + sleep 2 assert_http_localhost_response "http" "${TEST_APP}.${DOKKU_DOMAIN}" "80" "/" "" "502" } From f3e29c7c94bbe616099761062573df7ec1f817de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Apr 2026 13:53:53 +0000 Subject: [PATCH 014/546] chore(deps): bump dokku/openresty-docker-proxy Bumps dokku/openresty-docker-proxy from 0.9.3 to 0.10.0. --- updated-dependencies: - dependency-name: dokku/openresty-docker-proxy dependency-version: 0.10.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- plugins/openresty-vhosts/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/openresty-vhosts/Dockerfile b/plugins/openresty-vhosts/Dockerfile index 61e41b33e..aba3f9815 100644 --- a/plugins/openresty-vhosts/Dockerfile +++ b/plugins/openresty-vhosts/Dockerfile @@ -1 +1 @@ -FROM dokku/openresty-docker-proxy:0.9.3 +FROM dokku/openresty-docker-proxy:0.10.0 From 2745e4605479f27c990de89eee19a5fe8862f7cd Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 25 Apr 2026 00:55:55 -0400 Subject: [PATCH 015/546] fix(tests): expect 502 for cloned app without deploy The apps:clone --skip-deploy test expected 404 for the cloned app's domain. With the new empty nginx config, undeployed apps now correctly return 502 Bad Gateway instead of falling through to nginx's default. --- tests/unit/apps_2.bats | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/apps_2.bats b/tests/unit/apps_2.bats index a176cece0..661bc9822 100644 --- a/tests/unit/apps_2.bats +++ b/tests/unit/apps_2.bats @@ -166,7 +166,7 @@ teardown() { assert_success assert_output "http://$TEST_APP.$DOKKU_DOMAIN" assert_http_localhost_response "http" "$TEST_APP.$DOKKU_DOMAIN" "80" "/hello" - assert_http_localhost_response "http" "great-test-name.dokku.me" "80" "/hello" "" "404" + assert_http_localhost_response "http" "great-test-name.dokku.me" "80" "/hello" "" "502" run /bin/bash -c "dokku --force apps:destroy great-test-name" echo "output: $output" echo "status: $status" From 1308e219470e52a9c6d6f6d18c404af0f4ea304c Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 25 Apr 2026 05:11:07 -0400 Subject: [PATCH 016/546] feat: migrate environment variables to plugin properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Standardize how environment variables are migrated to properties during install triggers and migrate all remaining DOKKU_* config vars to their appropriate plugin properties. Adds a reusable MigrateConfigToProperties() function in the common package with Transform callback and ListProperty support, plus a migrate-config-to-property subcommand for the prop binary so shell plugins can use the same code path. Migrated variables and their new property owners: - DOKKU_APP_PROXY_TYPE/DOKKU_PROXY_TYPE → proxy type - DOKKU_DISABLE_PROXY → proxy disabled - DOKKU_PROXY_PORT → ports proxy-port - DOKKU_PROXY_SSL_PORT → ports proxy-ssl-port - DOKKU_APP_RESTORE → ps restore - DOKKU_SKIP_DEPLOY → ps skip-deploy - DOKKU_START_CMD → ps start-cmd - DOKKU_DOCKERFILE_START_CMD → ps dockerfile-start-cmd - DOKKU_DISABLE_APP_AUTOCREATION → apps disable-autocreation - DOKKU_APP_SHELL → scheduler shell - DOKKU_SKIP_CLEANUP → builder skip-cleanup - DOKKU_CHECKS_DISABLED → checks disabled - DOKKU_CHECKS_SKIPPED → checks skipped - DOKKU_CHECKS_WAIT → checks wait - DOKKU_CHECKS_TIMEOUT → checks timeout - DOKKU_CHECKS_ATTEMPTS → checks attempts - DOKKU_DEFAULT_CHECKS_WAIT → checks default-wait - DOKKU_SKIP_ALL_CHECKS → checks disabled (legacy) - DOKKU_SKIP_DEFAULT_CHECKS → checks skipped (legacy) Also refactors existing bespoke migration loops in scheduler, ports, ps, builder, and nginx-vhosts plugins to use the standardized utility. Removes DOKKU_PARALLEL_ARGUMENTS from documentation (unused in core). Deprecates fn-migrate-config-to-property bash function. closes #1558 --- docs/configuration/environment-variables.md | 50 ++++---- docs/deployment/application-deployment.md | 4 +- docs/deployment/zero-downtime-deploys.md | 14 +-- plugins/apps/apps.go | 2 + plugins/apps/functions.go | 6 +- plugins/apps/triggers.go | 9 ++ plugins/builder/builder.go | 12 +- plugins/builder/triggers.go | 35 ++---- plugins/checks/functions | 6 +- plugins/checks/install | 49 ++++---- plugins/checks/internal-functions | 4 +- plugins/checks/subcommands/disable | 18 +-- plugins/checks/subcommands/enable | 18 +-- plugins/checks/subcommands/skip | 18 +-- plugins/common/common.go | 31 +---- plugins/common/docker.go | 20 ++- plugins/common/functions | 6 +- plugins/common/properties.go | 115 ++++++++++++++++++ plugins/common/src/prop/prop.go | 70 +++++++++++ plugins/config/config.go | 14 ++- plugins/nginx-vhosts/install | 22 +--- plugins/ports/functions.go | 53 +++----- plugins/ports/proxy.go | 14 +++ plugins/ports/triggers.go | 81 ++++-------- plugins/proxy/Makefile | 2 +- plugins/proxy/functions.go | 6 +- plugins/proxy/proxy.go | 31 ++--- plugins/proxy/src/triggers/triggers.go | 13 ++ plugins/proxy/subcommands.go | 11 +- plugins/proxy/triggers.go | 50 ++++++++ plugins/ps/ps.go | 17 +-- plugins/ps/report.go | 13 +- plugins/ps/triggers.go | 84 +++++++------ .../bin/scheduler-deploy-process-container | 2 +- plugins/scheduler-docker-local/check-deploy | 12 +- .../scheduler-docker-local/scheduler-deploy | 2 +- .../scheduler-docker-local/scheduler-enter | 7 +- plugins/scheduler-docker-local/scheduler-run | 4 +- plugins/scheduler-k3s/functions.go | 16 +-- plugins/scheduler/scheduler.go | 2 + plugins/scheduler/triggers.go | 61 ++-------- tests/unit/apps_1.bats | 4 +- tests/unit/apps_2.bats | 6 +- tests/unit/checks.bats | 52 ++++---- tests/unit/nginx-vhosts_5.bats | 6 +- 45 files changed, 604 insertions(+), 468 deletions(-) diff --git a/docs/configuration/environment-variables.md b/docs/configuration/environment-variables.md index dd805bd0f..cfe98ae22 100644 --- a/docs/configuration/environment-variables.md +++ b/docs/configuration/environment-variables.md @@ -98,26 +98,34 @@ The following config variables have special meanings and can be set in a variety | `DOKKU_QUIET_OUTPUT` | none | `--quiet` flag | Silences certain header output for `dokku` commands. | | `DOKKU_RM_CONTAINER` | none | `dokku config:set`
| Deprecated: Whether to keep `dokku run` containers around or not. | | `DOKKU_TRACE` | none | `dokku trace:on`
`dokku trace:off`
`--trace` flag | Turn on very verbose debugging. | -| `DOKKU_APP_PROXY_TYPE` | `nginx` | `dokku proxy:set` | | -| `DOKKU_APP_RESTORE` | `1` | `dokku config:set`
`dokku ps:stop` | | -| `DOKKU_APP_SHELL` | `/bin/bash` | `dokku config:set` | Allows users to change the default shell used by Dokku for `dokku enter` and execution of deployment tasks. | -| `DOKKU_CHECKS_DISABLED` | none | `dokku checks:disable` | | -| `DOKKU_CHECKS_ENABLED` | none | `dokku checks:enable` | | -| `DOKKU_CHECKS_SKIPPED` | none | `dokku checks:skip` | | -| `DOKKU_CHECKS_WAIT` | `5` | `dokku config:set` | Wait this many seconds for the container to start before running checks. -| `DOKKU_CHECKS_TIMEOUT` | `30` | `dokku config:set` | Wait this many seconds for each response before marking it as a failure. -| `DOKKU_CHECKS_ATTEMPTS` | `5` | `dokku config:set` | Number of retries for to run for a specific check before marking it as a failure -| `DOKKU_DEFAULT_CHECKS_WAIT` | `10` | `dokku config:set` | If no user-defined checks are specified - or if the process being checked is not a `web` process - this is the period of time Dokku will wait before checking that a container is still running. | -| `DOKKU_DISABLE_PROXY` | none | `dokku proxy:disable`
`dokku proxy:enable` | Disables the proxy in front of your application, resulting in publicly routing the docker container. | -| `DOKKU_DISABLE_APP_AUTOCREATION` | none | `dokku config:set` | Disables automatic creation of a non-existent app on deploy. | -| `DOKKU_DOCKERFILE_START_CMD` | none | `dokku config:set` | | -| `DOKKU_PARALLEL_ARGUMENTS`. | none | `dokku config:set` | Allows passing custom arguments to parallel for `ps:*all` commands | -| `DOKKU_PROXY_PORT` | automatically assigned | `/etc/environment`
`~dokku/.dokkurc`
`~dokku/.dokkurc/*`
`dokku config:set` | | -| `DOKKU_PROXY_SSL_PORT` | automatically assigned | `/etc/environment`
`~dokku/.dokkurc`
`~dokku/.dokkurc/*`
`dokku config:set` | | -| `DOKKU_SKIP_ALL_CHECKS` | none | `dokku config:set` | | -| `DOKKU_SKIP_CLEANUP` | | `/etc/environment`
`~dokku/.dokkurc`
`~dokku/.dokkurc/*` | When a deploy is triggered, if this is set to a non-empty value, then old docker containers and images will not be removed. | -| `DOKKU_SKIP_DEFAULT_CHECKS` | | `dokku config:set` | | -| `DOKKU_SKIP_DEPLOY` | | `dokku config:set` | | -| `DOKKU_START_CMD` | none | `dokku config:set` | Command to run instead of `/start $PROC_TYPE` | +| `DOKKU_SKIP_CLEANUP` | | `/etc/environment`
`~dokku/.dokkurc`
`~dokku/.dokkurc/*` | When a deploy is triggered, if this is set to a non-empty value, then old docker containers and images will not be removed. Falls back to the `builder:set skip-cleanup` property. | | `DOKKU_SYSTEM_GROUP` | `dokku` | `/etc/environment`
`~dokku/.dokkurc`
`~dokku/.dokkurc/*` | System group to chown files as. | | `DOKKU_SYSTEM_USER` | `dokku` | `/etc/environment`
`~dokku/.dokkurc`
`~dokku/.dokkurc/*` | System user to chown files as. | + +## Deprecated Environment Variables + +The following environment variables have been migrated to plugin properties. Existing values will be automatically migrated on upgrade. Use the corresponding property commands going forward. + +| Deprecated Env Var | Replacement Command | +|---|---| +| `DOKKU_APP_PROXY_TYPE` | `dokku proxy:set type ` | +| `DOKKU_APP_RESTORE` | `dokku ps:set restore ` | +| `DOKKU_APP_SHELL` | `dokku scheduler:set shell ` | +| `DOKKU_CHECKS_DISABLED` | `dokku checks:disable [proctypes]` | +| `DOKKU_CHECKS_ENABLED` | `dokku checks:enable [proctypes]` | +| `DOKKU_CHECKS_SKIPPED` | `dokku checks:skip [proctypes]` | +| `DOKKU_CHECKS_WAIT` | `dokku checks:set wait ` | +| `DOKKU_CHECKS_TIMEOUT` | `dokku checks:set timeout ` | +| `DOKKU_CHECKS_ATTEMPTS` | `dokku checks:set attempts ` | +| `DOKKU_DEFAULT_CHECKS_WAIT` | `dokku checks:set --global default-wait ` | +| `DOKKU_DISABLE_APP_AUTOCREATION` | `dokku apps:set --global disable-autocreation ` | +| `DOKKU_DISABLE_PROXY` | `dokku proxy:disable ` / `dokku proxy:enable ` | +| `DOKKU_DOCKERFILE_START_CMD` | `dokku ps:set dockerfile-start-cmd ` | +| `DOKKU_PARALLEL_ARGUMENTS` | Removed. No longer supported. | +| `DOKKU_PROXY_PORT` | `dokku ports:set proxy-port ` | +| `DOKKU_PROXY_SSL_PORT` | `dokku ports:set proxy-ssl-port ` | +| `DOKKU_SKIP_ALL_CHECKS` | `dokku checks:disable ` | +| `DOKKU_SKIP_CLEANUP` | `dokku builder:set skip-cleanup ` | +| `DOKKU_SKIP_DEFAULT_CHECKS` | `dokku checks:skip ` | +| `DOKKU_SKIP_DEPLOY` | `dokku ps:set skip-deploy ` | +| `DOKKU_START_CMD` | `dokku ps:set start-cmd ` | diff --git a/docs/deployment/application-deployment.md b/docs/deployment/application-deployment.md index 3c61a2c90..9f45c4d40 100644 --- a/docs/deployment/application-deployment.md +++ b/docs/deployment/application-deployment.md @@ -143,11 +143,11 @@ dokku letsencrypt:cron-job --add ### Skipping deployment -If you only want to rebuild and tag a container, you can skip the deployment phase by setting `$DOKKU_SKIP_DEPLOY` to `true` by running: +If you only want to rebuild and tag a container, you can skip the deployment phase by setting the `skip-deploy` property to `true`: ``` shell # on the Dokku host -dokku config:set ruby-getting-started DOKKU_SKIP_DEPLOY=true +dokku ps:set ruby-getting-started skip-deploy true ``` ### Redeploying or restarting diff --git a/docs/deployment/zero-downtime-deploys.md b/docs/deployment/zero-downtime-deploys.md index e769b2193..5ef21b4ee 100644 --- a/docs/deployment/zero-downtime-deploys.md +++ b/docs/deployment/zero-downtime-deploys.md @@ -40,17 +40,17 @@ Defaults to `60`. You can set the `stop-timeout-seconds` property on the `ps` plugin to change this value (default: `30`). See the [process management documentation](/docs/processes/process-management.md#changing-process-management-settings) for more information. -## Configuring check settings using the `config` plugin +## Configuring check settings -There are certain settings that can be configured via environment variables: +There are certain settings that can be configured via the `checks` plugin properties: -- `DOKKU_DEFAULT_CHECKS_WAIT`: (default: `10`) If no user-defined checks are specified - or if the process being checked is not a `web` process - this is the period of time Dokku will wait before checking that a container is still running. +- `default-wait`: (default: `10`) If no user-defined checks are specified - or if the process being checked is not a `web` process - this is the period of time Dokku will wait before checking that a container is still running. Set globally via `dokku checks:set --global default-wait `. -The following settings may also be specified in the `app.json` file, though are available as environment variables in order to ease application reuse. +The following settings may also be specified in the `app.json` file, though are available as properties in order to ease application reuse. -- `DOKKU_CHECKS_WAIT`: (default: `5`) Wait this many seconds for the container to start before running checks. -- `DOKKU_CHECKS_TIMEOUT`: (default: `30`) Wait this many seconds for each response before marking it as a failure. -- `DOKKU_CHECKS_ATTEMPTS`: (default: `5`) Number of retries for to run for a specific check before marking it as a failure +- `wait`: (default: `5`) Wait this many seconds for the container to start before running checks. Set via `dokku checks:set wait `. +- `timeout`: (default: `30`) Wait this many seconds for each response before marking it as a failure. Set via `dokku checks:set timeout `. +- `attempts`: (default: `5`) Number of retries for to run for a specific check before marking it as a failure. Set via `dokku checks:set attempts `. ## Skipping and Disabling Checks diff --git a/plugins/apps/apps.go b/plugins/apps/apps.go index 3259c6d4c..394bf803a 100644 --- a/plugins/apps/apps.go +++ b/plugins/apps/apps.go @@ -5,11 +5,13 @@ var ( DefaultProperties = map[string]string{ "deploy-source": "", "deploy-source-metadata": "", + "disable-autocreation": "", } // GlobalProperties is a map of all valid global apps properties GlobalProperties = map[string]bool{ "deploy-source": true, "deploy-source-metadata": true, + "disable-autocreation": true, } ) diff --git a/plugins/apps/functions.go b/plugins/apps/functions.go index 96a3e04f0..f146dec1f 100644 --- a/plugins/apps/functions.go +++ b/plugins/apps/functions.go @@ -132,11 +132,7 @@ func maybeCreateApp(appName string) error { return nil } - results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ - Trigger: "config-get-global", - Args: []string{"DOKKU_DISABLE_APP_AUTOCREATION"}, - }) - disableAutocreate := results.StdoutContents() + disableAutocreate := common.PropertyGetDefault("apps", "--global", "disable-autocreation", "") if disableAutocreate == "true" { common.LogWarn("App auto-creation disabled.") return fmt.Errorf("Re-enable app auto-creation or create an app with 'dokku apps:create %s'", appName) diff --git a/plugins/apps/triggers.go b/plugins/apps/triggers.go index 32bfbd515..78526e8a8 100644 --- a/plugins/apps/triggers.go +++ b/plugins/apps/triggers.go @@ -53,6 +53,15 @@ func TriggerInstall() error { return nil } + if err := common.MigrateConfigToProperties("apps", []common.MigrateConfigEntry{ + { + GlobalConfigVar: "DOKKU_DISABLE_APP_AUTOCREATION", + Property: "disable-autocreation", + }, + }); err != nil { + return err + } + // migrate all created-at values from app mod-time to property for _, appName := range apps { if common.PropertyExists("apps", appName, "created-at") { diff --git a/plugins/builder/builder.go b/plugins/builder/builder.go index 928d1a01c..65f5877f9 100644 --- a/plugins/builder/builder.go +++ b/plugins/builder/builder.go @@ -3,14 +3,16 @@ package builder var ( // DefaultProperties is a map of all valid builder properties with corresponding default property values DefaultProperties = map[string]string{ - "selected": "", - "detected": "", - "build-dir": "", + "build-dir": "", + "detected": "", + "selected": "", + "skip-cleanup": "", } // GlobalProperties is a map of all valid global builder properties GlobalProperties = map[string]bool{ - "selected": true, - "build-dir": true, + "build-dir": true, + "selected": true, + "skip-cleanup": true, } ) diff --git a/plugins/builder/triggers.go b/plugins/builder/triggers.go index a47c48a78..6db974f36 100644 --- a/plugins/builder/triggers.go +++ b/plugins/builder/triggers.go @@ -108,30 +108,21 @@ func TriggerInstall() error { return fmt.Errorf("Unable to install the builder plugin: %s", err.Error()) } - apps, err := common.UnfilteredDokkuApps() - if err != nil && !errors.Is(err, common.NoAppsExist) { - return nil + if err := common.MigrateConfigToProperties("builder", []common.MigrateConfigEntry{ + { + ConfigVar: "DOKKU_APP_TYPE", + Property: "detected", + }, + { + ConfigVar: "DOKKU_SKIP_CLEANUP", + GlobalConfigVar: "DOKKU_SKIP_CLEANUP", + Property: "skip-cleanup", + }, + }); err != nil { + return err } - for _, appName := range apps { - if common.PropertyExists("builder", appName, "detected") { - continue - } - - results, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ - Trigger: "config-get", - Args: []string{appName, "DOKKU_APP_TYPE"}, - }) - if err != nil { - return err - } - - if results.StdoutContents() != "" { - common.PropertyWrite("builder", appName, "detected", results.StdoutContents()) - } - } - - _, err = common.CallPlugnTrigger(common.PlugnTriggerInput{ + _, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ Trigger: "install-builder-prune", }) diff --git a/plugins/checks/functions b/plugins/checks/functions index 05f94327b..ceebd61fe 100755 --- a/plugins/checks/functions +++ b/plugins/checks/functions @@ -2,13 +2,13 @@ set -eo pipefail [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" -source "$PLUGIN_AVAILABLE_PATH/config/functions" +source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" is_app_proctype_checks_disabled() { declare desc="return true if app's proctype(s) checks are disabled" local APP="$1" local PROCTYPE="$2" status=false - local DOKKU_CHECKS_DISABLED=$(config_get "$APP" DOKKU_CHECKS_DISABLED || true) + local DOKKU_CHECKS_DISABLED=$(fn-plugin-property-get-default "checks" "$APP" "disabled" "") if [[ "$DOKKU_CHECKS_DISABLED" == "_all_" ]] || [[ "$(is_val_in_list "$PROCTYPE" "$DOKKU_CHECKS_DISABLED")" == "true" ]]; then status=true @@ -21,7 +21,7 @@ is_app_proctype_checks_skipped() { declare desc="return true if app's proctype(s) checks are skipped" local APP="$1" local PROCTYPE="$2" status=false - local DOKKU_CHECKS_SKIPPED=$(config_get "$APP" DOKKU_CHECKS_SKIPPED || true) + local DOKKU_CHECKS_SKIPPED=$(fn-plugin-property-get-default "checks" "$APP" "skipped" "") if [[ "$DOKKU_CHECKS_SKIPPED" == "_all_" ]] || [[ "$(is_val_in_list "$PROCTYPE" "$DOKKU_CHECKS_SKIPPED")" == "true" ]]; then status=true diff --git a/plugins/checks/install b/plugins/checks/install index 801d8dd6f..4678db572 100755 --- a/plugins/checks/install +++ b/plugins/checks/install @@ -3,16 +3,14 @@ set -eo pipefail [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" -source "$PLUGIN_AVAILABLE_PATH/config/functions" - -migrate_checks_vars_0_29_0() { - declare desc="migrates deprecated DOKKU_WAIT_TO_RETIRE config variables to property counter part introduced in 0.29.x" - - fn-migrate-config-to-property "checks" "wait-to-retire" "DOKKU_WAIT_TO_RETIRE" "DOKKU_WAIT_TO_RETIRE" -} migrate_checks_vars_0_5_0() { declare desc="migrates deprecated CHECKS config variables to simplified counter part introduced in 0.5.x" + + if ! declare -f -F config_get >/dev/null; then + source "$PLUGIN_AVAILABLE_PATH/config/functions" + fi + local GLOBAL_SKIP_ALL_CHECKS=$(config_get --global DOKKU_SKIP_ALL_CHECKS || true) local GLOBAL_SKIP_DEFAULT_CHECKS=$(config_get --global DOKKU_SKIP_DEFAULT_CHECKS || true) @@ -20,13 +18,13 @@ migrate_checks_vars_0_5_0() { local APP_SKIP_ALL_CHECKS=$(config_get "$app" DOKKU_SKIP_ALL_CHECKS || true) local APP_SKIP_DEFAULT_CHECKS=$(config_get "$app" DOKKU_SKIP_DEFAULT_CHECKS || true) - if [[ "$APP_SKIP_ALL_CHECKS" == "true" ]] || [[ "$APP_SKIP_DEFAULT_CHECKS" == "true" ]] || [[ "$GLOBAL_SKIP_ALL_CHECKS" == "true" ]] || [[ "$GLOBAL_SKIP_DEFAULT_CHECKS" == "true" ]]; then - dokku_log_info1 "Migrating zero downtime env variables to 0.5.x. The following variables have been deprecated" - dokku_log_info2 "DOKKU_SKIP_ALL_CHECKS DOKKU_SKIP_DEFAULT_CHECKS" - dokku_log_info2 "Please use dokku checks:[disable|enable] to control zero downtime functionality" - dokku_log_info2 "" - dokku_log_info2 "Zero downtime checks disabled for app ($app)" - DOKKU_QUIET_OUTPUT=1 config_set --no-restart "$app" DOKKU_CHECKS_ENABLED=0 + if [[ "$APP_SKIP_ALL_CHECKS" == "true" ]] || [[ "$GLOBAL_SKIP_ALL_CHECKS" == "true" ]]; then + dokku_log_info1 "Migrating DOKKU_SKIP_ALL_CHECKS to checks disabled property for $app. Use 'dokku checks:set $app disabled ' to manage this going forward." + fn-plugin-property-write "checks" "$app" "disabled" "_all_" + fi + if [[ "$APP_SKIP_DEFAULT_CHECKS" == "true" ]] || [[ "$GLOBAL_SKIP_DEFAULT_CHECKS" == "true" ]]; then + dokku_log_info1 "Migrating DOKKU_SKIP_DEFAULT_CHECKS to checks skipped property for $app. Use 'dokku checks:set $app skipped ' to manage this going forward." + fn-plugin-property-write "checks" "$app" "skipped" "_all_" fi if [[ -n "$APP_SKIP_ALL_CHECKS" ]] || [[ -n "$APP_SKIP_DEFAULT_CHECKS" ]]; then DOKKU_QUIET_OUTPUT=1 config_unset --no-restart "$app" DOKKU_SKIP_ALL_CHECKS DOKKU_SKIP_DEFAULT_CHECKS @@ -34,7 +32,6 @@ migrate_checks_vars_0_5_0() { done if [[ -n "$GLOBAL_SKIP_ALL_CHECKS" ]] || [[ -n "$GLOBAL_SKIP_DEFAULT_CHECKS" ]]; then - dokku_log_info1 "Removing global zero downtime settings" DOKKU_QUIET_OUTPUT=1 config_unset --global DOKKU_SKIP_ALL_CHECKS DOKKU_SKIP_DEFAULT_CHECKS fi } @@ -42,15 +39,16 @@ migrate_checks_vars_0_5_0() { migrate_checks_vars_0_6_0() { declare desc="migrates CHECKS config variables from 0.5.x to support fully-disabled zero-downtime checks" + if ! declare -f -F config_get >/dev/null; then + source "$PLUGIN_AVAILABLE_PATH/config/functions" + fi + for app in $(dokku_apps "false" 2>/dev/null); do local APP_DOKKU_CHECKS_ENABLED=$(config_get "$app" DOKKU_CHECKS_ENABLED || true) - if [[ $APP_DOKKU_CHECKS_ENABLED ]]; then - dokku_log_info1 "Migrating zero downtime env variables to 0.6.x. The following variables will be migrated" - dokku_log_info2 "DOKKU_CHECKS_ENABLED -> DOKKU_CHECKS_SKIPPED" + if [[ -n "$APP_DOKKU_CHECKS_ENABLED" ]]; then if [[ "$APP_DOKKU_CHECKS_ENABLED" == "0" ]]; then - dokku_log_info2 "" - dokku_log_info2 "Zero downtime checks disabled for app ($app)" - DOKKU_QUIET_OUTPUT=1 config_set --no-restart "$app" DOKKU_CHECKS_SKIPPED="_all_" + dokku_log_info1 "Migrating DOKKU_CHECKS_ENABLED to checks skipped property for $app. Use 'dokku checks:set $app skipped ' to manage this going forward." + fn-plugin-property-write "checks" "$app" "skipped" "_all_" fi DOKKU_QUIET_OUTPUT=1 config_unset --no-restart "$app" DOKKU_CHECKS_ENABLED || true fi @@ -64,7 +62,14 @@ trigger-checks-install() { fn-plugin-property-setup "checks" migrate_checks_vars_0_5_0 "$@" migrate_checks_vars_0_6_0 "$@" - migrate_checks_vars_0_29_0 "$@" + + "$PLUGIN_CORE_AVAILABLE_PATH/common/prop" migrate-config-to-property checks wait-to-retire DOKKU_WAIT_TO_RETIRE --global-config-var DOKKU_WAIT_TO_RETIRE + "$PLUGIN_CORE_AVAILABLE_PATH/common/prop" migrate-config-to-property checks disabled DOKKU_CHECKS_DISABLED + "$PLUGIN_CORE_AVAILABLE_PATH/common/prop" migrate-config-to-property checks skipped DOKKU_CHECKS_SKIPPED + "$PLUGIN_CORE_AVAILABLE_PATH/common/prop" migrate-config-to-property checks wait DOKKU_CHECKS_WAIT --global-config-var DOKKU_CHECKS_WAIT + "$PLUGIN_CORE_AVAILABLE_PATH/common/prop" migrate-config-to-property checks timeout DOKKU_CHECKS_TIMEOUT --global-config-var DOKKU_CHECKS_TIMEOUT + "$PLUGIN_CORE_AVAILABLE_PATH/common/prop" migrate-config-to-property checks attempts DOKKU_CHECKS_ATTEMPTS --global-config-var DOKKU_CHECKS_ATTEMPTS + "$PLUGIN_CORE_AVAILABLE_PATH/common/prop" migrate-config-to-property checks default-wait DOKKU_DEFAULT_CHECKS_WAIT --global-config-var DOKKU_DEFAULT_CHECKS_WAIT } trigger-checks-install "$@" diff --git a/plugins/checks/internal-functions b/plugins/checks/internal-functions index ae9e21fe9..6d55ba45b 100755 --- a/plugins/checks/internal-functions +++ b/plugins/checks/internal-functions @@ -72,7 +72,7 @@ cmd-checks-report-single() { fn-checks-disabled-list() { declare APP="$1" - local DOKKU_CHECKS_DISABLED=$(config_get "$APP" DOKKU_CHECKS_DISABLED) + local DOKKU_CHECKS_DISABLED=$(fn-plugin-property-get-default "checks" "$APP" "disabled" "") DOKKU_CHECKS_DISABLED="${DOKKU_CHECKS_DISABLED:-none}" echo "$DOKKU_CHECKS_DISABLED" } @@ -80,7 +80,7 @@ fn-checks-disabled-list() { fn-checks-skipped-list() { declare APP="$1" - local DOKKU_CHECKS_SKIPPED=$(config_get "$APP" DOKKU_CHECKS_SKIPPED) + local DOKKU_CHECKS_SKIPPED=$(fn-plugin-property-get-default "checks" "$APP" "skipped" "") DOKKU_CHECKS_SKIPPED="${DOKKU_CHECKS_SKIPPED:-none}" echo "$DOKKU_CHECKS_SKIPPED" } diff --git a/plugins/checks/subcommands/disable b/plugins/checks/subcommands/disable index 8f6058cc2..6f0971ed6 100755 --- a/plugins/checks/subcommands/disable +++ b/plugins/checks/subcommands/disable @@ -2,7 +2,7 @@ set -eo pipefail [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" -source "$PLUGIN_AVAILABLE_PATH/checks/functions" +source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" cmd-checks-disable() { declare desc="disable zero-downtime checks for app/proctypes" @@ -12,13 +12,13 @@ cmd-checks-disable() { verify_app_name "$APP" local PROCTYPES="${2:-_all_}" - local DOKKU_CHECKS_DISABLED=$(config_get "$APP" DOKKU_CHECKS_DISABLED || true) - local DOKKU_CHECKS_SKIPPED=$(config_get "$APP" DOKKU_CHECKS_SKIPPED || true) + local DOKKU_CHECKS_DISABLED=$(fn-plugin-property-get-default "checks" "$APP" "disabled" "") + local DOKKU_CHECKS_SKIPPED=$(fn-plugin-property-get-default "checks" "$APP" "skipped" "") if [[ "$PROCTYPES" == "_all_" ]]; then dokku_log_info1 "Disabling zero downtime for app ($APP)" - DOKKU_QUIET_OUTPUT=1 config_set --no-restart "$APP" DOKKU_CHECKS_DISABLED="$PROCTYPES" - DOKKU_QUIET_OUTPUT=1 config_unset --no-restart "$APP" DOKKU_CHECKS_SKIPPED + fn-plugin-property-write "checks" "$APP" "disabled" "$PROCTYPES" + fn-plugin-property-delete "checks" "$APP" "skipped" else dokku_log_info1 "Disabling zero downtime for app's ($APP) proctypes ($PROCTYPES)" local PROCTYPE OIFS="$IFS" IFS=, @@ -31,15 +31,15 @@ cmd-checks-disable() { done DOKKU_CHECKS_DISABLED="$(remove_val_from_list "_all_" "$DOKKU_CHECKS_DISABLED")" if [[ -z "$DOKKU_CHECKS_DISABLED" ]]; then - DOKKU_QUIET_OUTPUT=1 config_unset --no-restart "$APP" DOKKU_CHECKS_DISABLED + fn-plugin-property-delete "checks" "$APP" "disabled" else - DOKKU_QUIET_OUTPUT=1 config_set --no-restart "$APP" DOKKU_CHECKS_DISABLED="$DOKKU_CHECKS_DISABLED" + fn-plugin-property-write "checks" "$APP" "disabled" "$DOKKU_CHECKS_DISABLED" fi if [[ -z "$DOKKU_CHECKS_SKIPPED" ]]; then - DOKKU_QUIET_OUTPUT=1 config_unset --no-restart "$APP" DOKKU_CHECKS_SKIPPED + fn-plugin-property-delete "checks" "$APP" "skipped" else - DOKKU_QUIET_OUTPUT=1 config_set --no-restart "$APP" DOKKU_CHECKS_SKIPPED="$DOKKU_CHECKS_SKIPPED" + fn-plugin-property-write "checks" "$APP" "skipped" "$DOKKU_CHECKS_SKIPPED" fi fi } diff --git a/plugins/checks/subcommands/enable b/plugins/checks/subcommands/enable index 9431aca0b..858bf389c 100755 --- a/plugins/checks/subcommands/enable +++ b/plugins/checks/subcommands/enable @@ -2,7 +2,7 @@ set -eo pipefail [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" -source "$PLUGIN_AVAILABLE_PATH/checks/functions" +source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" cmd-checks-enable() { declare desc="enable zero-downtime checks for app/proctypes" @@ -13,13 +13,13 @@ cmd-checks-enable() { verify_app_name "$APP" local PROCTYPES="${2:-_all_}" - local DOKKU_CHECKS_DISABLED=$(config_get "$APP" DOKKU_CHECKS_DISABLED || true) - local DOKKU_CHECKS_SKIPPED=$(config_get "$APP" DOKKU_CHECKS_SKIPPED || true) + local DOKKU_CHECKS_DISABLED=$(fn-plugin-property-get-default "checks" "$APP" "disabled" "") + local DOKKU_CHECKS_SKIPPED=$(fn-plugin-property-get-default "checks" "$APP" "skipped" "") if [[ "$PROCTYPES" == "_all_" ]]; then dokku_log_info1 "Enabling zero downtime for app's ($APP)" - DOKKU_QUIET_OUTPUT=1 config_unset --no-restart "$APP" DOKKU_CHECKS_DISABLED - DOKKU_QUIET_OUTPUT=1 config_unset --no-restart "$APP" DOKKU_CHECKS_SKIPPED + fn-plugin-property-delete "checks" "$APP" "disabled" + fn-plugin-property-delete "checks" "$APP" "skipped" else dokku_log_info1 "Enabling zero downtime for app's ($APP) proctypes ($PROCTYPES)" local PROCTYPE OIFS="$IFS" IFS=, @@ -30,15 +30,15 @@ cmd-checks-enable() { IFS="$OIFS" if [[ -z "$DOKKU_CHECKS_DISABLED" ]]; then - DOKKU_QUIET_OUTPUT=1 config_unset --no-restart "$APP" DOKKU_CHECKS_DISABLED + fn-plugin-property-delete "checks" "$APP" "disabled" else - DOKKU_QUIET_OUTPUT=1 config_set --no-restart "$APP" DOKKU_CHECKS_DISABLED="$DOKKU_CHECKS_DISABLED" + fn-plugin-property-write "checks" "$APP" "disabled" "$DOKKU_CHECKS_DISABLED" fi if [[ -z "$DOKKU_CHECKS_SKIPPED" ]]; then - DOKKU_QUIET_OUTPUT=1 config_unset --no-restart "$APP" DOKKU_CHECKS_SKIPPED + fn-plugin-property-delete "checks" "$APP" "skipped" else - DOKKU_QUIET_OUTPUT=1 config_set --no-restart "$APP" DOKKU_CHECKS_SKIPPED="$DOKKU_CHECKS_SKIPPED" + fn-plugin-property-write "checks" "$APP" "skipped" "$DOKKU_CHECKS_SKIPPED" fi fi } diff --git a/plugins/checks/subcommands/skip b/plugins/checks/subcommands/skip index ff8719a1f..7a242b6c9 100755 --- a/plugins/checks/subcommands/skip +++ b/plugins/checks/subcommands/skip @@ -2,7 +2,7 @@ set -eo pipefail [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" -source "$PLUGIN_AVAILABLE_PATH/checks/functions" +source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" cmd-checks-skip() { declare desc="skip zero-downtime checks for app/proctypes" @@ -12,13 +12,13 @@ cmd-checks-skip() { verify_app_name "$APP" local PROCTYPES="${2:-_all_}" - local DOKKU_CHECKS_DISABLED=$(config_get "$APP" DOKKU_CHECKS_DISABLED || true) - local DOKKU_CHECKS_SKIPPED=$(config_get "$APP" DOKKU_CHECKS_SKIPPED || true) + local DOKKU_CHECKS_DISABLED=$(fn-plugin-property-get-default "checks" "$APP" "disabled" "") + local DOKKU_CHECKS_SKIPPED=$(fn-plugin-property-get-default "checks" "$APP" "skipped" "") if [[ "$PROCTYPES" == "_all_" ]]; then dokku_log_info1 "Skipping zero downtime for app ($APP)" - DOKKU_QUIET_OUTPUT=1 config_set --no-restart "$APP" DOKKU_CHECKS_SKIPPED="$PROCTYPES" - DOKKU_QUIET_OUTPUT=1 config_unset --no-restart "$APP" DOKKU_CHECKS_DISABLED + fn-plugin-property-write "checks" "$APP" "skipped" "$PROCTYPES" + fn-plugin-property-delete "checks" "$APP" "disabled" else dokku_log_info1 "Skipping zero downtime for app's ($APP) proctypes ($PROCTYPES)" local PROCTYPE OIFS="$IFS" IFS=, @@ -31,15 +31,15 @@ cmd-checks-skip() { done DOKKU_CHECKS_SKIPPED="$(remove_val_from_list "_all_" "$DOKKU_CHECKS_SKIPPED")" if [[ -z "$DOKKU_CHECKS_DISABLED" ]]; then - DOKKU_QUIET_OUTPUT=1 config_unset --no-restart "$APP" DOKKU_CHECKS_DISABLED + fn-plugin-property-delete "checks" "$APP" "disabled" else - DOKKU_QUIET_OUTPUT=1 config_set --no-restart "$APP" DOKKU_CHECKS_DISABLED="$DOKKU_CHECKS_DISABLED" + fn-plugin-property-write "checks" "$APP" "disabled" "$DOKKU_CHECKS_DISABLED" fi if [[ -z "$DOKKU_CHECKS_SKIPPED" ]]; then - DOKKU_QUIET_OUTPUT=1 config_unset --no-restart "$APP" DOKKU_CHECKS_SKIPPED + fn-plugin-property-delete "checks" "$APP" "skipped" else - DOKKU_QUIET_OUTPUT=1 config_set --no-restart "$APP" DOKKU_CHECKS_SKIPPED="$DOKKU_CHECKS_SKIPPED" + fn-plugin-property-write "checks" "$APP" "skipped" "$DOKKU_CHECKS_SKIPPED" fi fi } diff --git a/plugins/common/common.go b/plugins/common/common.go index a5045d67f..21847aa7e 100644 --- a/plugins/common/common.go +++ b/plugins/common/common.go @@ -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 diff --git a/plugins/common/docker.go b/plugins/common/docker.go index 11a0c1a39..b01514163 100644 --- a/plugins/common/docker.go +++ b/plugins/common/docker.go @@ -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 diff --git a/plugins/common/functions b/plugins/common/functions index d62d452f1..59ea62fc6 100755 --- a/plugins/common/functions +++ b/plugins/common/functions @@ -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 diff --git a/plugins/common/properties.go b/plugins/common/properties.go index c6c6e3b3e..7653e168b 100644 --- a/plugins/common/properties.go +++ b/plugins/common/properties.go @@ -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 ' 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 ' 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) diff --git a/plugins/common/src/prop/prop.go b/plugins/common/src/prop/prop.go index f5b788dc3..c95ff8c1d 100644 --- a/plugins/common/src/prop/prop.go +++ b/plugins/common/src/prop/prop.go @@ -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) diff --git a/plugins/config/config.go b/plugins/config/config.go index 18d4d9403..45cbe9b97 100644 --- a/plugins/config/config.go +++ b/plugins/config/config.go @@ -62,7 +62,7 @@ func SetMany(appName string, entries map[string]string, replace bool, restart bo }) triggerUpdate(appName, "set", keys) } - if !global && restart && env.GetBoolDefault("DOKKU_APP_RESTORE", true) { + if !global && restart && shouldRestart(appName) { triggerRestart(appName) } return @@ -98,7 +98,7 @@ func UnsetMany(appName string, keys []string, restart bool) (err error) { }) triggerUpdate(appName, "unset", keys) } - if !global && restart && env.GetBoolDefault("DOKKU_APP_RESTORE", true) { + if !global && restart && shouldRestart(appName) { triggerRestart(appName) } return @@ -125,12 +125,20 @@ func UnsetAll(appName string, restart bool) (err error) { }) triggerUpdate(appName, "clear", []string{}) } - if !global && restart && env.GetBoolDefault("DOKKU_APP_RESTORE", true) { + if !global && restart && shouldRestart(appName) { triggerRestart(appName) } return } +func shouldRestart(appName string) bool { + results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ + Trigger: "ps-get-property", + Args: []string{appName, "restore"}, + }) + return results.StdoutContents() != "false" +} + func triggerRestart(appName string) { if !common.IsDeployed(appName) { return diff --git a/plugins/nginx-vhosts/install b/plugins/nginx-vhosts/install index 748dec90e..636325e6d 100755 --- a/plugins/nginx-vhosts/install +++ b/plugins/nginx-vhosts/install @@ -7,26 +7,8 @@ source "$PLUGIN_AVAILABLE_PATH/config/functions" source "$PLUGIN_AVAILABLE_PATH/nginx-vhosts/internal-functions" fn-nginx-vhosts-migrate-env-vars() { - # @TODO: Remove this after a few versions - for app in $(dokku_apps "false" 2>/dev/null); do - nginx_port="$(config_get "$app" DOKKU_NGINX_PORT || true)" - nginx_ssl_port="$(config_get "$app" DOKKU_NGINX_SSL_PORT || true)" - if [[ -n "$nginx_port" ]] || [[ -n "$nginx_ssl_port" ]]; then - dokku_log_info1 "Migrating DOKKU_NGINX env variables. The following variables will be migrated" - dokku_log_info2 "DOKKU_NGINX_PORT -> DOKKU_PROXY_PORT" - dokku_log_info2 "DOKKU_NGINX_SSL_PORT -> DOKKU_PROXY_SSL_PORT" - fi - if [[ -n "$nginx_port" ]]; then - dokku_log_info1 "Migrating DOKKU_NGINX_PORT to DOKKU_PROXY_PORT for $app" - DOKKU_QUIET_OUTPUT=1 config_set --no-restart "$app" DOKKU_PROXY_PORT="$nginx_port" - DOKKU_QUIET_OUTPUT=1 config_unset --no-restart "$app" DOKKU_NGINX_PORT - fi - if [[ -n "$nginx_ssl_port" ]]; then - dokku_log_info1 "Migrating DOKKU_NGINX_SSL_PORT to DOKKU_PROXY_SSL_PORT for $app" - DOKKU_QUIET_OUTPUT=1 config_set --no-restart "$app" DOKKU_PROXY_SSL_PORT="$nginx_ssl_port" - DOKKU_QUIET_OUTPUT=1 config_unset --no-restart "$app" DOKKU_NGINX_SSL_PORT - fi - done + "$PLUGIN_CORE_AVAILABLE_PATH/common/prop" migrate-config-to-property ports proxy-port DOKKU_NGINX_PORT + "$PLUGIN_CORE_AVAILABLE_PATH/common/prop" migrate-config-to-property ports proxy-ssl-port DOKKU_NGINX_SSL_PORT } fn-nginx-vhosts-migrate-nginx-conf-sigil() { diff --git a/plugins/ports/functions.go b/plugins/ports/functions.go index 76aeaed3d..9698b2518 100644 --- a/plugins/ports/functions.go +++ b/plugins/ports/functions.go @@ -10,7 +10,6 @@ import ( "strings" "github.com/dokku/dokku/plugins/common" - "github.com/dokku/dokku/plugins/config" "github.com/ryanuber/columnize" ) @@ -162,11 +161,8 @@ func getDetectedPortMaps(appName string) []PortMap { // getGlobalProxyPort gets the global proxy port func getGlobalProxyPort() int { port := 0 - results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ - Trigger: "config-get-global", - Args: []string{"DOKKU_PROXY_PORT"}, - }) - if intVar, err := strconv.Atoi(results.StdoutContents()); err == nil { + value := common.PropertyGet("ports", "--global", "proxy-port") + if intVar, err := strconv.Atoi(value); err == nil { port = intVar } @@ -176,11 +172,8 @@ func getGlobalProxyPort() int { // getGlobalProxySSLPort gets the global proxy ssl port func getGlobalProxySSLPort() int { port := 0 - results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ - Trigger: "config-get-global", - Args: []string{"DOKKU_PROXY_SSL_PORT"}, - }) - if intVar, err := strconv.Atoi(results.StdoutContents()); err == nil { + value := common.PropertyGet("ports", "--global", "proxy-ssl-port") + if intVar, err := strconv.Atoi(value); err == nil { port = intVar } @@ -201,11 +194,8 @@ func getPortMaps(appName string) []PortMap { // getProxyPort gets the proxy port for an app func getProxyPort(appName string) int { port := 0 - results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ - Trigger: "config-get", - Args: []string{appName, "DOKKU_PROXY_PORT"}, - }) - if intVar, err := strconv.Atoi(results.StdoutContents()); err == nil { + value := common.PropertyGet("ports", appName, "proxy-port") + if intVar, err := strconv.Atoi(value); err == nil { port = intVar } @@ -215,11 +205,8 @@ func getProxyPort(appName string) int { // getProxySSLPort gets the proxy ssl port for an app func getProxySSLPort(appName string) int { port := 0 - results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ - Trigger: "config-get", - Args: []string{appName, "DOKKU_PROXY_SSL_PORT"}, - }) - if intVar, err := strconv.Atoi(results.StdoutContents()); err == nil { + value := common.PropertyGet("ports", appName, "proxy-ssl-port") + if intVar, err := strconv.Atoi(value); err == nil { port = intVar } @@ -445,22 +432,22 @@ func setPortMaps(appName string, portMaps []PortMap) error { // setProxyPort sets the proxy port for an app func setProxyPort(appName string, port int) error { - return common.EnvWrap(func() error { - entries := map[string]string{ - "DOKKU_PROXY_PORT": fmt.Sprint(port), - } - return config.SetMany(appName, entries, false, false) - }, map[string]string{"DOKKU_QUIET_OUTPUT": "1"}) + return common.PropertyWrite("ports", appName, "proxy-port", fmt.Sprint(port)) } // setProxySSLPort sets the proxy ssl port for an app func setProxySSLPort(appName string, port int) error { - return common.EnvWrap(func() error { - entries := map[string]string{ - "DOKKU_PROXY_SSL_PORT": fmt.Sprint(port), - } - return config.SetMany(appName, entries, false, false) - }, map[string]string{"DOKKU_QUIET_OUTPUT": "1"}) + return common.PropertyWrite("ports", appName, "proxy-ssl-port", fmt.Sprint(port)) +} + +// transformPortMap normalizes a port map string for migration to list properties +func transformPortMap(value string) string { + portMaps, _ := parsePortMapString(value) + var parts []string + for _, portMap := range portMaps { + parts = append(parts, portMap.String()) + } + return strings.Join(parts, " ") } // uniquePortMaps returns a unique set of port maps diff --git a/plugins/ports/proxy.go b/plugins/ports/proxy.go index 8daf37c43..b17550010 100644 --- a/plugins/ports/proxy.go +++ b/plugins/ports/proxy.go @@ -4,6 +4,20 @@ import ( "fmt" ) +var ( + // DefaultProperties is a map of all valid ports properties with corresponding default property values + DefaultProperties = map[string]string{ + "proxy-port": "", + "proxy-ssl-port": "", + } + + // GlobalProperties is a map of all valid global ports properties + GlobalProperties = map[string]bool{ + "proxy-port": true, + "proxy-ssl-port": true, + } +) + // PortMap is a struct that contains a scheme:host-port:container-port mapping type PortMap struct { // ContainerPort is the port on the container diff --git a/plugins/ports/triggers.go b/plugins/ports/triggers.go index c8c4288a0..192e8cc64 100644 --- a/plugins/ports/triggers.go +++ b/plugins/ports/triggers.go @@ -2,12 +2,10 @@ package ports import ( "encoding/json" - "errors" "fmt" "sort" "github.com/dokku/dokku/plugins/common" - "github.com/dokku/dokku/plugins/config" ) // TriggerInstall migrates the ports config to properties @@ -16,44 +14,25 @@ func TriggerInstall() error { return fmt.Errorf("Unable to install the ports plugin: %s", err.Error()) } - apps, err := common.UnfilteredDokkuApps() - if err != nil && !errors.Is(err, common.NoAppsExist) { - return nil - } - - for _, appName := range apps { - if common.PropertyExists("ports", appName, "map") { - continue - } - - results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ - Trigger: "config-get", - Args: []string{appName, "DOKKU_PROXY_PORT_MAP"}, - }) - portMapString := results.StdoutContents() - if portMapString == "" { - continue - } - - common.LogVerboseQuiet(fmt.Sprintf("Setting %s ports property 'map' to %v", appName, portMapString)) - portMaps, _ := parsePortMapString(portMapString) - - propertyValue := []string{} - for _, portMap := range portMaps { - propertyValue = append(propertyValue, portMap.String()) - } - - if err := common.PropertyListWrite("ports", appName, "map", propertyValue); err != nil { - return err - } - - _, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ - Trigger: "config-unset", - Args: []string{appName, "DOKKU_PROXY_PORT_MAP"}, - }) - if err != nil { - return err - } + if err := common.MigrateConfigToProperties("ports", []common.MigrateConfigEntry{ + { + ConfigVar: "DOKKU_PROXY_PORT_MAP", + Property: "map", + ListProperty: true, + Transform: transformPortMap, + }, + { + ConfigVar: "DOKKU_PROXY_PORT", + GlobalConfigVar: "DOKKU_PROXY_PORT", + Property: "proxy-port", + }, + { + ConfigVar: "DOKKU_PROXY_SSL_PORT", + GlobalConfigVar: "DOKKU_PROXY_SSL_PORT", + Property: "proxy-ssl-port", + }, + }); err != nil { + return err } return nil @@ -185,34 +164,26 @@ func TriggerPostAppRenameSetup(oldAppName string, newAppName string) error { return nil } -// TriggerPostCertsRemove unsets port config vars after SSL cert is added +// TriggerPostCertsRemove unsets port properties after SSL cert is removed func TriggerPostCertsRemove(appName string) error { - keys := []string{"DOKKU_PROXY_SSL_PORT"} - if err := config.UnsetMany(appName, keys, false); err != nil { + if err := common.PropertyDelete("ports", appName, "proxy-ssl-port"); err != nil { return err } return removePortMaps(appName, filterAppPortMaps(appName, "https", 443)) } -// TriggerPostCertsUpdate sets port config vars after SSL cert is added +// TriggerPostCertsUpdate sets port properties after SSL cert is added func TriggerPostCertsUpdate(appName string) error { - port := config.GetWithDefault(appName, "DOKKU_PROXY_PORT", "") - sslPort := config.GetWithDefault(appName, "DOKKU_PROXY_SSL_PORT", "") + port := common.PropertyGet("ports", appName, "proxy-port") + sslPort := common.PropertyGet("ports", appName, "proxy-ssl-port") portMaps := getPortMaps(appName) - toUnset := []string{} if port == "80" { - toUnset = append(toUnset, "DOKKU_PROXY_PORT") + common.PropertyDelete("ports", appName, "proxy-port") } if sslPort == "443" { - toUnset = append(toUnset, "DOKKU_PROXY_SSL_PORT") - } - - if len(toUnset) > 0 { - if err := config.UnsetMany(appName, toUnset, false); err != nil { - return err - } + common.PropertyDelete("ports", appName, "proxy-ssl-port") } var http80Ports []PortMap diff --git a/plugins/proxy/Makefile b/plugins/proxy/Makefile index 6b127937f..3ddf822fb 100644 --- a/plugins/proxy/Makefile +++ b/plugins/proxy/Makefile @@ -1,5 +1,5 @@ SUBCOMMANDS = subcommands/build-config subcommands/clear-config subcommands/disable subcommands/enable subcommands/ports subcommands/ports-add subcommands/ports-clear subcommands/ports-remove subcommands/ports-set subcommands/report subcommands/set -TRIGGERS = triggers/proxy-is-enabled triggers/proxy-type triggers/report +TRIGGERS = triggers/install triggers/proxy-is-enabled triggers/proxy-type triggers/post-app-clone-setup triggers/post-app-rename-setup triggers/post-delete triggers/report BUILD = commands subcommands triggers PLUGIN_NAME = proxy diff --git a/plugins/proxy/functions.go b/plugins/proxy/functions.go index d7638f78c..11b4823b5 100644 --- a/plugins/proxy/functions.go +++ b/plugins/proxy/functions.go @@ -1,11 +1,11 @@ package proxy import ( - "github.com/dokku/dokku/plugins/config" + "github.com/dokku/dokku/plugins/common" ) func getAppProxyType(appName string) string { - return config.GetWithDefault(appName, "DOKKU_APP_PROXY_TYPE", "") + return common.PropertyGet("proxy", appName, "type") } func getComputedProxyType(appName string) string { @@ -18,5 +18,5 @@ func getComputedProxyType(appName string) string { } func getGlobalProxyType() string { - return config.GetWithDefault("--global", "DOKKU_PROXY_TYPE", "nginx") + return common.PropertyGetDefault("proxy", "--global", "type", "nginx") } diff --git a/plugins/proxy/proxy.go b/plugins/proxy/proxy.go index b674f72b0..9f64815f7 100644 --- a/plugins/proxy/proxy.go +++ b/plugins/proxy/proxy.go @@ -2,13 +2,25 @@ package proxy import ( "github.com/dokku/dokku/plugins/common" - "github.com/dokku/dokku/plugins/config" ) // RunInSerial is the default value for whether to run a command in parallel or not // and defaults to -1 (false) const RunInSerial = 0 +var ( + // DefaultProperties is a map of all valid proxy properties with corresponding default property values + DefaultProperties = map[string]string{ + "disabled": "false", + "type": "", + } + + // GlobalProperties is a map of all valid global proxy properties + GlobalProperties = map[string]bool{ + "type": true, + } +) + // BuildConfig rebuilds the proxy config for the specified app func BuildConfig(appName string) error { _, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ @@ -37,11 +49,7 @@ func Disable(appName string) error { } common.LogInfo1("Disabling proxy for app") - entries := map[string]string{ - "DOKKU_DISABLE_PROXY": "1", - } - - if err := config.SetMany(appName, entries, false, false); err != nil { + if err := common.PropertyWrite("proxy", appName, "disabled", "true"); err != nil { return err } @@ -61,10 +69,10 @@ func Enable(appName string) error { } common.LogInfo1("Enabling proxy for app") - keys := []string{"DOKKU_DISABLE_PROXY"} - if err := config.UnsetMany(appName, keys, false); err != nil { + if err := common.PropertyDelete("proxy", appName, "disabled"); err != nil { return err } + _, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ Trigger: "proxy-enable", Args: []string{appName}, @@ -75,10 +83,5 @@ func Enable(appName string) error { // IsAppProxyEnabled returns true if proxy is enabled; otherwise return false func IsAppProxyEnabled(appName string) bool { - proxyEnabled := true - disableProxy := config.GetWithDefault(appName, "DOKKU_DISABLE_PROXY", "") - if disableProxy != "" { - proxyEnabled = false - } - return proxyEnabled + return common.PropertyGetDefault("proxy", appName, "disabled", "false") != "true" } diff --git a/plugins/proxy/src/triggers/triggers.go b/plugins/proxy/src/triggers/triggers.go index 713e1976d..e8acb3b6c 100644 --- a/plugins/proxy/src/triggers/triggers.go +++ b/plugins/proxy/src/triggers/triggers.go @@ -18,12 +18,25 @@ func main() { var err error switch trigger { + case "install": + err = proxy.TriggerInstall() case "proxy-is-enabled": appName := flag.Arg(0) err = proxy.TriggerProxyIsEnabled(appName) case "proxy-type": appName := flag.Arg(0) err = proxy.TriggerProxyType(appName) + case "post-app-clone-setup": + oldAppName := flag.Arg(0) + newAppName := flag.Arg(1) + err = proxy.TriggerPostAppCloneSetup(oldAppName, newAppName) + case "post-app-rename-setup": + oldAppName := flag.Arg(0) + newAppName := flag.Arg(1) + err = proxy.TriggerPostAppRenameSetup(oldAppName, newAppName) + case "post-delete": + appName := flag.Arg(0) + err = proxy.TriggerPostDelete(appName) case "report": appName := flag.Arg(0) err = proxy.ReportSingleApp(appName, "", "") diff --git a/plugins/proxy/subcommands.go b/plugins/proxy/subcommands.go index ccf7fa9f5..b431a1284 100644 --- a/plugins/proxy/subcommands.go +++ b/plugins/proxy/subcommands.go @@ -5,7 +5,6 @@ import ( "strings" "github.com/dokku/dokku/plugins/common" - "github.com/dokku/dokku/plugins/config" ) // CommandBuildConfig rebuilds config for a given app @@ -99,12 +98,6 @@ func CommandSet(appName string, proxyType string) error { return errors.New("Consider using ports:set command or specifying a valid proxy") } - key := "DOKKU_APP_PROXY_TYPE" - if appName == "--global" { - key = "DOKKU_PROXY_TYPE" - } - entries := map[string]string{ - key: proxyType, - } - return config.SetMany(appName, entries, false, false) + common.CommandPropertySet("proxy", appName, "type", proxyType, DefaultProperties, GlobalProperties) + return nil } diff --git a/plugins/proxy/triggers.go b/plugins/proxy/triggers.go index 0dff90629..5a3eaeea8 100644 --- a/plugins/proxy/triggers.go +++ b/plugins/proxy/triggers.go @@ -2,8 +2,39 @@ package proxy import ( "fmt" + + "github.com/dokku/dokku/plugins/common" ) +// TriggerInstall runs the install step for the proxy plugin +func TriggerInstall() error { + if err := common.PropertySetup("proxy"); err != nil { + return fmt.Errorf("Unable to install the proxy plugin: %s", err.Error()) + } + + if err := common.MigrateConfigToProperties("proxy", []common.MigrateConfigEntry{ + { + ConfigVar: "DOKKU_APP_PROXY_TYPE", + GlobalConfigVar: "DOKKU_PROXY_TYPE", + Property: "type", + }, + { + ConfigVar: "DOKKU_DISABLE_PROXY", + Property: "disabled", + Transform: func(value string) string { + if value != "" { + return "true" + } + return value + }, + }, + }); err != nil { + return err + } + + return nil +} + // TriggerProxyIsEnabled prints true or false depending on whether the proxy is enabled func TriggerProxyIsEnabled(appName string) error { if IsAppProxyEnabled(appName) { @@ -22,3 +53,22 @@ func TriggerProxyType(appName string) error { return nil } + +// TriggerPostAppCloneSetup creates new proxy files +func TriggerPostAppCloneSetup(oldAppName string, newAppName string) error { + return common.PropertyClone("proxy", oldAppName, newAppName) +} + +// TriggerPostAppRenameSetup renames proxy files +func TriggerPostAppRenameSetup(oldAppName string, newAppName string) error { + if err := common.PropertyClone("proxy", oldAppName, newAppName); err != nil { + return err + } + + return common.PropertyDestroy("proxy", oldAppName) +} + +// TriggerPostDelete destroys the proxy property for a given app container +func TriggerPostDelete(appName string) error { + return common.PropertyDestroy("proxy", appName) +} diff --git a/plugins/ps/ps.go b/plugins/ps/ps.go index 307306f24..1386c47aa 100644 --- a/plugins/ps/ps.go +++ b/plugins/ps/ps.go @@ -13,14 +13,19 @@ const RunInSerial = 0 var ( // DefaultProperties is a map of all valid ps properties with corresponding default property values DefaultProperties = map[string]string{ - "restart-policy": "on-failure:10", + "dockerfile-start-cmd": "", "procfile-path": "", + "restart-policy": "on-failure:10", + "restore": "true", + "skip-deploy": "", + "start-cmd": "", "stop-timeout-seconds": "30", } // GlobalProperties is a map of all valid global ps properties GlobalProperties = map[string]bool{ "procfile-path": true, + "skip-deploy": true, "stop-timeout-seconds": true, } ) @@ -165,13 +170,9 @@ func Restore(appName string) error { return nil } - results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ - Trigger: "config-get", - Args: []string{appName, "DOKKU_APP_RESTORE"}, - }) - restore := results.StdoutContents() - if restore == "0" { - common.LogWarn(fmt.Sprintf("Skipping ps:restore for %s as DOKKU_APP_RESTORE=%s", appName, restore)) + restore := common.PropertyGetDefault("ps", appName, "restore", "true") + if restore == "false" { + common.LogWarn(fmt.Sprintf("Skipping ps:restore for %s as restore property is false", appName)) return nil } diff --git a/plugins/ps/report.go b/plugins/ps/report.go index 74c4c17a9..a95dca567 100644 --- a/plugins/ps/report.go +++ b/plugins/ps/report.go @@ -132,18 +132,7 @@ func reportRestartPolicy(appName string) string { } func reportRestore(appName string) string { - results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ - Trigger: "config-get", - Args: []string{appName, "DOKKU_APP_RESTORE"}, - }) - restore := results.StdoutContents() - if restore == "0" { - restore = "false" - } else { - restore = "true" - } - - return restore + return common.PropertyGetDefault("ps", appName, "restore", "true") } func reportRunningState(appName string) string { diff --git a/plugins/ps/triggers.go b/plugins/ps/triggers.go index 22471053d..7c4c128b4 100644 --- a/plugins/ps/triggers.go +++ b/plugins/ps/triggers.go @@ -9,7 +9,6 @@ import ( "strings" "github.com/dokku/dokku/plugins/common" - "github.com/dokku/dokku/plugins/config" dockeroptions "github.com/dokku/dokku/plugins/docker-options" ) @@ -37,13 +36,7 @@ func TriggerCorePostDeploy(appName string) error { return err } - entries := map[string]string{ - "DOKKU_APP_RESTORE": "1", - } - - return common.SuppressOutput(func() error { - return config.SetMany(appName, entries, false, false) - }) + return common.PropertyWrite("ps", appName, "restore", "true") } // TriggerCorePostExtract ensures that the main Procfile is the one specified by procfile-path @@ -135,29 +128,40 @@ func TriggerInstall() error { if common.FileExists(dokkuScaleExtracted) { os.Remove(dokkuScaleExtracted) } - - results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ - Trigger: "config-get", - Args: []string{appName, "DOKKU_DOCKER_STOP_TIMEOUT"}, - }) - stopTimeout := results.StdoutContents() - if stopTimeout == "" { - continue - } - - common.LogVerboseQuiet(fmt.Sprintf("Setting %s ps property 'stop-timeout-seconds' to %v", appName, stopTimeout)) - if err := common.PropertyWrite("ps", appName, "stop-timeout-seconds", stopTimeout); err != nil { - return err - } - - _, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ - Trigger: "config-unset", - Args: []string{appName, "DOKKU_DOCKER_STOP_TIMEOUT"}, - }) - if err != nil { - return err - } } + + if err := common.MigrateConfigToProperties("ps", []common.MigrateConfigEntry{ + { + ConfigVar: "DOKKU_DOCKER_STOP_TIMEOUT", + Property: "stop-timeout-seconds", + }, + { + ConfigVar: "DOKKU_APP_RESTORE", + Property: "restore", + Transform: func(value string) string { + if value == "0" { + return "false" + } + return "true" + }, + }, + { + ConfigVar: "DOKKU_SKIP_DEPLOY", + GlobalConfigVar: "DOKKU_SKIP_DEPLOY", + Property: "skip-deploy", + }, + { + ConfigVar: "DOKKU_START_CMD", + Property: "start-cmd", + }, + { + ConfigVar: "DOKKU_DOCKERFILE_START_CMD", + Property: "dockerfile-start-cmd", + }, + }); err != nil { + return err + } + return nil } @@ -240,13 +244,7 @@ func TriggerPostDelete(appName string) error { // TriggerPostStop sets the restore property to false func TriggerPostStop(appName string) error { - entries := map[string]string{ - "DOKKU_APP_RESTORE": "0", - } - - return common.SuppressOutput(func() error { - return config.SetMany(appName, entries, false, false) - }) + return common.PropertyWrite("ps", appName, "restore", "false") } // TriggerPostReleaseBuilder ensures an app has an up to date scale parameters @@ -321,14 +319,24 @@ func TriggerPsSetScale(appName string, skipDeploy bool, clearExisting bool, proc func TriggerPsGetProperty(appName string, property string) error { computedValueMap := map[string]common.ReportFunc{ + "restore": reportRestore, + "skip-deploy": reportComputedSkipDeploy, "stop-timeout-seconds": reportComputedStopTimeoutSeconds, } fn, ok := computedValueMap[property] if !ok { - return fmt.Errorf("Invalid network property specified: %v", property) + return fmt.Errorf("Invalid ps property specified: %v", property) } fmt.Println(fn(appName)) return nil } + +func reportComputedSkipDeploy(appName string) string { + value := common.PropertyGet("ps", appName, "skip-deploy") + if value == "" { + value = common.PropertyGet("ps", "--global", "skip-deploy") + } + return value +} diff --git a/plugins/scheduler-docker-local/bin/scheduler-deploy-process-container b/plugins/scheduler-docker-local/bin/scheduler-deploy-process-container index 487dde936..8028b9eaf 100755 --- a/plugins/scheduler-docker-local/bin/scheduler-deploy-process-container +++ b/plugins/scheduler-docker-local/bin/scheduler-deploy-process-container @@ -122,7 +122,7 @@ fn-scheduler-docker-local-extract-start-cmd() { return fi - DOKKU_DOCKERFILE_START_CMD=$(config_get "$APP" DOKKU_DOCKERFILE_START_CMD || true) + DOKKU_DOCKERFILE_START_CMD=$(fn-plugin-property-get-default "ps" "$APP" "dockerfile-start-cmd" "") DOKKU_PROCFILE_START_CMD=$(plugn trigger procfile-get-command "$APP" "$PROC_TYPE" "$PORT" 2>/dev/null || echo '') START_CMD=${DOKKU_DOCKERFILE_START_CMD:-$DOKKU_PROCFILE_START_CMD} echo "$START_CMD" diff --git a/plugins/scheduler-docker-local/check-deploy b/plugins/scheduler-docker-local/check-deploy index 4a6362ead..6dd794aff 100755 --- a/plugins/scheduler-docker-local/check-deploy +++ b/plugins/scheduler-docker-local/check-deploy @@ -59,21 +59,17 @@ trigger-scheduler-docker-local-check-deploy() { local DOKKU_APP_CONTAINER_ID=${DOKKU_APP_CIDS[0]} fi - # source global and in-app envs to get DOKKU_CHECKS_WAIT and any other necessary vars - eval "$(config_export global)" - eval "$(config_export app "$APP")" - if [[ "$(is_app_proctype_checks_skipped "$APP" "$DOKKU_APP_CONTAINER_TYPE")" == "true" ]]; then dokku_log_info2_quiet "Zero downtime checks have been skipped ($DOKKU_APP_CONTAINER_TYPE.$CONTAINER_INDEX)" exit 0 fi # Wait this many seconds (default 5) for server to start before running checks. - local WAIT="${DOKKU_CHECKS_WAIT:-5}" + local WAIT="$(fn-plugin-property-get-default "checks" "$APP" "wait" "5")" # Wait this many seconds (default 30) for each response. - local TIMEOUT="${DOKKU_CHECKS_TIMEOUT:-30}" + local TIMEOUT="$(fn-plugin-property-get-default "checks" "$APP" "timeout" "30")" # use this number of retries for checks - local ATTEMPTS="${DOKKU_CHECKS_ATTEMPTS:-5}" + local ATTEMPTS="$(fn-plugin-property-get-default "checks" "$APP" "attempts" "5")" local CHECKS_FILENAME="$(fn-scheduler-docker-local-get-process-specific-checks-file-path "$APP")" local IMAGE_TAG="$(get_running_image_tag "$APP")" @@ -121,7 +117,7 @@ trigger-scheduler-docker-local-check-deploy() { } trap "checks_check_deploy_cleanup $APP $DOKKU_APP_CONTAINER_TYPE $DOKKU_APP_CONTAINER_ID $CONTAINER_INDEX $TMP_APP_JSON_OUTPUT" RETURN INT TERM EXIT - local DOKKU_DEFAULT_CHECKS_WAIT="${DOKKU_DEFAULT_CHECKS_WAIT:-10}" + local DOKKU_DEFAULT_CHECKS_WAIT="$(fn-plugin-property-get-default "checks" "--global" "default-wait" "10")" content="$(docker-container-healthchecker add "$DOKKU_APP_CONTAINER_TYPE" --app-json "$TMP_APP_JSON_OUTPUT" --if-empty --pretty --uptime "$DOKKU_DEFAULT_CHECKS_WAIT")" echo "$content" >"$TMP_APP_JSON_OUTPUT" diff --git a/plugins/scheduler-docker-local/scheduler-deploy b/plugins/scheduler-docker-local/scheduler-deploy index a3c246c7c..8daaa0532 100755 --- a/plugins/scheduler-docker-local/scheduler-deploy +++ b/plugins/scheduler-docker-local/scheduler-deploy @@ -35,7 +35,7 @@ trigger-scheduler-docker-local-scheduler-deploy() { DOKKU_DOCKER_STOP_TIMEOUT="$(plugn trigger ps-get-property "$APP" stop-timeout-seconds)" [[ $DOKKU_DOCKER_STOP_TIMEOUT ]] && DOCKER_STOP_TIME_ARG="-t=${DOKKU_DOCKER_STOP_TIMEOUT}" - DOKKU_START_CMD="$(config_get "$APP" DOKKU_START_CMD || true)" + DOKKU_START_CMD="$(fn-plugin-property-get-default "ps" "$APP" "start-cmd" "")" local PROCFILE_EXISTS=false if plugn trigger procfile-exists "$APP" 2>/dev/null; then diff --git a/plugins/scheduler-docker-local/scheduler-enter b/plugins/scheduler-docker-local/scheduler-enter index e1a4e8def..61a3fa202 100755 --- a/plugins/scheduler-docker-local/scheduler-enter +++ b/plugins/scheduler-docker-local/scheduler-enter @@ -92,10 +92,9 @@ trigger-scheduler-docker-local-scheduler-enter() { shift fi - local DOKKU_APP_SHELL="/bin/bash" - DOKKU_APP_SHELL="$(config_get --global DOKKU_APP_SHELL || echo "$DOKKU_APP_SHELL")" - DOKKU_APP_SHELL="$(config_get "$APP" DOKKU_APP_SHELL || echo "$DOKKU_APP_SHELL")" - [[ -z "$DOKKU_APP_SHELL" ]] && DOKKU_APP_SHELL="/bin/bash" + local DOKKU_APP_SHELL + DOKKU_APP_SHELL="$(fn-plugin-property-get-default "scheduler" "$APP" "shell" "")" + [[ -z "$DOKKU_APP_SHELL" ]] && DOKKU_APP_SHELL="$(fn-plugin-property-get-default "scheduler" "--global" "shell" "/bin/bash")" local EXEC_CMD="" has_tty && local DOKKU_RUN_OPTS+=" -i -t" diff --git a/plugins/scheduler-docker-local/scheduler-run b/plugins/scheduler-docker-local/scheduler-run index b9b5cb9e3..27d4236e9 100755 --- a/plugins/scheduler-docker-local/scheduler-run +++ b/plugins/scheduler-docker-local/scheduler-run @@ -137,8 +137,8 @@ trigger-scheduler-docker-local-scheduler-run() { RUN_COMMAND=("$@") if [[ "${#RUN_COMMAND[@]}" -eq 0 ]]; then if [[ -z "$DOKKU_SHELL" ]]; then - local DOKKU_APP_SHELL=$(config_get "$APP" DOKKU_APP_SHELL || true) - local DOKKU_GLOBAL_SHELL=$(config_get --global DOKKU_APP_SHELL || true) + local DOKKU_APP_SHELL=$(fn-plugin-property-get-default "scheduler" "$APP" "shell" "") + local DOKKU_GLOBAL_SHELL=$(fn-plugin-property-get-default "scheduler" "--global" "shell" "") local DOKKU_SHELL=${DOKKU_APP_SHELL:="$DOKKU_GLOBAL_SHELL"} fi diff --git a/plugins/scheduler-k3s/functions.go b/plugins/scheduler-k3s/functions.go index 3cbbc3251..4d56fcb2d 100644 --- a/plugins/scheduler-k3s/functions.go +++ b/plugins/scheduler-k3s/functions.go @@ -434,21 +434,13 @@ func extractStartCommand(input StartCommandInput) string { return "/start " + input.ProcessType } - resp, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ - Trigger: "config-get", - Args: []string{input.AppName, "DOKKU_START_CMD"}, - }) - if err == nil && resp.ExitCode == 0 && len(resp.Stdout) > 0 { - command = strings.TrimSpace(resp.Stdout) + if startCmd := common.PropertyGet("ps", input.AppName, "start-cmd"); startCmd != "" { + command = startCmd } if input.ImageSourceType == "dockerfile" { - resp, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ - Trigger: "config-get", - Args: []string{input.AppName, "DOKKU_DOCKERFILE_START_CMD"}, - }) - if err == nil && resp.ExitCode == 0 && len(resp.Stdout) > 0 { - command = strings.TrimSpace(resp.Stdout) + if dockerfileStartCmd := common.PropertyGet("ps", input.AppName, "dockerfile-start-cmd"); dockerfileStartCmd != "" { + command = dockerfileStartCmd } } diff --git a/plugins/scheduler/scheduler.go b/plugins/scheduler/scheduler.go index 1bd7c0371..006a9af6f 100644 --- a/plugins/scheduler/scheduler.go +++ b/plugins/scheduler/scheduler.go @@ -4,10 +4,12 @@ var ( // DefaultProperties is a map of all valid scheduler properties with corresponding default property values DefaultProperties = map[string]string{ "selected": "docker-local", + "shell": "", } // GlobalProperties is a map of all valid global scheduler properties GlobalProperties = map[string]bool{ "selected": true, + "shell": true, } ) diff --git a/plugins/scheduler/triggers.go b/plugins/scheduler/triggers.go index a7f38ec5f..1c942d65f 100644 --- a/plugins/scheduler/triggers.go +++ b/plugins/scheduler/triggers.go @@ -1,7 +1,6 @@ package scheduler import ( - "errors" "fmt" "github.com/dokku/dokku/plugins/common" @@ -31,53 +30,19 @@ func TriggerInstall() error { return fmt.Errorf("Unable to install the scheduler plugin: %s", err.Error()) } - apps, err := common.UnfilteredDokkuApps() - if err != nil && !errors.Is(err, common.NoAppsExist) { - return nil - } - - results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ - Trigger: "config-get-global", - Args: []string{"DOKKU_SCHEDULER"}, - }) - globalScheduler := results.StdoutContents() - if globalScheduler != "" { - common.LogVerboseQuiet(fmt.Sprintf("Setting scheduler property 'selected' to %v", globalScheduler)) - if err := common.PropertyWrite("scheduler", "--global", "selected", globalScheduler); err != nil { - return err - } - - _, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ - Trigger: "config-unset", - Args: []string{"--global", "DOKKU_SCHEDULER"}, - }) - if err != nil { - common.LogWarn(err.Error()) - } - } - - for _, appName := range apps { - results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ - Trigger: "config-get", - Args: []string{appName, "DOKKU_SCHEDULER"}, - }) - scheduler := results.StdoutContents() - if scheduler == "" { - continue - } - - common.LogVerboseQuiet(fmt.Sprintf("Setting %s scheduler property 'selected' to %v", appName, scheduler)) - if err := common.PropertyWrite("scheduler", appName, "selected", scheduler); err != nil { - return err - } - - _, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ - Trigger: "config-unset", - Args: []string{appName, "DOKKU_SCHEDULER"}, - }) - if err != nil { - common.LogWarn(err.Error()) - } + if err := common.MigrateConfigToProperties("scheduler", []common.MigrateConfigEntry{ + { + ConfigVar: "DOKKU_SCHEDULER", + GlobalConfigVar: "DOKKU_SCHEDULER", + Property: "selected", + }, + { + ConfigVar: "DOKKU_APP_SHELL", + GlobalConfigVar: "DOKKU_APP_SHELL", + Property: "shell", + }, + }); err != nil { + return err } return nil diff --git a/tests/unit/apps_1.bats b/tests/unit/apps_1.bats index 9d6d3f425..d99132469 100644 --- a/tests/unit/apps_1.bats +++ b/tests/unit/apps_1.bats @@ -113,7 +113,7 @@ teardown() { } @test "(apps) app autocreate disabled" { - run /bin/bash -c "dokku config:set --no-restart --global DOKKU_DISABLE_APP_AUTOCREATION='true'" + run /bin/bash -c "dokku apps:set --global disable-autocreation true" echo "output: $output" echo "status: $status" assert_success @@ -122,7 +122,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_failure - run /bin/bash -c "dokku config:unset --no-restart --global DOKKU_DISABLE_APP_AUTOCREATION" + run /bin/bash -c "dokku apps:set --global disable-autocreation" } @test "(apps) apps:destroy" { diff --git a/tests/unit/apps_2.bats b/tests/unit/apps_2.bats index a176cece0..49c30f037 100644 --- a/tests/unit/apps_2.bats +++ b/tests/unit/apps_2.bats @@ -116,7 +116,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku config:set --no-restart $TEST_APP DOKKU_PROXY_SSL_PORT=443" + run /bin/bash -c "dokku ports:set $TEST_APP proxy-ssl-port 443" echo "output: $output" echo "status: $status" assert_success @@ -132,10 +132,6 @@ teardown() { echo "output: $output" echo "status: $status" assert_output "http 80 5000" - run /bin/bash -c "dokku config:get app-without-ssl DOKKU_PROXY_SSL_PORT" - echo "output: $output" - echo "status: $status" - assert_output_not_exists run /bin/bash -c "dokku --force apps:destroy app-without-ssl" echo "output: $output" echo "status: $status" diff --git a/tests/unit/checks.bats b/tests/unit/checks.bats index 35e0576fa..138b389c1 100644 --- a/tests/unit/checks.bats +++ b/tests/unit/checks.bats @@ -36,7 +36,7 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_DISABLED" + run /bin/bash -c "dokku checks:report $TEST_APP --checks-disabled-list" echo "output: $output" echo "status: $status" assert_output "_all_" @@ -48,30 +48,30 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_DISABLED" + run /bin/bash -c "dokku checks:report $TEST_APP --checks-disabled-list" echo "output: $output" echo "status: $status" assert_output "_all_" - run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_SKIPPED" + run /bin/bash -c "dokku checks:report $TEST_APP --checks-skipped-list" echo "output: $output" echo "status: $status" - assert_output_not_exists + assert_output "none" run /bin/bash -c "dokku checks:enable $TEST_APP" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_DISABLED" + run /bin/bash -c "dokku checks:report $TEST_APP --checks-disabled-list" echo "output: $output" echo "status: $status" - assert_output_not_exists + assert_output "none" - run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_SKIPPED" + run /bin/bash -c "dokku checks:report $TEST_APP --checks-skipped-list" echo "output: $output" echo "status: $status" - assert_output_not_exists + assert_output "none" } @test "(checks) checks:disable -> checks:skip" { @@ -80,27 +80,27 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_DISABLED" + run /bin/bash -c "dokku checks:report $TEST_APP --checks-disabled-list" echo "output: $output" echo "status: $status" assert_output "web,worker,urgentworker,notifications" - run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_SKIPPED" + run /bin/bash -c "dokku checks:report $TEST_APP --checks-skipped-list" echo "output: $output" echo "status: $status" - assert_output_not_exists + assert_output "none" run /bin/bash -c "dokku checks:skip $TEST_APP urgentworker,worker" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_SKIPPED" + run /bin/bash -c "dokku checks:report $TEST_APP --checks-skipped-list" echo "output: $output" echo "status: $status" assert_output "urgentworker,worker" - run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_DISABLED" + run /bin/bash -c "dokku checks:report $TEST_APP --checks-disabled-list" echo "output: $output" echo "status: $status" assert_output "web,notifications" @@ -112,7 +112,7 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_SKIPPED" + run /bin/bash -c "dokku checks:report $TEST_APP --checks-skipped-list" echo "output: $output" echo "status: $status" assert_output "_all_" @@ -124,30 +124,30 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_SKIPPED" + run /bin/bash -c "dokku checks:report $TEST_APP --checks-skipped-list" echo "output: $output" echo "status: $status" assert_output "_all_" - run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_DISABLED" + run /bin/bash -c "dokku checks:report $TEST_APP --checks-disabled-list" echo "output: $output" echo "status: $status" - assert_output_not_exists + assert_output "none" run /bin/bash -c "dokku checks:enable $TEST_APP" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_SKIPPED" + run /bin/bash -c "dokku checks:report $TEST_APP --checks-skipped-list" echo "output: $output" echo "status: $status" - assert_output_not_exists + assert_output "none" - run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_DISABLED" + run /bin/bash -c "dokku checks:report $TEST_APP --checks-disabled-list" echo "output: $output" echo "status: $status" - assert_output_not_exists + assert_output "none" } @test "(checks) checks:skip -> checks:disable" { @@ -156,27 +156,27 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_SKIPPED" + run /bin/bash -c "dokku checks:report $TEST_APP --checks-skipped-list" echo "output: $output" echo "status: $status" assert_output "web,worker,urgentworker,notifications" - run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_DISABLED" + run /bin/bash -c "dokku checks:report $TEST_APP --checks-disabled-list" echo "output: $output" echo "status: $status" - assert_output_not_exists + assert_output "none" run /bin/bash -c "dokku checks:disable $TEST_APP urgentworker,worker" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_DISABLED" + run /bin/bash -c "dokku checks:report $TEST_APP --checks-disabled-list" echo "output: $output" echo "status: $status" assert_output "urgentworker,worker" - run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_SKIPPED" + run /bin/bash -c "dokku checks:report $TEST_APP --checks-skipped-list" echo "output: $output" echo "status: $status" assert_output "web,notifications" diff --git a/tests/unit/nginx-vhosts_5.bats b/tests/unit/nginx-vhosts_5.bats index 2a0d171e1..1c6ebc88f 100644 --- a/tests/unit/nginx-vhosts_5.bats +++ b/tests/unit/nginx-vhosts_5.bats @@ -60,9 +60,9 @@ teardown() { assert_success } -@test "(nginx-vhosts) proxy:build-config (global DOKKU_PROXY_PORT)" { +@test "(nginx-vhosts) proxy:build-config (global proxy-port)" { local GLOBAL_PORT=30999 - run /bin/bash -c "dokku config:set --global DOKKU_PROXY_PORT=${GLOBAL_PORT}" + run /bin/bash -c "dokku ports:set --global proxy-port ${GLOBAL_PORT}" echo "output: $output" echo "status: $status" assert_success @@ -74,7 +74,7 @@ teardown() { check_urls "http://${TEST_APP}.${DOKKU_DOMAIN}:${GLOBAL_PORT}" assert_http_success "http://${TEST_APP}.${DOKKU_DOMAIN}:${GLOBAL_PORT}" - run /bin/bash -c "dokku config:unset --global DOKKU_PROXY_PORT" + run /bin/bash -c "dokku ports:set --global proxy-port" echo "output: $output" echo "status: $status" assert_success From 24f460f5d3ce748d1619f5db92beb1c93beffaf2 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 25 Apr 2026 16:33:34 -0400 Subject: [PATCH 017/546] fix: address CI failures from env var migration - Add apps:set subcommand for managing apps properties (mirrors ps:set, scheduler:set, builder:set pattern) - Update nginx-vhosts functions/pre-disable-vhost/pre-enable-vhost to read proxy port from properties instead of config vars - Fix test commands to use correct property-setting approaches: prop binary for ports properties, apps:set for apps properties --- plugins/apps/Makefile | 2 +- plugins/apps/src/commands/commands.go | 1 + plugins/apps/src/subcommands/subcommands.go | 7 +++++++ plugins/apps/subcommands.go | 6 ++++++ plugins/nginx-vhosts/functions | 4 ++-- plugins/nginx-vhosts/pre-disable-vhost | 15 +++++---------- plugins/nginx-vhosts/pre-enable-vhost | 15 +++++---------- tests/unit/apps_2.bats | 2 +- tests/unit/nginx-vhosts_5.bats | 4 ++-- 9 files changed, 30 insertions(+), 26 deletions(-) diff --git a/plugins/apps/Makefile b/plugins/apps/Makefile index 7bf928361..ff5fb0725 100644 --- a/plugins/apps/Makefile +++ b/plugins/apps/Makefile @@ -1,4 +1,4 @@ -SUBCOMMANDS = subcommands/clone subcommands/create subcommands/destroy subcommands/exists subcommands/list subcommands/lock subcommands/locked subcommands/rename subcommands/report subcommands/unlock +SUBCOMMANDS = subcommands/clone subcommands/create subcommands/destroy subcommands/exists subcommands/list subcommands/lock subcommands/locked subcommands/rename subcommands/report subcommands/set subcommands/unlock TRIGGERS = triggers/app-create triggers/app-destroy triggers/app-exists triggers/app-maybe-create triggers/deploy-source-set triggers/install triggers/post-app-clone-setup triggers/post-app-rename triggers/post-app-rename-setup triggers/post-create triggers/post-delete triggers/report BUILD = commands subcommands triggers PLUGIN_NAME = apps diff --git a/plugins/apps/src/commands/commands.go b/plugins/apps/src/commands/commands.go index 3f6d37859..8cc38f158 100644 --- a/plugins/apps/src/commands/commands.go +++ b/plugins/apps/src/commands/commands.go @@ -28,6 +28,7 @@ Additional commands:` apps:locked , Checks if an app is locked for deployment apps:rename , Rename an app apps:report [] [], Display report about an app + apps:set |--global , Set or clear an apps property apps:unlock , Unlocks an app for deployment` ) diff --git a/plugins/apps/src/subcommands/subcommands.go b/plugins/apps/src/subcommands/subcommands.go index 0874f7084..a61e46d45 100644 --- a/plugins/apps/src/subcommands/subcommands.go +++ b/plugins/apps/src/subcommands/subcommands.go @@ -72,6 +72,13 @@ func main() { appName := args.Arg(0) err = apps.CommandReport(appName, *format, infoFlag) } + case "set": + args := flag.NewFlagSet("apps:set", flag.ExitOnError) + args.Parse(os.Args[2:]) + appName := args.Arg(0) + property := args.Arg(1) + value := args.Arg(2) + err = apps.CommandSet(appName, property, value) case "unlock": args := flag.NewFlagSet("apps:unlock", flag.ExitOnError) args.Parse(os.Args[2:]) diff --git a/plugins/apps/subcommands.go b/plugins/apps/subcommands.go index 40291c2c8..8c02d33bc 100644 --- a/plugins/apps/subcommands.go +++ b/plugins/apps/subcommands.go @@ -226,6 +226,12 @@ func CommandReport(appName string, format string, infoFlag string) error { return ReportSingleApp(appName, format, infoFlag) } +// CommandSet set or clear an apps property for an app +func CommandSet(appName string, property string, value string) error { + common.CommandPropertySet("apps", appName, property, value, DefaultProperties, GlobalProperties) + return nil +} + // CommandUnlock unlocks an app for deployment func CommandUnlock(appName string) error { if err := common.VerifyAppName(appName); err != nil { diff --git a/plugins/nginx-vhosts/functions b/plugins/nginx-vhosts/functions index 0f81141c4..1b15cde8e 100755 --- a/plugins/nginx-vhosts/functions +++ b/plugins/nginx-vhosts/functions @@ -227,8 +227,8 @@ nginx_build_config() { # setup nginx listen ports plugn trigger ports-configure "$APP" - local PROXY_PORT=$(config_get "$APP" DOKKU_PROXY_PORT) - local PROXY_SSL_PORT=$(config_get "$APP" DOKKU_PROXY_SSL_PORT) + local PROXY_PORT=$(plugn trigger ports-get-property "$APP" "proxy-port") + local PROXY_SSL_PORT=$(plugn trigger ports-get-property "$APP" "proxy-ssl-port") local PORT_MAP PROXY_PORT_MAP proxy_port_map while read -r PORT_MAP; do diff --git a/plugins/nginx-vhosts/pre-disable-vhost b/plugins/nginx-vhosts/pre-disable-vhost index 4aa94cec9..027dedcd6 100755 --- a/plugins/nginx-vhosts/pre-disable-vhost +++ b/plugins/nginx-vhosts/pre-disable-vhost @@ -2,7 +2,7 @@ set -eo pipefail [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" -source "$PLUGIN_AVAILABLE_PATH/config/functions" +source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" trigger-nginx-vhosts-pre-disable-vhost() { declare desc="unset port prior to vhosts being disabled" @@ -14,19 +14,14 @@ trigger-nginx-vhosts-pre-disable-vhost() { fi # only clear ports if they are port 80 or 443 - local PROXY_PORT=$(config_get "$APP" DOKKU_PROXY_PORT) - local PROXY_SSL_PORT=$(config_get "$APP" DOKKU_PROXY_SSL_PORT) + local PROXY_PORT=$(plugn trigger ports-get-property "$APP" "proxy-port") + local PROXY_SSL_PORT=$(plugn trigger ports-get-property "$APP" "proxy-ssl-port") - ports_to_clear=() if [[ "$PROXY_PORT" == "80" ]]; then - ports_to_clear+=("DOKKU_PROXY_PORT") + fn-plugin-property-delete "ports" "$APP" "proxy-port" fi if [[ "$PROXY_SSL_PORT" == "443" ]]; then - ports_to_clear+=("DOKKU_PROXY_SSL_PORT") - fi - - if [[ "${#ports_to_clear[@]}" -gt 0 ]]; then - DOKKU_QUIET_OUTPUT=1 config_unset --no-restart "$APP" "${ports_to_clear[@]}" + fn-plugin-property-delete "ports" "$APP" "proxy-ssl-port" fi # clear ports if there is a mapping starting with http:80 or https:443 diff --git a/plugins/nginx-vhosts/pre-enable-vhost b/plugins/nginx-vhosts/pre-enable-vhost index c8a7f4e56..11615f4be 100755 --- a/plugins/nginx-vhosts/pre-enable-vhost +++ b/plugins/nginx-vhosts/pre-enable-vhost @@ -2,7 +2,7 @@ set -eo pipefail [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" -source "$PLUGIN_AVAILABLE_PATH/config/functions" +source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" trigger-nginx-vhosts-pre-enable-vhost() { declare desc="unset port prior to vhosts being enabled" @@ -14,19 +14,14 @@ trigger-nginx-vhosts-pre-enable-vhost() { fi # only clear ports if they are port 80 or 443 - local PROXY_PORT=$(config_get "$APP" DOKKU_PROXY_PORT) - local PROXY_SSL_PORT=$(config_get "$APP" DOKKU_PROXY_SSL_PORT) + local PROXY_PORT=$(plugn trigger ports-get-property "$APP" "proxy-port") + local PROXY_SSL_PORT=$(plugn trigger ports-get-property "$APP" "proxy-ssl-port") - ports_to_clear=() if [[ "$PROXY_PORT" == "80" ]]; then - ports_to_clear+=("DOKKU_PROXY_PORT") + fn-plugin-property-delete "ports" "$APP" "proxy-port" fi if [[ "$PROXY_SSL_PORT" == "443" ]]; then - ports_to_clear+=("DOKKU_PROXY_SSL_PORT") - fi - - if [[ "${#ports_to_clear[@]}" -gt 0 ]]; then - DOKKU_QUIET_OUTPUT=1 config_unset --no-restart "$APP" "${ports_to_clear[@]}" + fn-plugin-property-delete "ports" "$APP" "proxy-ssl-port" fi # clear ports if there is a mapping starting with http:80 or https:443 diff --git a/tests/unit/apps_2.bats b/tests/unit/apps_2.bats index 49c30f037..0926fe33c 100644 --- a/tests/unit/apps_2.bats +++ b/tests/unit/apps_2.bats @@ -116,7 +116,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku ports:set $TEST_APP proxy-ssl-port 443" + run /bin/bash -c "$PLUGIN_CORE_AVAILABLE_PATH/common/prop set ports $TEST_APP proxy-ssl-port 443" echo "output: $output" echo "status: $status" assert_success diff --git a/tests/unit/nginx-vhosts_5.bats b/tests/unit/nginx-vhosts_5.bats index 1c6ebc88f..9f8963950 100644 --- a/tests/unit/nginx-vhosts_5.bats +++ b/tests/unit/nginx-vhosts_5.bats @@ -62,7 +62,7 @@ teardown() { @test "(nginx-vhosts) proxy:build-config (global proxy-port)" { local GLOBAL_PORT=30999 - run /bin/bash -c "dokku ports:set --global proxy-port ${GLOBAL_PORT}" + run /bin/bash -c "$PLUGIN_CORE_AVAILABLE_PATH/common/prop set ports --global proxy-port ${GLOBAL_PORT}" echo "output: $output" echo "status: $status" assert_success @@ -74,7 +74,7 @@ teardown() { check_urls "http://${TEST_APP}.${DOKKU_DOMAIN}:${GLOBAL_PORT}" assert_http_success "http://${TEST_APP}.${DOKKU_DOMAIN}:${GLOBAL_PORT}" - run /bin/bash -c "dokku ports:set --global proxy-port" + run /bin/bash -c "$PLUGIN_CORE_AVAILABLE_PATH/common/prop del ports --global proxy-port" echo "output: $output" echo "status: $status" assert_success From 915172c0c5ee65f77350bdb3f9faf091e0031b10 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 25 Apr 2026 17:19:42 -0400 Subject: [PATCH 018/546] fix: move proxy-port properties to proxy plugin and fix CI failures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Move proxy-port and proxy-ssl-port properties from ports plugin to proxy plugin where they conceptually belong - Generalize proxy:set to handle any property (matching ps:set pattern) instead of only handling the type property - Fix apps:set --global flag parsing (pflag treats --global as a flag, not an argument — must be defined explicitly like ps:set does) - Add missing property-functions source to scheduler-docker-local shell scripts (scheduler-enter, scheduler-run, scheduler-deploy, check-deploy, bin/scheduler-deploy-process-container) - Update nginx-vhosts property references from ports to proxy plugin - Update tests to use proxy:set for proxy-port properties --- docs/configuration/environment-variables.md | 4 ++-- plugins/apps/src/subcommands/subcommands.go | 6 ++++++ plugins/nginx-vhosts/install | 4 ++-- plugins/nginx-vhosts/pre-disable-vhost | 4 ++-- plugins/nginx-vhosts/pre-enable-vhost | 4 ++-- plugins/ports/functions.go | 12 +++++------ plugins/ports/proxy.go | 13 ------------ plugins/ports/triggers.go | 20 +++++-------------- plugins/proxy/proxy.go | 10 +++++++--- plugins/proxy/src/subcommands/subcommands.go | 8 +++++--- plugins/proxy/subcommands.go | 20 ++----------------- plugins/proxy/triggers.go | 10 ++++++++++ .../bin/scheduler-deploy-process-container | 1 + plugins/scheduler-docker-local/check-deploy | 1 + .../scheduler-docker-local/scheduler-deploy | 1 + .../scheduler-docker-local/scheduler-enter | 1 + plugins/scheduler-docker-local/scheduler-run | 1 + tests/unit/apps_2.bats | 2 +- tests/unit/nginx-vhosts_5.bats | 4 ++-- 19 files changed, 57 insertions(+), 69 deletions(-) diff --git a/docs/configuration/environment-variables.md b/docs/configuration/environment-variables.md index cfe98ae22..a597ce464 100644 --- a/docs/configuration/environment-variables.md +++ b/docs/configuration/environment-variables.md @@ -122,8 +122,8 @@ The following environment variables have been migrated to plugin properties. Exi | `DOKKU_DISABLE_PROXY` | `dokku proxy:disable ` / `dokku proxy:enable ` | | `DOKKU_DOCKERFILE_START_CMD` | `dokku ps:set dockerfile-start-cmd ` | | `DOKKU_PARALLEL_ARGUMENTS` | Removed. No longer supported. | -| `DOKKU_PROXY_PORT` | `dokku ports:set proxy-port ` | -| `DOKKU_PROXY_SSL_PORT` | `dokku ports:set proxy-ssl-port ` | +| `DOKKU_PROXY_PORT` | `dokku proxy:set proxy-port ` | +| `DOKKU_PROXY_SSL_PORT` | `dokku proxy:set proxy-ssl-port ` | | `DOKKU_SKIP_ALL_CHECKS` | `dokku checks:disable ` | | `DOKKU_SKIP_CLEANUP` | `dokku builder:set skip-cleanup ` | | `DOKKU_SKIP_DEFAULT_CHECKS` | `dokku checks:skip ` | diff --git a/plugins/apps/src/subcommands/subcommands.go b/plugins/apps/src/subcommands/subcommands.go index a61e46d45..802d0e1ed 100644 --- a/plugins/apps/src/subcommands/subcommands.go +++ b/plugins/apps/src/subcommands/subcommands.go @@ -74,10 +74,16 @@ func main() { } case "set": args := flag.NewFlagSet("apps:set", flag.ExitOnError) + global := args.Bool("global", false, "--global: set a global property") args.Parse(os.Args[2:]) appName := args.Arg(0) property := args.Arg(1) value := args.Arg(2) + if *global { + appName = "--global" + property = args.Arg(0) + value = args.Arg(1) + } err = apps.CommandSet(appName, property, value) case "unlock": args := flag.NewFlagSet("apps:unlock", flag.ExitOnError) diff --git a/plugins/nginx-vhosts/install b/plugins/nginx-vhosts/install index 636325e6d..94bb56442 100755 --- a/plugins/nginx-vhosts/install +++ b/plugins/nginx-vhosts/install @@ -7,8 +7,8 @@ source "$PLUGIN_AVAILABLE_PATH/config/functions" source "$PLUGIN_AVAILABLE_PATH/nginx-vhosts/internal-functions" fn-nginx-vhosts-migrate-env-vars() { - "$PLUGIN_CORE_AVAILABLE_PATH/common/prop" migrate-config-to-property ports proxy-port DOKKU_NGINX_PORT - "$PLUGIN_CORE_AVAILABLE_PATH/common/prop" migrate-config-to-property ports proxy-ssl-port DOKKU_NGINX_SSL_PORT + "$PLUGIN_CORE_AVAILABLE_PATH/common/prop" migrate-config-to-property proxy proxy-port DOKKU_NGINX_PORT + "$PLUGIN_CORE_AVAILABLE_PATH/common/prop" migrate-config-to-property proxy proxy-ssl-port DOKKU_NGINX_SSL_PORT } fn-nginx-vhosts-migrate-nginx-conf-sigil() { diff --git a/plugins/nginx-vhosts/pre-disable-vhost b/plugins/nginx-vhosts/pre-disable-vhost index 027dedcd6..977524416 100755 --- a/plugins/nginx-vhosts/pre-disable-vhost +++ b/plugins/nginx-vhosts/pre-disable-vhost @@ -18,10 +18,10 @@ trigger-nginx-vhosts-pre-disable-vhost() { local PROXY_SSL_PORT=$(plugn trigger ports-get-property "$APP" "proxy-ssl-port") if [[ "$PROXY_PORT" == "80" ]]; then - fn-plugin-property-delete "ports" "$APP" "proxy-port" + fn-plugin-property-delete "proxy" "$APP" "proxy-port" fi if [[ "$PROXY_SSL_PORT" == "443" ]]; then - fn-plugin-property-delete "ports" "$APP" "proxy-ssl-port" + fn-plugin-property-delete "proxy" "$APP" "proxy-ssl-port" fi # clear ports if there is a mapping starting with http:80 or https:443 diff --git a/plugins/nginx-vhosts/pre-enable-vhost b/plugins/nginx-vhosts/pre-enable-vhost index 11615f4be..224887462 100755 --- a/plugins/nginx-vhosts/pre-enable-vhost +++ b/plugins/nginx-vhosts/pre-enable-vhost @@ -18,10 +18,10 @@ trigger-nginx-vhosts-pre-enable-vhost() { local PROXY_SSL_PORT=$(plugn trigger ports-get-property "$APP" "proxy-ssl-port") if [[ "$PROXY_PORT" == "80" ]]; then - fn-plugin-property-delete "ports" "$APP" "proxy-port" + fn-plugin-property-delete "proxy" "$APP" "proxy-port" fi if [[ "$PROXY_SSL_PORT" == "443" ]]; then - fn-plugin-property-delete "ports" "$APP" "proxy-ssl-port" + fn-plugin-property-delete "proxy" "$APP" "proxy-ssl-port" fi # clear ports if there is a mapping starting with http:80 or https:443 diff --git a/plugins/ports/functions.go b/plugins/ports/functions.go index 9698b2518..afeaf7120 100644 --- a/plugins/ports/functions.go +++ b/plugins/ports/functions.go @@ -161,7 +161,7 @@ func getDetectedPortMaps(appName string) []PortMap { // getGlobalProxyPort gets the global proxy port func getGlobalProxyPort() int { port := 0 - value := common.PropertyGet("ports", "--global", "proxy-port") + value := common.PropertyGet("proxy", "--global", "proxy-port") if intVar, err := strconv.Atoi(value); err == nil { port = intVar } @@ -172,7 +172,7 @@ func getGlobalProxyPort() int { // getGlobalProxySSLPort gets the global proxy ssl port func getGlobalProxySSLPort() int { port := 0 - value := common.PropertyGet("ports", "--global", "proxy-ssl-port") + value := common.PropertyGet("proxy", "--global", "proxy-ssl-port") if intVar, err := strconv.Atoi(value); err == nil { port = intVar } @@ -194,7 +194,7 @@ func getPortMaps(appName string) []PortMap { // getProxyPort gets the proxy port for an app func getProxyPort(appName string) int { port := 0 - value := common.PropertyGet("ports", appName, "proxy-port") + value := common.PropertyGet("proxy", appName, "proxy-port") if intVar, err := strconv.Atoi(value); err == nil { port = intVar } @@ -205,7 +205,7 @@ func getProxyPort(appName string) int { // getProxySSLPort gets the proxy ssl port for an app func getProxySSLPort(appName string) int { port := 0 - value := common.PropertyGet("ports", appName, "proxy-ssl-port") + value := common.PropertyGet("proxy", appName, "proxy-ssl-port") if intVar, err := strconv.Atoi(value); err == nil { port = intVar } @@ -432,12 +432,12 @@ func setPortMaps(appName string, portMaps []PortMap) error { // setProxyPort sets the proxy port for an app func setProxyPort(appName string, port int) error { - return common.PropertyWrite("ports", appName, "proxy-port", fmt.Sprint(port)) + return common.PropertyWrite("proxy", appName, "proxy-port", fmt.Sprint(port)) } // setProxySSLPort sets the proxy ssl port for an app func setProxySSLPort(appName string, port int) error { - return common.PropertyWrite("ports", appName, "proxy-ssl-port", fmt.Sprint(port)) + return common.PropertyWrite("proxy", appName, "proxy-ssl-port", fmt.Sprint(port)) } // transformPortMap normalizes a port map string for migration to list properties diff --git a/plugins/ports/proxy.go b/plugins/ports/proxy.go index b17550010..c8855b6e4 100644 --- a/plugins/ports/proxy.go +++ b/plugins/ports/proxy.go @@ -4,19 +4,6 @@ import ( "fmt" ) -var ( - // DefaultProperties is a map of all valid ports properties with corresponding default property values - DefaultProperties = map[string]string{ - "proxy-port": "", - "proxy-ssl-port": "", - } - - // GlobalProperties is a map of all valid global ports properties - GlobalProperties = map[string]bool{ - "proxy-port": true, - "proxy-ssl-port": true, - } -) // PortMap is a struct that contains a scheme:host-port:container-port mapping type PortMap struct { diff --git a/plugins/ports/triggers.go b/plugins/ports/triggers.go index 192e8cc64..8d6ce6cd7 100644 --- a/plugins/ports/triggers.go +++ b/plugins/ports/triggers.go @@ -21,16 +21,6 @@ func TriggerInstall() error { ListProperty: true, Transform: transformPortMap, }, - { - ConfigVar: "DOKKU_PROXY_PORT", - GlobalConfigVar: "DOKKU_PROXY_PORT", - Property: "proxy-port", - }, - { - ConfigVar: "DOKKU_PROXY_SSL_PORT", - GlobalConfigVar: "DOKKU_PROXY_SSL_PORT", - Property: "proxy-ssl-port", - }, }); err != nil { return err } @@ -166,7 +156,7 @@ func TriggerPostAppRenameSetup(oldAppName string, newAppName string) error { // TriggerPostCertsRemove unsets port properties after SSL cert is removed func TriggerPostCertsRemove(appName string) error { - if err := common.PropertyDelete("ports", appName, "proxy-ssl-port"); err != nil { + if err := common.PropertyDelete("proxy", appName, "proxy-ssl-port"); err != nil { return err } @@ -175,15 +165,15 @@ func TriggerPostCertsRemove(appName string) error { // TriggerPostCertsUpdate sets port properties after SSL cert is added func TriggerPostCertsUpdate(appName string) error { - port := common.PropertyGet("ports", appName, "proxy-port") - sslPort := common.PropertyGet("ports", appName, "proxy-ssl-port") + port := common.PropertyGet("proxy", appName, "proxy-port") + sslPort := common.PropertyGet("proxy", appName, "proxy-ssl-port") portMaps := getPortMaps(appName) if port == "80" { - common.PropertyDelete("ports", appName, "proxy-port") + common.PropertyDelete("proxy", appName, "proxy-port") } if sslPort == "443" { - common.PropertyDelete("ports", appName, "proxy-ssl-port") + common.PropertyDelete("proxy", appName, "proxy-ssl-port") } var http80Ports []PortMap diff --git a/plugins/proxy/proxy.go b/plugins/proxy/proxy.go index 9f64815f7..aec2c924d 100644 --- a/plugins/proxy/proxy.go +++ b/plugins/proxy/proxy.go @@ -11,13 +11,17 @@ const RunInSerial = 0 var ( // DefaultProperties is a map of all valid proxy properties with corresponding default property values DefaultProperties = map[string]string{ - "disabled": "false", - "type": "", + "disabled": "false", + "proxy-port": "", + "proxy-ssl-port": "", + "type": "", } // GlobalProperties is a map of all valid global proxy properties GlobalProperties = map[string]bool{ - "type": true, + "proxy-port": true, + "proxy-ssl-port": true, + "type": true, } ) diff --git a/plugins/proxy/src/subcommands/subcommands.go b/plugins/proxy/src/subcommands/subcommands.go index ca06d2660..11498b8b0 100644 --- a/plugins/proxy/src/subcommands/subcommands.go +++ b/plugins/proxy/src/subcommands/subcommands.go @@ -59,12 +59,14 @@ func main() { global := args.Bool("global", false, "--global: set a global property") args.Parse(os.Args[2:]) appName := args.Arg(0) - proxyType := args.Arg(1) + property := args.Arg(1) + value := args.Arg(2) if *global { appName = "--global" - proxyType = args.Arg(0) + property = args.Arg(0) + value = args.Arg(1) } - err = proxy.CommandSet(appName, proxyType) + err = proxy.CommandSet(appName, property, value) default: err = fmt.Errorf("Invalid plugin subcommand call: %s", subcommand) } diff --git a/plugins/proxy/subcommands.go b/plugins/proxy/subcommands.go index b431a1284..e603665c1 100644 --- a/plugins/proxy/subcommands.go +++ b/plugins/proxy/subcommands.go @@ -2,7 +2,6 @@ package proxy import ( "errors" - "strings" "github.com/dokku/dokku/plugins/common" ) @@ -82,22 +81,7 @@ func CommandReport(appName string, format string, infoFlag string) error { } // CommandSet sets a proxy for an app -func CommandSet(appName string, proxyType string) error { - if appName != "--global" { - if err := common.VerifyAppName(appName); err != nil { - return err - } - } - - if len(proxyType) < 2 { - return errors.New("Please specify a proxy type") - } - - if strings.Contains(proxyType, ":") { - common.LogWarn("Detected potential port mapping instead of proxy type") - return errors.New("Consider using ports:set command or specifying a valid proxy") - } - - common.CommandPropertySet("proxy", appName, "type", proxyType, DefaultProperties, GlobalProperties) +func CommandSet(appName string, property string, value string) error { + common.CommandPropertySet("proxy", appName, property, value, DefaultProperties, GlobalProperties) return nil } diff --git a/plugins/proxy/triggers.go b/plugins/proxy/triggers.go index 5a3eaeea8..935e63d77 100644 --- a/plugins/proxy/triggers.go +++ b/plugins/proxy/triggers.go @@ -28,6 +28,16 @@ func TriggerInstall() error { return value }, }, + { + ConfigVar: "DOKKU_PROXY_PORT", + GlobalConfigVar: "DOKKU_PROXY_PORT", + Property: "proxy-port", + }, + { + ConfigVar: "DOKKU_PROXY_SSL_PORT", + GlobalConfigVar: "DOKKU_PROXY_SSL_PORT", + Property: "proxy-ssl-port", + }, }); err != nil { return err } diff --git a/plugins/scheduler-docker-local/bin/scheduler-deploy-process-container b/plugins/scheduler-docker-local/bin/scheduler-deploy-process-container index 8028b9eaf..b9ec8a3c9 100755 --- a/plugins/scheduler-docker-local/bin/scheduler-deploy-process-container +++ b/plugins/scheduler-docker-local/bin/scheduler-deploy-process-container @@ -2,6 +2,7 @@ set -eo pipefail [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" source "$PLUGIN_AVAILABLE_PATH/scheduler-docker-local/internal-functions" main() { diff --git a/plugins/scheduler-docker-local/check-deploy b/plugins/scheduler-docker-local/check-deploy index 6dd794aff..8772aefd6 100755 --- a/plugins/scheduler-docker-local/check-deploy +++ b/plugins/scheduler-docker-local/check-deploy @@ -36,6 +36,7 @@ set -eo pipefail [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" source "$PLUGIN_AVAILABLE_PATH/checks/functions" source "$PLUGIN_AVAILABLE_PATH/config/functions" source "$PLUGIN_AVAILABLE_PATH/scheduler-docker-local/internal-functions" diff --git a/plugins/scheduler-docker-local/scheduler-deploy b/plugins/scheduler-docker-local/scheduler-deploy index 8daaa0532..7981c5a7f 100755 --- a/plugins/scheduler-docker-local/scheduler-deploy +++ b/plugins/scheduler-docker-local/scheduler-deploy @@ -2,6 +2,7 @@ set -eo pipefail [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" source "$PLUGIN_AVAILABLE_PATH/checks/functions" source "$PLUGIN_AVAILABLE_PATH/config/functions" source "$PLUGIN_AVAILABLE_PATH/scheduler-docker-local/internal-functions" diff --git a/plugins/scheduler-docker-local/scheduler-enter b/plugins/scheduler-docker-local/scheduler-enter index 61a3fa202..03d794a9a 100755 --- a/plugins/scheduler-docker-local/scheduler-enter +++ b/plugins/scheduler-docker-local/scheduler-enter @@ -1,5 +1,6 @@ #!/usr/bin/env bash source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" source "$PLUGIN_AVAILABLE_PATH/config/functions" set -eo pipefail [[ $DOKKU_TRACE ]] && set -x diff --git a/plugins/scheduler-docker-local/scheduler-run b/plugins/scheduler-docker-local/scheduler-run index 27d4236e9..658beff00 100755 --- a/plugins/scheduler-docker-local/scheduler-run +++ b/plugins/scheduler-docker-local/scheduler-run @@ -2,6 +2,7 @@ set -eo pipefail [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" source "$PLUGIN_AVAILABLE_PATH/config/functions" source "$PLUGIN_AVAILABLE_PATH/scheduler-docker-local/internal-functions" diff --git a/tests/unit/apps_2.bats b/tests/unit/apps_2.bats index 0926fe33c..5c481bde5 100644 --- a/tests/unit/apps_2.bats +++ b/tests/unit/apps_2.bats @@ -116,7 +116,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "$PLUGIN_CORE_AVAILABLE_PATH/common/prop set ports $TEST_APP proxy-ssl-port 443" + run /bin/bash -c "dokku proxy:set $TEST_APP proxy-ssl-port 443" echo "output: $output" echo "status: $status" assert_success diff --git a/tests/unit/nginx-vhosts_5.bats b/tests/unit/nginx-vhosts_5.bats index 9f8963950..9af7c2ea0 100644 --- a/tests/unit/nginx-vhosts_5.bats +++ b/tests/unit/nginx-vhosts_5.bats @@ -62,7 +62,7 @@ teardown() { @test "(nginx-vhosts) proxy:build-config (global proxy-port)" { local GLOBAL_PORT=30999 - run /bin/bash -c "$PLUGIN_CORE_AVAILABLE_PATH/common/prop set ports --global proxy-port ${GLOBAL_PORT}" + run /bin/bash -c "dokku proxy:set --global proxy-port ${GLOBAL_PORT}" echo "output: $output" echo "status: $status" assert_success @@ -74,7 +74,7 @@ teardown() { check_urls "http://${TEST_APP}.${DOKKU_DOMAIN}:${GLOBAL_PORT}" assert_http_success "http://${TEST_APP}.${DOKKU_DOMAIN}:${GLOBAL_PORT}" - run /bin/bash -c "$PLUGIN_CORE_AVAILABLE_PATH/common/prop del ports --global proxy-port" + run /bin/bash -c "dokku proxy:set --global proxy-port" echo "output: $output" echo "status: $status" assert_success From c7afc0523dca90f1c3df07e66f30591eca7ee257 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 25 Apr 2026 18:07:10 -0400 Subject: [PATCH 019/546] fix: maintain backward compatibility for proxy:set command proxy:set continues to work as before by detecting when the first argument is not a known property name and treating it as the type value. Also preserves the port mapping detection error for inputs containing colons. --- plugins/proxy/subcommands.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/plugins/proxy/subcommands.go b/plugins/proxy/subcommands.go index e603665c1..997125a38 100644 --- a/plugins/proxy/subcommands.go +++ b/plugins/proxy/subcommands.go @@ -2,6 +2,7 @@ package proxy import ( "errors" + "strings" "github.com/dokku/dokku/plugins/common" ) @@ -80,8 +81,19 @@ func CommandReport(appName string, format string, infoFlag string) error { return ReportSingleApp(appName, format, infoFlag) } -// CommandSet sets a proxy for an app +// CommandSet set or clear a proxy property for an app func CommandSet(appName string, property string, value string) error { + // backward compatibility: `proxy:set ` is treated as + // `proxy:set type ` + if _, ok := DefaultProperties[property]; !ok && value == "" { + if strings.Contains(property, ":") { + common.LogWarn("Detected potential port mapping instead of proxy type") + return errors.New("Consider using ports:set command or specifying a valid proxy") + } + value = property + property = "type" + } + common.CommandPropertySet("proxy", appName, property, value, DefaultProperties, GlobalProperties) return nil } From 9324ccf5b95aefedc7e9d5a28587d92cb4cf24d4 Mon Sep 17 00:00:00 2001 From: Dokku Bot Date: Sun, 26 Apr 2026 04:07:58 +0000 Subject: [PATCH 020/546] Release 0.37.10 # History ## 0.37.10 Install/update via the bootstrap script: ```shell wget -NP . https://dokku.com/install/v0.37.10/bootstrap.sh sudo DOKKU_TAG=v0.37.10 bash bootstrap.sh ``` ### Tests - #8491: @dependabot[bot] chore(deps-dev): bump heroku/heroku-buildpack-php from 285 to 287 in /tests/apps/php - #8478: @josegonzalez chore: label test app dependency updates as type: tests ### Dependencies - #8486: @dependabot[bot] chore(deps): bump click from 8.3.2 to 8.3.3 in /docs/_build - #8492: @dependabot[bot] chore(deps): bump github.com/fluxcd/pkg/kustomize from 1.29.0 to 1.31.0 in /plugins/scheduler-k3s - #8489: @dependabot[bot] chore(deps): bump github.com/traefik/traefik/v2 from 2.11.42 to 2.11.43 in /plugins/scheduler-k3s - #8488: @dependabot[bot] chore(deps): bump timberio/vector from 0.54.0-debian to 0.55.0-debian in /plugins/logs - #8487: @dependabot[bot] chore(deps): bump traefik from v3.6.13 to v3.6.14 in /plugins/traefik-vhosts - #8483: @dependabot[bot] chore(deps): update markdown requirement from <3.11,>=3.2.1 to >=3.10.2,<3.11 in /docs/_build - #8485: @dependabot[bot] chore(deps): bump k8s.io/kubectl from 0.35.2 to 0.35.4 in /plugins/scheduler-k3s - #8484: @dependabot[bot] chore(deps): bump k8s.io/client-go from 0.35.2 to 0.35.4 in /plugins/scheduler-k3s - #8482: @dependabot[bot] chore(deps): bump ruby from 4.0.2 to 4.0.3 in /tests/apps/dockerfile-entrypoint - #8481: @dependabot[bot] chore(deps): bump psycopg2-binary from 2.9.11 to 2.9.12 in /tests/apps/dockerfile-release - #8479: @dependabot[bot] chore(deps): bump github.com/go-openapi/jsonpointer from 0.23.0 to 0.23.1 in /plugins/scheduler-k3s - #8480: @dependabot[bot] chore(deps): bump k8s.io/kubernetes from 1.35.3 to 1.35.4 in /plugins/scheduler-k3s --- HISTORY.md | 29 +++++++++++ README.md | 4 +- debian/control | 2 +- docs/advanced-usage/plugin-management.md | 58 +++++++++++----------- docs/assets/favicons/browserconfig.xml | 8 +-- docs/assets/favicons/manifest.json | 12 ++--- docs/assets/style.css | 10 ++-- docs/assets/versions.json | 2 +- docs/development/release-process.md | 2 +- docs/getting-started/install/docker.md | 8 +-- docs/getting-started/installation/index.md | 4 +- docs/home.html | 40 +++++++-------- docs/template.html | 36 +++++++------- plugins/00_dokku-standard/plugin.toml | 2 +- plugins/20_events/plugin.toml | 2 +- plugins/app-json/plugin.toml | 2 +- plugins/apps/plugin.toml | 2 +- plugins/builder-dockerfile/plugin.toml | 2 +- plugins/builder-herokuish/plugin.toml | 2 +- plugins/builder-lambda/plugin.toml | 2 +- plugins/builder-nixpacks/plugin.toml | 2 +- plugins/builder-null/plugin.toml | 2 +- plugins/builder-pack/plugin.toml | 2 +- plugins/builder-railpack/plugin.toml | 2 +- plugins/builder/plugin.toml | 2 +- plugins/buildpacks/plugin.toml | 2 +- plugins/caddy-vhosts/plugin.toml | 2 +- plugins/certs/plugin.toml | 2 +- plugins/checks/plugin.toml | 2 +- plugins/common/plugin.toml | 2 +- plugins/config/plugin.toml | 2 +- plugins/cron/plugin.toml | 2 +- plugins/docker-options/plugin.toml | 2 +- plugins/domains/plugin.toml | 2 +- plugins/enter/plugin.toml | 2 +- plugins/git/plugin.toml | 2 +- plugins/haproxy-vhosts/plugin.toml | 2 +- plugins/logs/plugin.toml | 2 +- plugins/network/plugin.toml | 2 +- plugins/nginx-vhosts/plugin.toml | 2 +- plugins/openresty-vhosts/plugin.toml | 2 +- plugins/plugin/plugin.toml | 2 +- plugins/ports/plugin.toml | 2 +- plugins/proxy/plugin.toml | 2 +- plugins/ps/plugin.toml | 2 +- plugins/registry/plugin.toml | 2 +- plugins/repo/plugin.toml | 2 +- plugins/resource/plugin.toml | 2 +- plugins/run/plugin.toml | 2 +- plugins/scheduler-docker-local/plugin.toml | 2 +- plugins/scheduler-k3s/plugin.toml | 2 +- plugins/scheduler-null/plugin.toml | 2 +- plugins/scheduler/plugin.toml | 2 +- plugins/shell/plugin.toml | 2 +- plugins/ssh-keys/plugin.toml | 2 +- plugins/storage/plugin.toml | 2 +- plugins/trace/plugin.toml | 2 +- plugins/traefik-vhosts/plugin.toml | 2 +- 58 files changed, 167 insertions(+), 138 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 56642ddd4..0f7d8474b 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,34 @@ # History +## 0.37.10 + +Install/update via the bootstrap script: + +```shell +wget -NP . https://dokku.com/install/v0.37.10/bootstrap.sh +sudo DOKKU_TAG=v0.37.10 bash bootstrap.sh +``` + +### Tests + +- #8491: @dependabot[bot] chore(deps-dev): bump heroku/heroku-buildpack-php from 285 to 287 in /tests/apps/php +- #8478: @josegonzalez chore: label test app dependency updates as type: tests + +### Dependencies + +- #8486: @dependabot[bot] chore(deps): bump click from 8.3.2 to 8.3.3 in /docs/_build +- #8492: @dependabot[bot] chore(deps): bump github.com/fluxcd/pkg/kustomize from 1.29.0 to 1.31.0 in /plugins/scheduler-k3s +- #8489: @dependabot[bot] chore(deps): bump github.com/traefik/traefik/v2 from 2.11.42 to 2.11.43 in /plugins/scheduler-k3s +- #8488: @dependabot[bot] chore(deps): bump timberio/vector from 0.54.0-debian to 0.55.0-debian in /plugins/logs +- #8487: @dependabot[bot] chore(deps): bump traefik from v3.6.13 to v3.6.14 in /plugins/traefik-vhosts +- #8483: @dependabot[bot] chore(deps): update markdown requirement from <3.11,>=3.2.1 to >=3.10.2,<3.11 in /docs/_build +- #8485: @dependabot[bot] chore(deps): bump k8s.io/kubectl from 0.35.2 to 0.35.4 in /plugins/scheduler-k3s +- #8484: @dependabot[bot] chore(deps): bump k8s.io/client-go from 0.35.2 to 0.35.4 in /plugins/scheduler-k3s +- #8482: @dependabot[bot] chore(deps): bump ruby from 4.0.2 to 4.0.3 in /tests/apps/dockerfile-entrypoint +- #8481: @dependabot[bot] chore(deps): bump psycopg2-binary from 2.9.11 to 2.9.12 in /tests/apps/dockerfile-release +- #8479: @dependabot[bot] chore(deps): bump github.com/go-openapi/jsonpointer from 0.23.0 to 0.23.1 in /plugins/scheduler-k3s +- #8480: @dependabot[bot] chore(deps): bump k8s.io/kubernetes from 1.35.3 to 1.35.4 in /plugins/scheduler-k3s + ## 0.37.9 Install/update via the bootstrap script: diff --git a/README.md b/README.md index 1c928e9d1..73e07dd8a 100644 --- a/README.md +++ b/README.md @@ -85,8 +85,8 @@ Otherwise, you will need to import the keypair manually after installation using To install the latest stable release, run the following commands as a user who has access to `sudo`: ```shell -wget -NP . https://dokku.com/install/v0.37.9/bootstrap.sh -sudo DOKKU_TAG=v0.37.9 bash bootstrap.sh +wget -NP . https://dokku.com/install/v0.37.10/bootstrap.sh +sudo DOKKU_TAG=v0.37.10 bash bootstrap.sh ``` You can then proceed to configure your server domain (via `dokku domains:set-global`) and user access (via `dokku ssh-keys:add`) to complete the installation. diff --git a/debian/control b/debian/control index e2e70f3a8..e9720dfb0 100644 --- a/debian/control +++ b/debian/control @@ -1,5 +1,5 @@ Package: dokku -Version: 0.37.9 +Version: 0.37.10 Section: web Priority: optional Architecture: amd64 diff --git a/docs/advanced-usage/plugin-management.md b/docs/advanced-usage/plugin-management.md index a731866a5..7c46433ec 100644 --- a/docs/advanced-usage/plugin-management.md +++ b/docs/advanced-usage/plugin-management.md @@ -36,35 +36,35 @@ dokku plugin:list ``` plugn: dev - 00_dokku-standard 0.37.9 enabled dokku core standard plugin - 20_events 0.37.9 enabled dokku core events logging plugin - app-json 0.37.9 enabled dokku core app-json plugin - apps 0.37.9 enabled dokku core apps plugin - build-env 0.37.9 enabled dokku core build-env plugin - buildpacks 0.37.9 enabled dokku core buildpacks plugin - certs 0.37.9 enabled dokku core certificate management plugin - checks 0.37.9 enabled dokku core checks plugin - common 0.37.9 enabled dokku core common plugin - config 0.37.9 enabled dokku core config plugin - docker-options 0.37.9 enabled dokku core docker-options plugin - domains 0.37.9 enabled dokku core domains plugin - enter 0.37.9 enabled dokku core enter plugin - git 0.37.9 enabled dokku core git plugin - logs 0.37.9 enabled dokku core logs plugin - network 0.37.9 enabled dokku core network plugin - nginx-vhosts 0.37.9 enabled dokku core nginx-vhosts plugin - plugin 0.37.9 enabled dokku core plugin plugin - proxy 0.37.9 enabled dokku core proxy plugin - ps 0.37.9 enabled dokku core ps plugin - repo 0.37.9 enabled dokku core repo plugin - resource 0.37.9 enabled dokku core resource plugin - scheduler-docker-local 0.37.9 enabled dokku core scheduler-docker-local plugin - shell 0.37.9 enabled dokku core shell plugin - ssh-keys 0.37.9 enabled dokku core ssh-keys plugin - storage 0.37.9 enabled dokku core storage plugin - tags 0.37.9 enabled dokku core tags plugin - tar 0.37.9 enabled dokku core tar plugin - trace 0.37.9 enabled dokku core trace plugin + 00_dokku-standard 0.37.10 enabled dokku core standard plugin + 20_events 0.37.10 enabled dokku core events logging plugin + app-json 0.37.10 enabled dokku core app-json plugin + apps 0.37.10 enabled dokku core apps plugin + build-env 0.37.10 enabled dokku core build-env plugin + buildpacks 0.37.10 enabled dokku core buildpacks plugin + certs 0.37.10 enabled dokku core certificate management plugin + checks 0.37.10 enabled dokku core checks plugin + common 0.37.10 enabled dokku core common plugin + config 0.37.10 enabled dokku core config plugin + docker-options 0.37.10 enabled dokku core docker-options plugin + domains 0.37.10 enabled dokku core domains plugin + enter 0.37.10 enabled dokku core enter plugin + git 0.37.10 enabled dokku core git plugin + logs 0.37.10 enabled dokku core logs plugin + network 0.37.10 enabled dokku core network plugin + nginx-vhosts 0.37.10 enabled dokku core nginx-vhosts plugin + plugin 0.37.10 enabled dokku core plugin plugin + proxy 0.37.10 enabled dokku core proxy plugin + ps 0.37.10 enabled dokku core ps plugin + repo 0.37.10 enabled dokku core repo plugin + resource 0.37.10 enabled dokku core resource plugin + scheduler-docker-local 0.37.10 enabled dokku core scheduler-docker-local plugin + shell 0.37.10 enabled dokku core shell plugin + ssh-keys 0.37.10 enabled dokku core ssh-keys plugin + storage 0.37.10 enabled dokku core storage plugin + tags 0.37.10 enabled dokku core tags plugin + tar 0.37.10 enabled dokku core tar plugin + trace 0.37.10 enabled dokku core trace plugin ``` > [!WARNING] diff --git a/docs/assets/favicons/browserconfig.xml b/docs/assets/favicons/browserconfig.xml index b318b8fef..130d4d581 100644 --- a/docs/assets/favicons/browserconfig.xml +++ b/docs/assets/favicons/browserconfig.xml @@ -2,10 +2,10 @@ - - - - + + + + #da532c diff --git a/docs/assets/favicons/manifest.json b/docs/assets/favicons/manifest.json index a5554f080..e98890aee 100644 --- a/docs/assets/favicons/manifest.json +++ b/docs/assets/favicons/manifest.json @@ -2,37 +2,37 @@ "name": "Dokku", "icons": [ { - "src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.37.9\/docs\/assets\/favicons\/android-chrome-36x36.png", + "src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.37.10\/docs\/assets\/favicons\/android-chrome-36x36.png", "sizes": "36x36", "type": "image\/png", "density": "0.75" }, { - "src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.37.9\/docs\/assets\/favicons\/android-chrome-48x48.png", + "src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.37.10\/docs\/assets\/favicons\/android-chrome-48x48.png", "sizes": "48x48", "type": "image\/png", "density": "1.0" }, { - "src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.37.9\/docs\/assets\/favicons\/android-chrome-72x72.png", + "src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.37.10\/docs\/assets\/favicons\/android-chrome-72x72.png", "sizes": "72x72", "type": "image\/png", "density": "1.5" }, { - "src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.37.9\/docs\/assets\/favicons\/android-chrome-96x96.png", + "src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.37.10\/docs\/assets\/favicons\/android-chrome-96x96.png", "sizes": "96x96", "type": "image\/png", "density": "2.0" }, { - "src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.37.9\/docs\/assets\/favicons\/android-chrome-144x144.png", + "src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.37.10\/docs\/assets\/favicons\/android-chrome-144x144.png", "sizes": "144x144", "type": "image\/png", "density": "3.0" }, { - "src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.37.9\/docs\/assets\/favicons\/android-chrome-192x192.png", + "src": "https:\/\/cdn.jsdelivr.net\/dokku\/dokku@v0.37.10\/docs\/assets\/favicons\/android-chrome-192x192.png", "sizes": "192x192", "type": "image\/png", "density": "4.0" diff --git a/docs/assets/style.css b/docs/assets/style.css index 69d057fb7..03a64c2ff 100644 --- a/docs/assets/style.css +++ b/docs/assets/style.css @@ -25,9 +25,9 @@ /* Avatar section darkmode fade */ --avatar-before-gradient: rgba(0, 0, 0, 0) linear-gradient(90deg, white 2.52%, rgba(255, 255, 255, 0) 100%) repeat scroll 0% 0%; - --avatar-darkmode-before-gradient: rgba(0, 0, 0, 0) linear-gradient(90deg, #0.37.9 2.52%, rgba(255, 255, 255, 0) 100%) repeat scroll 0% 0%; + --avatar-darkmode-before-gradient: rgba(0, 0, 0, 0) linear-gradient(90deg, #0.37.10 2.52%, rgba(255, 255, 255, 0) 100%) repeat scroll 0% 0%; --avatar-after-gradient: rgba(0, 0, 0, 0) linear-gradient(270deg, white 2.52%, rgba(255, 255, 255, 0) 100%) repeat scroll 0% 0%; - --avatar-darkmode-after-gradient: rgba(0, 0, 0, 0) linear-gradient(270deg, #0.37.9 2.52%, rgba(255, 255, 255, 0) 100%) repeat scroll 0% 0%; + --avatar-darkmode-after-gradient: rgba(0, 0, 0, 0) linear-gradient(270deg, #0.37.10 2.52%, rgba(255, 255, 255, 0) 100%) repeat scroll 0% 0%; } body { @@ -1191,15 +1191,15 @@ ul.fas li .fa-large:before { .getting-started::after { background: linear-gradient(0deg, rgba(52, 52, 87, 0.2) -40.97%, rgba(0, 0, 0, 0) 103.19%), - #0.37.9; + #0.37.10; } .tab-content { - background: #0.37.9; + background: #0.37.10; } .nav-tabs { - background: #0.37.9; + background: #0.37.10; } .powered-by_brands { diff --git a/docs/assets/versions.json b/docs/assets/versions.json index c3b641193..05e88e35c 100644 --- a/docs/assets/versions.json +++ b/docs/assets/versions.json @@ -34,6 +34,6 @@ "0.34.9", "0.35.20", "0.36.11", - "0.37.9" + "0.37.10" ] } diff --git a/docs/development/release-process.md b/docs/development/release-process.md index bce4324ac..0b04db26e 100644 --- a/docs/development/release-process.md +++ b/docs/development/release-process.md @@ -29,7 +29,7 @@ The workflow looks like this: ```shell # having dokku-arch in ../dokku-arch vagrant up build-arch -# wait for "==> build-arch: ==> Finished making: dokku 0.37.9-2 (Mon Feb 22 23:20:37 CET 2016)" +# wait for "==> build-arch: ==> Finished making: dokku 0.37.10-2 (Mon Feb 22 23:20:37 CET 2016)" cd ../dokku-arch git add PKGBUILD .SRCINFO git commit -m 'Update to dokku 0.9.9' diff --git a/docs/getting-started/install/docker.md b/docs/getting-started/install/docker.md index 2446f5fe4..7b90c7e81 100644 --- a/docs/getting-started/install/docker.md +++ b/docs/getting-started/install/docker.md @@ -3,7 +3,7 @@ Pull the dokku/dokku image: ```shell -docker pull dokku/dokku:0.37.9 +docker pull dokku/dokku:0.37.10 ``` Next, run the image. @@ -19,7 +19,7 @@ docker container run -d \ --publish 8443:443 \ --volume /var/lib/dokku:/mnt/dokku \ --volume /var/run/docker.sock:/var/run/docker.sock \ - dokku/dokku:0.37.9 + dokku/dokku:0.37.10 ``` Alternatively, you can use `docker-compose.yml`: @@ -27,7 +27,7 @@ Alternatively, you can use `docker-compose.yml`: ```yaml services: dokku: - image: dokku/dokku:0.37.9 + image: dokku/dokku:0.37.10 container_name: dokku network_mode: bridge ports: @@ -83,7 +83,7 @@ redis: https://github.com/dokku/dokku-redis.git The alternative is to build a custom docker image via a custom Dockerfile. This Dockerfile can run any `plugin:install` command, though the `install` trigger must be skipped via the `--skip-install-trigger`. The version installed at that time will be the one that persists. Below is an example Dockerfile showing this method. ```Dockerfile -FROM dokku/dokku:0.37.9 +FROM dokku/dokku:0.37.10 RUN dokku plugin:install https://github.com/dokku/dokku-postgres.git --skip-install-trigger RUN dokku plugin:install https://github.com/dokku/dokku-redis.git --skip-install-trigger ``` diff --git a/docs/getting-started/installation/index.md b/docs/getting-started/installation/index.md index f47b80546..b83d83e5a 100644 --- a/docs/getting-started/installation/index.md +++ b/docs/getting-started/installation/index.md @@ -31,8 +31,8 @@ To install the latest stable version of Dokku, you can run the following shell c ```shell # for debian systems, installs Dokku via apt-get -wget -NP . https://dokku.com/install/v0.37.9/bootstrap.sh -sudo DOKKU_TAG=v0.37.9 bash bootstrap.sh +wget -NP . https://dokku.com/install/v0.37.10/bootstrap.sh +sudo DOKKU_TAG=v0.37.10 bash bootstrap.sh ``` The installation process takes about 5-10 minutes, depending upon internet connection speed. diff --git a/docs/home.html b/docs/home.html index 2e8a70f3b..0d6d6b4b6 100644 --- a/docs/home.html +++ b/docs/home.html @@ -10,26 +10,26 @@ Dokku - The smallest PaaS implementation you've ever seen - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - + + @@ -38,7 +38,7 @@ - + @@ -98,7 +98,7 @@
-
Latest release v0.37.9
+
Latest release v0.37.10

An open source PAAS alternative to Heroku.

Dokku helps you build and manage the lifecycle of applications from building to scaling.

@@ -125,7 +125,7 @@

$ - sudo DOKKU_TAG=v0.37.9 bash bootstrap.sh + sudo DOKKU_TAG=v0.37.10 bash bootstrap.sh


diff --git a/docs/template.html b/docs/template.html index 453d21446..0f3049701 100644 --- a/docs/template.html +++ b/docs/template.html @@ -9,26 +9,26 @@ Dokku - {{DOC_TITLE}} - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - + +