include/procfile.bash (100 lines of code) (raw):

yaml-esque-keys() { declare desc="Get process type keys from colon-separated structure" while read -r line || [[ -n "$line" ]]; do [[ "$line" =~ ^#.* ]] && continue [[ "$line" == *:* ]] || continue key=${line%%:*} echo "$key" done <<< "$(cat)" } yaml-esque-get() { declare desc="Get key value from colon-separated structure" declare key="$1" local inputkey cmd while read -r line || [[ -n "$line" ]]; do [[ "$line" =~ ^#.* ]] && continue inputkey=${line%%:*} cmd=${line#*:} if [[ "$inputkey" == "$key" ]]; then echo "$cmd" break fi done <<< "$(cat)" } procfile-parse() { declare desc="Get command string for a process type from Procfile" declare type="$1" # app_path is defined in outer scope # shellcheck disable=SC2154 cat "$app_path/Procfile" | yaml-esque-get "$type" } procfile-start() { declare desc="Run process type command from Procfile through exec" declare type="$1" local processcmd processcmd="$(procfile-parse "$type")" if [[ -z "$processcmd" ]]; then echo "Proc entrypoint ${type} does not exist. Please check your Procfile" exit 1 else procfile-exec "$processcmd" fi } procfile-exec() { declare desc="Run as unprivileged user with Heroku-like env" [[ "$USER" ]] || detect-unprivileged procfile-setup-home procfile-load-env procfile-load-profile cd "$app_path" || return 1 # unprivileged_user is defined in outer scope # shellcheck disable=SC2154,SC2046 if [[ "$HEROKUISH_SETUIDGUID" == "false" ]]; then exec $(eval echo "$@") else exec setuidgid "$unprivileged_user" $(eval echo "$@") fi } procfile-types() { title "Discovering process types" if [[ -f "$app_path/Procfile" ]]; then local types types="$(cat "$app_path/Procfile" | yaml-esque-keys | sort | uniq | xargs echo)" echo "Procfile declares types -> ${types// /, }" return fi if [[ -s "$app_path/.release" ]]; then local default_types default_types="$(cat "$app_path/.release" | yaml-keys default_process_types | xargs echo)" # selected_name is defined in outer scope # shellcheck disable=SC2154 [[ "$default_types" ]] && \ echo "Default types for $selected_name -> ${default_types// /, }" for type in $default_types; do echo "$type: $(cat "$app_path/.release" | yaml-get default_process_types "$type")" >> "$app_path/Procfile" done return fi echo "No process types found" } procfile-load-env() { local varname # env_path is defined in outer scope # shellcheck disable=SC2154 if [[ -d "$env_path" ]]; then shopt -s nullglob for e in $env_path/*; do varname=$(basename "$e") export "$varname=$(cat "$e")" done fi } procfile-load-profile() { shopt -s nullglob for file in /etc/profile.d/*.sh; do # shellcheck disable=SC1090 source "$file" done mkdir -p "$app_path/.profile.d" for file in $app_path/.profile.d/*.sh; do # shellcheck disable=SC1090 source "$file" done shopt -u nullglob hash -r } procfile-setup-home() { export HOME="$app_path" usermod --home "$app_path" "$unprivileged_user" > /dev/null 2>&1 # unprivileged_user & unprivileged_group are defined in outer scope # shellcheck disable=SC2154 find "$app_path" \( \! -user "$unprivileged_user" -o \! -group "$unprivileged_group" \) -print0 | xargs -0 -r chown "$unprivileged_user:$unprivileged_group" }