feat: allow builders to be detected based on repository contents

Rather than hardcode two builders, allow builders to specify a `builder-detect` trigger. This trigger can be used to specify if the builder should or should not be used for an application. Each builder takes stdin and can decide if it wants to emit it or emit it's own image source type.

If the final value is empty, then Dokku will default to herokuish (and cnb once that is stable). In addition, a future change may allow users to manually specify a builder in the case they wish to override the choice selected by Dokku.

This change enables users to build custom builder plugins and have those plugins used for building an image asset. By way of example, an enterprising user could create a `builder-lambda` based on lambci, and then pair this with a scheduler plugin that updates a lambda function on AWS. Alternatively, a user might decide they wish to place their Dockerfile in a specific directory for their applications - such as an `_infrastructure` directory - and create a plugin to override how that is detected within Dokku.
This commit is contained in:
Jose Diaz-Gonzalez
2021-02-04 01:34:12 -05:00
parent a9821decca
commit 353438dbd3
9 changed files with 271 additions and 20 deletions

View File

@@ -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

View File

@@ -0,0 +1 @@
hook

View File

@@ -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 "$@"

View File

@@ -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 "$@"

View File

@@ -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 "$@"

View File

@@ -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
}

View File

@@ -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() {

View File

@@ -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() {

121
tests/unit/builder.bats Normal file
View File

@@ -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"
}