diff --git a/docs/deployment/builders/herokuish-buildpacks.md b/docs/deployment/builders/herokuish-buildpacks.md index 179e6c3219c..31b9976b6a3 100644 --- a/docs/deployment/builders/herokuish-buildpacks.md +++ b/docs/deployment/builders/herokuish-buildpacks.md @@ -12,6 +12,11 @@ buildpacks:set [--index 1] # Set new app buildpack buildpacks:set-property [--global|] # Set or clear a buildpacks property for an app ``` +``` +builder-herokuish:report [] [] # Displays a builder-herokuish report for one or more apps +builder-herokuish:set () # Set or clear a builder-herokuish property for 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. Dokku normally defaults to using [Heroku buildpacks](https://devcenter.heroku.com/articles/buildpacks) for deployment, though this may be overridden by committing a valid `Dockerfile` to the root of your repository and pushing the repository to your Dokku installation. To avoid this automatic `Dockerfile` deployment detection, you may do one of the following: @@ -168,6 +173,36 @@ dokku buildpacks:set-property --global stack gliderlabs/herokuish:latest dokku buildpacks:set-property --global stack ``` +### Allowing herokuish for non-amd64 platforms + +> New as of 0.29.0 + +By default, the builder-herokuish plugin is not enabled for non-amd64 platforms, and attempting to use it is blocked. This is because the majority of buildpacks are not cross-platform compatible, and thus building apps will either be considerably slower - due to emulating the amd64 platform - or won't work - due to building amd64 packages on arm/arm64 platforms. + +To force-enable herokuish on non-amd64 platforms, the `allowed` property can be set via `builder-herokuish:set`. The default value depends on the host platform architecture (`true` on amd64, `false` otherwise). + +```shell +dokku builder-herokuish:set node-js-app allowed true +``` + +The default value may be set by passing an empty value for the option: + +```shell +dokku builder-herokuish:set node-js-app allowed +``` + +The `allowed` property can also be set globally. The global default is platform-dependent, and the global value is used when no app-specific value is set. + +```shell +dokku builder-herokuish:set --global allowed true +``` + +The default value may be set by passing an empty value for the option. + +```shell +dokku builder-herokuish:set --global allowed +``` + ### Displaying buildpack reports for an app You can get a report about the app's buildpacks status using the `buildpacks:report` command: @@ -211,6 +246,54 @@ You can pass flags which will output only the value of the specific information dokku buildpacks:report node-js-app --buildpacks-list ``` +### Displaying builder-herokuish reports for an app + +> New as of 0.29.0 + +You can get a report about the app's storage status using the `builder-herokuish:report` command: + +```shell +dokku builder-herokuish:report +``` + +``` +=====> node-js-app builder-herokuish information + Builder herokuish computed allowed: false + Builder herokuish global allowed: true + Builder herokuish allowed: false +=====> python-sample builder-herokuish information + Builder herokuish computed allowed: true + Builder herokuish global allowed: true + Builder herokuish allowed: +=====> ruby-sample builder-herokuish information + Builder herokuish computed allowed: true + Builder herokuish global allowed: true + Builder herokuish allowed: +``` + +You can run the command for a specific app also. + +```shell +dokku builder-herokuish:report node-js-app +``` + +``` +=====> node-js-app builder-herokuish information + Builder herokuish computed allowed: false + Builder herokuish global allowed: true + Builder herokuish allowed: false +``` + +You can pass flags which will output only the value of the specific information you want. For example: + +```shell +dokku builder-herokuish:report node-js-app --builder-herokuish-allowed +``` + +``` +false +``` + ## Errata ### Switching from Dockerfile deployments diff --git a/plugins/builder-herokuish/builder-herokuish-allowed b/plugins/builder-herokuish/builder-herokuish-allowed new file mode 100755 index 00000000000..592142da4d5 --- /dev/null +++ b/plugins/builder-herokuish/builder-herokuish-allowed @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +source "$PLUGIN_AVAILABLE_PATH/builder-herokuish/internal-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +trigger-builder-herokuish-builder-herokuish-allowed() { + declare desc="builder-herokuish builder-herokuish-allowed plugin trigger" + declare trigger="builder-herokuish-allowed" + declare APP="$1" + + if [[ "$(fn-builder-herokuish-computed-allowed "$APP")" != "true" ]]; then + return 1 + fi + + return 0 +} + +trigger-builder-herokuish-builder-herokuish-allowed "$@" diff --git a/plugins/builder-herokuish/help-functions b/plugins/builder-herokuish/help-functions new file mode 100755 index 00000000000..1ddd294fa3d --- /dev/null +++ b/plugins/builder-herokuish/help-functions @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +cmd-builder-herokuish-help() { + declare desc="help command" + declare CMD="$1" + local plugin_name="builder-herokuish" + local plugin_description="Manage the herokuish builder integration for an app" + + if [[ "$CMD" == "${plugin_name}:help" ]]; then + echo -e "Usage: dokku ${plugin_name}[:COMMAND]" + echo '' + echo "$plugin_description" + echo '' + echo 'Additional commands:' + fn-help-content | sort | column -c2 -t -s, + elif [[ $(ps -o command= $PPID) == *"--all"* ]]; then + fn-help-content + else + cat <] [], Displays a builder-herokuish report for one or more apps + builder-herokuish:set (), Set or clear a builder-herokuish property for an app +help_content +} diff --git a/plugins/builder-herokuish/internal-functions b/plugins/builder-herokuish/internal-functions new file mode 100755 index 00000000000..912b13f2e93 --- /dev/null +++ b/plugins/builder-herokuish/internal-functions @@ -0,0 +1,92 @@ +#!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +cmd-builder-herokuish-report() { + declare desc="displays a builder-herokuish report for one or more apps" + declare cmd="builder-herokuish:report" + [[ "$1" == "$cmd" ]] && shift 1 + declare APP="$1" INFO_FLAG="$2" + + if [[ -n "$APP" ]] && [[ "$APP" == --* ]]; then + INFO_FLAG="$APP" + APP="" + fi + + if [[ -z "$APP" ]] && [[ -z "$INFO_FLAG" ]]; then + INFO_FLAG="true" + fi + + if [[ -z "$APP" ]]; then + for app in $(dokku_apps); do + cmd-builder-herokuish-report-single "$app" "$INFO_FLAG" | tee || true + done + else + cmd-builder-herokuish-report-single "$APP" "$INFO_FLAG" + fi +} + +cmd-builder-herokuish-report-single() { + declare APP="$1" INFO_FLAG="$2" + if [[ "$INFO_FLAG" == "true" ]]; then + INFO_FLAG="" + fi + verify_app_name "$APP" + local flag_map=( + "--builder-herokuish-computed-allowed: $(fn-builder-herokuish-computed-allowed "$APP")" + "--builder-herokuish-global-allowed: $(fn-builder-herokuish-global-allowed)" + "--builder-herokuish-allowed: $(fn-builder-herokuish-allowed "$APP")" + ) + + if [[ -z "$INFO_FLAG" ]]; then + dokku_log_info2_quiet "${APP} builder-herokuish information" + for flag in "${flag_map[@]}"; do + key="$(echo "${flag#--}" | cut -f1 -d' ' | tr - ' ')" + dokku_log_verbose "$(printf "%-30s %-25s" "${key^}" "${flag#*: }")" + done + else + local match=false + local value_exists=false + for flag in "${flag_map[@]}"; do + valid_flags="${valid_flags} $(echo "$flag" | cut -d':' -f1)" + if [[ "$flag" == "${INFO_FLAG}:"* ]]; then + value=${flag#*: } + size="${#value}" + if [[ "$size" -ne 0 ]]; then + echo "$value" && match=true && value_exists=true + else + match=true + fi + fi + done + [[ "$match" == "true" ]] || dokku_log_fail "Invalid flag passed, valid flags:${valid_flags}" + [[ "$value_exists" == "true" ]] || dokku_log_fail "not deployed" + fi +} + +fn-builder-herokuish-computed-allowed() { + declare APP="$1" + + allowed="$(fn-builder-herokuish-allowed "$APP")" + if [[ -z "$allowed" ]]; then + allowed="$(fn-builder-herokuish-global-allowed)" + fi + + echo "$allowed" +} + +fn-builder-herokuish-global-allowed() { + local default_value="true" + + [[ "$(dpkg --print-architecture 2>/dev/null || true)" != "amd64" ]] && default_value="false" + + fn-plugin-property-get-default "builder-herokuish" "--global" "allowed" "$default_value" +} + +fn-builder-herokuish-allowed() { + declare APP="$1" + + fn-plugin-property-get-default "builder-herokuish" "$APP" "allowed" "" +} diff --git a/plugins/builder-herokuish/subcommands/default b/plugins/builder-herokuish/subcommands/default new file mode 100755 index 00000000000..09cdd8a82db --- /dev/null +++ b/plugins/builder-herokuish/subcommands/default @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +source "$PLUGIN_AVAILABLE_PATH/builder-herokuish/help-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +cmd-builder-herokuish-help "builder-herokuish:help" diff --git a/plugins/builder-herokuish/subcommands/report b/plugins/builder-herokuish/subcommands/report new file mode 100755 index 00000000000..d99bfa2fb11 --- /dev/null +++ b/plugins/builder-herokuish/subcommands/report @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +source "$PLUGIN_AVAILABLE_PATH/builder-herokuish/internal-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +cmd-builder-herokuish-report "$@" diff --git a/plugins/builder-herokuish/subcommands/set b/plugins/builder-herokuish/subcommands/set new file mode 100755 index 00000000000..777fbb07ffb --- /dev/null +++ b/plugins/builder-herokuish/subcommands/set @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +cmd-builder-herokuish-set() { + declare desc="set or clear a builder-herokuish property for an app" + declare cmd="builder-herokuish:set" + [[ "$1" == "$cmd" ]] && shift 1 + declare APP="$1" KEY="$2" VALUE="$3" + local VALID_KEYS=("allowed") + [[ "$APP" == "--global" ]] || verify_app_name "$APP" + + [[ -z "$KEY" ]] && dokku_log_fail "No key specified" + + if ! fn-in-array "$KEY" "${VALID_KEYS[@]}"; then + dokku_log_fail "Invalid key specified, valid keys include: allowed" + fi + + if [[ -n "$VALUE" ]]; then + dokku_log_info2_quiet "Setting ${KEY} to ${VALUE}" + fn-plugin-property-write "builder-herokuish" "$APP" "$KEY" "$VALUE" + else + dokku_log_info2_quiet "Unsetting ${KEY}" + fn-plugin-property-delete "builder-herokuish" "$APP" "$KEY" + fi +} + +cmd-builder-herokuish-set "$@" diff --git a/plugins/checks/internal-functions b/plugins/checks/internal-functions index dfe905b7372..ae9e21fe969 100755 --- a/plugins/checks/internal-functions +++ b/plugins/checks/internal-functions @@ -99,11 +99,11 @@ fn-checks-computed-wait-to-retire() { fn-checks-global-wait-to-retire() { declare APP="$1" - fn-plugin-property-get "checks" "--global" "wait-to-retire" "60" + fn-plugin-property-get-default "checks" "--global" "wait-to-retire" "60" } fn-checks-wait-to-retire() { declare APP="$1" - fn-plugin-property-get "checks" "$APP" "wait-to-retire" "" + fn-plugin-property-get-default "checks" "$APP" "wait-to-retire" "" } diff --git a/plugins/git/functions b/plugins/git/functions index 9543807793a..6d402d0db28 100755 --- a/plugins/git/functions +++ b/plugins/git/functions @@ -60,15 +60,17 @@ git_trigger_build() { BUILDER="$(plugn trigger builder-detect "$APP" "$TMP_WORK_DIR" | head -n1 || true)" if [[ -z "$BUILDER" ]]; then BUILDER="herokuish" - local ARCHITECTURE="$(dpkg --print-architecture 2>/dev/null || true)" - if [[ "$ARCHITECTURE" == "arm64" ]]; then - dokku_log_warn "Herokuish builder not supported on $ARCHITECTURE servers." - dokku_log_warn "Switching to pack builder." - BUILDER="pack" - elif [[ "$ARCHITECTURE" == "armhf" ]]; then - dokku_log_warn "Herokuish builder not supported on $ARCHITECTURE servers." - dokku_log_warn "Consider using a Dockerfile to build your app." - return 1 + if ! plugn trigger builder-herokuish-allowed "$APP" >/dev/null; then + local ARCHITECTURE="$(dpkg --print-architecture 2>/dev/null || true)" + if [[ "$ARCHITECTURE" == "arm64" ]]; then + dokku_log_warn "Herokuish builder not supported on $ARCHITECTURE servers." + dokku_log_warn "Switching to pack builder." + BUILDER="pack" + elif [[ "$ARCHITECTURE" == "armhf" ]]; then + dokku_log_warn "Herokuish builder not supported on $ARCHITECTURE servers." + dokku_log_warn "Consider using a Dockerfile to build your app." + return 1 + fi fi fi diff --git a/tests/unit/builder-herokuish.bats b/tests/unit/builder-herokuish.bats index fad1bf54254..356788e84a5 100644 --- a/tests/unit/builder-herokuish.bats +++ b/tests/unit/builder-herokuish.bats @@ -17,3 +17,74 @@ teardown() { assert_success assert_output_contains 'DOTENV_KEY=some_value' } + +@test "(builder-herokuish) builder-herokuish:set allowed" { + if [[ "$(dpkg --print-architecture 2>/dev/null || true)" == "amd64" ]]; then + skip "this test cannot be performed accurately on amd64 as it tests whether we can enable the plugin on armhf/arm64" + fi + + run /bin/bash -c "dokku builder-herokuish:set --global allowed" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku builder-herokuish:report $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app + echo "output: $output" + echo "status: $status" + assert_failure + + run create_app + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku builder-herokuish:set --global allowed true" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku builder-herokuish:report $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku builder-herokuish:set --global allowed" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku builder-herokuish:set $TEST_APP allowed true" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku builder-herokuish:report $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku ps:rebuild $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku builder-herokuish:set $TEST_APP allowed" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku ps:rebuild $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_failure +}