diff --git a/docs/development/plugin-triggers.md b/docs/development/plugin-triggers.md index 61d05c603..fa8505b9b 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -143,6 +143,24 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x # TODO ``` +### `builder-detect` + +- Description: Allows overriding the auto-detected `herokuish` builder in favor of a custom one. Dockerfile gets lowest builder precedence. +- Invoked by: `dokku deploy` +- Arguments: `$APP` `$SOURCECODE_WORK_DIR` +- Example: + +```shell +#!/usr/bin/env bash + +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +APP="$1"; SOURCECODE_WORK_DIR="$2" + +if [[ -f "$SOURCECODE_WORK_DIR/project.toml" ]]; then + echo -n "cnb" +fi +``` + ### `builder-create-dokku-image` - Description: Allows modification of the configured dokku-image diff --git a/plugins/20_events/builder-detect b/plugins/20_events/builder-detect new file mode 120000 index 000000000..5178a749f --- /dev/null +++ b/plugins/20_events/builder-detect @@ -0,0 +1 @@ +hook \ No newline at end of file diff --git a/plugins/builder-cnb/builder-detect b/plugins/builder-cnb/builder-detect new file mode 100755 index 000000000..65678e55d --- /dev/null +++ b/plugins/builder-cnb/builder-detect @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +source "$PLUGIN_AVAILABLE_PATH/config/functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +trigger-builder-cnb-builder-detect() { + declare desc="builder-cnb builder-detect plugin trigger" + declare trigger="builder-detect" + declare APP="$1" SOURCECODE_WORK_DIR="$2" + + if [[ -f "$SOURCECODE_WORK_DIR/project.toml" ]]; then + echo "cnb" + return + fi + + if [[ "$(config_get "$APP" DOKKU_CNB_EXPERIMENTAL || true)" == "1" ]]; then + echo "cnb" + return + fi +} + +trigger-builder-cnb-builder-detect "$@" diff --git a/plugins/builder-dockerfile/builder-detect b/plugins/builder-dockerfile/builder-detect new file mode 100755 index 000000000..1def88e9f --- /dev/null +++ b/plugins/builder-dockerfile/builder-detect @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +source "$PLUGIN_AVAILABLE_PATH/config/functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +trigger-builder-dockerfile-builder-detect() { + declare desc="builder-dockerfile builder-detect plugin trigger" + declare trigger="builder-detect" + declare APP="$1" SOURCECODE_WORK_DIR="$2" + + # buildpacks always win against dockerfile detection + # that includes cnb + if [[ -f "$SOURCECODE_WORK_DIR/project.toml" ]]; then + return + fi + + # buildpacks always win against dockerfile detection + # that includes cnb + if [[ "$(config_get "$APP" DOKKU_CNB_EXPERIMENTAL || true)" == "1" ]]; then + echo "cnb" + return + fi + + if [[ -f "$SOURCECODE_WORK_DIR/Dockerfile" ]]; then + echo "dockerfile" + return + fi +} + +trigger-builder-dockerfile-builder-detect "$@" diff --git a/plugins/builder-herokuish/builder-detect b/plugins/builder-herokuish/builder-detect new file mode 100755 index 000000000..7ee618691 --- /dev/null +++ b/plugins/builder-herokuish/builder-detect @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +source "$PLUGIN_AVAILABLE_PATH/builder-herokuish/functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +trigger-builder-herokuish-builder-detect() { + declare desc="builder-herokuish builder-detect plugin trigger" + declare trigger="builder-detect" + declare APP="$1" SOURCECODE_WORK_DIR="$2" + + if fn-has-buildpacks-file "$SOURCECODE_WORK_DIR"; then + echo "herokuish" + return + fi + + if fn-has-buildpack-dotenv "$SOURCECODE_WORK_DIR"; then + echo "herokuish" + return + fi + + if fn-has-buildpack-env "$APP"; then + echo "herokuish" + return + fi +} + +trigger-builder-herokuish-builder-detect "$@" diff --git a/plugins/builder-herokuish/functions b/plugins/builder-herokuish/functions new file mode 100755 index 000000000..8452214a1 --- /dev/null +++ b/plugins/builder-herokuish/functions @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +source "$PLUGIN_AVAILABLE_PATH/config/functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +fn-has-buildpacks-file() { + declare SOURCECODE_WORK_DIR="$1" + + if [[ -f "$SOURCECODE_WORK_DIR/.buildpacks" ]]; then + return 0 + fi + + return 1 +} + +fn-has-buildpack-dotenv() { + declare SOURCECODE_WORK_DIR="$1" + + if [[ ! -f "$SOURCECODE_WORK_DIR/.env" ]]; then + return 1 + fi + + if grep -q BUILDPACK_URL "$SOURCECODE_WORK_DIR/.env"; then + return 0 + fi + + return 1 +} + +fn-has-buildpack-env() { + declare APP="$1" + + if [[ -n $(config_get "$APP" BUILDPACK_URL || true) ]]; then + return 0 + fi + + return 1 +} diff --git a/plugins/git/functions b/plugins/git/functions index 114c28971..a67b795e1 100755 --- a/plugins/git/functions +++ b/plugins/git/functions @@ -49,18 +49,15 @@ git_build_app_repo() { git_trigger_build() { declare desc="triggers the actual build process for a given app within a directory at a particular revision" declare APP="$1" TMP_WORK_DIR="$2" REV="$3" + local BUILDER plugn trigger post-extract "$APP" "$TMP_WORK_DIR" "$REV" - if [[ -f Dockerfile ]] && [[ "$( - [[ -f .env ]] && grep -q BUILDPACK_URL .env - echo $? - )" != "0" ]] && [[ ! -f ".buildpacks" ]] && [[ -z $(config_get "$APP" BUILDPACK_URL || true) ]]; then - plugn trigger pre-receive-app "$APP" "dockerfile" "$TMP_WORK_DIR" "$REV" - dokku_receive "$APP" "dockerfile" "$TMP_WORK_DIR" - else - plugn trigger pre-receive-app "$APP" "herokuish" "$TMP_WORK_DIR" "$REV" - dokku_receive "$APP" "herokuish" "$TMP_WORK_DIR" - fi + + BUILDER="$(plugn trigger builder-detect "$APP" "$TMP_WORK_DIR" | tail -n1 || true)" + [[ -z "$BUILDER" ]] && BUILDER="herokuish" + + plugn trigger pre-receive-app "$APP" "$BUILDER" "$TMP_WORK_DIR" "$REV" + dokku_receive "$APP" "$BUILDER" "$TMP_WORK_DIR" } git_deploy_branch() { diff --git a/plugins/tar/functions b/plugins/tar/functions index 9a8302c99..c95d29303 100755 --- a/plugins/tar/functions +++ b/plugins/tar/functions @@ -41,18 +41,15 @@ tar_build() { tar_trigger_build() { declare desc="triggers the actual build process for a given app within a directory at a particular revision" declare APP="$1" TMP_WORK_DIR="$2" REV="$3" + local BUILDER plugn trigger post-extract "$APP" "$TMP_WORK_DIR" "$REV" - if [[ -f Dockerfile ]] && [[ "$( - [[ -f .env ]] && grep -q BUILDPACK_URL .env - echo $? - )" != "0" ]] && [[ ! -f ".buildpacks" ]] && [[ -z $(config_get "$APP" BUILDPACK_URL || true) ]]; then - plugn trigger pre-receive-app "$APP" "dockerfile" "$TMP_WORK_DIR" "$REV" - dokku_receive "$APP" "dockerfile" "$TMP_WORK_DIR" - else - plugn trigger pre-receive-app "$APP" "herokuish" "$TMP_WORK_DIR" "$REV" - dokku_receive "$APP" "herokuish" "$TMP_WORK_DIR" - fi + + BUILDER="$(plugn trigger builder-detect "$APP" "$TMP_WORK_DIR" | tail -n1 || true)" + [[ -z "$BUILDER" ]] && BUILDER="herokuish" + + plugn trigger pre-receive-app "$APP" "$BUILDER" "$TMP_WORK_DIR" "$REV" + dokku_receive "$APP" "$BUILDER" "$TMP_WORK_DIR" } cmd-tar-in() { diff --git a/tests/unit/builder.bats b/tests/unit/builder.bats new file mode 100644 index 000000000..d6f620c4e --- /dev/null +++ b/tests/unit/builder.bats @@ -0,0 +1,121 @@ +#!/usr/bin/env bats + +load test_helper + +setup() { + create_app +} + +teardown() { + destroy_app +} + +@test "(builder) builder-detect [cnb]" { + local TMP=$(mktemp -d "/tmp/dokku.me.XXXXX") + trap 'popd &>/dev/null || true; rm -rf "$TMP"' INT TERM + + # test project.toml + run touch "$TMP/project.toml" + echo "output: $output" + echo "status: $status" + assert_success + + chown -R dokku:dokku "$TMP" + run /bin/bash -c "dokku plugin:trigger builder-detect $TEST_APP $TMP | tail -n1" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "cnb" + + sudo rm -rf $TMP/* + echo "output: $output" + echo "status: $status" + assert_success + + # test DOKKU_CNB_EXPERIMENTAL env var + run /bin/bash -c "dokku config:set $TEST_APP DOKKU_CNB_EXPERIMENTAL=1" + echo "output: $output" + echo "status: $status" + assert_success + + chown -R dokku:dokku "$TMP" + run /bin/bash -c "dokku plugin:trigger builder-detect $TEST_APP $TMP | tail -n1" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "cnb" +} + +@test "(builder) builder-detect [dockerfile]" { + local TMP=$(mktemp -d "/tmp/dokku.me.XXXXX") + trap 'popd &>/dev/null || true; rm -rf "$TMP"' INT TERM + + run touch "$TMP/Dockerfile" + echo "output: $output" + echo "status: $status" + assert_success + + chown -R dokku:dokku "$TMP" + run /bin/bash -c "dokku plugin:trigger builder-detect $TEST_APP $TMP | tail -n1" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "dockerfile" +} + +@test "(builder) builder-detect [herokuish]" { + local TMP=$(mktemp -d "/tmp/dokku.me.XXXXX") + trap 'popd &>/dev/null || true; rm -rf "$TMP"' INT TERM + + touch "$TMP/Dockerfile" + + # test buildpacks + chown -R dokku:dokku "$TMP" + run /bin/bash -c "touch $TMP/.buildpacks" + echo "output: $output" + echo "status: $status" + assert_success + + chown -R dokku:dokku "$TMP" + run /bin/bash -c "dokku plugin:trigger builder-detect $TEST_APP $TMP | tail -n1" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "herokuish" + + sudo rm -rf $TMP/* + echo "output: $output" + echo "status: $status" + assert_success + + # test .env + run /bin/bash -c "echo BUILDPACK_URL=null > $TMP/.env" + echo "output: $output" + echo "status: $status" + assert_success + + chown -R dokku:dokku "$TMP" + run /bin/bash -c "dokku plugin:trigger builder-detect $TEST_APP $TMP | tail -n1" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "herokuish" + + sudo rm -rf $TMP/* + echo "output: $output" + echo "status: $status" + assert_success + + # test BUILDPACK_URL env var + run /bin/bash -c "dokku config:set $TEST_APP BUILDPACK_URL=null" + echo "output: $output" + echo "status: $status" + assert_success + + chown -R dokku:dokku "$TMP" + run /bin/bash -c "dokku plugin:trigger builder-detect $TEST_APP $TMP | tail -n1" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "herokuish" +}