diff --git a/docs/configuration/environment-variables.md b/docs/configuration/environment-variables.md
index b3e9e9030..ab46dece2 100644
--- a/docs/configuration/environment-variables.md
+++ b/docs/configuration/environment-variables.md
@@ -78,7 +78,7 @@ The following config variables have special meanings and can be set in a variety
| Name | Default | How to modify | Description |
| ------------------------------ | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- |
| `DOKKU_ROOT` | `~dokku` | `/etc/environment` | The root directory where dokku will store application repositories, as well as certain configuration files. |
-| `DOKKU_IMAGE` | `gliderlabs/herokuish` | `/etc/environment`
`~dokku/.dokkurc`
`~dokku/.dokkurc/*` | The default image to use when building herokuish containers. |
+| `DOKKU_IMAGE` | `gliderlabs/herokuish` | `/etc/environment`
`~dokku/.dokkurc`
`~dokku/.dokkurc/*` | The default image to use when building herokuish containers. Deprecated in favor of using `buildpacks:stack-set` |
| `DOKKU_LIB_ROOT` | `/var/lib/dokku` | `/etc/environment`
`~dokku/.dokkurc`
`~dokku/.dokkurc/*` | The directory where plugins, certain data, and general configuration is stored. |
| `PLUGIN_PATH` | `$DOKKU_LIB_ROOT/plugins"` | `/etc/environment`
`~dokku/.dokkurc`
`~dokku/.dokkurc/*` | The top-level directory where plugins are stored. |
| `PLUGIN_AVAILABLE_PATH` | `$PLUGIN_PATH/available"` | `/etc/environment`
`~dokku/.dokkurc`
`~dokku/.dokkurc/*` | The directory that holds all available plugins, including core. |
diff --git a/docs/deployment/methods/herokuish-buildpacks.md b/docs/deployment/methods/herokuish-buildpacks.md
index e72d3236c..f9fc632a7 100644
--- a/docs/deployment/methods/herokuish-buildpacks.md
+++ b/docs/deployment/methods/herokuish-buildpacks.md
@@ -9,6 +9,7 @@ buildpacks:list # List all buildpacks for an app
buildpacks:remove # Remove a buildpack set on the app
buildpacks:report [] [] # Displays a buildpack report for one or more apps
buildpacks:set [--index 1] # Set new app buildpack at a given position defaulting to the first buildpack if no index is specified
+buildpacks:stacks-set # Sets the stack of an app
```
> Warning: If using the `buildpacks` plugin, be sure to unset any `BUILDPACK_URL` and remove any such entries from a committed `.env` file. A specified `BUILDPACK_URL` will always override a `.buildpacks` file or the buildpacks plugin.
@@ -123,6 +124,32 @@ The `buildpacks:clear` command can be used to clear all configured buildpacks fo
dokku buildpacks:clear node-js-app
```
+### Customizing the Buildpack stack
+
+> New as of 0.23.0
+
+The default stack in use by Herokuish buildpacks in Dokku is based on `gliderlabs/herokuish`. Typically, this is installed via an OS package which pulls the requisite Docker image. Users may desire to switch the stack to a custom version, either to update the stack operating system or to customize packages included with the stack. This can be performed via teh `buildpacks:stack-set` command.
+
+```shell
+dokku buildpacks:stack-set node-js-app gliderlabs/herokuish:latest
+```
+
+The specified stack can also be unset by omitting the name of the stack when calling `buildpacks:stack-set`.
+
+```shell
+dokku buildpacks:stack-set node-js-app
+```
+
+Finally, stacks can be set or unset globally as a fallback. This will take precedence over a globally set `DOKKU_IMAGE` environment variable (`gliderlabs/herokuish:latest` by default).
+
+```shell
+# set globally
+dokku buildpacks:stack-set --global gliderlabs/herokuish:latest
+
+# unset globally
+dokku buildpacks:stack-set --global
+```
+
### Displaying buildpack reports for an app
You can get a report about the app's buildpacks status using the `buildpacks:report` command:
@@ -133,11 +160,20 @@ dokku buildpacks:report
```
=====> node-js-app buildpacks information
- Buildpacks list: https://github.com/heroku/heroku-buildpack-nodejs.git
+ Buildpacks computed stack: gliderlabs/herokuish:v0.5.23-20
+ Buildpacks global stack: gliderlabs/herokuish:latest
+ Buildpacks list: https://github.com/heroku/heroku-buildpack-nodejs.git
+ Buildpacks stack: gliderlabs/herokuish:v0.5.23-20
=====> python-sample buildpacks information
- Buildpacks list: https://github.com/heroku/heroku-buildpack-nodejs.git,https://github.com/heroku/heroku-buildpack-python.git
+ Buildpacks computed stack: gliderlabs/herokuish:v0.5.23-20
+ Buildpacks global stack: gliderlabs/herokuish:latest
+ Buildpacks list: https://github.com/heroku/heroku-buildpack-nodejs.git,https://github.com/heroku/heroku-buildpack-python.git
+ Buildpacks stack:
=====> ruby-sample buildpacks information
+ Buildpacks computed stack: gliderlabs/herokuish:v0.5.23-20
+ Buildpacks global stack: gliderlabs/herokuish:latest
Buildpacks list:
+ Buildpacks stack:
```
You can run the command for a specific app also.
diff --git a/plugins/builder-herokuish/builder-build b/plugins/builder-herokuish/builder-build
index b905f7f6c..76be1b392 100755
--- a/plugins/builder-herokuish/builder-build
+++ b/plugins/builder-herokuish/builder-build
@@ -21,9 +21,9 @@ trigger-builder-herokuish-builder-build() {
pushd "$SOURCECODE_WORK_DIR" &>/dev/null
- eval "$(config_export app "$APP")"
+ DOKKU_IMAGE="$(plugn trigger buildpack-stack-name "$APP")"
- DOKKU_IMAGE="$(config_get "$APP" DOKKU_IMAGE || echo "$DOKKU_IMAGE")"
+ eval "$(config_export app "$APP")"
plugn trigger builder-create-dokku-image "$BUILDER_TYPE" "$APP" "$SOURCECODE_WORK_DIR" "$DOKKU_IMAGE"
NEW_DOKKU_IMAGE=$(plugn trigger builder-dokku-image "$BUILDER_TYPE" "$APP" "$SOURCECODE_WORK_DIR" "$DOKKU_IMAGE")
[[ -n "$NEW_DOKKU_IMAGE" ]] && DOKKU_IMAGE="$NEW_DOKKU_IMAGE"
diff --git a/plugins/buildpacks/Makefile b/plugins/buildpacks/Makefile
index 700601cc5..5abb38ed3 100644
--- a/plugins/buildpacks/Makefile
+++ b/plugins/buildpacks/Makefile
@@ -1,5 +1,5 @@
-SUBCOMMANDS = subcommands/add subcommands/clear subcommands/list subcommands/remove subcommands/report subcommands/set
-TRIGGERS = triggers/install triggers/post-app-clone-setup triggers/post-app-rename-setup triggers/post-delete triggers/post-extract triggers/report
+SUBCOMMANDS = subcommands/add subcommands/clear subcommands/list subcommands/remove subcommands/report subcommands/set subcommands/stacks-set
+TRIGGERS = triggers/buildpack-stack-name triggers/install triggers/post-app-clone-setup triggers/post-app-rename-setup triggers/post-delete triggers/post-extract triggers/report
BUILD = commands subcommands triggers
PLUGIN_NAME = buildpacks
diff --git a/plugins/buildpacks/buildpacks.go b/plugins/buildpacks/buildpacks.go
new file mode 100644
index 000000000..8f4e3b956
--- /dev/null
+++ b/plugins/buildpacks/buildpacks.go
@@ -0,0 +1,13 @@
+package buildpacks
+
+var (
+ // DefaultProperties is a map of all valid buildpacks properties with corresponding default property values
+ DefaultProperties = map[string]string{
+ "stack": "",
+ }
+
+ // GlobalProperties is a map of all valid global buildpacks properties
+ GlobalProperties = map[string]bool{
+ "stack": true,
+ }
+)
diff --git a/plugins/buildpacks/report.go b/plugins/buildpacks/report.go
index ca53623c2..084884b62 100644
--- a/plugins/buildpacks/report.go
+++ b/plugins/buildpacks/report.go
@@ -1,6 +1,7 @@
package buildpacks
import (
+ "os"
"strings"
"github.com/dokku/dokku/plugins/common"
@@ -13,7 +14,10 @@ func ReportSingleApp(appName, infoFlag string) error {
}
flags := map[string]common.ReportFunc{
- "--buildpacks-list": reportList,
+ "--buildpacks-computed-stack": reportComputedStack,
+ "--buildpacks-global-stack": reportGlobalStack,
+ "--buildpacks-list": reportList,
+ "--buildpacks-stack": reportStack,
}
flagKeys := []string{}
@@ -27,6 +31,28 @@ func ReportSingleApp(appName, infoFlag string) error {
return common.ReportSingleApp("buildpacks", appName, infoFlag, infoFlags, flagKeys, trimPrefix, uppercaseFirstCharacter)
}
+func reportComputedStack(appName string) string {
+ if stack := common.PropertyGetDefault("buildpacks", appName, "stack", ""); stack != "" {
+ return stack
+ }
+
+ if stack := common.PropertyGetDefault("buildpacks", "--global", "stack", ""); stack != "" {
+ return stack
+ }
+
+ b, _ := common.PlugnTriggerOutput("config-get", []string{appName, "DOKKU_IMAGE"}...)
+ if dokkuImage := strings.TrimSpace(string(b[:])); dokkuImage != "" {
+ common.LogWarn("Deprecated: use buildpacks:stacks-set instead of specifying DOKKU_IMAGE environment variable")
+ return dokkuImage
+ }
+
+ return os.Getenv("DOKKU_IMAGE")
+}
+
+func reportGlobalStack(appName string) string {
+ return common.PropertyGetDefault("buildpacks", "--global", "stack", "")
+}
+
func reportList(appName string) string {
buildpacks, err := common.PropertyListGet("buildpacks", appName, "buildpacks")
if err != nil {
@@ -35,3 +61,7 @@ func reportList(appName string) string {
return strings.Join(buildpacks, ",")
}
+
+func reportStack(appName string) string {
+ return common.PropertyGetDefault("buildpacks", appName, "stack", "")
+}
diff --git a/plugins/buildpacks/src/commands/commands.go b/plugins/buildpacks/src/commands/commands.go
index 5d9cfac57..baf3144a0 100644
--- a/plugins/buildpacks/src/commands/commands.go
+++ b/plugins/buildpacks/src/commands/commands.go
@@ -24,6 +24,7 @@ Additional commands:`
buildpacks:remove , Remove a buildpack set on the app
buildpacks:report [] [], Displays a buildpack report for one or more apps
buildpacks:set [--index 1] , Set new app buildpack at a given position defaulting to the first buildpack if no index is specified
+ buildpacks:stacks-set , Sets the stack of an app
`
)
diff --git a/plugins/buildpacks/src/subcommands/subcommands.go b/plugins/buildpacks/src/subcommands/subcommands.go
index 7bf7a2303..b17a3c5b3 100644
--- a/plugins/buildpacks/src/subcommands/subcommands.go
+++ b/plugins/buildpacks/src/subcommands/subcommands.go
@@ -57,6 +57,17 @@ func main() {
appName := args.Arg(0)
buildpack := args.Arg(1)
err = buildpacks.CommandSet(appName, buildpack, *index)
+ case "stacks-set":
+ args := flag.NewFlagSet("buildpacks:stacks-set", flag.ExitOnError)
+ global := args.Bool("global", false, "--global: set a global property")
+ args.Parse(os.Args[2:])
+ appName := args.Arg(0)
+ stack := args.Arg(1)
+ if *global {
+ appName = "--global"
+ stack = args.Arg(0)
+ }
+ err = buildpacks.CommandStacksSet(appName, stack)
default:
common.LogFail(fmt.Sprintf("Invalid plugin subcommand call: %s", subcommand))
}
diff --git a/plugins/buildpacks/src/triggers/triggers.go b/plugins/buildpacks/src/triggers/triggers.go
index 63534684e..369038897 100644
--- a/plugins/buildpacks/src/triggers/triggers.go
+++ b/plugins/buildpacks/src/triggers/triggers.go
@@ -18,6 +18,9 @@ func main() {
var err error
switch trigger {
+ case "buildpack-stack-name":
+ appName := flag.Arg(0)
+ err = buildpacks.TriggerBuildpackStackName(appName)
case "install":
err = buildpacks.TriggerInstall()
case "post-app-clone-setup":
diff --git a/plugins/buildpacks/subcommands.go b/plugins/buildpacks/subcommands.go
index a6caea1ba..3d1b81c39 100644
--- a/plugins/buildpacks/subcommands.go
+++ b/plugins/buildpacks/subcommands.go
@@ -138,3 +138,9 @@ func CommandSet(appName string, buildpack string, index int) error {
return common.PropertyListSet("buildpacks", appName, "buildpacks", buildpack, index)
}
+
+// CommandStacksSet implements buildpacks:stacks-set
+func CommandStacksSet(appName string, stack string) error {
+ common.CommandPropertySet("buildpacks", appName, "stack", stack, DefaultProperties, GlobalProperties)
+ return common.PlugnTrigger("post-stack-set", []string{appName, stack}...)
+}
diff --git a/plugins/buildpacks/triggers.go b/plugins/buildpacks/triggers.go
index 293c74dd6..195c41dec 100644
--- a/plugins/buildpacks/triggers.go
+++ b/plugins/buildpacks/triggers.go
@@ -5,10 +5,36 @@ import (
"fmt"
"os"
"path/filepath"
+ "strings"
"github.com/dokku/dokku/plugins/common"
)
+// TriggerBuildpackStackName echos the stack name for the app
+func TriggerBuildpackStackName(appName string) error {
+ if stack := common.PropertyGetDefault("buildpacks", appName, "stack", ""); stack != "" {
+ fmt.Println(stack)
+ return nil
+ }
+
+ if stack := common.PropertyGetDefault("buildpacks", "--global", "stack", ""); stack != "" {
+ fmt.Println(stack)
+ return nil
+ }
+
+ b, _ := common.PlugnTriggerOutput("config-get", []string{appName, "DOKKU_IMAGE"}...)
+ dokkuImage := strings.TrimSpace(string(b[:]))
+ if dokkuImage != "" {
+ common.LogWarn("Deprecated: use buildpacks:stacks-set instead of specifying DOKKU_IMAGE environment variable")
+ fmt.Println(dokkuImage)
+ return nil
+ }
+
+ fmt.Println(os.Getenv("DOKKU_IMAGE"))
+
+ return nil
+}
+
// TriggerInstall runs the install step for the buildpacks plugin
func TriggerInstall() error {
if err := common.PropertySetup("buildpacks"); err != nil {