Fix bug in upgrade config handling

Fix bug in upgrade config handling

#!/bin/sh

set -e

# Set VERBOSE to enable verbose logging of the boot script
#VERBOSE=true
if [ ! -z $DEBUG_BOOT ]; then
    set -x
fi;

my_readlink_f() {
    TARGET_FILE="$1"
    cd $(dirname "$TARGET_FILE")
    TARGET_FILE=$(basename "$TARGET_FILE")

    # Iterate down a (possible) chain of symlinks
    while [ -L "$TARGET_FILE" ]
    do
        TARGET_FILE=$(readlink "$TARGET_FILE")
        cd $(dirname "$TARGET_FILE")
        TARGET_FILE=$(basename "$TARGET_FILE")
    done
    # Compute the canonicalized name by finding the physical path
    # for the directory we're in and appending the target file.
    PHYS_DIR=$(pwd -P)
    RESULT="$PHYS_DIR/$TARGET_FILE"
    echo "$RESULT"
}

# Path to this script
if uname | grep -q 'Darwin'; then
  # on OSX, best to install coreutils from homebrew or similar
  # to get greadlink
  if command -v greadlink >/dev/null 2>&1; then
    SCRIPT="${SCRIPT:-$(greadlink -f "$0")}"
  else
    SCRIPT="${SCRIPT:-$(my_readlink_f "$0")}"
  fi
else
  SCRIPT="${SCRIPT:-$(readlink -f "$0" )}"
fi

# Parent directory of this script
SCRIPT_DIR="${SCRIPT_DIR:-$(dirname "${SCRIPT}")}"
# Root directory of all releases
RELEASE_ROOT_DIR="${RELEASE_ROOT_DIR:-$(dirname $(dirname ${SCRIPT_DIR}))}"

# Disable flow control for run_erl
# Flow control can cause several problems. On Linux, if you
# accidentally hit Ctrl-S (instead of Ctrl-D to detach) and
# then some other key, the entire beam process will hang when
# attempting to write to stdout. On Solaris, the beam process
# will hang on writing if ScrollLock is on.
RUN_ERL_DISABLE_FLOWCNTRL="${RUN_ERL_DISABLE_FLOWCNTRL:-true}"
# Name of the release
REL_NAME="${REL_NAME:-<%= release_name %>}"
# Current version of the release
REL_VSN="${REL_VSN:-<%= release_version %>}"
# Current version of ERTS being used
# If this is not present/unset, it will be detected
<%= if ((is_binary(include_erts) or include_erts) and not is_nil(erts_vsn)) do %>ERTS_VSN="${ERTS_VSN:-<%= erts_vsn %>}"<% end %>
# VM code loading mode, embedded by default, can also be interactive
# See http://erlang.org/doc/man/code.html
CODE_LOADING_MODE="${CODE_LOADING_MODE:-embedded}"
# Path to start_erl.data
START_ERL_DATA="${START_ERL_DATA:-$RELEASE_ROOT_DIR/releases/start_erl.data}"
# Directory containing the current version of this release
REL_DIR="${REL_DIR:-$RELEASE_ROOT_DIR/releases/$REL_VSN}"
# The lib directory for this release
REL_LIB_DIR="${REL_LIB_DIR:-$RELEASE_ROOT_DIR/lib}"
# Options passed to erl
ERL_OPTS="${ERL_OPTS:-<%= release.profile.erl_opts %>}"
# When stdout is piped to a file, this is the directory those files will
# be stored in. defaults to /log in the release root directory

# RELEASE_MUTABLE_DIR
RELEASE_MUTABLE_DIR="${RELEASE_MUTABLE_DIR:-$RELEASE_ROOT_DIR/var}"

RUNNER_LOG_DIR="${RUNNER_LOG_DIR:-$RELEASE_MUTABLE_DIR/log}"
# A string of extra options to pass to erl, here for plugins
EXTRA_OPTS="${EXTRA_OPTS:-}"

# Pre-start/stop event hook paths
PRE_CONFIGURE_HOOKS="$REL_DIR/hooks/pre_configure.d"
PRE_START_HOOKS="$REL_DIR/hooks/pre_start.d"
POST_START_HOOKS="$REL_DIR/hooks/post_start.d"
PRE_STOP_HOOKS="$REL_DIR/hooks/pre_stop.d"
POST_STOP_HOOKS="$REL_DIR/hooks/post_stop.d"
PRE_UPGRADE_HOOKS="$REL_DIR/hooks/pre_upgrade.d"
POST_UPGRADE_HOOKS="$REL_DIR/hooks/post_upgrade.d"

# Get node pid
get_pid() {
    if output="$(nodetool rpcterms os getpid)"
    then
        echo "$output" | sed -e 's/"//g'
        return 0
    else
        echo "$output"
        return 1
    fi
}

# Generate a unique nodename
gen_nodename() {
    id="longname$(gen_id)-${NAME}"
    "$BINDIR/erl" -boot start_clean -eval '[Host] = tl(string:tokens(atom_to_list(node()),"@")), io:format("~s~n", [Host]), halt()' -noshell ${NAME_TYPE} $id
}

# Generate a random id
gen_id() {
    od -t x -N 4 /dev/urandom | head -n1 | awk '{print $2}'
}

# Control a node
# Use like `nodetool "ping"`
nodetool() {
    command="$1"; shift
    name=${PEERNAME:-$NAME}
    "$ERTS_DIR/bin/escript" "$ROOTDIR/bin/nodetool" "$NAME_TYPE" "$name" \
                            -setcookie "$COOKIE" "$command" $@
}

# Run an escript in the node's environment
# Use like `escript "path/to/escript"`
escript() {
    shift; scriptpath="$1"; shift
    export RELEASE_ROOT_DIR

    "$ERTS_DIR/bin/escript" "$ROOTDIR/$scriptpath" $@
}

# Private. Load target cookie, either from vm.args or $HOME/.cookie
_load_cookie() {
    COOKIE_ARG="$(grep '^-setcookie' "$VMARGS_PATH" || true)"
    DEFAULT_COOKIE_FILE="$HOME/.erlang.cookie"
    if [ -z "$COOKIE_ARG" ]; then
        if [ -f "$DEFAULT_COOKIE_FILE" ]; then
            COOKIE="$(cat "$DEFAULT_COOKIE_FILE")"
        fi
    else
        # Extract cookie name from COOKIE_ARG
        COOKIE="$(echo "$COOKIE_ARG" | awk '{print $2}')"
    fi
}

# Private. Ensure that cookie is set.
_check_cookie() {
    # Attempt reloading cookie in case it has been set in a hook
    if [ -z "$COOKIE" ]; then
        _load_cookie
    fi
    # Die if cookie is still not set, as connecting via distribution will fail
    if [ -z "$COOKIE" ]; then
        printf "a secret cookie must be provided in one of the following ways:\n  - with vm.args using the -setcookie parameter,\n  or\n  by writing the cookie to '$DEFAULT_COOKIE_FILE', with permissions set to 0400"
        exit 1
    fi
}

# Private. For setting up the Erlang runtime to use
_init_erts_config() {
    if [ -z "$ERTS_VSN" ]; then
        set +e # temporarily disable so we can display a nice error message to the user
        __erl="$(which erl)"
        set -e
        if [ -z "${__erl}"  ]; then
            echo 'erlang runtime not found. If erlang is installed check your $PATH. aborting.'
            exit 1
        fi
        export ROOTDIR="$("$__erl" -boot start_clean -eval 'io:format("~s~n", [code:root_dir()]), halt().' -noshell)"
        export ERTS_VSN="$(_get_erts_vsn)"
        export ERTS_DIR="$ROOTDIR"/"erts-$ERTS_VSN"
    else
        __erts_dir="$RELEASE_ROOT_DIR/erts-$ERTS_VSN"
        if [ -d "$__erts_dir" ]; then
            export ROOTDIR="$RELEASE_ROOT_DIR"
            export ERTS_DIR="$__erts_dir"
        else
            unset ERTS_VSN
            _init_erts_config
        fi
    fi
}

# Private. For determining the version of ERTS available to the release
# This is only ever called if ERTS_VSN is not set
_get_erts_vsn() {
    if [ -z "$ERTS_VSN" ]; then
        "$ROOTDIR"/bin/erl \
            -boot start_clean \
            -eval 'Ver = erlang:system_info(version), io:format("~s~n", [Ver]), halt()' \
            -noshell
    else
        echo "$ERTS_VSN"
    fi
}

# Private. Gets a list of code paths for this release
_get_code_paths() {
    "$ERTS_DIR/bin/escript" "$ROOTDIR/bin/release_utils.escript" "get_code_paths" "$ROOTDIR" "$ERTS_DIR" "$REL_NAME" "$REL_VSN"
}

# Private. Open a shell on a remote node
_rem_sh() {
    # Generate a unique id used to allow multiple remsh to the same node
    # transparently
    id="remsh$(gen_id)-${NAME}"

    # Get the node's ticktime so that we use the same thing.
    TICKTIME="$(nodetool rpcterms net_kernel get_net_ticktime)"

    # Setup remote shell command to control node
    if [ ! -z "$USE_ERL_SHELL" ]; then
        exec "$BINDIR/erl" \
            -hidden \
            -boot start_clean -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
            -kernel net_ticktime $TICKTIME \
            "$NAME_TYPE" "$id" -remsh "$NAME" -setcookie "$COOKIE"
    else
        __code_paths=$(_get_code_paths)
        exec "$BINDIR/erl" \
            -pa "$CONSOLIDATED_DIR" \
            ${__code_paths} \
            -hidden -noshell \
            -boot start_clean -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
            -kernel net_ticktime $TICKTIME \
            -user Elixir.IEx.CLI "$NAME_TYPE" "$id" -setcookie "$COOKIE" \
            -extra --no-halt +iex -"$NAME_TYPE" "$id" --cookie "$COOKIE" --remsh "$NAME"
    fi
}

# Output a start command for the last argument of run_erl
_start_command() {
    printf "exec \"%s\" \"%s\" -- %s %s" "$RELEASE_ROOT_DIR/bin/$REL_NAME" \
           "$START_OPTION" "${ARGS}" "${EXTRA_OPTS}"
}

# Do a textual replacement of ${VAR} occurrences in $1 and pipe to $2
_replace_os_vars() {
    awk '
        function escape(s) {
            gsub(/'\&'/, "\\\\&", s);
            return s;
        }
        {
            while(match($0,"[$]{[^}]*}")) {
                var=substr($0,RSTART+2,RLENGTH-3);
                gsub("[$]{"var"}", escape(ENVIRON[var]))
            }
        }1' < "$1" > "$1.bak"
    mv -- "$1.bak" "$1"
}

# Private. Run hooks from directory.
_run_hooks_from_dir() {
   if [ -d "$1" ]; then
      for file in $1/[0-9a-zA-Z]*.sh; do
        [ -f "$file" ] || continue
        . "$file"
      done
   fi
}

# Private. Sets config paths for sys.config and vm.args,
# and ensures that env var replacements are performed
_init_configs() {
    # Set VMARGS_PATH, the path to the vm.args file to use
    # Use $RELEASE_CONFIG_DIR/vm.args if exists, otherwise releases/VSN/vm.args
    if [ -z "$VMARGS_PATH" ]; then
        if [ -f "$RELEASE_CONFIG_DIR/vm.args" ]; then
            export VMARGS_PATH="$RELEASE_CONFIG_DIR/vm.args"
        else
            export VMARGS_PATH="$REL_DIR/vm.args"
        fi
        export SRC_VMARGS_PATH="$VMARGS_PATH"
    else
        if [ "$VMARGS_PATH" != "$RELEASE_MUTABLE_DIR/vm.args" ]; then
            export SRC_VMARGS_PATH="$VMARGS_PATH"
        else
            if [ -f "$RELEASE_CONFIG_DIR/vm.args" ]; then
                export VMARGS_PATH="$RELEASE_CONFIG_DIR/vm.args"
            else
                export VMARGS_PATH="$REL_DIR/vm.args"
            fi
            export SRC_VMARGS_PATH="$VMARGS_PATH"
        fi
    fi
    if [ "$SRC_VMARGS_PATH" != "$RELEASE_MUTABLE_DIR/vm.args" ]; then
        echo "#### Generated - edit/create $RELEASE_CONFIG_DIR/vm.args instead." \
            >  "$RELEASE_MUTABLE_DIR/vm.args"
        cat  "$SRC_VMARGS_PATH"                              \
            >> "$RELEASE_MUTABLE_DIR/vm.args"
        export VMARGS_PATH="$RELEASE_MUTABLE_DIR"/vm.args
    fi
    if [ $REPLACE_OS_VARS ]; then
        _replace_os_vars "$VMARGS_PATH"
    fi

    # Set SYS_CONFIG_PATH, the path to the sys.config file to use
    # Use $RELEASE_CONFIG_DIR/sys.config if exists, otherwise releases/VSN/sys.config
    if [ -z "$SYS_CONFIG_PATH" ]; then
        if [ -f "$RELEASE_CONFIG_DIR/sys.config" ]; then
            export SYS_CONFIG_PATH="$RELEASE_CONFIG_DIR/sys.config"
        else
            export SYS_CONFIG_PATH="$REL_DIR/sys.config"
        fi
        export SRC_SYS_CONFIG_PATH="$SYS_CONFIG_PATH"
    else
        if [ "$SYS_CONFIG_PATH" != "$RELEASE_MUTABLE_DIR/sys.config" ]; then
            export SRC_SYS_CONFIG_PATH="$SYS_CONFIG_PATH"
        else
            if [ -f "$RELEASE_CONFIG_DIR/sys.config" ]; then
                export SYS_CONFIG_PATH="$RELEASE_CONFIG_DIR/sys.config"
            else
                export SYS_CONFIG_PATH="$REL_DIR/sys.config"
            fi
            export SRC_SYS_CONFIG_PATH="$SYS_CONFIG_PATH"
        fi
    fi
    if [ "$SRC_SYS_CONFIG_PATH" != "$RELEASE_MUTABLE_DIR/sys.config" ]; then
        (echo "%% Generated - edit/create $RELEASE_CONFIG_DIR/sys.config instead."; \
        cat  "$SRC_SYS_CONFIG_PATH")                              \
            > "$RELEASE_MUTABLE_DIR/sys.config"
        export SYS_CONFIG_PATH="$RELEASE_MUTABLE_DIR"/sys.config
    fi
    if [ $REPLACE_OS_VARS ]; then
        _replace_os_vars "$SYS_CONFIG_PATH"
    fi
}

# If TERM is not set, set it to xterm
if [ -z "$TERM" ]; then
    export TERM=xterm
fi

# Make sure ERTS configuration is set
_init_erts_config
echo "$ERTS_VSN $REL_VSN" > "$START_ERL_DATA"

# Allow override of where to read configuration from
# By default it's RELEASE_ROOT_DIR
if [ -z "$RELEASE_CONFIG_DIR" ]; then
    export RELEASE_CONFIG_DIR="$RELEASE_ROOT_DIR"
fi

# Make sure directories exist
mkdir -p "$RELEASE_MUTABLE_DIR"
echo "Files in this directory are regenerated frequently, edits will be lost" \
    > "$RELEASE_MUTABLE_DIR/WARNING_README"
mkdir -p "$RUNNER_LOG_DIR"

# Make sure config paths are setup
_init_configs

# Extract the target node name from node.args
# Should be `-sname somename` or `-name somename@somehost`
export NAME_ARG="$(egrep '^-s?name' "$VMARGS_PATH" || true)"
if [ -z "$NAME_ARG" ]; then
    echo "vm.args needs to have either -name or -sname parameter."
    exit 1
fi

# Extract the name type and name from the NAME_ARG for REMSH
# NAME_TYPE should be -name or -sname
export NAME_TYPE="$(echo "$NAME_ARG" | awk '{print $1}')"
# NAME will be either `somename` or `somename@somehost`
export NAME="$(echo "$NAME_ARG" | awk '{print $2}')"

# Where the pipe will be stored when using `start`
# This is used so you can attach the running application to the current
# shell using `attach`
PIPE_DIR="${PIPE_DIR:-$RELEASE_MUTABLE_DIR/erl_pipes/$NAME/}"

# Extract the target cookie
_load_cookie

export ROOTDIR="$RELEASE_ROOT_DIR"
export BINDIR="$ERTS_DIR/bin"
export EMU="beam"
export PROGNAME="erl"
export LD_LIBRARY_PATH="$ERTS_DIR/lib:$LD_LIBRARY_PATH"
export ERTS_LIB_DIR="$ERTS_DIR/../lib"
CONSOLIDATED_DIR="$ROOTDIR/lib/${REL_NAME}-${REL_VSN}/consolidated"

cd "$ROOTDIR"

# User can specify an sname without @hostname
# This will fail when creating remote shell
# So here we check for @ and add @hostname if missing
case $NAME in
    *@*)
        # Nothing to do
        ;;
    *)
        NAME=$NAME@$(gen_nodename)
        ;;
esac

# For Elixir runtime
export DISTILLERY_TASK="$1"

# Check the first argument for instructions
case "$1" in
    start|start_boot)

        # Make sure there is not already a node running
        #RES=`$NODETOOL ping`
        #if [ "$RES" = "pong" ]; then
        #    echo "Node is already running!"
        #    exit 1
        #fi
        # Save this for later.
        CMD=$1
        case "$1" in
            start)
                shift
                START_OPTION="console"
                HEART_OPTION="start"
                ;;
            start_boot)
                shift
                START_OPTION="console_boot"
                HEART_OPTION="start_boot"
                ;;
        esac
        ARGS="$@"
        RUN_PARAM="$@"

        _check_cookie
        _run_hooks_from_dir "$PRE_CONFIGURE_HOOKS"
        _run_hooks_from_dir "$PRE_START_HOOKS"

        # Set arguments for the heart command
        set -- "$SCRIPT_DIR/$REL_NAME" "$HEART_OPTION"
        [ "$RUN_PARAM" ] && set -- "$@" "$RUN_PARAM"

        # Export the HEART_COMMAND
        HEART_COMMAND="$RELEASE_ROOT_DIR/bin/$REL_NAME $CMD"
        export HEART_COMMAND

        mkdir -p "$PIPE_DIR"

        "$BINDIR/run_erl" -daemon "$PIPE_DIR" "$RUNNER_LOG_DIR" \
                          "$(_start_command)"

        _run_hooks_from_dir "$POST_START_HOOKS"
        ;;

    stop)
        _check_cookie
        _run_hooks_from_dir "$PRE_STOP_HOOKS"
        # Wait for the node to completely stop...
        PID="$(get_pid)"
        if ! nodetool "stop"; then
            exit 1
        fi
        while $(kill -s 0 "$PID" 2>/dev/null);
        do
            sleep 1
        done
        _run_hooks_from_dir "$POST_STOP_HOOKS"
        ;;

    restart)
        _check_cookie
        _run_hooks_from_dir "$PRE_CONFIGURE_HOOKS"
        _run_hooks_from_dir "$PRE_START_HOOKS"
        ## Restart the VM without exiting the process
        if ! nodetool "restart"; then
            exit 1
        fi
        ;;

    reboot)
        _check_cookie
        _run_hooks_from_dir "$PRE_CONFIGURE_HOOKS"
        _run_hooks_from_dir "$PRE_START_HOOKS"
        ## Restart the VM completely (uses heart to restart it)
        if ! nodetool "reboot"; then
            exit 1
        fi
        ;;

    pid)
        ## Get the VM's pid
        _check_cookie
        if ! get_pid; then
            exit 1
        fi
        ;;

    ping)
        _check_cookie
        ## See if the VM is alive
        if ! nodetool "ping"; then
            exit 1
        fi
        ;;

    pingpeer)
        PEERNAME=$2 nodetool "ping"
        exit_status=$?
        if [ "$exit_status" -ne 0 ]; then
            exit $exit_status
        fi
        ;;

    escript)
        _check_cookie
        ## Run an escript under the node's environment
        if ! escript $@; then
            exit 1
        fi
        ;;

    attach)
        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        shift
        exec "$BINDIR/to_erl" "$PIPE_DIR"
        ;;

    remote_console)
        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        shift
        _rem_sh
        ;;

    upgrade|downgrade|install)
        if [ -z "$2" ]; then
            echo "Missing package argument"
            echo "Usage: $REL_NAME $1 {package base name}"
            echo "NOTE {package base name} MUST NOT include the .tar.gz suffix"
            exit 1
        fi

        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        SOURCE_VERSION="$REL_VSN"
        TARGET_VERSION="$2"

        "$BINDIR/escript" "$ROOTDIR/bin/release_utils.escript" \
             "unpack_release" "$REL_NAME" "$NAME_TYPE"  "$NAME" "$COOKIE" "$TARGET_VERSION"

        # Update environment to reflect target version environment
        REL_VSN="$TARGET_VERSION"
        REL_DIR="$RELEASE_ROOT_DIR/releases/$TARGET_VERSION"

        # Prepare new configs
        _init_configs
        # We have to do some juggling to ensure the correct config is used by the upgrade handler
        # First, we detect if there was a failed upgrade, so we can start over
        if [ -f "$REL_DIR/sys.config.bak" ]; then
            mv "$REL_DIR/sys.config.bak" "$REL_DIR/sys.config"
        fi
        if [ -f "$REL_DIR/vm.args.bak" ]; then
            mv "$REL_DIR/vm.args.bak" "$REL_DIR/vm.args"
        fi
        # Then, backup the packaged configs
        cp "$REL_DIR/sys.config" "$REL_DIR/sys.config.bak"
        cp "$REL_DIR/vm.args" "$REL_DIR/vm.args.bak"
        # Then, substitute in the prepared configs
        cp "$SYS_CONFIG_PATH" "$REL_DIR/sys.config"
        cp "$VMARGS_PATH" "$REL_DIR/vm.args"
        # Make sure any pre-configuration hooks are run
        _run_hooks_from_dir "$PRE_CONFIGURE_HOOKS"
        # Run any pre-upgrade tasks
        _run_hooks_from_dir "$PRE_UPGRADE_HOOKS"

        "$BINDIR/escript" "$ROOTDIR/bin/release_utils.escript" \
             "install_release" "$REL_NAME" "$NAME_TYPE"  "$NAME" "$COOKIE" "$TARGET_VERSION"

        # We were successful, clean up the configs
        mv "$REL_DIR/sys.config.bak" "$REL_DIR/sys.config"
        mv "$REL_DIR/vm.args.bak" "$REL_DIR/vm.args"

        # Run any post-upgrade hooks
        _run_hooks_from_dir "$POST_UPGRADE_HOOKS"

        ;;
    unpack)
        if [ -z "$2" ]; then
            echo "Missing package argument"
            echo "Usage: $REL_NAME $1 {package base name}"
            echo "NOTE {package base name} MUST NOT include the .tar.gz suffix"
            exit 1
        fi

        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        exec "$BINDIR/escript" "$ROOTDIR/bin/release_utils.escript" \
             "unpack_release" "$REL_NAME" "$NAME_TYPE" "$NAME" "$COOKIE" "$2"
        ;;

    console|console_clean|console_boot)
        # .boot file typically just $REL_NAME (ie, the app name)
        # however, for debugging, sometimes start_clean.boot is useful.
        # For e.g. 'setup', one may even want to name another boot script.
        case "$1" in
            console)
                if [ -f "$REL_DIR/$REL_NAME.boot" ]; then
                  BOOTFILE="$REL_DIR/$REL_NAME"
                else
                  BOOTFILE="$REL_DIR/start"
                fi
                ;;
            console_clean)
                BOOTFILE="$ROOTDIR/bin/start_clean"
                __code_paths=$(_get_code_paths)
                EXTRA_CODE_PATHS=${__code_paths}
                ;;
            console_boot)
                shift
                BOOTFILE="$1"
                shift
                ;;
        esac
        # Setup beam-required vars
        EMU="beam"
        PROGNAME="${0#*/}"

        export EMU
        export PROGNAME

        # Store passed arguments since they will be erased by `set`
        ARGS="$@"

        _check_cookie

        # Start the VM, executing pre_start hook along
        # the way. We can't run the post_start hook because
        # the console will crash with no TTY attached
        _run_hooks_from_dir "$PRE_CONFIGURE_HOOKS"
        _run_hooks_from_dir "$PRE_START_HOOKS"

        # Build an array of arguments to pass to exec later on
        # Build it here because this command will be used for logging.
        set -- "$BINDIR/erlexec" \
            -boot "$BOOTFILE" \
            -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
            -env ERL_LIBS "$REL_LIB_DIR" \
            -pa "$CONSOLIDATED_DIR" \
            ${EXTRA_CODE_PATHS} \
            -args_file "$VMARGS_PATH" \
            -config "$SYS_CONFIG_PATH" \
            -mode "$CODE_LOADING_MODE" \
            ${ERL_OPTS} \
            -user Elixir.IEx.CLI \
            -extra --no-halt +iex

        # Dump environment info for logging purposes
        if [ ! -z $VERBOSE ]; then
            echo "Exec: $@" -- ${1+$ARGS} ${EXTRA_OPTS}
            echo "Root: $ROOTDIR"

            # Log the startup
            echo "$RELEASE_ROOT_DIR"
        fi;

        logger -t "$REL_NAME[$$]" "Starting up"

        exec "$@" -- ${1+$ARGS} ${EXTRA_OPTS}
        ;;

    foreground)
        # start up the release in the foreground for use by runit
        # or other supervision services

        [ -f "$REL_DIR/$REL_NAME.boot" ] && BOOTFILE="$REL_NAME" || BOOTFILE=start
        FOREGROUNDOPTIONS="-noshell -noinput +Bd"

        # Setup beam-required vars
        EMU=beam
        PROGNAME="${0#*/}"

        export EMU
        export PROGNAME

        # Store passed arguments since they will be erased by `set`
        ARGS="$@"

        _check_cookie

        # Start the VM, executing pre and post start hooks
        _run_hooks_from_dir "$PRE_CONFIGURE_HOOKS"
        _run_hooks_from_dir "$PRE_START_HOOKS"

        # Build an array of arguments to pass to exec later on
        # Build it here because this command will be used for logging.
        set -- "$BINDIR/erlexec" $FOREGROUNDOPTIONS \
            -boot "$REL_DIR/$BOOTFILE" \
            -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
            -env ERL_LIBS "$REL_LIB_DIR" \
            -pa "$CONSOLIDATED_DIR" \
            -args_file "$VMARGS_PATH" \
            -config "$SYS_CONFIG_PATH" \
            -mode "$CODE_LOADING_MODE" \
            ${ERL_OPTS} \
            -extra ${EXTRA_OPTS}

        # Dump environment info for logging purposes
        if [ ! -z $VERBOSE ]; then
            echo "Exec: $@" -- ${1+$ARGS}
            echo "Root: $ROOTDIR"
        fi;

        "$@" -- ${1+$ARGS} &
        __bg_pid=$!
        _run_hooks_from_dir "$POST_START_HOOKS"
        wait $__bg_pid
        ;;
    rpc)
        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        shift

        nodetool rpc $@
        ;;
    rpcterms)
        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        shift

        nodetool rpcterms $@
        ;;
    eval)
        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        shift
        nodetool "eval" $@
        ;;
    command)
        # Execute as command-line utility
        #
        # Like the escript command, this does not start the OTP application.
        # If your command depends on a running OTP application,
        # use
        #
        #     {:ok, _} = Application.ensure_all_started(:your_app)

        _check_cookie

        shift
        MODULE="$1"; shift
        FUNCTION="$1"; shift

        # Save extra arguments
        ARGS="$@"

        # Build arguments for erlexec
        __code_paths=$(_get_code_paths)
        set -- "$ERL_OPTS"
        [ "$SYS_CONFIG_PATH" ] && set -- "$@" -config "$SYS_CONFIG_PATH"
        set -- "$@" -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR"
        set -- "$@" -noshell
        set -- "$@" -boot start_clean
        set -- "$@" -pa "$CONSOLIDATED_DIR"
        set -- "$@" ${__code_paths}
        set -- "$@" -s "$MODULE" "$FUNCTION"

        $BINDIR/erlexec $@ -extra $ARGS
        exit "$?"
        ;;
    reload_config)
        # Reload the running system's configuration
        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        _run_hooks_from_dir "$PRE_CONFIGURE_HOOKS"

        shift
        nodetool "reload_config" "$SYS_CONFIG_PATH"
        ;;
    describe)
        ## Print a summary of information about this release
        echo "$REL_NAME-$REL_VSN\n"
        echo "erts:       $ERTS_VSN"
        echo "path:       $REL_DIR"
        echo "sys.config: $SYS_CONFIG_PATH"
        echo "vm.args:    $VMARGS_PATH"
        echo "name:       $NAME"
        echo "cookie:     $COOKIE"
        echo "erl_opts:   ${ERL_OPTS:-none provided}"
        echo ""
        echo "hooks:"
        __has_hooks=0
        for hook in "$REL_DIR"/hooks/*.d/[0-9a-zA-Z]*.sh; do
            [ -f "$hook" ] || continue
            __has_hooks=1
            echo "$hook"
        done
        if [ $__has_hooks -eq 0 ]; then
            echo "No custom hooks found."
        fi
        echo ""
        echo "commands:"
        __has_commands=0
        for command in "$REL_DIR"/commands/*.sh; do
            [ -f "$command" ] || continue
            __has_commands=1
            echo "$command"
        done
        if [ $__has_commands -eq 0 ]; then
            echo "No custom commands found."
        fi
        exit 0
        ;;
    *)
        __command_path="$REL_DIR/commands/$1"
        if [ -f "$__command_path" ]; then
            _check_cookie
            . "$__command_path"
        else
            echo "Usage: $REL_NAME <task>"
            echo
            echo "Service Control"
            echo "======================="
            echo "start                          # start $REL_NAME as a daemon"
            echo "start_boot <file>              # start $REL_NAME as a daemon, but supply a custom .boot file"
            echo "foreground                     # start $REL_NAME in the foreground"
            echo "console                        # start $REL_NAME with a console attached"
            echo "console_clean                  # start a console with code paths set but no apps loaded/started"
            echo "console_boot <file>            # start $REL_NAME with a console attached, but supply a custom .boot file"
            echo "stop                           # stop the $REL_NAME daemon"
            echo "restart                        # restart the $REL_NAME daemon without shutting down the VM"
            echo "reboot                         # restart the $REL_NAME daemon"
            echo "reload_config                  # reload the current system's configuration from disk"
            echo
            echo "Upgrades"
            echo "======================="
            echo "upgrade <version>              # upgrade $REL_NAME to <version>"
            echo "downgrade <version>            # downgrade $REL_NAME to <version>"
            echo "install <version>              # install the $REL_NAME-<version> release, but do not upgrade to it"
            echo
            echo "Utilities"
            echo "======================="
            echo "attach                         # attach the current TTY to $REL_NAME's console"
            echo "remote_console                 # remote shell to $REL_NAME's console"
            echo "pid                            # get the pid of the running $REL_NAME instance"
            echo "ping                           # checks if $REL_NAME is running, pong is returned if successful"
            echo "pingpeer <peer>                # check if a peer node is running, pong is returned if successful"
            echo "escript <file>                 # execute an escript"
            echo "rpc <mod> <fun> [<args..>]     # execute an RPC call using the given MFA"
            echo "rpcterms <mod> <fun> [<expr>]  # execute an RPC call using the given Erlang expression for args"
            echo "eval <expr>                    # execute the given Erlang expression on the running node"
            echo "command <mod> <fun> [<args..>] # execute the given MFA"
            echo "describe                       # print useful information about the $REL_NAME release"
            echo
            echo "Custom Commands"
            echo "======================="
            __has_commands=0
            for command in "$REL_DIR"/commands/*.sh; do
                [ -f "$command" ] || continue
                __has_commands=1
                echo "$command"
            done
            if [ $__has_commands -eq 0 ]; then
                echo "No custom commands found."
            fi
            echo ""
            exit 1
        fi
        ;;
esac

exit 0

#!/bin/sh

set -e

# Set VERBOSE to enable verbose logging of the boot script
#VERBOSE=true
if [ ! -z $DEBUG_BOOT ]; then
    set -x
fi;

my_readlink_f() {
    TARGET_FILE="$1"
    cd $(dirname "$TARGET_FILE")
    TARGET_FILE=$(basename "$TARGET_FILE")

    # Iterate down a (possible) chain of symlinks
    while [ -L "$TARGET_FILE" ]
    do
        TARGET_FILE=$(readlink "$TARGET_FILE")
        cd $(dirname "$TARGET_FILE")
        TARGET_FILE=$(basename "$TARGET_FILE")
    done
    # Compute the canonicalized name by finding the physical path
    # for the directory we're in and appending the target file.
    PHYS_DIR=$(pwd -P)
    RESULT="$PHYS_DIR/$TARGET_FILE"
    echo "$RESULT"
}

# Path to this script
if uname | grep -q 'Darwin'; then
  # on OSX, best to install coreutils from homebrew or similar
  # to get greadlink
  if command -v greadlink >/dev/null 2>&1; then
    SCRIPT="${SCRIPT:-$(greadlink -f "$0")}"
  else
    SCRIPT="${SCRIPT:-$(my_readlink_f "$0")}"
  fi
else
  SCRIPT="${SCRIPT:-$(readlink -f "$0" )}"
fi

# Parent directory of this script
SCRIPT_DIR="${SCRIPT_DIR:-$(dirname "${SCRIPT}")}"
# Root directory of all releases
RELEASE_ROOT_DIR="${RELEASE_ROOT_DIR:-$(dirname $(dirname ${SCRIPT_DIR}))}"

# Disable flow control for run_erl
# Flow control can cause several problems. On Linux, if you
# accidentally hit Ctrl-S (instead of Ctrl-D to detach) and
# then some other key, the entire beam process will hang when
# attempting to write to stdout. On Solaris, the beam process
# will hang on writing if ScrollLock is on.
RUN_ERL_DISABLE_FLOWCNTRL="${RUN_ERL_DISABLE_FLOWCNTRL:-true}"
# Name of the release
REL_NAME="${REL_NAME:-<%= release_name %>}"
# Current version of the release
REL_VSN="${REL_VSN:-<%= release_version %>}"
# Current version of ERTS being used
# If this is not present/unset, it will be detected
<%= if ((is_binary(include_erts) or include_erts) and not is_nil(erts_vsn)) do %>ERTS_VSN="${ERTS_VSN:-<%= erts_vsn %>}"<% end %>
# VM code loading mode, embedded by default, can also be interactive
# See http://erlang.org/doc/man/code.html
CODE_LOADING_MODE="${CODE_LOADING_MODE:-embedded}"
# Path to start_erl.data
START_ERL_DATA="${START_ERL_DATA:-$RELEASE_ROOT_DIR/releases/start_erl.data}"
# Directory containing the current version of this release
REL_DIR="${REL_DIR:-$RELEASE_ROOT_DIR/releases/$REL_VSN}"
# The lib directory for this release
REL_LIB_DIR="${REL_LIB_DIR:-$RELEASE_ROOT_DIR/lib}"
# Options passed to erl
ERL_OPTS="${ERL_OPTS:-<%= release.profile.erl_opts %>}"
# When stdout is piped to a file, this is the directory those files will
# be stored in. defaults to /log in the release root directory

# RELEASE_MUTABLE_DIR
RELEASE_MUTABLE_DIR="${RELEASE_MUTABLE_DIR:-$RELEASE_ROOT_DIR/var}"

RUNNER_LOG_DIR="${RUNNER_LOG_DIR:-$RELEASE_MUTABLE_DIR/log}"
# A string of extra options to pass to erl, here for plugins
EXTRA_OPTS="${EXTRA_OPTS:-}"

# Pre-start/stop event hook paths
PRE_CONFIGURE_HOOKS="$REL_DIR/hooks/pre_configure.d"
PRE_START_HOOKS="$REL_DIR/hooks/pre_start.d"
POST_START_HOOKS="$REL_DIR/hooks/post_start.d"
PRE_STOP_HOOKS="$REL_DIR/hooks/pre_stop.d"
POST_STOP_HOOKS="$REL_DIR/hooks/post_stop.d"
PRE_UPGRADE_HOOKS="$REL_DIR/hooks/pre_upgrade.d"
POST_UPGRADE_HOOKS="$REL_DIR/hooks/post_upgrade.d"

# Get node pid
get_pid() {
    if output="$(nodetool rpcterms os getpid)"
    then
        echo "$output" | sed -e 's/"//g'
        return 0
    else
        echo "$output"
        return 1
    fi
}

# Generate a unique nodename
gen_nodename() {
    id="longname$(gen_id)-${NAME}"
    "$BINDIR/erl" -boot start_clean -eval '[Host] = tl(string:tokens(atom_to_list(node()),"@")), io:format("~s~n", [Host]), halt()' -noshell ${NAME_TYPE} $id
}

# Generate a random id
gen_id() {
    od -t x -N 4 /dev/urandom | head -n1 | awk '{print $2}'
}

# Control a node
# Use like `nodetool "ping"`
nodetool() {
    command="$1"; shift
    name=${PEERNAME:-$NAME}
    "$ERTS_DIR/bin/escript" "$ROOTDIR/bin/nodetool" "$NAME_TYPE" "$name" \
                            -setcookie "$COOKIE" "$command" $@
}

# Run an escript in the node's environment
# Use like `escript "path/to/escript"`
escript() {
    shift; scriptpath="$1"; shift
    export RELEASE_ROOT_DIR

    "$ERTS_DIR/bin/escript" "$ROOTDIR/$scriptpath" $@
}

# Private. Load target cookie, either from vm.args or $HOME/.cookie
_load_cookie() {
    COOKIE_ARG="$(grep '^-setcookie' "$VMARGS_PATH" || true)"
    DEFAULT_COOKIE_FILE="$HOME/.erlang.cookie"
    if [ -z "$COOKIE_ARG" ]; then
        if [ -f "$DEFAULT_COOKIE_FILE" ]; then
            COOKIE="$(cat "$DEFAULT_COOKIE_FILE")"
        fi
    else
        # Extract cookie name from COOKIE_ARG
        COOKIE="$(echo "$COOKIE_ARG" | awk '{print $2}')"
    fi
}

# Private. Ensure that cookie is set.
_check_cookie() {
    # Attempt reloading cookie in case it has been set in a hook
    if [ -z "$COOKIE" ]; then
        _load_cookie
    fi
    # Die if cookie is still not set, as connecting via distribution will fail
    if [ -z "$COOKIE" ]; then
        printf "a secret cookie must be provided in one of the following ways:\n  - with vm.args using the -setcookie parameter,\n  or\n  by writing the cookie to '$DEFAULT_COOKIE_FILE', with permissions set to 0400"
        exit 1
    fi
}

# Private. For setting up the Erlang runtime to use
_init_erts_config() {
    if [ -z "$ERTS_VSN" ]; then
        set +e # temporarily disable so we can display a nice error message to the user
        __erl="$(which erl)"
        set -e
        if [ -z "${__erl}"  ]; then
            echo 'erlang runtime not found. If erlang is installed check your $PATH. aborting.'
            exit 1
        fi
        export ROOTDIR="$("$__erl" -boot start_clean -eval 'io:format("~s~n", [code:root_dir()]), halt().' -noshell)"
        export ERTS_VSN="$(_get_erts_vsn)"
        export ERTS_DIR="$ROOTDIR"/"erts-$ERTS_VSN"
    else
        __erts_dir="$RELEASE_ROOT_DIR/erts-$ERTS_VSN"
        if [ -d "$__erts_dir" ]; then
            export ROOTDIR="$RELEASE_ROOT_DIR"
            export ERTS_DIR="$__erts_dir"
        else
            unset ERTS_VSN
            _init_erts_config
        fi
    fi
}

# Private. For determining the version of ERTS available to the release
# This is only ever called if ERTS_VSN is not set
_get_erts_vsn() {
    if [ -z "$ERTS_VSN" ]; then
        "$ROOTDIR"/bin/erl \
            -boot start_clean \
            -eval 'Ver = erlang:system_info(version), io:format("~s~n", [Ver]), halt()' \
            -noshell
    else
        echo "$ERTS_VSN"
    fi
}

# Private. Gets a list of code paths for this release
_get_code_paths() {
    "$ERTS_DIR/bin/escript" "$ROOTDIR/bin/release_utils.escript" "get_code_paths" "$ROOTDIR" "$ERTS_DIR" "$REL_NAME" "$REL_VSN"
}

# Private. Open a shell on a remote node
_rem_sh() {
    # Generate a unique id used to allow multiple remsh to the same node
    # transparently
    id="remsh$(gen_id)-${NAME}"

    # Get the node's ticktime so that we use the same thing.
    TICKTIME="$(nodetool rpcterms net_kernel get_net_ticktime)"

    # Setup remote shell command to control node
    if [ ! -z "$USE_ERL_SHELL" ]; then
        exec "$BINDIR/erl" \
            -hidden \
            -boot start_clean -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
            -kernel net_ticktime $TICKTIME \
            "$NAME_TYPE" "$id" -remsh "$NAME" -setcookie "$COOKIE"
    else
        __code_paths=$(_get_code_paths)
        exec "$BINDIR/erl" \
            -pa "$CONSOLIDATED_DIR" \
            ${__code_paths} \
            -hidden -noshell \
            -boot start_clean -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
            -kernel net_ticktime $TICKTIME \
            -user Elixir.IEx.CLI "$NAME_TYPE" "$id" -setcookie "$COOKIE" \
            -extra --no-halt +iex -"$NAME_TYPE" "$id" --cookie "$COOKIE" --remsh "$NAME"
    fi
}

# Output a start command for the last argument of run_erl
_start_command() {
    printf "exec \"%s\" \"%s\" -- %s %s" "$RELEASE_ROOT_DIR/bin/$REL_NAME" \
           "$START_OPTION" "${ARGS}" "${EXTRA_OPTS}"
}

# Do a textual replacement of ${VAR} occurrences in $1 and pipe to $2
_replace_os_vars() {
    awk '
        function escape(s) {
            gsub(/'\&'/, "\\\\&", s);
            return s;
        }
        {
            while(match($0,"[$]{[^}]*}")) {
                var=substr($0,RSTART+2,RLENGTH-3);
                gsub("[$]{"var"}", escape(ENVIRON[var]))
            }
        }1' < "$1" > "$1.bak"
    mv -- "$1.bak" "$1"
}

# Private. Run hooks from directory.
_run_hooks_from_dir() {
   if [ -d "$1" ]; then
      for file in $1/[0-9a-zA-Z]*.sh; do
        [ -f "$file" ] || continue
        . "$file"
      done
   fi
}

# Private. Sets config paths for sys.config and vm.args,
# and ensures that env var replacements are performed
_init_configs() {
    # Set VMARGS_PATH, the path to the vm.args file to use
    # Use $RELEASE_CONFIG_DIR/vm.args if exists, otherwise releases/VSN/vm.args
    if [ -z "$VMARGS_PATH" ]; then
        if [ -f "$RELEASE_CONFIG_DIR/vm.args" ]; then
            export VMARGS_PATH="$RELEASE_CONFIG_DIR/vm.args"
        else
            export VMARGS_PATH="$REL_DIR/vm.args"
        fi
        export SRC_VMARGS_PATH="$VMARGS_PATH"
    else
        if [ "$VMARGS_PATH" != "$RELEASE_MUTABLE_DIR/vm.args" ]; then
            export SRC_VMARGS_PATH="$VMARGS_PATH"
        fi
    fi
    if [ "$SRC_VMARGS_PATH" != "$RELEASE_MUTABLE_DIR/vm.args" ]; then
        echo "#### Generated - edit/create $RELEASE_CONFIG_DIR/vm.args instead." \
            >  "$RELEASE_MUTABLE_DIR/vm.args"
        cat  "$SRC_VMARGS_PATH"                              \
            >> "$RELEASE_MUTABLE_DIR/vm.args"
        export VMARGS_PATH="$RELEASE_MUTABLE_DIR"/vm.args
    fi
    if [ $REPLACE_OS_VARS ]; then
        _replace_os_vars "$VMARGS_PATH"
    fi

    # Set SYS_CONFIG_PATH, the path to the sys.config file to use
    # Use $RELEASE_CONFIG_DIR/sys.config if exists, otherwise releases/VSN/sys.config
    if [ -z "$SYS_CONFIG_PATH" ]; then
        if [ -f "$RELEASE_CONFIG_DIR/sys.config" ]; then
            export SYS_CONFIG_PATH="$RELEASE_CONFIG_DIR/sys.config"
        else
            export SYS_CONFIG_PATH="$REL_DIR/sys.config"
        fi
        export SRC_SYS_CONFIG_PATH="$SYS_CONFIG_PATH"
    else
        if [ "$SYS_CONFIG_PATH" != "$RELEASE_MUTABLE_DIR/sys.config" ]; then
            export SRC_SYS_CONFIG_PATH="$SYS_CONFIG_PATH"
        fi
    fi
    if [ "$SRC_SYS_CONFIG_PATH" != "$RELEASE_MUTABLE_DIR/sys.config" ]; then
        (echo "%% Generated - edit/create $RELEASE_CONFIG_DIR/sys.config instead."; \
        cat  "$SRC_SYS_CONFIG_PATH")                              \
            > "$RELEASE_MUTABLE_DIR/sys.config"
        export SYS_CONFIG_PATH="$RELEASE_MUTABLE_DIR"/sys.config
    fi
    if [ $REPLACE_OS_VARS ]; then
        _replace_os_vars "$SYS_CONFIG_PATH"
    fi
}

# If TERM is not set, set it to xterm
if [ -z "$TERM" ]; then
    export TERM=xterm
fi

# Make sure ERTS configuration is set
_init_erts_config
echo "$ERTS_VSN $REL_VSN" > "$START_ERL_DATA"

# Allow override of where to read configuration from
# By default it's RELEASE_ROOT_DIR
if [ -z "$RELEASE_CONFIG_DIR" ]; then
    export RELEASE_CONFIG_DIR="$RELEASE_ROOT_DIR"
fi

# Make sure directories exist
mkdir -p "$RELEASE_MUTABLE_DIR"
echo "Files in this directory are regenerated frequently, edits will be lost" \
    > "$RELEASE_MUTABLE_DIR/WARNING_README"
mkdir -p "$RUNNER_LOG_DIR"

# Make sure config paths are setup
_init_configs

# Extract the target node name from node.args
# Should be `-sname somename` or `-name somename@somehost`
export NAME_ARG="$(egrep '^-s?name' "$VMARGS_PATH" || true)"
if [ -z "$NAME_ARG" ]; then
    echo "vm.args needs to have either -name or -sname parameter."
    exit 1
fi

# Extract the name type and name from the NAME_ARG for REMSH
# NAME_TYPE should be -name or -sname
export NAME_TYPE="$(echo "$NAME_ARG" | awk '{print $1}')"
# NAME will be either `somename` or `somename@somehost`
export NAME="$(echo "$NAME_ARG" | awk '{print $2}')"

# Where the pipe will be stored when using `start`
# This is used so you can attach the running application to the current
# shell using `attach`
PIPE_DIR="${PIPE_DIR:-$RELEASE_MUTABLE_DIR/erl_pipes/$NAME/}"

# Extract the target cookie
_load_cookie

export ROOTDIR="$RELEASE_ROOT_DIR"
export BINDIR="$ERTS_DIR/bin"
export EMU="beam"
export PROGNAME="erl"
export LD_LIBRARY_PATH="$ERTS_DIR/lib:$LD_LIBRARY_PATH"
export ERTS_LIB_DIR="$ERTS_DIR/../lib"
CONSOLIDATED_DIR="$ROOTDIR/lib/${REL_NAME}-${REL_VSN}/consolidated"

cd "$ROOTDIR"

# User can specify an sname without @hostname
# This will fail when creating remote shell
# So here we check for @ and add @hostname if missing
case $NAME in
    *@*)
        # Nothing to do
        ;;
    *)
        NAME=$NAME@$(gen_nodename)
        ;;
esac

# For Elixir runtime
export DISTILLERY_TASK="$1"

# Check the first argument for instructions
case "$1" in
    start|start_boot)

        # Make sure there is not already a node running
        #RES=`$NODETOOL ping`
        #if [ "$RES" = "pong" ]; then
        #    echo "Node is already running!"
        #    exit 1
        #fi
        # Save this for later.
        CMD=$1
        case "$1" in
            start)
                shift
                START_OPTION="console"
                HEART_OPTION="start"
                ;;
            start_boot)
                shift
                START_OPTION="console_boot"
                HEART_OPTION="start_boot"
                ;;
        esac
        ARGS="$@"
        RUN_PARAM="$@"

        _check_cookie
        _run_hooks_from_dir "$PRE_CONFIGURE_HOOKS"
        _run_hooks_from_dir "$PRE_START_HOOKS"

        # Set arguments for the heart command
        set -- "$SCRIPT_DIR/$REL_NAME" "$HEART_OPTION"
        [ "$RUN_PARAM" ] && set -- "$@" "$RUN_PARAM"

        # Export the HEART_COMMAND
        HEART_COMMAND="$RELEASE_ROOT_DIR/bin/$REL_NAME $CMD"
        export HEART_COMMAND

        mkdir -p "$PIPE_DIR"

        "$BINDIR/run_erl" -daemon "$PIPE_DIR" "$RUNNER_LOG_DIR" \
                          "$(_start_command)"

        _run_hooks_from_dir "$POST_START_HOOKS"
        ;;

    stop)
        _check_cookie
        _run_hooks_from_dir "$PRE_STOP_HOOKS"
        # Wait for the node to completely stop...
        PID="$(get_pid)"
        if ! nodetool "stop"; then
            exit 1
        fi
        while $(kill -s 0 "$PID" 2>/dev/null);
        do
            sleep 1
        done
        _run_hooks_from_dir "$POST_STOP_HOOKS"
        ;;

    restart)
        _check_cookie
        _run_hooks_from_dir "$PRE_CONFIGURE_HOOKS"
        _run_hooks_from_dir "$PRE_START_HOOKS"
        ## Restart the VM without exiting the process
        if ! nodetool "restart"; then
            exit 1
        fi
        ;;

    reboot)
        _check_cookie
        _run_hooks_from_dir "$PRE_CONFIGURE_HOOKS"
        _run_hooks_from_dir "$PRE_START_HOOKS"
        ## Restart the VM completely (uses heart to restart it)
        if ! nodetool "reboot"; then
            exit 1
        fi
        ;;

    pid)
        ## Get the VM's pid
        _check_cookie
        if ! get_pid; then
            exit 1
        fi
        ;;

    ping)
        _check_cookie
        ## See if the VM is alive
        if ! nodetool "ping"; then
            exit 1
        fi
        ;;

    pingpeer)
        PEERNAME=$2 nodetool "ping"
        exit_status=$?
        if [ "$exit_status" -ne 0 ]; then
            exit $exit_status
        fi
        ;;

    escript)
        _check_cookie
        ## Run an escript under the node's environment
        if ! escript $@; then
            exit 1
        fi
        ;;

    attach)
        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        shift
        exec "$BINDIR/to_erl" "$PIPE_DIR"
        ;;

    remote_console)
        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        shift
        _rem_sh
        ;;

    upgrade|downgrade|install)
        if [ -z "$2" ]; then
            echo "Missing package argument"
            echo "Usage: $REL_NAME $1 {package base name}"
            echo "NOTE {package base name} MUST NOT include the .tar.gz suffix"
            exit 1
        fi

        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        SOURCE_VERSION="$REL_VSN"
        TARGET_VERSION="$2"

        "$BINDIR/escript" "$ROOTDIR/bin/release_utils.escript" \
             "unpack_release" "$REL_NAME" "$NAME_TYPE"  "$NAME" "$COOKIE" "$TARGET_VERSION"

        # Update environment to reflect target version environment
        REL_VSN="$TARGET_VERSION"
        REL_DIR="$RELEASE_ROOT_DIR/releases/$TARGET_VERSION"

        # Prepare new configs
        _init_configs
        # We have to do some juggling to ensure the correct config is used by the upgrade handler
        # First, we detect if there was a failed upgrade, so we can start over
        if [ -f "$REL_DIR/sys.config.bak" ]; then
            mv "$REL_DIR/sys.config.bak" "$REL_DIR/sys.config"
        fi
        if [ -f "$REL_DIR/vm.args.bak" ]; then
            mv "$REL_DIR/vm.args.bak" "$REL_DIR/vm.args"
        fi
        # Then, backup the packaged configs
        cp "$REL_DIR/sys.config" "$REL_DIR/sys.config.bak"
        cp "$REL_DIR/vm.args" "$REL_DIR/vm.args.bak"
        # Then, substitute in the prepared configs
        cp "$SYS_CONFIG_PATH" "$REL_DIR/sys.config"
        cp "$VMARGS_PATH" "$REL_DIR/vm.args"
        # Make sure any pre-configuration hooks are run
        _run_hooks_from_dir "$PRE_CONFIGURE_HOOKS"
        # Run any pre-upgrade tasks
        _run_hooks_from_dir "$PRE_UPGRADE_HOOKS"

        "$BINDIR/escript" "$ROOTDIR/bin/release_utils.escript" \
             "install_release" "$REL_NAME" "$NAME_TYPE"  "$NAME" "$COOKIE" "$TARGET_VERSION"

        # We were successful, clean up the configs
        mv "$REL_DIR/sys.config.bak" "$REL_DIR/sys.config"
        mv "$REL_DIR/vm.args.bak" "$REL_DIR/vm.args"

        # Run any post-upgrade hooks
        _run_hooks_from_dir "$POST_UPGRADE_HOOKS"

        ;;
    unpack)
        if [ -z "$2" ]; then
            echo "Missing package argument"
            echo "Usage: $REL_NAME $1 {package base name}"
            echo "NOTE {package base name} MUST NOT include the .tar.gz suffix"
            exit 1
        fi

        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        exec "$BINDIR/escript" "$ROOTDIR/bin/release_utils.escript" \
             "unpack_release" "$REL_NAME" "$NAME_TYPE" "$NAME" "$COOKIE" "$2"
        ;;

    console|console_clean|console_boot)
        # .boot file typically just $REL_NAME (ie, the app name)
        # however, for debugging, sometimes start_clean.boot is useful.
        # For e.g. 'setup', one may even want to name another boot script.
        case "$1" in
            console)
                if [ -f "$REL_DIR/$REL_NAME.boot" ]; then
                  BOOTFILE="$REL_DIR/$REL_NAME"
                else
                  BOOTFILE="$REL_DIR/start"
                fi
                ;;
            console_clean)
                BOOTFILE="$ROOTDIR/bin/start_clean"
                __code_paths=$(_get_code_paths)
                EXTRA_CODE_PATHS=${__code_paths}
                ;;
            console_boot)
                shift
                BOOTFILE="$1"
                shift
                ;;
        esac
        # Setup beam-required vars
        EMU="beam"
        PROGNAME="${0#*/}"

        export EMU
        export PROGNAME

        # Store passed arguments since they will be erased by `set`
        ARGS="$@"

        _check_cookie

        # Start the VM, executing pre_start hook along
        # the way. We can't run the post_start hook because
        # the console will crash with no TTY attached
        _run_hooks_from_dir "$PRE_CONFIGURE_HOOKS"
        _run_hooks_from_dir "$PRE_START_HOOKS"

        # Build an array of arguments to pass to exec later on
        # Build it here because this command will be used for logging.
        set -- "$BINDIR/erlexec" \
            -boot "$BOOTFILE" \
            -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
            -env ERL_LIBS "$REL_LIB_DIR" \
            -pa "$CONSOLIDATED_DIR" \
            ${EXTRA_CODE_PATHS} \
            -args_file "$VMARGS_PATH" \
            -config "$SYS_CONFIG_PATH" \
            -mode "$CODE_LOADING_MODE" \
            ${ERL_OPTS} \
            -user Elixir.IEx.CLI \
            -extra --no-halt +iex

        # Dump environment info for logging purposes
        if [ ! -z $VERBOSE ]; then
            echo "Exec: $@" -- ${1+$ARGS} ${EXTRA_OPTS}
            echo "Root: $ROOTDIR"

            # Log the startup
            echo "$RELEASE_ROOT_DIR"
        fi;

        logger -t "$REL_NAME[$$]" "Starting up"

        exec "$@" -- ${1+$ARGS} ${EXTRA_OPTS}
        ;;

    foreground)
        # start up the release in the foreground for use by runit
        # or other supervision services

        [ -f "$REL_DIR/$REL_NAME.boot" ] && BOOTFILE="$REL_NAME" || BOOTFILE=start
        FOREGROUNDOPTIONS="-noshell -noinput +Bd"

        # Setup beam-required vars
        EMU=beam
        PROGNAME="${0#*/}"

        export EMU
        export PROGNAME

        # Store passed arguments since they will be erased by `set`
        ARGS="$@"

        _check_cookie

        # Start the VM, executing pre and post start hooks
        _run_hooks_from_dir "$PRE_CONFIGURE_HOOKS"
        _run_hooks_from_dir "$PRE_START_HOOKS"

        # Build an array of arguments to pass to exec later on
        # Build it here because this command will be used for logging.
        set -- "$BINDIR/erlexec" $FOREGROUNDOPTIONS \
            -boot "$REL_DIR/$BOOTFILE" \
            -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
            -env ERL_LIBS "$REL_LIB_DIR" \
            -pa "$CONSOLIDATED_DIR" \
            -args_file "$VMARGS_PATH" \
            -config "$SYS_CONFIG_PATH" \
            -mode "$CODE_LOADING_MODE" \
            ${ERL_OPTS} \
            -extra ${EXTRA_OPTS}

        # Dump environment info for logging purposes
        if [ ! -z $VERBOSE ]; then
            echo "Exec: $@" -- ${1+$ARGS}
            echo "Root: $ROOTDIR"
        fi;

        "$@" -- ${1+$ARGS} &
        __bg_pid=$!
        _run_hooks_from_dir "$POST_START_HOOKS"
        wait $__bg_pid
        ;;
    rpc)
        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        shift

        nodetool rpc $@
        ;;
    rpcterms)
        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        shift

        nodetool rpcterms $@
        ;;
    eval)
        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        shift
        nodetool "eval" $@
        ;;
    command)
        # Execute as command-line utility
        #
        # Like the escript command, this does not start the OTP application.
        # If your command depends on a running OTP application,
        # use
        #
        #     {:ok, _} = Application.ensure_all_started(:your_app)

        _check_cookie

        shift
        MODULE="$1"; shift
        FUNCTION="$1"; shift

        # Save extra arguments
        ARGS="$@"

        # Build arguments for erlexec
        __code_paths=$(_get_code_paths)
        set -- "$ERL_OPTS"
        [ "$SYS_CONFIG_PATH" ] && set -- "$@" -config "$SYS_CONFIG_PATH"
        set -- "$@" -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR"
        set -- "$@" -noshell
        set -- "$@" -boot start_clean
        set -- "$@" -pa "$CONSOLIDATED_DIR"
        set -- "$@" ${__code_paths}
        set -- "$@" -s "$MODULE" "$FUNCTION"

        $BINDIR/erlexec $@ -extra $ARGS
        exit "$?"
        ;;
    reload_config)
        # Reload the running system's configuration
        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        _run_hooks_from_dir "$PRE_CONFIGURE_HOOKS"

        shift
        nodetool "reload_config" "$SYS_CONFIG_PATH"
        ;;
    describe)
        ## Print a summary of information about this release
        echo "$REL_NAME-$REL_VSN\n"
        echo "erts:       $ERTS_VSN"
        echo "path:       $REL_DIR"
        echo "sys.config: $SYS_CONFIG_PATH"
        echo "vm.args:    $VMARGS_PATH"
        echo "name:       $NAME"
        echo "cookie:     $COOKIE"
        echo "erl_opts:   ${ERL_OPTS:-none provided}"
        echo ""
        echo "hooks:"
        __has_hooks=0
        for hook in "$REL_DIR"/hooks/*.d/[0-9a-zA-Z]*.sh; do
            [ -f "$hook" ] || continue
            __has_hooks=1
            echo "$hook"
        done
        if [ $__has_hooks -eq 0 ]; then
            echo "No custom hooks found."
        fi
        echo ""
        echo "commands:"
        __has_commands=0
        for command in "$REL_DIR"/commands/*.sh; do
            [ -f "$command" ] || continue
            __has_commands=1
            echo "$command"
        done
        if [ $__has_commands -eq 0 ]; then
            echo "No custom commands found."
        fi
        exit 0
        ;;
    *)
        __command_path="$REL_DIR/commands/$1"
        if [ -f "$__command_path" ]; then
            _check_cookie
            . "$__command_path"
        else
            echo "Usage: $REL_NAME <task>"
            echo
            echo "Service Control"
            echo "======================="
            echo "start                          # start $REL_NAME as a daemon"
            echo "start_boot <file>              # start $REL_NAME as a daemon, but supply a custom .boot file"
            echo "foreground                     # start $REL_NAME in the foreground"
            echo "console                        # start $REL_NAME with a console attached"
            echo "console_clean                  # start a console with code paths set but no apps loaded/started"
            echo "console_boot <file>            # start $REL_NAME with a console attached, but supply a custom .boot file"
            echo "stop                           # stop the $REL_NAME daemon"
            echo "restart                        # restart the $REL_NAME daemon without shutting down the VM"
            echo "reboot                         # restart the $REL_NAME daemon"
            echo "reload_config                  # reload the current system's configuration from disk"
            echo
            echo "Upgrades"
            echo "======================="
            echo "upgrade <version>              # upgrade $REL_NAME to <version>"
            echo "downgrade <version>            # downgrade $REL_NAME to <version>"
            echo "install <version>              # install the $REL_NAME-<version> release, but do not upgrade to it"
            echo
            echo "Utilities"
            echo "======================="
            echo "attach                         # attach the current TTY to $REL_NAME's console"
            echo "remote_console                 # remote shell to $REL_NAME's console"
            echo "pid                            # get the pid of the running $REL_NAME instance"
            echo "ping                           # checks if $REL_NAME is running, pong is returned if successful"
            echo "pingpeer <peer>                # check if a peer node is running, pong is returned if successful"
            echo "escript <file>                 # execute an escript"
            echo "rpc <mod> <fun> [<args..>]     # execute an RPC call using the given MFA"
            echo "rpcterms <mod> <fun> [<expr>]  # execute an RPC call using the given Erlang expression for args"
            echo "eval <expr>                    # execute the given Erlang expression on the running node"
            echo "command <mod> <fun> [<args..>] # execute the given MFA"
            echo "describe                       # print useful information about the $REL_NAME release"
            echo
            echo "Custom Commands"
            echo "======================="
            __has_commands=0
            for command in "$REL_DIR"/commands/*.sh; do
                [ -f "$command" ] || continue
                __has_commands=1
                echo "$command"
            done
            if [ $__has_commands -eq 0 ]; then
                echo "No custom commands found."
            fi
            echo ""
            exit 1
        fi
        ;;
esac

exit 0


Reorganize head

Reorganize head

<!DOCTYPE html>

    <title>
      Today I Learned - <%= Application.get_env(:tilex, :organization_name) %><%= page_title(assigns) %>
    </title>

    <meta name="csrf" content="<%= Plug.CSRFProtection.get_csrf_token() %>">

    <meta name="format-detection" content="telephone=no">
    <meta name="description" content="TIL is an open-source project by Hashrocket that exists to catalogue the sharing & accumulation of knowledge as it happens day-to-day.">

    <meta itemprop="name" content="Today I Learned">
    <meta itemprop="brand" content="Today I Learned">
    <meta itemprop="description" content="TIL is an open-source project by Hashrocket that exists to catalogue the sharing & accumulation of knowledge as it happens day-to-day.">

    <meta name="viewport" content="width=device-width, initial-scale=1">

    <meta name="twitter:card" content="summary_large_image">
    <meta name="twitter:site" content="@hashrockettil">
    <meta name="twitter:creator" content="@hashrockettil">
    <meta name="twitter:title" content="Today I Learned: a Hashrocket Project">
    <meta name="twitter:description" content="TIL is an open-source project by Hashrocket that exists to catalogue the sharing & accumulation of knowledge as it happens day-to-day. Posts have a 200-word limit, and posting is open to any Rocketeer as well as select friends of the team. We hope you enjoy learning along with us.">
    <meta name="twitter:image" content="https://til.hashrocket.com/assets/til_twittercard.png">

    <link href='//fonts.googleapis.com/css?family=Raleway:700,900' rel='stylesheet' type='text/css'>
    <link href='//fonts.googleapis.com/css?family=Lora:400,700italic,700,400italic' rel='stylesheet' type='text/css'>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">

    <link rel="stylesheet" href="<%= static_path(@conn, "/css/app.css") %>">

    <%= if developer = Guardian.Plug.current_resource(@conn) do %>

 <%= link(developer.username, to: developer_path(@conn, :show, developer)) %>

 <%= link("Sign Out", to: auth_path(@conn, :delete), method: :delete) %>

 <%= link("Create Post", to: post_path(@conn, :new)) %>

 <%= link("Profile", to: developer_path(@conn, :edit)) %>

    <% end %>

Today I Learned

          A Hashrocket project

            Follow on Twitter

      <%= if message = get_flash(@conn, :info) do %>

          <%= message %>

      <% end %>

      <%= render @view_module, @view_template, assigns %>

          <%= link("search", to: "#", class: "site_nav__link") %>

            <%= form_tag post_path(@conn, :index), method: :get do %>
              <input type="search" name="q" id="q">
              <%= submit "Search" %>
            <% end %>

          <%= link("?", to: "#", class: "site_nav__link") %>

              Today I Learned is an open-source project by

                Hashrocket

              that exists to catalogue the sharing & accumulation of knowledge
              as it happens day-to-day. Posts have a 200-word limit, and
              posting is open to any Rocketeer as well as selected friends of
              Hashrocket. We hope you enjoy learning along with us.

                  <%= link("/hashrockettil", to: "https://twitter.com/hashrockettil", class: "social_twitter") %>

                  <%= link("/hashrocket/tilex", to: "https://github.com/hashrocket/tilex", class: "social_github") %>

          <%= link("Statistics", to: "/statistics", class: "site_nav__link") %>

            <%= link("Twitter", to: "https://twitter.com/hashrockettil", class: "site_nav__link") %>

            <%= link("Surprise Me", to: "/random", class: "site_nav__link") %>

<!DOCTYPE html>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="">
    <meta name="author" content="">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1">
    <meta name="csrf" content="<%= Plug.CSRFProtection.get_csrf_token() %>">

    <title>
      Today I Learned - <%= Application.get_env(:tilex, :organization_name) %><%= page_title(assigns) %>
    </title>

    <link rel="stylesheet" href="<%= static_path(@conn, "/css/app.css") %>">
    <link href='//fonts.googleapis.com/css?family=Raleway:700,900' rel='stylesheet' type='text/css'>
    <link href='//fonts.googleapis.com/css?family=Lora:400,700italic,700,400italic' rel='stylesheet' type='text/css'>

    <meta name="twitter:card" content="summary_large_image">
    <meta name="twitter:site" content="@hashrockettil">
    <meta name="twitter:creator" content="@hashrockettil">
    <meta name="twitter:title" content="Today I Learned: a Hashrocket Project">
    <meta name="twitter:description" content="TIL is an open-source project by Hashrocket that exists to catalogue the sharing & accumulation of knowledge as it happens day-to-day. Posts have a 200-word limit, and posting is open to any Rocketeer as well as select friends of the team. We hope you enjoy learning along with us.">
    <meta name="twitter:image" content="https://til.hashrocket.com/assets/til_twittercard.png">

    <%= if developer = Guardian.Plug.current_resource(@conn) do %>

 <%= link(developer.username, to: developer_path(@conn, :show, developer)) %>

 <%= link("Sign Out", to: auth_path(@conn, :delete), method: :delete) %>

 <%= link("Create Post", to: post_path(@conn, :new)) %>

 <%= link("Profile", to: developer_path(@conn, :edit)) %>

    <% end %>

Today I Learned

          A Hashrocket project

            Follow on Twitter

      <%= if message = get_flash(@conn, :info) do %>

          <%= message %>

      <% end %>

      <%= render @view_module, @view_template, assigns %>

          <%= link("search", to: "#", class: "site_nav__link") %>

            <%= form_tag post_path(@conn, :index), method: :get do %>
              <input type="search" name="q" id="q">
              <%= submit "Search" %>
            <% end %>

          <%= link("?", to: "#", class: "site_nav__link") %>

              Today I Learned is an open-source project by

                Hashrocket

              that exists to catalogue the sharing & accumulation of knowledge
              as it happens day-to-day. Posts have a 200-word limit, and
              posting is open to any Rocketeer as well as selected friends of
              Hashrocket. We hope you enjoy learning along with us.

                  <%= link("/hashrockettil", to: "https://twitter.com/hashrockettil", class: "social_twitter") %>

                  <%= link("/hashrocket/tilex", to: "https://github.com/hashrocket/tilex", class: "social_github") %>

          <%= link("Statistics", to: "/statistics", class: "site_nav__link") %>

            <%= link("Twitter", to: "https://twitter.com/hashrockettil", class: "site_nav__link") %>

            <%= link("Surprise Me", to: "/random", class: "site_nav__link") %>


Default strip_debug_info: true in release config

use Mix.Releases.Config,
  # This sets the default release built by `mix release`
  default_release: :default,
  # This sets the default environment used by `mix release`
  default_environment: :dev
<%= unless no_docs do %>
# For a full list of config options for both releases
# and environments, visit https://hexdocs.pm/distillery/config/distillery.html<% end %>
<%= unless no_docs do %>
# You may define one or more environments in this file,
# an environment's settings will override those of a release
# when building in that environment, this combination of release
# and environment configuration is called a profile
<% end %>
environment :dev do
  set(cookie: :"Unused with Nerves. See vm.args")
end

environment :prod do
  set(cookie: :"Unused with Nerves. See vm.args")
end
<%= unless no_docs do %>
# You may define one or more releases in this file.
# If you have not set a default release, or selected one
# when running `mix release`, the first release in the file
# will be used by default<% end %>
<%= for release <- releases do %><%= if Keyword.get(release, :is_umbrella) do %>
release :<%= Keyword.get(release, :release_name) %> do
  set(version: "0.1.0")
  set(strip_debug_info: true)
  set(applications: [
<%= Enum.map(Keyword.get(release, :release_applications), fn {app, start_type} ->
    "    #{app}: :#{start_type}"
    end) |> Enum.join(",\n") %>
  ])
  plugin(Nerves)
  plugin(Shoehorn)
end<% else %>
release :<%= Keyword.get(release, :release_name) %> do
  set(version: current_version(:<%= Keyword.get(release, :release_name)%>))
  set(strip_debug_info: true)
  plugin(Nerves)
  plugin(Shoehorn)
end<% end %><% end %>

use Mix.Releases.Config,
  # This sets the default release built by `mix release`
  default_release: :default,
  # This sets the default environment used by `mix release`
  default_environment: :dev
<%= unless no_docs do %>
# For a full list of config options for both releases
# and environments, visit https://hexdocs.pm/distillery/config/distillery.html<% end %>
<%= unless no_docs do %>
# You may define one or more environments in this file,
# an environment's settings will override those of a release
# when building in that environment, this combination of release
# and environment configuration is called a profile
<% end %>
environment :dev do
  set(cookie: :"Unused with Nerves. See vm.args")
end

environment :prod do
  set(cookie: :"Unused with Nerves. See vm.args")
end
<%= unless no_docs do %>
# You may define one or more releases in this file.
# If you have not set a default release, or selected one
# when running `mix release`, the first release in the file
# will be used by default<% end %>
<%= for release <- releases do %><%= if Keyword.get(release, :is_umbrella) do %>
release :<%= Keyword.get(release, :release_name) %> do
  set version: "0.1.0"
  set applications: [
<%= Enum.map(Keyword.get(release, :release_applications), fn {app, start_type} ->
    "    #{app}: :#{start_type}"
    end) |> Enum.join(",\n") %>
  ]
  plugin Nerves
  plugin Shoehorn
end<% else %>
release :<%= Keyword.get(release, :release_name) %> do
  set(version: current_version(:<%= Keyword.get(release, :release_name)%>))
  plugin(Nerves)
  plugin(Shoehorn)
end<% end %><% end %>


Add 'Join Us' href on landing page (#162)

Erlang Ecosystem Foundation

Supporting the next generation of advanced, reliable, realtime applications

The Erlang Ecosystem Foundation is a 501(c)(3) not-for-profit organization supported by over 100 members who embrace the foundations' collaborative Working Group model, and community-building events.

Our members include industry leaders who are dedicated to furthering state of the art for Erlang, Elixir, LFE and other technologies based on the BEAM.

                        Join Now →

Upcoming Events

                        <%= for event <- @events do %>

                                <div style="background-color: <%= event.organizer_brand_color %>; width: 6rem;" class="p-2 p-md-4 mr-4 rounded-left event-card-image-wrapper d-flex align-items-center">
                                    <img src="<%= event.organizer_brand_logo %>" class="img-fluid d-block mx-auto event-card-image">

                                        <%= link(event.title, to: event.url, class: "text-dark font-weight-bold") %>

<%= event_dates(event.start, event.end) %>

                        <% end %>

                        View All Events →

                    <%= right_svg_hero() do %>

                    <% end %>

Latest from the EEF

            <%= render(ErlefWeb.LayoutView, "twitter_feed.html", assigns) %>

        <%= render(ErlefWeb.LayoutView, "news_feed.html", assigns) %>

What does the EEF do?

                        In order to meet the needs of the ecosystem, the EEF sponsors Working Groups to solve specific challenges faced by users of BEAM technology. Areas of particular interest include documentation, interoperability, and performance.

            <%= render(ErlefWeb.LayoutView, "working_groups.html", assigns) %>

        <%= render(ErlefWeb.LayoutView, "subscribe.html", assigns) %>


Interpolate name, title, state and part of bill sponsor

    <%= @chamber %>

Recent Bills Passed

  <%= for bill <- @bills_passed do %>

      <a href="<%= bill["congressdotgov_url"] %>">
        <%= bill["title"] %>

      <%= gettext("%{title} %{sponsor_name} (%{party} - %{state})", title: bill["sponsor_title"], sponsor_name: bill["sponsor_name"], party: bill["sponsor_party"] , state: bill["sponsor_state"]) %>

  <% end %>

Recent Bills Introduced

  <%= for bill <- @bills_introduced do %>

      <a href="<%= bill[" congressdotgov_url "] %>">
        <%= bill["title"] %>

    <% end %>

Members

  <%= for member <- @members do %>

<%= member["first_name"] %> <%= member["last_name"] %>

  <% end %>

    <%= @chamber %>

Recent Bills Passed

  <%= for bill <- @bills_passed do %>

      <a href="<%= bill["congressdotgov_url"] %>">
        <%= bill["title"] %>

  <% end %>

Members

  <%= for member <- @members do %>

<%= member["first_name"] %> <%= member["last_name"] %>

  <% end %>


Replace textareas with text_inputs

Replace textareas with text_inputs

<%= form_for @changeset, @action, fn f -> %>
  <%= if @changeset.action do %>

Oops, something went wrong! Please check the errors below.

  <% end %>

    <%= label f, :name, class: "control-label" %>
    <%= error_tag f, :name %>
    <%= text_input f, :name, class: "form-control" %>

    <%= label f, :nickname, "Nickname (optional)", class: "control-label" %>
    <%= error_tag f, :nickname %>
    <%= text_input f, :nickname, class: "form-control" %>

    <%= label f, :kind, "Kind (anything you might want to know when picking this animal)", class: "control-label" %>
    <%= error_tag f, :kind %>
    <%= text_input f, :kind, class: "form-control" %>

    <%= label f, :procedure_description_kind, "Species", class: "control-label" %>
    <%= error_tag f, :procedure_description_kind %>
    <%= select f, :procedure_description_kind, @valid_species, class: "form-control" %>

    <%= label f, :date_removed_from_service, class: "control-label" %>
    <%= error_tag f, :date_removed_from_service %>

      <%= text_input f, :date_removed_from_service, class: "form-control" %>

        ...

    <%= submit "Submit", class: "btn btn-primary" %>

<% end %>

<%= form_for @changeset, @action, fn f -> %>
  <%= if @changeset.action do %>

Oops, something went wrong! Please check the errors below.

  <% end %>

    <%= label f, :name, class: "control-label" %>
    <%= error_tag f, :name %>
    <%= textarea f, :name, class: "form-control" %>

    <%= label f, :nickname, "Nickname (optional)", class: "control-label" %>
    <%= error_tag f, :nickname %>
    <%= textarea f, :nickname, class: "form-control" %>

    <%= label f, :kind, "Kind (anything you might want to know when picking this animal)", class: "control-label" %>
    <%= error_tag f, :kind %>
    <%= textarea f, :kind, class: "form-control" %>

    <%= label f, :procedure_description_kind, "Species", class: "control-label" %>
    <%= error_tag f, :procedure_description_kind %>
    <%= select f, :procedure_description_kind, @valid_species, class: "form-control" %>

    <%= label f, :date_removed_from_service, class: "control-label" %>
    <%= error_tag f, :date_removed_from_service %>

      <%= text_input f, :date_removed_from_service, class: "form-control" %>

        ...

    <%= submit "Submit", class: "btn btn-primary" %>

<% end %>


vote list design

vote list design

	<%= for group <- @groups do %>

			<%= for poll <- group do %>
				<a href="/polls/<%= poll.id %>" style="display: block; margin-bottom: 4px; margin-left: <%= to_string(poll.level * 25) %>px;">
					<%= if poll.reference_for_choice do %>
						<%= number_format(poll.reference_for_choice) %>
					<% end %>
					<%= poll.title %>
					<%= if poll.score do %>
						<%= number_format_simple(poll.score) %>
					<% end %>
					<%= if poll.approval_score do %>
						<%= if poll.approval_score == 1 do %>
							(approved reference)
						<% else %>
							(<%= poll.approval_score * 100 %>%)
						<% end %>
					<% end %>

			<% end %>

	<% end %>

	<%= for group <- @groups do %>

			<%= for poll <- group do %>
				<a href="/polls/<%= poll.id %>" style="display: block; margin-left: <%= to_string(poll.level * 50) %>px;">
					<%= raw "#{poll.reference_for_choice} #{poll.title}: #{poll.score} (#{poll.approval_score})" %>

			<% end %>

	<% end %>


Update landing page copy

Update landing page copy

  <iframe scrolling="no" frameborder="0" allowtransparency="true" class="twitter-share-button twitter-share-button-rendered twitter-tweet-button" style="position: static; visibility: visible; width: 61px; height: 20px;" title="Twitter Tweet Button" src="https://platform.twitter.com/widgets/tweet_button.2e798283373a8137c24e277b9b9620d6.en.html#dnt=false&id=twitter-widget-0&lang=en&original_referer=https%3A%2F%2Fsemantic-ui.com%2F&size=m&text=I%20%E2%9D%A4%20RemoteRetro!%20Like,%20big%20time.&time=1519956156939&type=share&url=http%3A%2F%2Fremoteretro.org&via=StrideDev&hashtags=myelixirstatus,opensource" data-url="http://remoteretro.org">

    Remote

    Retro

    Agile retrospectives for distributed teams

RemoteRetro lets you get together, even when you're far apart.

    Sign in with Google to Get Started

            Free!

Costs less than a song!

            Collaborative!

Interact with teammates in real time!

            Guided!

A retro on rails!

            Completely Open Source!

            GitHub

Trusted By

          RemoteRetro provides a framework for the entire retrospective process, from idea generation to discussion to identification of action items, and it does so no matter what the limitations of the physical space may be. It's a great equalizer, and a major step forward for retro organizers and participants.’

          David A. Black

          author, The Well-Grounded Rubyist

        Sign in with Google to Get Started

        F.A.Q.

<link rel="prefetch" href="<%= static_path(@conn, "/js/jquery.min.js") %>">
<link rel="prefetch" href="<%= static_path(@conn, "/js/semantic.min.js") %>">

  <iframe scrolling="no" frameborder="0" allowtransparency="true" class="twitter-share-button twitter-share-button-rendered twitter-tweet-button" style="position: static; visibility: visible; width: 61px; height: 20px;" title="Twitter Tweet Button" src="https://platform.twitter.com/widgets/tweet_button.2e798283373a8137c24e277b9b9620d6.en.html#dnt=false&id=twitter-widget-0&lang=en&original_referer=https%3A%2F%2Fsemantic-ui.com%2F&size=m&text=I%20%E2%9D%A4%20RemoteRetro!%20Like,%20big%20time.&time=1519956156939&type=share&url=http%3A%2F%2Fremoteretro.org&via=StrideDev&hashtags=myelixirstatus,opensource" data-url="http://remoteretro.org">

    Remote

    Retro

    Effective retrospectives for distributed teams

RemoteRetro lets you come together & improve, even when you're far apart.

    Sign in with Google to Get Started

            Free!

Costs less than a song!

            Collaborative!

Interact with teammates in real time!

            Guided!

A retro on rails!

            Completely Open Source!

            GitHub

Trusted By

          RemoteRetro provides a framework for the entire retrospective process, from idea generation to discussion to identification of action items, and it does so no matter what the limitations of the physical space may be. It's a great equalizer, and a major step forward for retro organizers and participants.’

          David A. Black

          author, The Well-Grounded Rubyist

        Sign in with Google to Get Started

        F.A.Q.

<link rel="prefetch" href="<%= static_path(@conn, "/js/jquery.min.js") %>">
<link rel="prefetch" href="<%= static_path(@conn, "/js/semantic.min.js") %>">


Fixed undefined function bug in work_experiences

Closes #447

<%# header tag begins in parent template %>

<%= gettext("About this Experience") %>

<%= render Vutuv.LayoutView, "flash.html", assigns %>

  <%= gen_breadcrumbs([
    gettext("Users"),
    {full_name(@user), user_path(@conn, :show, @user)},
    {gettext("Experience"), user_work_experience_path(@conn, :index, @user)},
    gettext("Show")]) %>

    <%= if same_user?(@user, @current_user) do %>

      <%= link to: user_work_experience_path(@conn, :edit, @user, @work_experience), class: "button button-link button--icon button--small" do %>

      <% end %>
      <%= button to: user_work_experience_path(@conn, :delete, @user, @work_experience), class: "button button-link button--icon button--small", form: [class: "button button--icon button--small"] do %>

      <% end %>

    <% end %>

<%= gettext("Job Title") %>

<%= @work_experience.title %>

<%= gettext("Organization") %>

<%= @work_experience.organization %>

<%= gettext("Time Period") %>

      <%= format_duration(@work_experience.start_month, @work_experience.start_year, @work_experience.end_month, @work_experience.end_year) %>

  <%= render Vutuv.SharedView, "_ad.html" %>

<%# header tag begins in parent template %>

<%= gettext("About this Experience") %>

<%= render Vutuv.LayoutView, "flash.html", assigns %>

  <%= gen_breadcrumbs([
    gettext("Users"),
    {full_name(@user), user_path(@conn, :show, @user)},
    {gettext("Experience"), user_work_experience_path(@conn, :index, @user)},
    gettext("Show")]) %>

    <%= if same_user?(@user, @current_user) do %>

      <%= link to: user_work_experience_path(@conn, :edit, @user, @work_experience), class: "button button-link button--icon button--small" do %>

      <% end %>
      <%= button to: user_work_experience_path(@conn, :delete, @user, @work_experience), class: "button button-link button--icon button--small", form: [class: "button button--icon button--small"] do %>

      <% end %>

    <% end %>

<%= gettext("Job Title") %>

<%= @work_experience.title %>

<%= gettext("Organization") %>

<%= @work_experience.organization %>

<%= gettext("Time Period") %>

      <%= "#{@work_experience.start_month}/#{@work_experience.start_year}" %>
      <%= if(Vutuv.WorkExperience.has_end_date? @work_experience) do %>
      <%= "- #{@work_experience.end_month}/#{@work_experience.end_year}" %>
      <% else %>
      - Present
      <% end %>

  <%= render Vutuv.SharedView, "_ad.html" %>


style(admin): fix index template code style

Listing

      <%
        new_direction = case @direction do
          "asc" ->
            "desc"
          _ ->
            "asc"
        end
      %>
      <%= for {id, column} <- @model.admin_fields do %>
        <%= if column.type == :integer || column.type == :string || column.type == :boolean || column.type == :datetime do %>

            <%
              if id == @sort do
                local_direction = new_direction
              else
                local_direction = @direction
              end
            %>
            <a href="<%= admin_path(@conn, :index, @resource, sort: id, direction: local_direction) %>"
               class="<%= if id == @sort do %><%= if @direction == "asc" do %>down<% else %>up<% end %><% end %>">
              <%= column[:label] %>

        <% end %>
      <% end %>

<%= for entry <- @entries do %>

      <%= for {id, column} <- @model.admin_fields do %>
        <%= if column.type == :integer || column.type == :string || column.type == :boolean || column.type == :datetime do %>
          <%= Map.get(entry, id, nil) %>
        <% end %>
      <% end %>

        <%= link "Edit", to: admin_path(@conn, :edit, @resource, entry), class: "btn btn-default btn-xs" %>
        <%= link "Delete", to: admin_path(@conn, :delete, @resource, entry), method: :delete, data: [confirm: "Are you sure?"], class: "btn btn-danger btn-xs" %>

<% end %>

<%= link "New", to: admin_path(@conn, :new, @resource) %>

Listing

      <%
        if @direction == "asc" do
          new_direction = "desc"
        else
          new_direction = "asc"
        end
      %>
      <%= for {id, column} <- @model.admin_fields do %>
        <%= if column.type == :integer || column.type == :string || column.type == :boolean || column.type == :datetime do %>

            <%
              if id == @sort do
                local_direction = new_direction
              else
                local_direction = @direction
              end
            %>
            <a href="<%= admin_path(@conn, :index, @resource, sort: id, direction: local_direction) %>"
               class="<%= if id == @sort do %><%= if @direction == "asc" do %>down<% else %>up<% end %><% end %>">
              <%= column[:label] %>

        <% end %>
      <% end %>

<%= for entry <- @entries do %>

      <%= for {id, column} <- @model.admin_fields do %>
        <%= if column.type == :integer || column.type == :string || column.type == :boolean || column.type == :datetime do %>
          <%= Map.get(entry, id, nil) %>
        <% end %>
      <% end %>

        <%= link "Edit", to: admin_path(@conn, :edit, @resource, entry), class: "btn btn-default btn-xs" %>
        <%= link "Delete", to: admin_path(@conn, :delete, @resource, entry), method: :delete, data: [confirm: "Are you sure?"], class: "btn btn-danger btn-xs" %>

<% end %>

<%= link "New", to: admin_path(@conn, :new, @resource) %>


Fixing some problems that could cause for the menu to be open

      Classes

      <%= link "New class", to: class_path(@conn, :new), class: "mdl-button mdl-js-button mdl-button--raised" %>

  <%= for term <- @terms do %>

            <%= term.name %>

            Class date
            Starting at
            Finishes at
            Classroom
            <%= if @conn.assigns.current_user.role == "Coordinator" do %>

            <% end %>

          <%= for offered_course <- term.offered_courses do %>

              <%= offered_course.course.name %>

            <%= Enum.with_index(offered_course.classes) |> Enum.map(fn {class,index} -> %>

                  <%= class.date %>

                  <%= class.date
                      |> Ecto.DateTime.from_date_and_time(class.starting_at)
                      |> Timex.format!("{h24}:{m}") %>

                  <%= class.date
                      |> Ecto.DateTime.from_date_and_time(class.finishes_at)
                      |> Timex.format!("{h24}:{m}") %>

                  <%= class.classroom %>

                <%= if @conn.assigns.current_user.role == "Coordinator" do %>

                    <button id="tr_menu_<%= class.id %>"
                            class="mdl-button mdl-js-button mdl-button--icon"
                    >
                      more_vert
                    </button>
                    <ul
                      class="
                        mdl-menu mdl-js-menu
                        <%=
                          if index > 10 and index > length(@classes)-4 do
                            'mdl-menu--top-right'
                          else
                            'mdl-menu--bottom-right'
                          end
                        %>
                      "
                      for="tr_menu_<%= class.id %>"
                    >

                        <%= link "Edit", to: class_path(@conn, :edit, class) %>

                        <%= link "Delete", to: class_path(@conn, :delete, class), method: :delete,
                          data: [confirm: """
                          Are you sure?
                          All the attendances of that class will be removed.
                          """] %>

                <% end %>

            <% end) %>
          <% end %>

  <% end %>

      Classes

      <%= link "New class", to: class_path(@conn, :new), class: "mdl-button mdl-js-button mdl-button--raised" %>

  <%= for term <- @terms do %>

      <%= CoursePlannerWeb.SharedView.card do %>
        <%= CoursePlannerWeb.SharedView.card_content vpadding: true do %>

                  <%= term.name %>

                  Class date
                  Starting at
                  Finishes at
                  Classroom
                  <%= if @conn.assigns.current_user.role == "Coordinator" do %>

                  <% end %>

                <%= for offered_course <- term.offered_courses do %>

                    <%= offered_course.course.name %>

                  <%= Enum.with_index(offered_course.classes) |> Enum.map(fn {class,index} -> %>

                        <%= class.date %>

                        <%= class.date
                            |> Ecto.DateTime.from_date_and_time(class.starting_at)
                            |> Timex.format!("{h24}:{m}") %>

                        <%= class.date
                            |> Ecto.DateTime.from_date_and_time(class.finishes_at)
                            |> Timex.format!("{h24}:{m}") %>

                        <%= class.classroom %>

                      <%= if @conn.assigns.current_user.role == "Coordinator" do %>

                          <button id="tr_menu_<%= class.id %>"
                                  class="mdl-button mdl-js-button mdl-button--icon"
                          >
                            more_vert
                          </button>
                          <ul
                            class="
                              mdl-menu mdl-js-menu
                              <%=
                                if index > 10 and index > length(@classes)-4 do
                                  'mdl-menu--top-right'
                                else
                                  'mdl-menu--bottom-right'
                                end
                              %>
                            "
                            for="tr_menu_<%= class.id %>"
                          >

                              <%= link "Edit", to: class_path(@conn, :edit, class) %>

                              <%= link "Delete", to: class_path(@conn, :delete, class), method: :delete,
                                data: [confirm: """
                                Are you sure?
                                All the attendances of that class will be removed.
                                """] %>

                      <% end %>

                  <% end) %>
                <% end %>

        <% end %>
      <% end %>

  <% end %>


removing offending line

removing offending line

<a href="https://steemit.com/@<%= @post.author %>/<%= @post.permlink %>"><%= @post.title %>

Author: <a href="https://beyondboinc.net/authors/<%= @post.author %>/"><%= @post.author %>

        <%= raw(@post.body) %>

		  Home
		  /

Author: <a href="https://beyondboinc.net/authors/<%= @post.author %>/"><%= @post.author %>

		  /

<a href="https://steemit.com/@<%= @post.author %>/<%= @post.permlink %>"><%= @post.permlink %>


Use lightgrey for toggle shields

Use lightgrey for toggle shields

<!DOCTYPE html>

<meta charset="utf-8">

<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="/style.css">

<title>Android SDK API level - <%=@lib%></title>

Android SDK API level of @aar artifacts

<form method="get" style="display:flex">
<input type="search" name="library" placeholder="groupId:artifactId:version" value="<%=@lib%>" autocapitalize="none">
<input type="submit" value="Search">

</form>

<a id="url" href="<%=@path%>@svg">
<%=@host%><%=@path%>@svg

<a id="badge" href="<%=@path%>">
<img alt="API level <%=@api%>" src="<%=@path%>@svg">

Android version history

Android 10
[2019]

Android 9.0 Pie
[2018]

Android 8.1 Oreo
[2017]

Android 8.0 Oreo
[2017]

Android 7.1 Nougat
[2016]

Android 7.0 Nougat
[2016]

Android 6.0 Marshmallow
[2015]

Android 5.1 Lollipops
[2015]

Android 5.0 Lollipop
[2014]

<input id="y" type="checkbox">

Android 4.4 and before
[2008-2014]

Android 4.4 KitKat Wear
[2014]

Android 4.4 KitKat
[2013]

Android 4.3 Jelly Bean
[2013]

Android 4.2 Jelly Bean
[2012]

Android 4.1 Jelly Bean
[2012]

Android 4.0.3 Ice Cream Sandwich
[2011]

Android 4.0 Ice Cream Sandwich
[2011]

<input id="r" type="checkbox">

Android 3.2 and before
[2008-2011]

Android 3.2 Honeycomb
[2011]

Android 3.1 Honeycomb
[2011]

Android 3.0 Honeycomb
[2011]

Android 2.3.3 Gingerbread
[2011]

Android 2.3 Gingerbread
[2010]

Android 2.2 Froyo
[2010]

Android 2.1 Eclair
[2010]

Android 2.0.1 Eclair
[2009]

Android 2.0 Eclair
[2009]

Android 1.6 Donut
[2009]

Android 1.5 Cupcake
[2009]

Android 1.1
[2009]

Android 1.0
[2008]

Repositories

Maven Central

Google

JCenter

JitPack

References

shields.io

Android

Versions

Markdown

Contribute

asapi/lv

support

clear cache

© 2017   tynn   ♥ Berlin


Copy lib before assets.deploy

Copy lib before assets.deploy

# Find eligible builder and runner images on Docker Hub. We use Ubuntu/Debian instead of
# Alpine to avoid DNS resolution issues in production.
#
# https://hub.docker.com/r/hexpm/elixir/tags?page=1&name=ubuntu
# https://hub.docker.com/_/ubuntu?tab=tags
#
#
# This file is based on these images:
#
#   - https://hub.docker.com/r/hexpm/elixir/tags - for the build image
#   - https://hub.docker.com/_/debian?tab=tags&page=1&name=bullseye-20210902-slim - for the release image
#   - https://pkgs.org/ - resource for finding needed packages
#   - Ex: hexpm/elixir:<%= elixir_vsn %>-erlang-<%= otp_vsn %>-debian-bullseye-20210902-slim
#
ARG BUILDER_IMAGE="hexpm/elixir:<%= elixir_vsn %>-erlang-<%= otp_vsn %>-debian-bullseye-20210902-slim"
ARG RUNNER_IMAGE="debian:bullseye-20210902-slim"

FROM ${BUILDER_IMAGE} as builder

# install build dependencies
RUN apt-get update -y && apt-get install -y build-essential git \
    && apt-get clean && rm -f /var/lib/apt/lists/*_*

# prepare build dir
WORKDIR /app

# install hex + rebar
RUN mix local.hex --force && \
    mix local.rebar --force

# set build ENV
ENV MIX_ENV="prod"

# install mix dependencies
COPY mix.exs mix.lock ./
RUN mix deps.get --only $MIX_ENV
RUN mkdir config

# copy compile-time config files before we compile dependencies
# to ensure any relevant config change will trigger the dependencies
# to be re-compiled.
COPY config/config.exs config/${MIX_ENV}.exs config/
RUN mix deps.compile

COPY priv priv

COPY lib lib

COPY assets assets

# compile assets
RUN mix assets.deploy

# Compile the release
RUN mix compile

# Changes to config/runtime.exs don't require recompiling the code
COPY config/runtime.exs config/

COPY rel rel
RUN mix release

# start a new build stage so that the final image will only contain
# the compiled release and other runtime necessities
FROM ${RUNNER_IMAGE}

RUN apt-get update -y && apt-get install -y libstdc++6 openssl libncurses5 locales \
  && apt-get clean && rm -f /var/lib/apt/lists/*_*

# Set the locale
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen

ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8

WORKDIR "/app"
RUN chown nobody /app

# set runner ENV
ENV MIX_ENV="prod"

# Only copy the final release from the build stage
COPY --from=builder --chown=nobody:root /app/_build/${MIX_ENV}/rel/<%= otp_app %> ./

USER nobody

CMD ["/app/bin/server"]

# Find eligible builder and runner images on Docker Hub. We use Ubuntu/Debian instead of
# Alpine to avoid DNS resolution issues in production.
#
# https://hub.docker.com/r/hexpm/elixir/tags?page=1&name=ubuntu
# https://hub.docker.com/_/ubuntu?tab=tags
#
#
# This file is based on these images:
#
#   - https://hub.docker.com/r/hexpm/elixir/tags - for the build image
#   - https://hub.docker.com/_/debian?tab=tags&page=1&name=bullseye-20210902-slim - for the release image
#   - https://pkgs.org/ - resource for finding needed packages
#   - Ex: hexpm/elixir:<%= elixir_vsn %>-erlang-<%= otp_vsn %>-debian-bullseye-20210902-slim
#
ARG BUILDER_IMAGE="hexpm/elixir:<%= elixir_vsn %>-erlang-<%= otp_vsn %>-debian-bullseye-20210902-slim"
ARG RUNNER_IMAGE="debian:bullseye-20210902-slim"

FROM ${BUILDER_IMAGE} as builder

# install build dependencies
RUN apt-get update -y && apt-get install -y build-essential git \
    && apt-get clean && rm -f /var/lib/apt/lists/*_*

# prepare build dir
WORKDIR /app

# install hex + rebar
RUN mix local.hex --force && \
    mix local.rebar --force

# set build ENV
ENV MIX_ENV="prod"

# install mix dependencies
COPY mix.exs mix.lock ./
RUN mix deps.get --only $MIX_ENV
RUN mkdir config

# copy compile-time config files before we compile dependencies
# to ensure any relevant config change will trigger the dependencies
# to be re-compiled.
COPY config/config.exs config/${MIX_ENV}.exs config/
RUN mix deps.compile

COPY priv priv

# note: if your project uses a tool like https://purgecss.com/,
# which customizes asset compilation based on what it finds in
# your Elixir templates, you will need to move the asset compilation
# step down so that `lib` is available.
COPY assets assets

# compile assets
RUN mix assets.deploy

# Compile the release
COPY lib lib

RUN mix compile

# Changes to config/runtime.exs don't require recompiling the code
COPY config/runtime.exs config/

COPY rel rel
RUN mix release

# start a new build stage so that the final image will only contain
# the compiled release and other runtime necessities
FROM ${RUNNER_IMAGE}

RUN apt-get update -y && apt-get install -y libstdc++6 openssl libncurses5 locales \
  && apt-get clean && rm -f /var/lib/apt/lists/*_*

# Set the locale
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen

ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8

WORKDIR "/app"
RUN chown nobody /app

# set runner ENV
ENV MIX_ENV="prod"

# Only copy the final release from the build stage
COPY --from=builder --chown=nobody:root /app/_build/${MIX_ENV}/rel/<%= otp_app %> ./

USER nobody

CMD ["/app/bin/server"]


add integration test using httpc

add integration test using httpc

defmodule <%= @module %>Test do
  use ExUnit.Case, async: true
  doctest <%= @module %>

  setup %{} do
    # OS will assign a free port when service is started with port 0.
    {:ok, service} = <%= @module %>.WWW.start_link(%{}, port: 0, cleartext: true)
    {:ok, port} = Ace.HTTP.Service.port(service)

    {:ok, port: port}
  end

  test "Serves homepage", %{port: port} do
    assert {:ok, response} = :httpc.request('http://localhost:#{port}')
    assert {{_, 200, 'OK'}, _headers, _body} = response
  end
end

defmodule <%= @module %>Test do
  use ExUnit.Case
  doctest <%= @module %>

end


Edited user_skill#index

Edited user_skill#index

Now uses table instead of user_skill card_list template

  <%= for user_skill <- @user_skills do %>
    <li class="<%= if @current_user && Vutuv.Endorsement.skill_endorsed?(user_skill.id, @current_user_id), do: "upvoted" %>">
        <%= link to: user_user_skill_path(@conn, :show, @user, user_skill), class: "tag" do %>
          <%= Skill.resolve_name(user_skill.skill_id) %>
          <%= Enum.count(user_skill.endorsements) %>
        <% end %>
      <%= if @current_user do %>
        <%= if Vutuv.Endorsement.skill_endorsed?(user_skill.id, @current_user_id) do %>
          <%= button to: user_endorsement_path(@conn, :delete, @user, user_skill), method: :delete , class: "button-link", form: [class: "upvote button_list"] do %>

          <% end %>
        <%= else %>
          <%= button to: user_endorsement_path(@conn, :create, @user, id: user_skill), method: :post, class: "button-link", form: [class: "upvote button_list"] do %>

          <% end %>
        <%= end %>
      <% end %>

  <% end %>

  <%= for user_skill <- @user_skills do %>
    <li class="<%= if @current_user && Vutuv.Endorsement.skill_endorsed?(user_skill.id, @current_user_id), do: "upvoted" %>">
        <%= link to: user_user_skill_path(@conn, :show, @user, user_skill), class: "tag" do %>
          <%= Skill.resolve_name(user_skill.skill_id) %>
          <%= Enum.count(user_skill.endorsements) %>
        <% end %>
      <%= if @current_user do %>
        <%= if Vutuv.Endorsement.skill_endorsed?(user_skill.id, @current_user_id) do %>
          <%= button to: user_endorsement_path(@conn, :delete, @user, user_skill), method: :delete , class: "button-link", form: [class: "upvote button_list"] do %>

          <% end %>
        <%= else %>
          <%= button to: user_endorsement_path(@conn, :create, @user, id: user_skill), method: :post, class: "button-link", form: [class: "upvote button_list"] do %>

          <% end %>
        <%= end %>
      <% end %>
      <%= if same_user?(@user, @current_user) do %>
      <%= button to: user_user_skill_path(@conn, :delete, @user, user_skill), method: :delete, class: "delete button button--icon button--small", style: "height:25px;margin-right:-5px;", form: [class: "button button--icon button--small"] do %>

      <% end %>
      <% end %>

  <% end %>