Merge pull request #1836 from dokku/1824_mh-app-json
support scripts.dokku. in app.json
This commit is contained in:
55
docs/deployment/deployment-tasks.md
Normal file
55
docs/deployment/deployment-tasks.md
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# Deployment Tasks
|
||||||
|
|
||||||
|
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 initialised
|
||||||
|
* Running database migrations
|
||||||
|
* Any commands required to set up the server (e.g. something like a Django `collectstatic`)
|
||||||
|
|
||||||
|
## `app.json` and `scripts.dokku`
|
||||||
|
|
||||||
|
Dokku accomplishes this by using an `app.json` file. We (mostly) use the same format as 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`.
|
||||||
|
Simply place an `app.json` file in the root of your repository.
|
||||||
|
NOTE: `app.json` is only supported in buildpack-deployed apps and postdeploy changes are not committed to the app image.
|
||||||
|
|
||||||
|
### Example app.json
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"name": "barebones nodejs",
|
||||||
|
"description": "A barebones Node.js app using Express 4.",
|
||||||
|
"keywords": [
|
||||||
|
"nodejs",
|
||||||
|
"express"
|
||||||
|
],
|
||||||
|
"repository": "https://github.com/michaelshobbs/node-js-sample",
|
||||||
|
"scripts": {
|
||||||
|
"dokku": {
|
||||||
|
"predeploy": "touch /app/predeploy.test",
|
||||||
|
"postdeploy": "touch /app/postdeploy.test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"SECRET_TOKEN": {
|
||||||
|
"description": "A secret key for verifying the integrity of signed cookies.",
|
||||||
|
"value": "secret"
|
||||||
|
},
|
||||||
|
"WEB_CONCURRENCY": {
|
||||||
|
"description": "The number of processes to run.",
|
||||||
|
"generator": "echo 5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"image": "gliderlabs/herokuish",
|
||||||
|
"addons": [
|
||||||
|
"dokku-postgres",
|
||||||
|
"dokku-redis"
|
||||||
|
],
|
||||||
|
"buildpacks": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/heroku/heroku-buildpack-nodejs"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -405,7 +405,7 @@ dokku postgres:link $POSTGRES $APP
|
|||||||
|
|
||||||
- Description: Allows running of commands after a deploy has completed. Dokku core currently uses this to switch traffic on nginx.
|
- Description: Allows running of commands after a deploy has completed. Dokku core currently uses this to switch traffic on nginx.
|
||||||
- Invoked by: `dokku deploy`
|
- Invoked by: `dokku deploy`
|
||||||
- Arguments: `$APP $INTERNAL_PORT $INTERNAL_IP_ADDRESS`
|
- Arguments: `$APP $INTERNAL_PORT $INTERNAL_IP_ADDRESS $IMAGE_TAG`
|
||||||
- Example:
|
- Example:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
|||||||
2
dokku
2
dokku
@@ -207,7 +207,7 @@ case "$1" in
|
|||||||
done < "$DOKKU_SCALE_FILE"
|
done < "$DOKKU_SCALE_FILE"
|
||||||
|
|
||||||
dokku_log_info1 "Running post-deploy"
|
dokku_log_info1 "Running post-deploy"
|
||||||
plugn trigger post-deploy $APP $port $ipaddr
|
plugn trigger post-deploy $APP $port $ipaddr $IMAGE_TAG
|
||||||
|
|
||||||
# kill the old container
|
# kill the old container
|
||||||
if [[ -n "$oldids" ]]; then
|
if [[ -n "$oldids" ]]; then
|
||||||
|
|||||||
59
plugins/00_dokku-standard/exec-app-json-scripts
Executable file
59
plugins/00_dokku-standard/exec-app-json-scripts
Executable file
@@ -0,0 +1,59 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
|
||||||
|
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
|
||||||
|
APP="$1"
|
||||||
|
|
||||||
|
case "$0" in
|
||||||
|
*pre-deploy)
|
||||||
|
IMAGE_TAG="$2"
|
||||||
|
IMAGE=$(get_app_image_name $APP $IMAGE_TAG)
|
||||||
|
PHASE_SCRIPT_KEY="predeploy"
|
||||||
|
;;
|
||||||
|
*post-deploy)
|
||||||
|
IMAGE_TAG="$4"
|
||||||
|
IMAGE=$(get_app_image_name $APP $IMAGE_TAG)
|
||||||
|
PHASE_SCRIPT_KEY="postdeploy"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
get_phase_script() {
|
||||||
|
local PHASE_SCRIPT_KEY="$1"
|
||||||
|
local TMP_WORK_DIR=$(mktemp -d)
|
||||||
|
local APP_JSON_FILE="$TMP_WORK_DIR/app.json"
|
||||||
|
trap 'rm -rf "$TMP_WORK_DIR" > /dev/null' RETURN
|
||||||
|
|
||||||
|
copy_from_image "$IMAGE" "/app/app.json" "$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
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$VALUE"
|
||||||
|
}
|
||||||
|
|
||||||
|
execute_script() {
|
||||||
|
local SCRIPT_CMD=$(get_phase_script "$PHASE_SCRIPT_KEY")
|
||||||
|
if [[ -n "$SCRIPT_CMD" ]];then
|
||||||
|
dokku_log_info1 "Running '$SCRIPT_CMD' in app container"
|
||||||
|
local COMMAND="cd /app ; "
|
||||||
|
local COMMAND+=" for file in /app/.profile.d/*; do source \$file; done ; "
|
||||||
|
local COMMAND+=" echo restoring installation cache... ; "
|
||||||
|
local COMMAND+=" cp -rfp /cache /tmp/ || true ; "
|
||||||
|
local COMMAND+=" $SCRIPT_CMD ; "
|
||||||
|
local COMMAND+=" echo removing installation cache... ; "
|
||||||
|
local COMMAND+=" rm -rf /tmp/cache || true "
|
||||||
|
local DOCKER_ARGS=$(: | plugn trigger docker-args-deploy $APP)
|
||||||
|
local CACHE_DIR="$DOKKU_ROOT/$APP/cache"
|
||||||
|
id=$(docker run $DOKKU_GLOBAL_RUN_ARGS --label=dokku_phase_script=${PHASE_SCRIPT_KEY} -d -v $CACHE_DIR:/cache $DOCKER_ARGS $IMAGE /bin/bash -c "$COMMAND")
|
||||||
|
test "$(docker wait $id)" -eq 0
|
||||||
|
dokku_container_log_verbose_quiet $id
|
||||||
|
if [[ "$PHASE_SCRIPT_KEY" != "postdeploy" ]];then
|
||||||
|
docker commit "$id" "$IMAGE" > /dev/null
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
dokku_log_info1 "Attempting to run scripts.dokku.$PHASE_SCRIPT_KEY from app.json (if defined)"
|
||||||
|
execute_script
|
||||||
1
plugins/00_dokku-standard/post-deploy
Symbolic link
1
plugins/00_dokku-standard/post-deploy
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
exec-app-json-scripts
|
||||||
1
plugins/00_dokku-standard/pre-deploy
Symbolic link
1
plugins/00_dokku-standard/pre-deploy
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
exec-app-json-scripts
|
||||||
@@ -599,3 +599,26 @@ get_app_urls() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_json_value() {
|
||||||
|
# return value of provided json key from a json stream on stdin
|
||||||
|
# JSON_NODE should be expresses as either a top-level object that has no children
|
||||||
|
# or in the format of json.node.path
|
||||||
|
local JSON_NODE="$1"
|
||||||
|
local JSON_NODE=${JSON_NODE//\./\"][\"}
|
||||||
|
local JSON_NODE="[\"${JSON_NODE}\"]"
|
||||||
|
cat | python -c 'import json,sys;obj=json.load(sys.stdin);print json.dumps(obj'"${JSON_NODE}"').strip("\"")';
|
||||||
|
}
|
||||||
|
|
||||||
|
get_json_keys() {
|
||||||
|
# return space-separated list of json keys from json provided on stdin
|
||||||
|
# JSON_NODE should be expressed as json.node.path and is expected to have children
|
||||||
|
local JSON_NODE="$1"
|
||||||
|
local JSON_NODE=${JSON_NODE//\./\"][\"}
|
||||||
|
local JSON_NODE="[\"${JSON_NODE}\"]"
|
||||||
|
if [[ "$JSON_NODE" == "[\"\"]" ]]; then
|
||||||
|
cat | python -c 'import json,sys;obj=json.load(sys.stdin);print " ".join(obj.keys())';
|
||||||
|
else
|
||||||
|
cat | python -c 'import json,sys;obj=json.load(sys.stdin);print " ".join(obj'"${JSON_NODE}"'.keys())';
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|||||||
38
tests/apps/nodejs-express/app.json
Normal file
38
tests/apps/nodejs-express/app.json
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "Sample node.js express app",
|
||||||
|
"description": "Used in dokku's test suite",
|
||||||
|
"keywords": [
|
||||||
|
"nodejs",
|
||||||
|
"express",
|
||||||
|
"testing"
|
||||||
|
],
|
||||||
|
"website": "http://dokku.viewdocs.io/dokku/",
|
||||||
|
"repository": "https://github.com/dokku/dokku",
|
||||||
|
"logo": "https://raw.githubusercontent.com/dokku/dokku/master/docs/assets/dokku.png",
|
||||||
|
"scripts": {
|
||||||
|
"dokku": {
|
||||||
|
"predeploy": "touch /app/predeploy.test",
|
||||||
|
"postdeploy": "touch /app/postdeploy.test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"SECRET_TOKEN": {
|
||||||
|
"description": "A secret key for verifying the integrity of signed cookies.",
|
||||||
|
"value": "secret"
|
||||||
|
},
|
||||||
|
"WEB_CONCURRENCY": {
|
||||||
|
"description": "The number of processes to run.",
|
||||||
|
"generator": "echo 5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"image": "gliderlabs/herokuish",
|
||||||
|
"addons": [
|
||||||
|
"dokku-postgres",
|
||||||
|
"dokku-redis"
|
||||||
|
],
|
||||||
|
"buildpacks": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/heroku/heroku-buildpack-nodejs"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -146,3 +146,24 @@ EOF
|
|||||||
echo "status: "$status
|
echo "status: "$status
|
||||||
assert_output "3001/udp 3000/tcp 3003"
|
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
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user