Update boot.eex

Update boot.eex

#!/bin/sh

set -e

# Path to this script
SCRIPT=$(readlink $0 || true)
if [ -z $SCRIPT ]; then
    SCRIPT=$0
fi;
# Parent directory of this script
SCRIPT_DIR="$(cd `dirname "$SCRIPT"` && pwd -P)"
# Root directory of all releases
RELEASE_ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd -P)"
# Name of the release
REL_NAME="<%= release_name %>"
# Current version of the release
REL_VSN="<%= release_version %>"
# Current version of ERTS being used
ERTS_VSN="<%= erts_vsn %>"
# 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}"
# Directory containing the current version of this release
REL_DIR="$RELEASE_ROOT_DIR/releases/$REL_VSN"
# The lib directory for this release
REL_LIB_DIR="$RELEASE_ROOT_DIR/lib"
# Options passed to erl
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
RUNNER_LOG_DIR="${RUNNER_LOG_DIR:-$RELEASE_ROOT_DIR/log}"
# A string of extra options to pass to erl, here for plugins
EXTRA_OPTS=""

# Pre-start/stop event hook paths
PRE_START_HOOK="$REL_DIR/hooks/pre_start"
POST_START_HOOK="$REL_DIR/hooks/post_start"
PRE_STOP_HOOK="$REL_DIR/hooks/pre_stop"
POST_STOP_HOOK="$REL_DIR/hooks/post_stop"

# 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

    "$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. For setting ERTS_DIR and ROOTDIR
_find_erts_dir() {
    __erts_dir="$RELEASE_ROOT_DIR/erts-$ERTS_VSN"
    if [ -d "$__erts_dir" ]; then
        ERTS_DIR="$__erts_dir";
        ROOTDIR="$RELEASE_ROOT_DIR"
    else
        __erl="$(which erl)"
        code="io:format(\"~s\", [code:root_dir()]), halt()."
        __erl_root="$("$__erl" -noshell -eval "$code")"
        ERTS_DIR="$__erl_root/erts-$ERTS_VSN"
        ROOTDIR="$__erl_root"
    fi
}

# 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
        exec "$BINDIR/erl" \
            -pa "$ROOTDIR"/lib/*/ebin -pa "$CONSOLIDATED_DIR" \
            -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}"
}

# Allow override of where to look for config files
# By default it's $CWD/
if [ -z "$RELEASE_CONFIG_DIR" ]; then
    RELEASE_CONFIG_DIR="$RELEASE_ROOT_DIR"
fi

# 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
        VMARGS_PATH="$RELEASE_CONFIG_DIR/vm.args"
    else
        VMARGS_PATH="$REL_DIR/vm.args"
    fi
fi

orig_vmargs_path="$VMARGS_PATH.orig"
if [ $REPLACE_OS_VARS ]; then
    #Make sure we don't break dev mode by keeping the symbolic link to
    #the user's vm.args
    if [ ! -L "$orig_vmargs_path" ]; then
       #we're in copy mode, rename the vm.args file to vm.args.orig
       mv "$VMARGS_PATH" "$orig_vmargs_path"
    fi

    awk '{while(match($0,"[$]{[^}]*}")) {var=substr($0,RSTART+2,RLENGTH -3);gsub("[$]{"var"}",ENVIRON[var])}}1' < "$orig_vmargs_path" > "$VMARGS_PATH"
 else
    #We don't need to replace env. vars, just rename the
    #symlink vm.args.orig to vm.args, and keep it as a
    #symlink.
    if [ -L "$orig_vmargs_path" ]; then
       mv "$orig_vmargs_path" "$VMARGS_PATH"
    fi
fi

# Make sure log directory exists
mkdir -p "$RUNNER_LOG_DIR"

# 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
        SYS_CONFIG_PATH="$RELEASE_CONFIG_DIR/sys.config"
    else
        SYS_CONFIG_PATH="$REL_DIR/sys.config"
    fi
fi

orig_sys_config_path="$SYS_CONFIG_PATH.orig"
if [ $REPLACE_OS_VARS ]; then
    #Make sure we don't break dev mode by keeping the symbolic link to
    #the user's sys.config
    if [ ! -L "$orig_sys_config_path" ]; then
       #We're in copy mode, rename sys.config to sys.config.orig
       mv "$SYS_CONFIG_PATH" "$orig_sys_config_path"
    fi

    awk '{while(match($0,"[$]{[^}]*}")) {var=substr($0,RSTART+2,RLENGTH -3);gsub("[$]{"var"}",ENVIRON[var])}}1' < "$orig_sys_config_path" > "$SYS_CONFIG_PATH"
 else
    #We don't need to replace env. vars, just rename the
    #symlink sys.config.orig to sys.config. Keep it as
    #a symlink.
    if [ -L "$orig_sys_config_path" ]; then
       mv "$orig_sys_config_path"  "$SYS_CONFIG_PATH"
    fi
fi

# Extract the target node name from node.args
# Should be `-sname somename` or `-name somename@somehost`
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
NAME_TYPE="$(echo "$NAME_ARG" | awk '{print $1}')"
# NAME will be either `somename` or `somename@somehost`
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:-/tmp/erl_pipes/$NAME/}"

# Extract the target 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")"
    else
        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
else
    # Extract cookie name from COOKIE_ARG
    COOKIE="$(echo "$COOKIE_ARG" | awk '{print $2}')"
fi

_find_erts_dir
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"
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

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

        [ -f "$PRE_START_HOOK" ] && . "$PRE_START_HOOK"

        # 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)"

        [ -f "$POST_START_HOOK" ] && . "$POST_START_HOOK"
        ;;

    stop)
        [ -f "$PRE_STOP_HOOK" ] && . "$PRE_STOP_HOOK"
        # 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
        [ -f "$POST_STOP_HOOK" ] && . "$POST_STOP_HOOK"
        ;;

    restart)
        [ -f "$PRE_START_HOOK" ] && . "$PRE_START_HOOK"
        ## Restart the VM without exiting the process
        if ! nodetool "restart"; then
            exit 1
        fi
        ;;

    reboot)
        [ -f "$PRE_START_HOOK" ] && . "$PRE_START_HOOK"
        ## Restart the VM completely (uses heart to restart it)
        if ! nodetool "reboot"; then
            exit 1
        fi
        ;;

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

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

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

    attach)
        # 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)
        # 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

        # 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/install_upgrade.escript" \
             "install" "$REL_NAME" "$NAME_TYPE" "$NAME" "$COOKIE" "$2"
        ;;

    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

        # 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/install_upgrade.escript" \
             "unpack" "$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"
                ;;
            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="$@"

        # 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
        [ -f "$PRE_START_HOOK" ] && . "$PRE_START_HOOK"

        # 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" \
            -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
        echo "Exec: $@" -- ${1+$ARGS} ${EXTRA_OPTS}
        echo "Root: $ROOTDIR"

        # Log the startup
        echo "$RELEASE_ROOT_DIR"
        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="$@"

        # Start the VM, executing pre and post start hooks
        [ -f "$PRE_START_HOOK" ] && . "$PRE_START_HOOK"

        # 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" -mode "$CODE_LOADING_MODE" -config "$SYS_CONFIG_PATH" \
            -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
            -args_file "$VMARGS_PATH" \
            -env ERL_LIBS "$REL_LIB_DIR" \
            -pa "$CONSOLIDATED_DIR" \
            ${ERL_OPTS} \
            -extra ${EXTRA_OPTS}

        # Dump environment info for logging purposes
        echo "Exec: $@" -- ${1+$ARGS}
        echo "Root: $ROOTDIR"

        trap '{}' QUIT TERM KILL HUP SIGUSR1 SIGUSR2
        "$@" -- ${1+$ARGS} &
        __bg_pid=$!
        [ -f "$POST_START_HOOK" ] && . "$POST_START_HOOK"
        wait $__bg_pid
        ;;
    rpc)
        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        shift

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

        shift

        nodetool rpcterms $@
        ;;
    eval)
        # 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)

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

        # Save extra arguments
        ARGS="$@"

        # Build arguments for erlexec
        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 -- "$@" -s "$MODULE" "$FUNCTION"

        $BINDIR/erlexec $@ -extra $ARGS
        exit "$?"
        ;;
    *)
        __command_path="$REL_DIR/commands/$1"
        if [ -f "$__command_path" ]; then
            . "$__command_path"
        else
            echo "Usage: $REL_NAME {start|start_boot <file>|foreground|stop|restart|reboot|pid|ping|console|console_clean|console_boot <file>|attach|remote_console|upgrade|downgrade|install|escript|rpc|rpcterms|eval|command <module> <function> <args>}"
            exit 1
        fi
        ;;
esac

exit 0

#!/bin/sh

set -e

# Path to this script
SCRIPT=$(readlink $0 || true)
if [ -z $SCRIPT ]; then
    SCRIPT=$0
fi;
# Parent directory of this script
SCRIPT_DIR="$(cd `dirname "$SCRIPT"` && pwd -P)"
# Root directory of all releases
RELEASE_ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd -P)"
# Name of the release
REL_NAME="<%= release_name %>"
# Current version of the release
REL_VSN="<%= release_version %>"
# Current version of ERTS being used
ERTS_VSN="<%= erts_vsn %>"
# 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}"
# Directory containing the current version of this release
REL_DIR="$RELEASE_ROOT_DIR/releases/$REL_VSN"
# The lib directory for this release
REL_LIB_DIR="$RELEASE_ROOT_DIR/lib"
# Options passed to erl
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
RUNNER_LOG_DIR="${RUNNER_LOG_DIR:-$RELEASE_ROOT_DIR/log}"
# A string of extra options to pass to erl, here for plugins
EXTRA_OPTS=""

# Pre-start/stop event hook paths
PRE_START_HOOK="$REL_DIR/hooks/pre_start"
POST_START_HOOK="$REL_DIR/hooks/post_start"
PRE_STOP_HOOK="$REL_DIR/hooks/pre_stop"
POST_STOP_HOOK="$REL_DIR/hooks/post_stop"

# 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

    "$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. For setting ERTS_DIR and ROOTDIR
_find_erts_dir() {
    __erts_dir="$RELEASE_ROOT_DIR/erts-$ERTS_VSN"
    if [ -d "$__erts_dir" ]; then
        ERTS_DIR="$__erts_dir";
        ROOTDIR="$RELEASE_ROOT_DIR"
    else
        __erl="$(which erl)"
        code="io:format(\"~s\", [code:root_dir()]), halt()."
        __erl_root="$("$__erl" -noshell -eval "$code")"
        ERTS_DIR="$__erl_root/erts-$ERTS_VSN"
        ROOTDIR="$__erl_root"
    fi
}

# 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
        exec "$BINDIR/erl" \
            -pa "$ROOTDIR"/lib/*/ebin -pa "$CONSOLIDATED_DIR" \
            -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}"
}

# Allow override of where to look for config files
# By default it's $CWD/
if [ -z "$RELEASE_CONFIG_DIR" ]; then
    RELEASE_CONFIG_DIR="$RELEASE_ROOT_DIR"
fi

# 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
        VMARGS_PATH="$RELEASE_CONFIG_DIR/vm.args"
    else
        VMARGS_PATH="$REL_DIR/vm.args"
    fi
fi

orig_vmargs_path="$VMARGS_PATH.orig"
if [ $REPLACE_OS_VARS ]; then
    #Make sure we don't break dev mode by keeping the symbolic link to
    #the user's vm.args
    if [ ! -L "$orig_vmargs_path" ]; then
       #we're in copy mode, rename the vm.args file to vm.args.orig
       mv "$VMARGS_PATH" "$orig_vmargs_path"
    fi

    awk '{while(match($0,"[$]{[^}]*}")) {var=substr($0,RSTART+2,RLENGTH -3);gsub("[$]{"var"}",ENVIRON[var])}}1' < "$orig_vmargs_path" > "$VMARGS_PATH"
 else
    #We don't need to replace env. vars, just rename the
    #symlink vm.args.orig to vm.args, and keep it as a
    #symlink.
    if [ -L "$orig_vmargs_path" ]; then
       mv "$orig_vmargs_path" "$VMARGS_PATH"
    fi
fi

# Make sure log directory exists
mkdir -p "$RUNNER_LOG_DIR"

# 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
        SYS_CONFIG_PATH="$RELEASE_CONFIG_DIR/sys.config"
    else
        SYS_CONFIG_PATH="$REL_DIR/sys.config"
    fi
fi

orig_sys_config_path="$SYS_CONFIG_PATH.orig"
if [ $REPLACE_OS_VARS ]; then
    #Make sure we don't break dev mode by keeping the symbolic link to
    #the user's sys.config
    if [ ! -L "$orig_sys_config_path" ]; then
       #We're in copy mode, rename sys.config to sys.config.orig
       mv "$SYS_CONFIG_PATH" "$orig_sys_config_path"
    fi

    awk '{while(match($0,"[$]{[^}]*}")) {var=substr($0,RSTART+2,RLENGTH -3);gsub("[$]{"var"}",ENVIRON[var])}}1' < "$orig_sys_config_path" > "$SYS_CONFIG_PATH"
 else
    #We don't need to replace env. vars, just rename the
    #symlink sys.config.orig to sys.config. Keep it as
    #a symlink.
    if [ -L "$orig_sys_config_path" ]; then
       mv "$orig_sys_config_path"  "$SYS_CONFIG_PATH"
    fi
fi

# Extract the target node name from node.args
# Should be `-sname somename` or `-name somename@somehost`
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
NAME_TYPE="$(echo "$NAME_ARG" | awk '{print $1}')"
# NAME will be either `somename` or `somename@somehost`
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:-/tmp/erl_pipes/$NAME/}"

# Extract the target 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")"
    else
        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
else
    # Extract cookie name from COOKIE_ARG
    COOKIE="$(echo "$COOKIE_ARG" | awk '{print $2}')"
fi

_find_erts_dir
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"
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

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

        [ -f "$PRE_START_HOOK" ] && . "$PRE_START_HOOK"

        # 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)"

        [ -f "$POST_START_HOOK" ] && . "$POST_START_HOOK"
        ;;

    stop)
        [ -f "$PRE_STOP_HOOK" ] && . "$PRE_STOP_HOOK"
        # 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
        [ -f "$POST_STOP_HOOK" ] && . "$POST_STOP_HOOK"
        ;;

    restart)
        [ -f "$PRE_START_HOOK" ] && . "$PRE_START_HOOK"
        ## Restart the VM without exiting the process
        if ! nodetool "restart"; then
            exit 1
        fi
        ;;

    reboot)
        [ -f "$PRE_START_HOOK" ] && . "$PRE_START_HOOK"
        ## Restart the VM completely (uses heart to restart it)
        if ! nodetool "reboot"; then
            exit 1
        fi
        ;;

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

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

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

    attach)
        # 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)
        # 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

        # 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/install_upgrade.escript" \
             "install" "$REL_NAME" "$NAME_TYPE" "$NAME" "$COOKIE" "$2"
        ;;

    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

        # 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/install_upgrade.escript" \
             "unpack" "$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"
                ;;
            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="$@"

        # 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
        [ -f "$PRE_START_HOOK" ] && . "$PRE_START_HOOK"

        # 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" \
            -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
        echo "Exec: $@" -- ${1+$ARGS} ${EXTRA_OPTS}
        echo "Root: $ROOTDIR"

        # Log the startup
        echo "$RELEASE_ROOT_DIR"
        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="$@"

        # Start the VM, executing pre and post start hooks
        [ -f "$PRE_START_HOOK" ] && . "$PRE_START_HOOK"

        # 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" -mode "$CODE_LOADING_MODE" -config "$SYS_CONFIG_PATH" \
            -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
            -args_file "$VMARGS_PATH" \
            -env ERL_LIBS "$REL_LIB_DIR" \
            -pa "$CONSOLIDATED_DIR" \
            ${ERL_OPTS} \
            -extra ${EXTRA_OPTS}

        # Dump environment info for logging purposes
        echo "Exec: $@" -- ${1+$ARGS}
        echo "Root: $ROOTDIR"

        trap '{}' QUIT TERM KILL HUP SIGUSR1 SIGUSR2
        "$@" -- ${1+$ARGS} &
        __bg_pid=$!
        [ -f "$POST_START_HOOK" ] && . "$POST_START_HOOK"
        wait $__bg_pid
        ;;
    rpc)
        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        shift

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

        shift

        nodetool rpcterms $@
        ;;
    eval)
        # 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)

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

        # Save extra arguments
        ARGS="$@"

        # Build arguments for erlexec
        set -- "$ERL_OPTS"
        [ "$SYS_CONFIG" ] && set -- "$@" -config "$SYS_CONFIG_PATH"
        set -- "$@" -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR"
        set -- "$@" -noshell
        set -- "$@" -boot start_clean
        set -- "$@" -s "$MODULE" "$FUNCTION"

        $BINDIR/erlexec $@ -extra $ARGS
        exit "$?"
        ;;
    *)
        __command_path="$REL_DIR/commands/$1"
        if [ -f "$__command_path" ]; then
            . "$__command_path"
        else
            echo "Usage: $REL_NAME {start|start_boot <file>|foreground|stop|restart|reboot|pid|ping|console|console_clean|console_boot <file>|attach|remote_console|upgrade|downgrade|install|escript|rpc|rpcterms|eval|command <module> <function> <args>}"
            exit 1
        fi
        ;;
esac

exit 0


Add note about dev mode and Phoenix to initial config

# Import all plugins from `rel/plugins`
# They can then be used by adding `plugin MyPlugin` to
# either an environment, or release definition, where
# `MyPlugin` is the name of the plugin module.
Path.join(["rel", "plugins", "*.exs"])
|> Path.wildcard()
|> Enum.map(&Code.eval_file(&1))

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: Mix.env()
<%= unless no_docs do %>
# For a full list of config options for both releases
# and environments, visit https://hexdocs.pm/distillery/configuration.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
  # If you are running Phoenix, you should make sure that
  # server: true is set and the code reloader is disabled,
  # even in dev mode.
  # It is recommended that you build with MIX_ENV=prod and pass
  # the --env flag to Distillery explicitly if you want to use
  # dev mode.
  set dev_mode: true
  set include_erts: false
  set cookie: <%= inspect(get_cookie.()) %>
end

environment :prod do
  set include_erts: true
  set include_src: false
  set cookie: <%= inspect(get_cookie.()) %>
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: [
    :runtime_tools,
<%= Enum.map(Keyword.get(release, :release_applications), fn {app, start_type} ->
    "    #{app}: :#{start_type}"
    end) |> Enum.join(",\n") %>
  ]
end<% else %>
release :<%= Keyword.get(release, :release_name) %> do
  set version: current_version(:<%= Keyword.get(release, :release_name)%>)
  set applications: [
    :runtime_tools
  ]
end<% end %>
<% end %>

# Import all plugins from `rel/plugins`
# They can then be used by adding `plugin MyPlugin` to
# either an environment, or release definition, where
# `MyPlugin` is the name of the plugin module.
Path.join(["rel", "plugins", "*.exs"])
|> Path.wildcard()
|> Enum.map(&Code.eval_file(&1))

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: Mix.env()
<%= unless no_docs do %>
# For a full list of config options for both releases
# and environments, visit https://hexdocs.pm/distillery/configuration.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 dev_mode: true
  set include_erts: false
  set cookie: <%= inspect(get_cookie.()) %>
end

environment :prod do
  set include_erts: true
  set include_src: false
  set cookie: <%= inspect(get_cookie.()) %>
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: [
    :runtime_tools,
<%= Enum.map(Keyword.get(release, :release_applications), fn {app, start_type} ->
    "    #{app}: :#{start_type}"
    end) |> Enum.join(",\n") %>
  ]
end<% else %>
release :<%= Keyword.get(release, :release_name) %> do
  set version: current_version(:<%= Keyword.get(release, :release_name)%>)
  set applications: [
    :runtime_tools
  ]
end<% end %>
<% end %>


Update Dockerfile template (#4937)

Update Dockerfile template (#4937)

Use newer Debian base image.

Newer Elixir + Erlang releases do not exist on the older 2021 base. At least not as created by the hexpm builder.

# 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-20220801-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 ELIXIR_VERSION=<%= elixir_vsn %>
ARG OTP_VERSION=<%= otp_vsn %>
ARG DEBIAN_VERSION=bullseye-20220801-slim

ARG BUILDER_IMAGE="hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}"
ARG RUNNER_IMAGE="debian:${DEBIAN_VERSION}"

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
<%= if assets_dir_exists? do %>
COPY assets assets

# compile assets
RUN mix assets.deploy
<% end %>
# 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 ELIXIR_VERSION=<%= elixir_vsn %>
ARG OTP_VERSION=<%= otp_vsn %>
ARG DEBIAN_VERSION=bullseye-20210902-slim

ARG BUILDER_IMAGE="hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}"
ARG RUNNER_IMAGE="debian:${DEBIAN_VERSION}"

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
<%= if assets_dir_exists? do %>
COPY assets assets

# compile assets
RUN mix assets.deploy
<% end %>
# 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"]


Update self-hosting instructions

Update self-hosting instructions

Download this recording

        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          ×
        </button>

          <%= case @asciicast.version do %>
            <% 1 -> %>
              You can download this recording in asciicast v1 format, as a .json file.
            <% 2 -> %>
              You can download this recording in asciicast v2 format, as a .cast file.
            <% _ -> %>
          <% end %>

        <a href="<%= asciicast_file_url(@asciicast) %>?dl=1" class="btn btn-primary" download="<%= download_filename(@asciicast) %>" rel="nofollow">Download

Replay in terminal

          You can replay this recording in your terminal with asciinema play command:

asciinema play <%= download_filename(@asciicast) %>

          If you don't have asciinema cli installed then <a href="<%= Routes.doc_path(@conn, :show, :installation) %>">see installation instructions.

Use with stand-alone player on your website

          Download asciinema player from player's releases page
          (you only need .js and .css file), then use it like this:

<html>
<head>
  <link rel="stylesheet" type="text/css" href="asciinema-player.css" />
</head>
<body>
  <div id="player"></div>
  <script src="asciinema-player.min.js"></script>
  <script>
    AsciinemaPlayer.create(
      '<%= download_filename(@asciicast) %>',
      document.getElementById('player'),
      { cols: <%= @asciicast.cols %>, rows: <%= @asciicast.rows %> }
    );
  </script>
</body>
</html>

See asciinema player README for full usage instructions.

Download this recording

        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          ×
        </button>

          <%= case @asciicast.version do %>
            <% 1 -> %>
              You can download this recording in asciicast v1 format, as a .json file.
            <% 2 -> %>
              You can download this recording in asciicast v2 format, as a .cast file.
            <% _ -> %>
          <% end %>

        <a href="<%= asciicast_file_url(@asciicast) %>?dl=1" class="btn btn-primary" download="<%= download_filename(@asciicast) %>" rel="nofollow">Download

Replay in terminal

          You can replay this recording in your terminal with asciinema play command:

asciinema play <%= download_filename(@asciicast) %>

          If you don't have asciinema cli installed then <a href="<%= Routes.doc_path(@conn, :show, :installation) %>">see installation instructions.

Use with stand-alone player on your website

          Download asciinema player from player's releases page
          (you only need .js and .css file), then use it like this:

<html>
<head>
  <link rel="stylesheet" type="text/css" href="/asciinema-player.css" />
</head>
<body>
  <asciinema-player src="/<%= download_filename(@asciicast) %>" cols="<%= @asciicast.cols %>" rows="<%= @asciicast.rows %>"></asciinema-player>
  ...
  <script src="/asciinema-player.js"></script>
</body>
</html>

See asciinema player README for full usage instructions.


shrinking the size.

shrinking the size.

<%= for date <- @dates do %>

        <%= get_date date %>

      <%= for item <- get_events @events, date do %>
        <%= render "event.html", e: item %>
      <% end %>

<% end %>


Rename title to match Rails site

Rename title to match Rails site

<!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</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") %>

<!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 in Elixir</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") %>


Eliminate the newline after the Erlang version number.

<img src='<%= static_path(@conn, "/images/readerl-icon-text.png") %>' alt='ReadErl' width='227' height='48'/>™

An RSS feed aggregator written in Elixir/Phoenix

I'm working on it: github.com/billstclair/readerl

  <%= :erlang.system_info(:system_version) %>

  Erlang: <%= {:ok, version} = :file.read_file(:filename.join([:code.root_dir(), "releases", :erlang.system_info(:otp_release), "OTP_VERSION"])); Regex.named_captures(~r/(?<v>.*)\n*/, version)["v"] %>,
  Elixir: <%= System.version %>, Phoenix: <%= {:ok, vsn} = :application.get_key(:phoenix, :vsn); vsn %>

<img src='<%= static_path(@conn, "/images/readerl-icon-text.png") %>' alt='ReadErl' width='227' height='48'/>™

An RSS feed aggregator written in Elixir/Phoenix

I'm working on it: github.com/billstclair/readerl

  <%= :erlang.system_info(:system_version) %>

  Erlang: <%= {:ok, version} = :file.read_file(:filename.join([:code.root_dir(), "releases", :erlang.system_info(:otp_release), "OTP_VERSION"])); version %>,
  Elixir: <%= System.version %>, Phoenix: <%= {:ok, vsn} = :application.get_key(:phoenix, :vsn); vsn %>


Fix #298 - TERM=dumb breaks remote shell

#!/bin/bash --posix
set -e

unset CDPATH

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
RELEASE_ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
RELEASES_DIR="$RELEASE_ROOT_DIR/releases"
REL_NAME="${REL_NAME:-<%= release_name %>}"
REL_VSN="${REL_VSN:-$(cut -d' ' -f2 "$RELEASES_DIR"/start_erl.data)}"
ERTS_VSN="${ERTS_VSN:-$(cut -d' ' -f1 "$RELEASES_DIR"/start_erl.data)}"

# Set DEBUG_BOOT to output verbose debugging info during execution
if [ ! -z "$DEBUG_BOOT" ]; then
    set -x
fi

# Ensure TERM is set to something usable
# for remote shells. The only time we'll
# override this value is if it is set to "dumb"
export TERM="${TERM:-xterm}"
if [ "$TERM" = "dumb" ]; then
    TERM=xterm
fi

# Include helpers, if the current release version supports it
if [ -d "$RELEASES_DIR/$REL_VSN/libexec" ]; then
    # shellcheck source=../libexec/logger.sh
    . "$RELEASES_DIR/$REL_VSN/libexec/logger.sh"
    # shellcheck source=../libexec/erts.sh
    . "$RELEASES_DIR/$REL_VSN/libexec/erts.sh"
    OTP_VER="$(otp_vsn)"
else
    # For backwards compatibility, do not perform the OTP check
    # If the active release version was built with an older Distillery
    OTP_VER="0"
fi
export OTP_VER

# Handles graceful shutdown in OTP <20
__pid=""
_cleanup() {
    trap - HUP INT TERM QUIT EXIT USR1 USR2
    set +e
    if "$RELEASES_DIR/$REL_VSN/$REL_NAME.sh" rpc init stop >/dev/null; then
        wait $__pid
        exit $?
    else
        exit 0
    fi
}

if [ "$OTP_VER" -ge 20 ]; then
    # Use OTP break handler
    exec "$RELEASES_DIR/$REL_VSN/$REL_NAME.sh" "$@"
else
    # Use our own break handling
    case "$1" in
        foreground)
            trap '_cleanup' HUP INT TERM QUIT EXIT USR1 USR2

            "$RELEASES_DIR/$REL_VSN/$REL_NAME.sh" "$@" &
            __pid=$!

            wait $__pid
            exit $?
            ;;
        *)
            exec "$RELEASES_DIR/$REL_VSN/$REL_NAME.sh" "$@"
            ;;
    esac
fi

#!/bin/bash --posix
set -e

unset CDPATH

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
RELEASE_ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
RELEASES_DIR="$RELEASE_ROOT_DIR/releases"
REL_NAME="${REL_NAME:-<%= release_name %>}"
REL_VSN="${REL_VSN:-$(cut -d' ' -f2 "$RELEASES_DIR"/start_erl.data)}"
ERTS_VSN="${ERTS_VSN:-$(cut -d' ' -f1 "$RELEASES_DIR"/start_erl.data)}"

# Set DEBUG_BOOT to output verbose debugging info during execution
if [ ! -z "$DEBUG_BOOT" ]; then
    set -x
fi

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

# Include helpers, if the current release version supports it
if [ -d "$RELEASES_DIR/$REL_VSN/libexec" ]; then
    # shellcheck source=../libexec/logger.sh
    . "$RELEASES_DIR/$REL_VSN/libexec/logger.sh"
    # shellcheck source=../libexec/erts.sh
    . "$RELEASES_DIR/$REL_VSN/libexec/erts.sh"
    OTP_VER="$(otp_vsn)"
else
    # For backwards compatibility, do not perform the OTP check
    # If the active release version was built with an older Distillery
    OTP_VER="0"
fi
export OTP_VER

# Handles graceful shutdown in OTP <20
__pid=""
_cleanup() {
    trap - HUP INT TERM QUIT EXIT USR1 USR2
    set +e
    if "$RELEASES_DIR/$REL_VSN/$REL_NAME.sh" rpc init stop >/dev/null; then
        wait $__pid
        exit $?
    else
        exit 0
    fi
}

if [ "$OTP_VER" -ge 20 ]; then
    # Use OTP break handler
    exec "$RELEASES_DIR/$REL_VSN/$REL_NAME.sh" "$@"
else
    # Use our own break handling
    case "$1" in
        foreground)
            trap '_cleanup' HUP INT TERM QUIT EXIT USR1 USR2

            "$RELEASES_DIR/$REL_VSN/$REL_NAME.sh" "$@" &
            __pid=$!

            wait $__pid
            exit $?
            ;;
        *)
            exec "$RELEASES_DIR/$REL_VSN/$REL_NAME.sh" "$@"
            ;;
    esac
fi


Add button to preview

Add button to preview

    <%= link(@offer["title"], to: "#") %>

      <%= @offer["company"] %>

      <%= human_readable_date(Ecto.DateTime.utc) %>
      <%= @offer["location"] %>

        <%= human_get_place(@offer["job_place"]) %>

        <%= human_get_type(@offer["job_type"]) %>

    <%= sanitized_markdown(@offer["description"]) %>

  <%= link(gettext("Go to the offer!"), to: @offer["url"], class: "btn btn-info", target: "_blank") %>

    <%= link(@offer["title"], to: "#") %>

      <%= @offer["company"] %>

      <%= human_readable_date(Ecto.DateTime.uutc) %>
      <%= @offer["location"] %>

        <%= human_get_place(@offer["job_place"]) %>

        <%= human_get_type(@offer["job_type"]) %>

    <%= sanitized_markdown(@offer["description"]) %>


Basic bare run time charts with plotly <3

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf8">
    <script src="assets/javascripts/plotly-latest.min.js"></script>
  </head>
  <body>
    <%= for {name, _} <- suite.run_times do %>
      <div class="raw-run-times" data-job-name="<%= name %>"></canvas>
    <% end %>

    <script>
      var drawRunTimesChart = function(node, runTimeData) {

        var data = [
          {
            y: runTimeData,
            type: 'bar'
          }
        ];

        Plotly.newPlot(node, data)
      };

      var suite = <%= suite_json %>;
      var runTimes = suite.run_times;
      var statistics = suite.statistics;
      runTimeNodes = document.getElementsByClassName("raw-run-times");
      runTimesNodesArray = Array.from(runTimeNodes); // Oh JavaScript
      runTimesNodesArray.forEach(function(node) {
        var runTimeData = runTimes[node.getAttribute("data-job-name")];
        drawRunTimesChart(node, runTimeData);
      });

    </script>
  </body>
</html>

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf8">
    <script src="assets/javascripts/plotly-latest.min.js"></script>
  </head>
  <body>
    <%= for {name, _} <- suite.run_times do %>
      <canvas class="raw-run-times" data-job-name="<%= name %>"></canvas>
    <% end %>

    <script>
      drawRunTimesChart = function(canvas, runTimeData) {
        var myChart = new Chart(canvas, {
          type: 'bar',
          data: {
            labels: runTimeData.map(function(_) {return "";}),
            datasets: [{
                label:       'run time',
                data:        runTimeData,
                borderWidth: 1
            }]
          },
          options: {
            scales: {
              yAxes: [{
                ticks: {
                  beginAtZero: true
                }
              }]
            }
          }
        });
      };

      var suite = <%= suite_json %>;
      var runTimes = suite.run_times;
      var statistics = suite.statistics;
      runTimeNodes = document.getElementsByClassName("raw-run-times");
      runTimesNodesArray = Array.from(runTimeNodes); // Oh JavaScript
      runTimesNodesArray.forEach(function(node) {
        var runTimeData = runTimes[node.getAttribute("data-job-name")];
        drawRunTimesChart(node, runTimeData);
      });

    </script>
  </body>
</html>


Revert to use standard classes

Revert to use standard classes

Elixir <%= system.elixir%>, Erlang <%= system.erlang %>


Update public/libs/bower_components/ace-builds/demo/kitchen-sink/docs/html_elixir.eex

Signed-off-by: Bernard Ojengwa <fccb4cc00c1ba7e51bf5c2b5f77130b9fe8d917e@gmail.com>

Listing Books

    Title
    Summary

<%= for book <- @books do %>

    <%# comment %>
    <%= book.title %>
    <%= book.content %>
    <%= link "Show", to: book_path(@conn, :show, book) %>
    <%= link "Edit", to: book_path(@conn, :edit, book) %>
    <%= link "Delete", to: book_path(@conn, :delete, book), method: :delete, data: [confirm: "Are you sure?"] %>

<% end %>

<%= link "New book", to: book_path(@conn, :new) %>


typo changset -> changeset

typo changset -> changeset

<%%= form_for @changeset, @action, [as: :registration], fn f  -> %>

  <%%= if @changeset.action do %>

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

  <%% end %>

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

  <%%= unless (login_field = Coherence.Config.login_field) == :email do %>

      <%%= required_label f, login_field, class: "control-label" %>
      <%%= text_input f, login_field, class: "form-control", required: "" %>
      <%%= error_tag f, login_field %>

  <%% end %>

    <%%= required_label f, :email, class: "control-label" %>
    <%%= text_input f, :email, class: "form-control", required: "" %>
    <%%= error_tag f, :email %>

  <%%= if Coherence.Config.require_current_password and not is_nil(@changeset.data.id) do %>

      <%%= required_label f, :current_password, class: "control-label" %>
      <%%= password_input f, :current_password, [class: "form-control"] ++ @required %>
      <%%= error_tag f, :current_password %>

  <%% end %>

    <%%= required_label f, :password, class: "control-label" %>
    <%%= password_input f, :password, [class: "form-control"] ++ @required %>
    <%%= error_tag f, :password %>

    <%%= required_label f, :password_confirmation, class: "control-label" %>
    <%%= password_input f, :password_confirmation, [class: "form-control"] ++ @required %>
    <%%= error_tag f, :password_confirmation %>

    <%%= submit @label, class: "btn btn-primary" %>
    <%%= link "Cancel", to: Coherence.Config.logged_out_url("/"), class: "btn" %>

<%% end %>

<%%= form_for @changeset, @action, [as: :registration], fn f  -> %>

  <%%= if @changeset.action do %>

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

  <%% end %>

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

  <%%= unless (login_field = Coherence.Config.login_field) == :email do %>

      <%%= required_label f, login_field, class: "control-label" %>
      <%%= text_input f, login_field, class: "form-control", required: "" %>
      <%%= error_tag f, login_field %>

  <%% end %>

    <%%= required_label f, :email, class: "control-label" %>
    <%%= text_input f, :email, class: "form-control", required: "" %>
    <%%= error_tag f, :email %>

  <%%= if Coherence.Config.require_current_password and not is_nil(@changset.data.id) do %>

      <%%= required_label f, :current_password, class: "control-label" %>
      <%%= password_input f, :current_password, [class: "form-control"] ++ @required %>
      <%%= error_tag f, :current_password %>

  <%% end %>

    <%%= required_label f, :password, class: "control-label" %>
    <%%= password_input f, :password, [class: "form-control"] ++ @required %>
    <%%= error_tag f, :password %>

    <%%= required_label f, :password_confirmation, class: "control-label" %>
    <%%= password_input f, :password_confirmation, [class: "form-control"] ++ @required %>
    <%%= error_tag f, :password_confirmation %>

    <%%= submit @label, class: "btn btn-primary" %>
    <%%= link "Cancel", to: Coherence.Config.logged_out_url("/"), class: "btn" %>

<%% end %>


Removed password fields from people listing

Listing people

      Name
      Nickname
      Email
      Description
      Image url
      Role
      Active
      Organization

<%= for person <- @people do %>

      <%= person.name %>
      <%= person.nickname %>
      <%= person.email %>
      <%= person.description %>
      <%= person.image_url %>
      <%= person.role %>
      <%= person.active %>
      <%= person.organization_id %>

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

<% end %>

<%= link "New person", to: person_path(@conn, :new) %>

Listing people

      Name
      Nickname
      Email
      Password hash
      Password salt
      Description
      Image url
      Role
      Active
      Organization

<%= for person <- @people do %>

      <%= person.name %>
      <%= person.nickname %>
      <%= person.email %>
      <%= person.password_hash %>
      <%= person.password_salt %>
      <%= person.description %>
      <%= person.image_url %>
      <%= person.role %>
      <%= person.active %>
      <%= person.organization_id %>

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

<% end %>

<%= link "New person", to: person_path(@conn, :new) %>


Add a "Edit my skills" button to skill edit page

Available skills

    <%= button "Edit my skills", to: skill_level_path(@conn, :edit), method: :get %>
    <%= button "Create a new skill", to: skill_path(@conn, :new), method: :get %>
    <%= button "Create a new category", to: skill_category_path(@conn, :new), method: :get %>

    <%= for {category, skills} <-
          Enum.group_by(@skills, fn skill -> skill.category end) do %>

        <%= render "category_header.html", conn: @conn, category: category %>

          <%= for skill <- skills do %>

              <%= skill.name %>
              <%= skill.description %>

                <%= link "✎",
                      to: skill_path(@conn, :edit, skill.id),
                      class: "tooltip",
                      data: [tooltip: "Edit skill"]
                %>

                <%= link "✗",
                      to: skill_path(@conn, :delete, skill.id), method: :delete,
                      class: "tooltip",
                      data: [tooltip: "Delete skill",
                       confirm: "Are you sure you want to delete the #{skill.name} skill?"]
                %>

          <% end %>

    <% end %>

Available skills

    <%= button "Create a new skill", to: skill_path(@conn, :new), method: :get %>
    <%= button "Create a new category", to: skill_category_path(@conn, :new), method: :get %>

    <%= for {category, skills} <-
          Enum.group_by(@skills, fn skill -> skill.category end) do %>

        <%= render "category_header.html", conn: @conn, category: category %>

          <%= for skill <- skills do %>

              <%= skill.name %>
              <%= skill.description %>

                <%= link "✎",
                      to: skill_path(@conn, :edit, skill.id),
                      class: "tooltip",
                      data: [tooltip: "Edit skill"]
                %>

                <%= link "✗",
                      to: skill_path(@conn, :delete, skill.id), method: :delete,
                      class: "tooltip",
                      data: [tooltip: "Delete skill",
                       confirm: "Are you sure you want to delete the #{skill.name} skill?"]
                %>

          <% end %>

    <% end %>


code cleanup

code cleanup

  <%= unless assigns[:atom] do %>

<%= @note.title %>

<%= link(to: page_path(@conn, :about), rel: "me") do %><img src="<%= static_path(@conn, "/images/" <> @note.author.avatar) %>" class="avatar u-photo" alt=""> <%= @note.author.name %><% end %>,
        <time class="dt-published" datetime="<%= @note |> Wwwtech.Note.inserted_at_timex |> DateFormat.format!("%FT%T%:z", :strftime) %>"><%= link Wwwtech.LayoutView.time_ago_in_words(@note |> Wwwtech.Note.inserted_at_timex), to: note_path(@conn, :show, @note), class: "u-url" %></time>

  <% end %>

  <%= if @note.in_reply_to != "" and @note.in_reply_to != nil do %>

      In reply to <%= link @note.in_reply_to, to: @note.in_reply_to, class: "u-in-reply-to" %>

  <% end %>

    <%= {:safe, Wwwtech.Note.to_html(@note)} %>
    <%= if assigns[:home] == nil do %>
<%= link 'Perma-Link', to: note_path(@conn, :show, @note) %>
<% end %>

  <%= if action_name(@conn) == :show and @note.posse == true do %>

      <%= link "Twitter POSSE", to: "https://www.brid.gy/publish/twitter" %>

  <% end %>

  <%= unless assigns[:atom] do %>

<%= @note.title %>

<%= link(to: page_path(@conn, :about), rel: "me") do %><%= unless assigns[:atom] do %><img src="<%= static_path(@conn, "/images/" <> @note.author.avatar) %>" class="avatar u-photo" alt=""> <% end %><%= @note.author.name %><% end %>,
        <time class="dt-published" datetime="<%= @note |> Wwwtech.Note.inserted_at_timex |> DateFormat.format!("%FT%T%:z", :strftime) %>"><%= link Wwwtech.LayoutView.time_ago_in_words(@note |> Wwwtech.Note.inserted_at_timex), to: note_path(@conn, :show, @note), class: "u-url" %></time>

  <% end %>

  <%= if @note.in_reply_to != "" and @note.in_reply_to != nil do %>

      In reply to <%= link @note.in_reply_to, to: @note.in_reply_to, class: "u-in-reply-to" %>

  <% end %>

    <%= {:safe, Wwwtech.Note.to_html(@note)} %>
    <%= if assigns[:home] == nil do %>
<%= link 'Perma-Link', to: note_path(@conn, :show, @note) %>
<% end %>

  <%= if action_name(@conn) == :show and @note.posse == true do %>

      <%= link "Twitter POSSE", to: "https://www.brid.gy/publish/twitter" %>

  <% end %>


Add links to sidebar

Add links to sidebar

      <img src="<%= static_path(@conn, "/images/logo.png") %>" class="logo"/>

<a href="<%= offer_path(@conn, :index) %>">ElixirJobs

<%= gettext "Find your next Elixir job." %>

    <input type="checkbox" class="sidebar-checkbox" id="sidebar-checkbox">

      <a class="btn-publish" href="<%= offer_path(@conn, :new) %>"><%= gettext "Post a job offer!" %>

      <%= if user_logged_in?(@conn) do %>
      <%= gettext "Admin" %>
      <%= link(gettext("Published offers"), to: admin_offer_path(@conn, :index_published), class: "item")%>
      <%= link(gettext("Pending offers"), to: admin_offer_path(@conn, :index_unpublished), class: "item")%>
      <%= link(gettext("Log out"), to: auth_path(@conn, :delete)) %>
      <% else %>
      <%= gettext "You can follow via" %>
      Twitter
      <a class="item" href="<%= offer_path(@conn, :rss) %>">RSS
      <% end %>

      <%= gettext "Running on Phoenix!" %>
      <%= gettext "Open Source" %>
      <%= gettext "About" %>

      <img src="<%= static_path(@conn, "/images/logo.png") %>" class="logo"/>

<a href="<%= offer_path(@conn, :index) %>">ElixirJobs

<%= gettext "Find your next Elixir job." %>

    <input type="checkbox" class="sidebar-checkbox" id="sidebar-checkbox">

      <a class="btn-publish" href="<%= offer_path(@conn, :new) %>"><%= gettext "Post a job offer!" %>

      <%= if user_logged_in?(@conn) do %>
      <%= gettext "Admin" %>
      Pending offers
      <a class="item" href="<%= auth_path(@conn, :delete) %>">Log out
      <% else %>
      <%= gettext "You can follow via" %>
      Twitter
      <a class="item" href="<%= offer_path(@conn, :rss) %>">RSS
      <% end %>

      <%= gettext "Running on Phoenix!" %>
      <%= gettext "Open Source" %>
      <%= gettext "About" %>


Fix footer positioning

Fix footer positioning

        Genomu

 {{ i.name }}

            <%= lc {name, url, icon, active} inlist @pages do %>
              <li <%= if active do %> class="active" <% end %> >
              <a href="<%= url %>"><i class="fa-icon-<%= icon %>"> <%= name %>

            <% end %>

Warning!

You need to have JavaScript enabled to use this site.

        <%= @view %>

        © Spawngrid, Inc. 2013


tag#show: style

tag#show: style

#<%= @tag.name %>

  <%= for story <- @stories do %>
    <%= render News.StoryView, "_item_story.html", story: story, conn: @conn %>
  <% end %>


add more blurb on the welcome page

add more blurb on the welcome page

      place
      Welcome to ticket poker.
      Please create your team and start estimating.

            What?

            An online tool to facilitate planning poker for development teams, especially remote or partially-remote teams.

           Why?

            I'm working in a distributed development team.
            Also, I wanted to build something using
            Elixir/Phoenix,
            ReactJS
            and websockets.
            This project also uses Materialize CSS
            and clipboard.js

            How?

 Just create your team and get started. There are no accounts or passwords. Don't forget to bookmark your team's URL.

When you create a ticket, share its URL with the members of your team and ask everyone to estimate its size.

What prevents someone changing a different person's estimate?

            Wheaton's Law
            If your team is not complying with Wheaton's Law, this tool will not help you.

            Who?

            Me:
            [Twitter]
            [Github]
            [Blog]

            Feedback/suggestions very welcome.

      place
      Welcome to ticket poker.
      Please create your team and start estimating.

            What?

            An online tool to facilitate planning poker for development teams, especially remote or partially-remote teams.

           Why?

            I'm working in a distributed development team.
            Also, I wanted to build something using Elixir/Phoenix, ReactJS and websockets.

            How?

 Just create your team and get started. There are no accounts or passwords. Don't forget to bookmark your team's URL.

When you create a ticket, share its URL with the members of your team and ask everyone to estimate its size.

What prevents someone changing a different person's estimate?

            Wheaton's Law
            If your team is not complying with Wheaton's Law, this tool will not help you.

            Who?

            Me:
            [Twitter]
            [Github]
            [Blog]

            Feedback/suggestions very welcome.


Template out applications in example 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: :default
<%= unless no_docs do %>
# A full list of config options for both releases
# and environments is as follows:
#
# - dev_mode (boolean);
#       symlink compiled files into the release,
#       so that you can release once, but see changes when you recompile
#       the project. This is only for development.
# - code_paths (list of strings);
#       a list of file paths, representing code paths
#       to search for compiled BEAMs
# - vm_args (string);
#       a path to a custom vm.args file
# - sys_config (string);
#       a path to a custom sys.config file
# - include_erts (boolean | string);
#       whether to include the system ERTS or not,
#       a path to an alternative ERTS can also be provided
# - include_src (boolean);
#       should source code be included in the release
# - include_system_libs (boolean | string);
#       should system libs be included in the release,
#       a path to system libs to be included can also be provided
# - strip_debug_info (boolean);
#       should debugging info be stripped from BEAM files in the release
# - erl_opts (list of strings);
#       a list of Erlang VM options to be used
# - overrides (keyword list);
#       During development its often the case that you want to substitute the app
#       that you are working on for a 'production' version of an app. You can
#       explicitly tell Mix to override all versions of an app that you specify
#       with an app in an arbitrary directory. Mix will then symlink that app
#       into the release in place of the specified app. be aware though that Mix
#       will check your app for consistancy so it should be a normal OTP app and
#       already be built.
# - overlay_vars (keyword list);
#       A keyword list of bindings to use in overlays
# - overlays (special keyword list);
#       A list of overlay operations to perform against the release,
#       such as copying files, symlinking files, etc.
#       copy: {from_path, to_path}
#       link: {from_path, to_path}
#       mkdir: path
#       template: {template_path, output_path}
# - pre_start_hook: nil,
# - post_start_hook: nil,
# - pre_stop_hook: nil,
# - post_stop_hook: nil
<% 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 :default do
  set dev_mode: false
  set include_erts: true
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 %>
release :<%= Keyword.get(release, :release_name) %> do
  set version: current_version(:<%= Keyword.get(release, :release_name)%>)
  set applications: [<%= Enum.map(Keyword.get(release, :release_applications), fn {app, start_type} ->
    "#{app}: :#{start_type}"
end) |> Enum.join(",\n   ") %>]
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: :default
<%= unless no_docs do %>
# A full list of config options for both releases
# and environments is as follows:
#
# - dev_mode (boolean);
#       symlink compiled files into the release,
#       so that you can release once, but see changes when you recompile
#       the project. This is only for development.
# - code_paths (list of strings);
#       a list of file paths, representing code paths
#       to search for compiled BEAMs
# - vm_args (string);
#       a path to a custom vm.args file
# - sys_config (string);
#       a path to a custom sys.config file
# - include_erts (boolean | string);
#       whether to include the system ERTS or not,
#       a path to an alternative ERTS can also be provided
# - include_src (boolean);
#       should source code be included in the release
# - include_system_libs (boolean | string);
#       should system libs be included in the release,
#       a path to system libs to be included can also be provided
# - strip_debug_info (boolean);
#       should debugging info be stripped from BEAM files in the release
# - erl_opts (list of strings);
#       a list of Erlang VM options to be used
# - overrides (keyword list);
#       During development its often the case that you want to substitute the app
#       that you are working on for a 'production' version of an app. You can
#       explicitly tell Mix to override all versions of an app that you specify
#       with an app in an arbitrary directory. Mix will then symlink that app
#       into the release in place of the specified app. be aware though that Mix
#       will check your app for consistancy so it should be a normal OTP app and
#       already be built.
# - overlay_vars (keyword list);
#       A keyword list of bindings to use in overlays
# - overlays (special keyword list);
#       A list of overlay operations to perform against the release,
#       such as copying files, symlinking files, etc.
#       copy: {from_path, to_path}
#       link: {from_path, to_path}
#       mkdir: path
#       template: {template_path, output_path}
# - pre_start_hook: nil,
# - post_start_hook: nil,
# - pre_stop_hook: nil,
# - post_stop_hook: nil
<% 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 :default do
  set dev_mode: false
  set include_erts: true
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 %>
release :<%= Keyword.get(release, :release_name) %> do
  set version: current_version(:<%= Keyword.get(release, :release_name)%>)
end
<% end %>


Fix room/edit template and remove unwanted template


Added the edit link generation to the show.html.eex template


fixed spelling mistake

fixed spelling mistake

Was ist SignDict?

SignDict ist offenes Gebärdensprach-Wörterbuch zum Mitmachen. Fehlt eine
      Gebärde oder gibt es andere Varianten? Dann kann diese einfach mit einem Klick
      aufgenommen und hinzugefügt werden. Alles was du dazu brauchst ist deine Webcam.
      Der Name SignDict setzt sich zusammen aus Sign, dem englischen Wort für Gebärde,
      und Dictionary, dem englischen Wort für
      Wörterbuch.

Dieses Projekt wird durch den Prototype Fund
      gefördert. Entwicklungsbeginn war der 1. März 2017.

Für wen wird das gebaut?

Dieses Wörterbuch richtet sich an Gebärdende und an Personen, die diese Sprache
      erlernen wollen. Es soll als Nachschlagewerk und Lernhilfe dienen und die
      Sichtbarkeit der Gebärdensprache im öffentlichen Raum erhöhen.

Wieso wird SignDict benötigt?

Das Ziel ist es, das erste interaktive Gebärdensprach-Wörterbuch im Internet
      aufzubauen, bei dem jede:r ohne technisches Vorwissen mitmachen kann.

Wenn du nach Gebärdensprach-Wörterbüchern im Internet suchst, wirst du einige
      finden. Leider haben die meisten nur ein paar hundert Gebärden und erlauben es
      nicht, weitere Gebärden hinzuzufügen. Dazu kommt, dass in der deutschen
      Gebärdensprache eine große Anzahl an Variationen für ein Wort existiert. In
      Wörterbüchern wird allerdings oft nur eine Variante angezeigt. Ich möchte
      versuchen, bei SignDict möglichst viele Gebärden-Varianten eines Wortes zu
      sammeln.

Daher kam die Idee, ein gemeinsames Wörterbuch aufzubauen: Jede:r kann
      mitmachen und Gebärden hinzufügen. Viele Computer haben heutzutage eine Webcam
      mit der du ganz einfach eine Gebärde aufnehmen und hinzufügen kannst. So bauen
      wir zusammen als Gebärdensprach-Community ein interaktives Nachschlagewerk auf,
      das so viele Gebärden wie möglich in mehreren Gebärdensprachen enthält. Den
      Anfang machen wir mit der deutschen Gebärdensprache (DGS).

Warum baue ich diese Seite?

      (Foto von Carolin Weinkopf)

Ich bin ein CODA, meine
      Eltern sind gehörlos, und ich träume schon seit dem ersten Auftauchen von
      Videos im Internet von genau so einem Wörterbuch. 2013 habe ich zusammen mit
      einigen weiteren Menschen in einem
        Programmierwettbewerb die
      Machbarkeit getestet. Leider musste ich damals feststellen, dass es technisch
      noch nicht ohne weiteres möglich war, dieses Projekt nach meinen Vorstellungen
      umzusetzen.  Zum Glück sind diese Probleme nun gelöst und unserem gemeinsamen
      Gebärdensprach-Wörterbuch steht nichts mehr im Weg.

OpenSource

Die Videos werden unter einer Creative Commons
      Lizenz und der Quelltext unter die MPL
      gestellt.

Finanzierung

Dieses Projekt ist Teilnehmer der 1. Runde des Prototype Fund
      und wird durch das
      Bundesministerium für Bildung und Forschung gefördert.

Danke an die Jury für die Auswahl meines
      Projektes für den Prototype Fund.

Was ist SignDict?

SignDict ist offenes Gebärdensprach-Wörterbuch zum Mitmachen. Fehlt eine
      Gebärde oder gibt es andere Varianten? Dann kann diese einfach mit einem Klick
      aufgenommen und hinzugefügt werden. Alles was du dazu brauchst ist deine Webcam.
      Der Name SignDict setzt sich zusammen aus Sign, dem englischen Wort für Gebärde,
      und Dictionary, dem englischen Wort für
      Wörterbuch.

Dieses Projekt wird durch den Prototype Fund
      gefördert. Entwicklungsbeginn war der 1. März 2017.

Für wen wird das gebaut?

Dieses Wörterbuch richtet sich an Gebärdende und an Personen, die diese Sprache
      erlernen wollen. Es soll als Nachschlagewerk und Lernhilfe dienen und die
      Sichtbarkeit der Gebärdensprache im öffentlichen Raum erhöhen.

Wieso wird SignDict benötigt?

Das Ziel ist es, das erste interaktives Gebärdensprach-Wörterbuch im Internet
      aufzubauen, bei dem jede:r ohne technisches Vorwissen mitmachen kann.

Wenn du nach Gebärdensprach-Wörterbüchern im Internet suchst, wirst du einige
      finden. Leider haben die meisten nur ein paar hundert Gebärden und erlauben es
      nicht, weitere Gebärden hinzuzufügen. Dazu kommt, dass in der deutschen
      Gebärdensprache eine große Anzahl an Variationen für ein Wort existiert. In
      Wörterbüchern wird allerdings oft nur eine Variante angezeigt. Ich möchte
      versuchen, bei SignDict möglichst viele Gebärden-Varianten eines Wortes zu
      sammeln.

Daher kam die Idee, ein gemeinsames Wörterbuch aufzubauen: Jede:r kann
      mitmachen und Gebärden hinzufügen. Viele Computer haben heutzutage eine Webcam
      mit der du ganz einfach eine Gebärde aufnehmen und hinzufügen kannst. So bauen
      wir zusammen als Gebärdensprach-Community ein interaktives Nachschlagewerk auf,
      das so viele Gebärden wie möglich in mehreren Gebärdensprachen enthält. Den
      Anfang machen wir mit der deutschen Gebärdensprache (DGS).

Warum baue ich diese Seite?

      (Foto von Carolin Weinkopf)

Ich bin ein CODA, meine
      Eltern sind gehörlos, und ich träume schon seit dem ersten Auftauchen von
      Videos im Internet von genau so einem Wörterbuch. 2013 habe ich zusammen mit
      einigen weiteren Menschen in einem
        Programmierwettbewerb die
      Machbarkeit getestet. Leider musste ich damals feststellen, dass es technisch
      noch nicht ohne weiteres möglich war, dieses Projekt nach meinen Vorstellungen
      umzusetzen.  Zum Glück sind diese Probleme nun gelöst und unserem gemeinsamen
      Gebärdensprach-Wörterbuch steht nichts mehr im Weg.

OpenSource

Die Videos werden unter einer Creative Commons
      Lizenz und der Quelltext unter die MPL
      gestellt.

Finanzierung

Dieses Projekt ist Teilnehmer der 1. Runde des Prototype Fund
      und wird durch das
      Bundesministerium für Bildung und Forschung gefördert.

Danke an die Jury für die Auswahl meines
      Projektes für den Prototype Fund.