From 952db832d991a7e8d0f7763c334d80995fb639f2 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 7 Jan 2019 07:02:07 -0500 Subject: [PATCH 1/4] refactor: move app.json script execution into it's own plugin Rather than have this live in 00_dokku-standard, move it out so that it may start to support various other features around the app.json manifest. --- .../internal-functions} | 0 plugins/app-json/plugin.toml | 4 ++ .../post-deploy | 10 ++--- .../pre-deploy | 10 ++--- tests/unit/30_core_1.bats | 21 ---------- tests/unit/40_app-json.bats | 38 +++++++++++++++++++ 6 files changed, 52 insertions(+), 31 deletions(-) rename plugins/{00_dokku-standard/exec-app-json-scripts => app-json/internal-functions} (100%) create mode 100644 plugins/app-json/plugin.toml rename plugins/{00_dokku-standard => app-json}/post-deploy (61%) rename plugins/{00_dokku-standard => app-json}/pre-deploy (61%) create mode 100644 tests/unit/40_app-json.bats diff --git a/plugins/00_dokku-standard/exec-app-json-scripts b/plugins/app-json/internal-functions similarity index 100% rename from plugins/00_dokku-standard/exec-app-json-scripts rename to plugins/app-json/internal-functions diff --git a/plugins/app-json/plugin.toml b/plugins/app-json/plugin.toml new file mode 100644 index 000000000..9069903ae --- /dev/null +++ b/plugins/app-json/plugin.toml @@ -0,0 +1,4 @@ +[plugin] +description = "dokku core app-json plugin" +version = "0.13.4" +[plugin.config] diff --git a/plugins/00_dokku-standard/post-deploy b/plugins/app-json/post-deploy similarity index 61% rename from plugins/00_dokku-standard/post-deploy rename to plugins/app-json/post-deploy index 3cc3e2fba..2b78e2ee4 100755 --- a/plugins/00_dokku-standard/post-deploy +++ b/plugins/app-json/post-deploy @@ -2,11 +2,11 @@ set -eo pipefail [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" -source "$PLUGIN_CORE_AVAILABLE_PATH/00_dokku-standard/exec-app-json-scripts" +source "$PLUGIN_CORE_AVAILABLE_PATH/app-json/internal-functions" -exec_app_json_scripts() { - declare desc="core app.json scripts execution" - local trigger="post-deploy app_json_scripts" +app_json_post_deploy() { + declare desc="app-json scripts execution" + local trigger="app_json_post_deploy" local APP="$1" local IMAGE_TAG="$4" local PHASE_SCRIPT_KEY="postdeploy" @@ -15,4 +15,4 @@ exec_app_json_scripts() { execute_script "$APP" "$IMAGE_TAG" "$PHASE_SCRIPT_KEY" } -exec_app_json_scripts "$@" +app_json_post_deploy "$@" diff --git a/plugins/00_dokku-standard/pre-deploy b/plugins/app-json/pre-deploy similarity index 61% rename from plugins/00_dokku-standard/pre-deploy rename to plugins/app-json/pre-deploy index 4c9d369af..43d58b895 100755 --- a/plugins/00_dokku-standard/pre-deploy +++ b/plugins/app-json/pre-deploy @@ -2,11 +2,11 @@ set -eo pipefail [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" -source "$PLUGIN_CORE_AVAILABLE_PATH/00_dokku-standard/exec-app-json-scripts" +source "$PLUGIN_CORE_AVAILABLE_PATH/app-json/internal-functions" -exec_app_json_scripts() { - declare desc="core app.json scripts execution" - local trigger="pre-deploy app_json_scripts" +app_json_pre_deploy() { + declare desc="app-json scripts execution" + local trigger="app_json_pre_deploy" local APP="$1" local IMAGE_TAG="$2" local PHASE_SCRIPT_KEY="predeploy" @@ -15,4 +15,4 @@ exec_app_json_scripts() { execute_script "$APP" "$IMAGE_TAG" "$PHASE_SCRIPT_KEY" } -exec_app_json_scripts "$@" +app_json_pre_deploy "$@" diff --git a/tests/unit/30_core_1.bats b/tests/unit/30_core_1.bats index ef72aecdd..edb22b6ce 100644 --- a/tests/unit/30_core_1.bats +++ b/tests/unit/30_core_1.bats @@ -180,24 +180,3 @@ EOF echo "status: $status" assert_output "3001/udp 3000/tcp 3003" } - -@test "(core) app.json scripts" { - deploy_app - - run /bin/bash -c "dokku run $TEST_APP ls /app/prebuild.test" - echo "output: $output" - echo "status: $status" - assert_failure - - run /bin/bash -c "dokku run $TEST_APP ls /app/predeploy.test" - echo "output: $output" - echo "status: $status" - assert_success - - CID=$(docker ps -a -q -f "ancestor=dokku/${TEST_APP}" -f "label=dokku_phase_script=postdeploy") - IMAGE_ID=$(docker commit $CID dokku-test/${TEST_APP}) - run /bin/bash -c "docker run -ti $IMAGE_ID ls /app/postdeploy.test" - echo "output: $output" - echo "status: $status" - assert_success -} diff --git a/tests/unit/40_app-json.bats b/tests/unit/40_app-json.bats new file mode 100644 index 000000000..b540daabb --- /dev/null +++ b/tests/unit/40_app-json.bats @@ -0,0 +1,38 @@ +#!/usr/bin/env bats + +load test_helper + +setup() { + global_setup + create_app + DOCKERFILE="$BATS_TMPDIR/Dockerfile" +} + +teardown() { + rm -rf /home/dokku/$TEST_APP/tls + destroy_app + dokku config:unset --global DOKKU_RM_CONTAINER + rm -f "$DOCKERFILE" + global_teardown +} + +@test "(app-json) app.json scripts" { + deploy_app + + run /bin/bash -c "dokku run $TEST_APP ls /app/prebuild.test" + echo "output: $output" + echo "status: $status" + assert_failure + + run /bin/bash -c "dokku run $TEST_APP ls /app/predeploy.test" + echo "output: $output" + echo "status: $status" + assert_success + + CID=$(docker ps -a -q -f "ancestor=dokku/${TEST_APP}" -f "label=dokku_phase_script=postdeploy") + IMAGE_ID=$(docker commit $CID dokku-test/${TEST_APP}) + run /bin/bash -c "docker run -ti $IMAGE_ID ls /app/postdeploy.test" + echo "output: $output" + echo "status: $status" + assert_success +} From dfefdab67ca42be5de6af71e6940bf30c0e92f32 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 7 Jan 2019 07:04:02 -0500 Subject: [PATCH 2/4] refactor: simplify script execution The previous method had a few too many nested if statements, making it difficult to track when and where the function stopped executing. --- plugins/app-json/internal-functions | 145 ++++++++++++++-------------- 1 file changed, 75 insertions(+), 70 deletions(-) diff --git a/plugins/app-json/internal-functions b/plugins/app-json/internal-functions index c38ed2828..1a16d4496 100755 --- a/plugins/app-json/internal-functions +++ b/plugins/app-json/internal-functions @@ -32,75 +32,80 @@ execute_script() { IMAGE=$(get_deploying_app_image_name "$APP" "$IMAGE_TAG") local SCRIPT_CMD=$(get_phase_script "$IMAGE" "$PHASE_SCRIPT_KEY" 2>/dev/null) - if [[ -n "$SCRIPT_CMD" ]]; then - dokku_log_info1 "Running '$SCRIPT_CMD' in app container" - local COMMAND - COMMAND="set -eo pipefail; [[ \$DOKKU_TRACE ]] && set -x ; " - COMMAND+=" if [[ -d '/app' ]]; then " - COMMAND+=" export HOME=/app ; " - COMMAND+=" cd \$HOME ; " - COMMAND+=" fi ; " - COMMAND+=" if [[ -d '/app/.profile.d' ]]; then " - COMMAND+=" for file in /app/.profile.d/*; do source \$file; done ; " - COMMAND+=" fi ; " - COMMAND+=" if [[ -d '/cache' ]]; then " - COMMAND+=" echo restoring installation cache... ; " - COMMAND+=" rm -rf /tmp/cache ; " - COMMAND+=" ln -sf /cache /tmp/cache ; " - COMMAND+=" fi ; " - - if [[ "$SCRIPT_CMD" == /* ]]; then - local SCRIPT_BIN="$(echo "$SCRIPT_CMD" | cut -d' ' -f1)" - COMMAND+=" if [[ ! -x \"$SCRIPT_BIN\" ]]; then " - COMMAND+=" echo specified binary is not executable ; " - COMMAND+=" exit 1 ; " - COMMAND+=" fi " - fi - - COMMAND+=" $SCRIPT_CMD || exit 1;" - COMMAND+=" if [[ -d '/cache' ]]; then " - COMMAND+=" echo removing installation cache... ; " - COMMAND+=" rm -f /tmp/cache ; " - COMMAND+=" fi " - - local CACHE_DIR="$DOKKU_ROOT/$APP/cache" - local CACHE_HOST_DIR="$DOKKU_HOST_ROOT/$APP/cache" - [[ -d $CACHE_DIR ]] || mkdir -p "$CACHE_DIR" - - local DOCKER_ARGS=$(: | plugn trigger docker-args-deploy "$APP" "$IMAGE_TAG") - # strip --restart args from DOCKER_ARGS - local DOCKER_ARGS=$(sed -e "s/--restart=[[:graph:]]\+[[:blank:]]\?//g" <<<"$DOCKER_ARGS") - - # eval args as array to respect escapes - declare -a ARG_ARRAY - eval "ARG_ARRAY=($DOCKER_ARGS)" - - 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" - - id=$(docker run "$DOKKU_GLOBAL_RUN_ARGS" -e DOKKU_TRACE="$DOKKU_TRACE" --label=dokku_phase_script="${PHASE_SCRIPT_KEY}" -d -v "$CACHE_HOST_DIR:/cache" "${ARG_ARRAY[@]}" "$IMAGE" "$DOKKU_APP_SHELL" -c "$COMMAND") - if test "$(docker wait "$id")" -eq 0; then - dokku_container_log_verbose_quiet "$id" - if [[ "$PHASE_SCRIPT_KEY" != "postdeploy" ]]; then - if ! is_image_herokuish_based "$IMAGE"; then - local DOKKU_DOCKERFILE_ENTRYPOINT=$(config_get "$APP" DOKKU_DOCKERFILE_ENTRYPOINT) - local DOKKU_DOCKERFILE_CMD=$(config_get "$APP" DOKKU_DOCKERFILE_CMD) - [[ -z "$DOKKU_DOCKERFILE_ENTRYPOINT" ]] && DOKKU_DOCKERFILE_ENTRYPOINT="$(get_entrypoint_from_image "$IMAGE")" - [[ -z "$DOKKU_DOCKERFILE_CMD" ]] && DOKKU_DOCKERFILE_CMD="$(get_cmd_from_image "$IMAGE")" - - [[ -n "$DOKKU_DOCKERFILE_ENTRYPOINT" ]] && local DOCKER_COMMIT_ENTRYPOINT_CHANGE_ARG="--change='$DOKKU_DOCKERFILE_ENTRYPOINT'" - [[ -n "$DOKKU_DOCKERFILE_CMD" ]] && local DOCKER_COMMIT_CMD_CHANGE_ARG="--change='$DOKKU_DOCKERFILE_CMD'" - - local DOCKER_COMMIT_ARGS="$DOCKER_COMMIT_ENTRYPOINT_CHANGE_ARG $DOCKER_COMMIT_CMD_CHANGE_ARG" - fi - # shellcheck disable=SC2086 - eval docker commit $DOCKER_COMMIT_ARGS "$id" "$IMAGE" >/dev/null - fi - else - dokku_container_log_verbose_quiet "$id" - dokku_log_fail "execution of '$SCRIPT_CMD' failed!" - fi + if [[ -z "$SCRIPT_CMD" ]]; then + return fi + + dokku_log_info1 "Running '$SCRIPT_CMD' in app container" + local COMMAND + COMMAND="set -eo pipefail; [[ \$DOKKU_TRACE ]] && set -x ; " + COMMAND+=" if [[ -d '/app' ]]; then " + COMMAND+=" export HOME=/app ; " + COMMAND+=" cd \$HOME ; " + COMMAND+=" fi ; " + COMMAND+=" if [[ -d '/app/.profile.d' ]]; then " + COMMAND+=" for file in /app/.profile.d/*; do source \$file; done ; " + COMMAND+=" fi ; " + COMMAND+=" if [[ -d '/cache' ]]; then " + COMMAND+=" echo restoring installation cache... ; " + COMMAND+=" rm -rf /tmp/cache ; " + COMMAND+=" ln -sf /cache /tmp/cache ; " + COMMAND+=" fi ; " + + if [[ "$SCRIPT_CMD" == /* ]]; then + local SCRIPT_BIN="$(echo "$SCRIPT_CMD" | cut -d' ' -f1)" + COMMAND+=" if [[ ! -x \"$SCRIPT_BIN\" ]]; then " + COMMAND+=" echo specified binary is not executable ; " + COMMAND+=" exit 1 ; " + COMMAND+=" fi " + fi + + COMMAND+=" $SCRIPT_CMD || exit 1;" + COMMAND+=" if [[ -d '/cache' ]]; then " + COMMAND+=" echo removing installation cache... ; " + COMMAND+=" rm -f /tmp/cache ; " + COMMAND+=" fi " + + local CACHE_DIR="$DOKKU_ROOT/$APP/cache" + local CACHE_HOST_DIR="$DOKKU_HOST_ROOT/$APP/cache" + [[ -d $CACHE_DIR ]] || mkdir -p "$CACHE_DIR" + + local DOCKER_ARGS=$(: | plugn trigger docker-args-deploy "$APP" "$IMAGE_TAG") + # strip --restart args from DOCKER_ARGS + local DOCKER_ARGS=$(sed -e "s/--restart=[[:graph:]]\+[[:blank:]]\?//g" <<<"$DOCKER_ARGS") + + # eval args as array to respect escapes + declare -a ARG_ARRAY + eval "ARG_ARRAY=($DOCKER_ARGS)" + + 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" + + id=$(docker run "$DOKKU_GLOBAL_RUN_ARGS" -e DOKKU_TRACE="$DOKKU_TRACE" --label=dokku_phase_script="${PHASE_SCRIPT_KEY}" -d -v "$CACHE_HOST_DIR:/cache" "${ARG_ARRAY[@]}" "$IMAGE" "$DOKKU_APP_SHELL" -c "$COMMAND") + if test "$(docker wait "$id")" -ne 0; then + dokku_container_log_verbose_quiet "$id" + dokku_log_fail "execution of '$SCRIPT_CMD' failed!" + fi + + dokku_container_log_verbose_quiet "$id" + if [[ "$PHASE_SCRIPT_KEY" != "predeploy" ]]; then + return + fi + + if ! is_image_herokuish_based "$IMAGE"; then + local DOKKU_DOCKERFILE_ENTRYPOINT=$(config_get "$APP" DOKKU_DOCKERFILE_ENTRYPOINT) + local DOKKU_DOCKERFILE_CMD=$(config_get "$APP" DOKKU_DOCKERFILE_CMD) + [[ -z "$DOKKU_DOCKERFILE_ENTRYPOINT" ]] && DOKKU_DOCKERFILE_ENTRYPOINT="$(get_entrypoint_from_image "$IMAGE")" + [[ -z "$DOKKU_DOCKERFILE_CMD" ]] && DOKKU_DOCKERFILE_CMD="$(get_cmd_from_image "$IMAGE")" + + [[ -n "$DOKKU_DOCKERFILE_ENTRYPOINT" ]] && local DOCKER_COMMIT_ENTRYPOINT_CHANGE_ARG="--change='$DOKKU_DOCKERFILE_ENTRYPOINT'" + [[ -n "$DOKKU_DOCKERFILE_CMD" ]] && local DOCKER_COMMIT_CMD_CHANGE_ARG="--change='$DOKKU_DOCKERFILE_CMD'" + + local DOCKER_COMMIT_ARGS="$DOCKER_COMMIT_ENTRYPOINT_CHANGE_ARG $DOCKER_COMMIT_CMD_CHANGE_ARG" + fi + + # shellcheck disable=SC2086 + eval docker commit $DOCKER_COMMIT_ARGS "$id" "$IMAGE" >/dev/null } From 14a2699ac446a85b26f745b5e2a663071d53418d Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 7 Jan 2019 07:14:05 -0500 Subject: [PATCH 3/4] feat: make the app-json script execution quieter --- plugins/app-json/post-deploy | 1 - plugins/app-json/pre-deploy | 1 - 2 files changed, 2 deletions(-) diff --git a/plugins/app-json/post-deploy b/plugins/app-json/post-deploy index 2b78e2ee4..f12a1ec2e 100755 --- a/plugins/app-json/post-deploy +++ b/plugins/app-json/post-deploy @@ -11,7 +11,6 @@ app_json_post_deploy() { local IMAGE_TAG="$4" local PHASE_SCRIPT_KEY="postdeploy" - dokku_log_info1 "Attempting to run scripts.dokku.$PHASE_SCRIPT_KEY from app.json (if defined)" execute_script "$APP" "$IMAGE_TAG" "$PHASE_SCRIPT_KEY" } diff --git a/plugins/app-json/pre-deploy b/plugins/app-json/pre-deploy index 43d58b895..eb8b3e620 100755 --- a/plugins/app-json/pre-deploy +++ b/plugins/app-json/pre-deploy @@ -11,7 +11,6 @@ app_json_pre_deploy() { local IMAGE_TAG="$2" local PHASE_SCRIPT_KEY="predeploy" - dokku_log_info1 "Attempting to run scripts.dokku.$PHASE_SCRIPT_KEY from app.json (if defined)" execute_script "$APP" "$IMAGE_TAG" "$PHASE_SCRIPT_KEY" } From a5a66dd9169dd3858a72512b40d181f1a475af30 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 7 Jan 2019 07:42:20 -0500 Subject: [PATCH 4/4] feat: add support for the Procfile release command Closes #3136 --- docs/advanced-usage/deployment-tasks.md | 63 +++++++++++++++++++++---- plugins/app-json/internal-functions | 33 ++++++++----- plugins/app-json/pre-deploy | 1 + tests/apps/nodejs-express/Procfile | 1 + 4 files changed, 77 insertions(+), 21 deletions(-) diff --git a/docs/advanced-usage/deployment-tasks.md b/docs/advanced-usage/deployment-tasks.md index 93e9a4d8f..bfe38490e 100644 --- a/docs/advanced-usage/deployment-tasks.md +++ b/docs/advanced-usage/deployment-tasks.md @@ -2,25 +2,52 @@ > New as of 0.5.0 -Sometimes you need to run a command on at deployment time, but before an app is completely deployed. -Common use cases include: +Sometimes you need to run a command on at deployment time, but before an app is completely deployed. Common use cases include: * Checking a database is initialized * Running database migrations * Any commands required to set up the server (e.g. something like a Django `collectstatic`) -## `app.json` and `scripts.dokku` +To support this, Dokku provides support for a special `release` command within your app's `Procfile`, as well as a special `scripts.dokku` key inside of your app's `app.json` file. Be aware that all commands are run within the context of the built docker image - no commands affect the host unless there are volume mounts attached to your app. -Dokku accomplishes this by using an `app.json` file. The format in use is similar to format of Heroku's [app.json](https://devcenter.heroku.com/articles/app-json-schema). -However, Dokku currently only supports the nodes `scripts.dokku.predeploy` and `scripts.dokku.postdeploy`. -For buildpack apps, simply place an `app.json` file in the root of your repository. -For dockerfile apps, place `app.json` in the configured `WORKDIR` directory; otherwise Dokku defaults to the buildpack app behavior of looking in `/app`. +Each "phase" has different expectations and limitations: -> NOTE: postdeploy changes are *NOT* committed to the app image. +- `app.json`: `scripts.dokku.predeploy` + - When to use: This should be used if your app does not support arbitrary build commands and you need to make changes to the built image. + - Are changes committed to the image at this phase: Yes + - Example use-cases + - Bundling assets in a slightly different way + - Installing a custom package from source or copying a binary into place +- `app.json`: `scripts.dokku.postdeploy` + - When to use: This should be used in conjunction with external systems to signal the completion of your deploy. + - Are changes committed to the image at this phase: No + - Example use-cases + - Notifying slack that your app is deployed + - Coordinating traffic routing with a central load balancer +- `Procfile`: `release` + - When to use: This should be used in conjunction with external systems to signal the completion of your app image build. + - Are changes committed to the image at this phase: No + - Example use-cases + - Sending CSS, JS, and other assets from your app’s slug to a CDN or S3 bucket + - Priming or invalidating cache stores + - Running database migrations -### Example app.json +Please keep the above in mind when utilizing deployment tasks. -> NOTE: Only the `scripts.dokku.predeploy` and `scripts.dokku.postdeploy` tasks are supported by Dokku at this time. All other fields will be ignored and can be omitted. +> To execute commands on the host during a release phase, see the [plugin creation documentation](/docs/development/plugin-creation) docs for more information on building your own custom plugin. + +## `app.json` deployment tasks + +Dokku provides limited support for the `app.json` manifest from Heroku (documentation available [here](https://devcenter.heroku.com/articles/app-json-schema)). The keys available for use with Deployment Tasks are: + +- `scripts.dokku.predeploy`: This is run _after_ an app's docker image is built, but _before_ any containers are scheduled. Changes made to your image are committed at this phase. +- `scripts.dokku.postdeploy`: This is run _after_ an app's containers are scheduled. Changes made to your image are *not* committed at this phase. + +For buildpack-based deployments, the location of the `app.json` file should be at the root of your repository. Dockefile-based app deploys should have the `app.json` in the configured `WORKDIR` directory; otherwise Dokku defaults to the buildpack app behavior of looking in `/app`. + +> Warning: Any failed `app.json` deployment task will fail the deploy. In the case of either phase, a failure will not affect any running containers. + +The following is an example `app.json` file. Please note that only the `scripts.dokku.predeploy` and `scripts.dokku.postdeploy` tasks are supported by Dokku at this time. All other fields will be ignored and can be omitted. ```json { @@ -32,3 +59,19 @@ For dockerfile apps, place `app.json` in the configured `WORKDIR` directory; oth } } ``` + +## Procfile Release command + +> New as of 0.14.0 + +The `Procfile` also supports a special `release` command which acts in a similar way to the [Heroku Release Phase](https://devcenter.heroku.com/articles/release-phase). This command is executed _after_ an app's docker image is built, but _before_ any containers are scheduled. This is also run _after_ any command executed by `scripts.dokku.predeploy`. + +To use the `release` command, simply add a `release` stanza to your Procfile. + +```Procfile +release: curl https://some.external.api.service.com/deployment?state=built +``` + +Unlike the `scripts.dokku.predeploy` command, changes made during by the `release` command are *not* persisted to disk. + +> Warning: scaling the release command up will likely result in unspecified issues within your deployment, and is highly discouraged. diff --git a/plugins/app-json/internal-functions b/plugins/app-json/internal-functions index 1a16d4496..4a2d5debc 100755 --- a/plugins/app-json/internal-functions +++ b/plugins/app-json/internal-functions @@ -3,35 +3,46 @@ set -eo pipefail [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" source "$PLUGIN_AVAILABLE_PATH/config/functions" +source "$PLUGIN_AVAILABLE_PATH/ps/functions" get_phase_script() { declare desc="extracts app.json from app image and returns the appropriate json key/value" - local IMAGE="$1" - local PHASE_SCRIPT_KEY="$2" + declare IMAGE_TAG="$1" PHASE_SCRIPT_KEY="$2" local GET_PHASE_SCRIPT_TMP_WORK_DIR=$(mktemp -d "/tmp/dokku_get_phase_script.XXXX") local APP_JSON_FILE="$GET_PHASE_SCRIPT_TMP_WORK_DIR/app.json" trap 'rm -rf "$GET_PHASE_SCRIPT_TMP_WORK_DIR" > /dev/null' RETURN INT TERM copy_from_image "$IMAGE" "app.json" "$GET_PHASE_SCRIPT_TMP_WORK_DIR" 2>/dev/null || true - if [[ -f "$APP_JSON_FILE" ]]; then - local VALUE=$(get_json_value "scripts.dokku.${PHASE_SCRIPT_KEY}" <"$APP_JSON_FILE") - else + if [[ ! -f "$APP_JSON_FILE" ]]; then return 0 fi - echo "$VALUE" + get_json_value "scripts.dokku.${PHASE_SCRIPT_KEY}" <"$APP_JSON_FILE" +} + +get_release_cmd() { + declare desc="extracts the release command from a given app's procfile" + declare APP="$1" IMAGE_TAG="$2" + + extract_procfile "$APP" "$IMAGE_TAG" >/dev/null + trap 'remove_procfile $APP' RETURN INT TERM EXIT + + get_cmd_from_procfile "$APP" "release" "5000" || true } execute_script() { declare desc="executes appropriate phase script key from app.json" - local APP="$1" - local IMAGE_TAG="$2" - local PHASE_SCRIPT_KEY="$3" - local IMAGE id + declare APP="$1" IMAGE_TAG="$2" PHASE_SCRIPT_KEY="$3" + local IMAGE id SCRIPT_CMD IMAGE=$(get_deploying_app_image_name "$APP" "$IMAGE_TAG") - local SCRIPT_CMD=$(get_phase_script "$IMAGE" "$PHASE_SCRIPT_KEY" 2>/dev/null) + if [[ "$PHASE_SCRIPT_KEY" == "release" ]]; then + SCRIPT_CMD=$(get_release_cmd "$APP" "$IMAGE" 2>/dev/null) + else + SCRIPT_CMD=$(get_phase_script "$IMAGE" "$PHASE_SCRIPT_KEY" 2>/dev/null) + fi + if [[ -z "$SCRIPT_CMD" ]]; then return fi diff --git a/plugins/app-json/pre-deploy b/plugins/app-json/pre-deploy index eb8b3e620..f1598a7ab 100755 --- a/plugins/app-json/pre-deploy +++ b/plugins/app-json/pre-deploy @@ -12,6 +12,7 @@ app_json_pre_deploy() { local PHASE_SCRIPT_KEY="predeploy" execute_script "$APP" "$IMAGE_TAG" "$PHASE_SCRIPT_KEY" + execute_script "$APP" "$IMAGE_TAG" "release" } app_json_pre_deploy "$@" diff --git a/tests/apps/nodejs-express/Procfile b/tests/apps/nodejs-express/Procfile index e9bf88240..f38a198e3 100644 --- a/tests/apps/nodejs-express/Procfile +++ b/tests/apps/nodejs-express/Procfile @@ -7,6 +7,7 @@ cron: node worker.js web: node web.js # testing inline comment worker: node worker.js custom: echo -n +release: touch /app/release.test # Old version with separate processes (use this if you have issues with the threaded version)