feat: add support for the Procfile release command

Closes #3136
This commit is contained in:
Jose Diaz-Gonzalez
2019-01-07 07:42:20 -05:00
parent 14a2699ac4
commit a5a66dd916
4 changed files with 77 additions and 21 deletions

View File

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

View File

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

View File

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

View File

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