Switch to multi-stage builder

pull/28/head
Christophe Romain 6 years ago
parent f96a6da3ae
commit a1fd44d938

@ -1,3 +1,32 @@
FROM ejabberd/mix as builder
ARG VERSION
ENV VERSION=${VERSION:-latest} \
MIX_ENV=prod
LABEL maintainer="ProcessOne <contact@process-one.net>" \
product="Ejabberd Community Server builder"
# Get ejabberd sources, dependencies, configuration
RUN git clone https://github.com/processone/ejabberd.git
WORKDIR /ejabberd
COPY vars.config .
COPY rel/*exs rel/
RUN git checkout ${VERSION/latest/HEAD} \
&& mix deps.get
# Compile
RUN mix do compile, release.init, release --env=prod
# Prepare runtime environment
RUN mkdir runtime \
&& tar -C runtime -zxf _build/prod/rel/ejabberd/releases/*/ejabberd.tar.gz \
&& cd runtime \
&& cp releases/*/start.boot bin \
&& echo 'beam_lib:strip_files(filelib:wildcard("lib/*/ebin/*beam")), init:stop().' | erts*/bin/erl -boot start_clean >/dev/null \
&& mv erts*/bin/* bin \
&& rm -rf releases erts* bin/*src bin/dialyzer bin/typer \
&& rm bin/ejabberd bin/ejabberd.bat bin/ejabberd_loader.sh
# Runtime container
FROM alpine:3.7 FROM alpine:3.7
ARG VERSION ARG VERSION
ENV TERM=xterm \ ENV TERM=xterm \
@ -14,37 +43,32 @@ LABEL maintainer="ProcessOne <contact@process-one.net>" \
# Create directory structure and user for ejabberd # Create directory structure and user for ejabberd
RUN addgroup ejabberd -g 9000 \ RUN addgroup ejabberd -g 9000 \
&& adduser -s /bin/sh -D -G ejabberd ejabberd -u 9000 \ && adduser -s /bin/sh -D -G ejabberd ejabberd -u 9000 \
&& mkdir -p /home/ejabberd/config /home/ejabberd/db /home/ejabberd/log \ && mkdir -p /home/ejabberd/conf /home/ejabberd/database /home/ejabberd/logs \
&& chown -R ejabberd:ejabberd /home/ejabberd && chown -R ejabberd:ejabberd /home/ejabberd
# Install required dependencies # Install required dependencies
RUN apk upgrade --update musl \ RUN apk upgrade --update musl \
&& apk add \ && apk add \
bash \
ca-certificates \
curl \
expat \ expat \
libstdc++ \ libstdc++ \
ncurses-libs \ ncurses-libs \
openssl \ openssl \
sqlite \ sqlite \
uthash \
yaml \ yaml \
zlib \ zlib \
&& rm -rf /var/cache/apk/* \ && rm -rf /var/cache/apk/*
&& update-ca-certificates
# Install ejabberd # Install ejabberd
ADD ejabberd-$VERSION.tar.gz $HOME WORKDIR $HOME
COPY ejabberd-api ejabberdctl $HOME/bin/ COPY --from=builder /ejabberd/runtime .
COPY --chown=ejabberd:ejabberd config/* $HOME/config/ COPY bin/* bin/
COPY docker-entrypoint.sh / COPY --chown=ejabberd:ejabberd conf conf/
ADD --chown=ejabberd:ejabberd https://curl.haxx.se/ca/cacert.pem conf/cacert.pem
# Setup runtime environment # Setup runtime environment
USER ejabberd USER ejabberd
WORKDIR $HOME VOLUME ["$HOME/database","$HOME/conf","$HOME/logs"]
VOLUME ["$HOME/db","$HOME/config","$HOME/log"]
EXPOSE 5222 5269 5280 EXPOSE 5222 5269 5280
ENTRYPOINT ["/docker-entrypoint.sh"] ENTRYPOINT ["/home/ejabberd/bin/ejabberdctl"]
CMD ["ejabberd"] CMD ["foreground"]

@ -1,4 +1,4 @@
## ejabberd Community Edition - Base ## ejabberd Community Server - Base
This ejabberd Docker image allows you to run a single node ejabberd instance in a Docker container. This ejabberd Docker image allows you to run a single node ejabberd instance in a Docker container.
@ -28,20 +28,26 @@ docker restart ejabberd
### Creating admin user ### Creating admin user
When the container is running (and thus ejabberd), you can exec commands inside the container. The ejabberd-api command-line tool can be used to exercise the API directly from the container, even if the API is not exposed to the outside world. When the container is running (and thus ejabberd), you can exec commands inside the container. The api command-line tool can be used to exercise the API directly from the container, even if the API is not exposed to the outside world. Note: ejabberd configuration must allow api calls from loopback interface.
To create an admin user (or any other user), you can use the following command: To create an admin user (or any other user), you can use the following command:
```bash ```bash
docker exec -it ejabberd /home/ejabberd/bin/ejabberd-api register --endpoint=http://127.0.0.1:5280/ --jid=admin@localhost --password=passw0rd docker exec -it ejabberd bin/ejabberdapi register --endpoint=http://127.0.0.1:5280/ --jid=admin@localhost --password=passw0rd
```
It's also possible to fallback to ejabberdctl commands:
```bash
docker exec -it ejabberd bin/ejabberdctl register admin localhost passw0rd
``` ```
### Running ejabberd with Erlang console attached ### Running ejabberd with Erlang console attached
If you would like to run it with console attached you can use the `console` command: If you would like to run it with Erlang console attached you can use the `live` command:
```bash ```bash
docker run -it -p 5222:5222 ejabberd/ecs console docker run -it -p 5222:5222 ejabberd/ecs live
``` ```
This command will use default configuration file and XMPP domain "localhost". This command will use default configuration file and XMPP domain "localhost".
@ -51,16 +57,32 @@ This command will use default configuration file and XMPP domain "localhost".
The following command will pass config file using Docker volume feature and share local directory to store database: The following command will pass config file using Docker volume feature and share local directory to store database:
```bash ```bash
mkdir db mkdir database
docker run -d --name ejabberd -v $(pwd)/ejabberd.yml:/home/ejabberd/cfg/ejabberd.yml -v $(pwd)/db:/home/ejabberd/db -p 5222:5222 ejabberd/ecs docker run -d --name ejabberd -v $(pwd)/ejabberd.yml:/home/ejabberd/conf/ejabberd.yml -v $(pwd)/database:/home/ejabberd/database -p 5222:5222 ejabberd/ecs
```
### Checking ejabberd log files
You can execute a Docker command to check the content of the log files from inside to container, even if you do not put it on a shared persistent drive:
```bash
docker exec -it ejabberd tail -f logs/ejabberd.log
``` ```
### Checking ejabberd log file ### Open ejabberd debug console
You can execute a Docker command to check the content of the log file from inside to container, even if you do not put it on a shared persistent drive: You can open a live debug Erlang console attached to a running container:
```bash ```bash
docker exec -it ejabberd /usr/bin/tail -f /home/ejabberd/log/ejabberd.log docker exec -it ejabberd bin/ejabberdctl debug
```
### Execute ejabberdctl command
You can run anu ejabberdctl command inside running container. Example:
```bash
docker exec -it ejabberd bin/ejabberdctl status
``` ```
## Docker image advanced configuration ## Docker image advanced configuration
@ -80,9 +102,9 @@ This is the kind of data you probably want to store on a persistent or local dri
Here are the volume you may want to map: Here are the volume you may want to map:
- /home/ejabberd/log/: Directory containing log files - /home/ejabberd/logs/: Directory containing log files
- /home/ejabberd/db/: Directory containing Mnesia database. You should backup or export the content of the directory to persistent storage (host storage, local storage, any storage plugin) - /home/ejabberd/database/: Directory containing Mnesia database. You should backup or export the content of the directory to persistent storage (host storage, local storage, any storage plugin)
- /home/ejabberd/config/: Directory containing configuration and certificates - /home/ejabberd/conf/: Directory containing configuration and certificates
## Generating ejabberd release ## Generating ejabberd release
@ -94,16 +116,22 @@ The configuration of ejabberd Erlang/OTP release is customized with:
- rel/config.exs: Customize ejabberd release - rel/config.exs: Customize ejabberd release
- rel/dev.exs: ejabberd environment configuration for development release - rel/dev.exs: ejabberd environment configuration for development release
- rel/docker.exs: ejabberd environment configuration for production Docker release - rel/prod.exs: ejabberd environment configuration for production Docker release
- config/ejabberd.yml: ejabberd default config file - vars.config: ejabberd compilation configuration options
- conf/ejabberd.yml: ejabberd default config file
Build ejabberd Community Server base image from ejabberd master on Github:
```bash
docker build -t ejabberd/ecs .
```
Run the build script to generate ejabberd Community Server base image from ejabberd master on Github: Build ejabberd Community Server base image for a given ejabberd version:
```bash ```bash
./build.sh docker build --build-arg VERSION=18.01 -t ejabberd/ecs:18.01 .
``` ```
### TODO ### TODO
- Embed command-line tool for ejabberd API to be able to create admin user for ejabberd. - Rebuild last version of bin/ejabberdapi tool when creating container.
- Rebuild last version of ejabberd-api tool when creating container.

@ -9,26 +9,27 @@ ERL_MAX_ETS_TABLES=1400
FIREWALL_WINDOW="4370-4379" FIREWALL_WINDOW="4370-4379"
INET_DIST_INTERFACE="" INET_DIST_INTERFACE=""
ERLANG_NODE=ejabberd@$(hostname -s) ERLANG_NODE=ejabberd@$(hostname -s)
EJABBERD_BYPASS_WARNINGS=true
# define default environment variables # define default environment variables
ROOT_DIR="/home/ejabberd" ROOT_DIR="/home/ejabberd"
HOME_DIR="$ROOT_DIR" HOME_DIR="$ROOT_DIR"
ERL="$ROOT_DIR"/erts-9.1.5/bin/erl ERL="$ROOT_DIR"/bin/erl
IEX="$ROOT_DIR"/erts-9.1.5/bin/iex IEX="$ROOT_DIR"/bin/iex
EPMD="$ROOT_DIR"/erts-9.1.5/bin/epmd EPMD="$ROOT_DIR"/bin/epmd
INSTALLUSER=ejabberd INSTALLUSER=ejabberd
# check the proper system user is used # check the proper system user is used
case $(id -un) in case $(id -un) in
"$INSTALLUSER") "$INSTALLUSER")
EXEC_CMD="as_current_user" EXEC_CMD="as_current_user"
[ -e "$HOME"/config/ejabberd.yml ] && HOME_DIR="$HOME" [ -e "$HOME"/conf/ejabberd.yml ] && HOME_DIR="$HOME"
;; ;;
root) root)
if [ -n "$INSTALLUSER" ] ; then if [ -n "$INSTALLUSER" ] ; then
EXEC_CMD="as_install_user" EXEC_CMD="as_install_user"
HOME=$(su - ejabberd -c pwd) HOME=$(su - ejabberd -c pwd)
[ -e "$HOME"/config/ejabberd.yml ] && HOME_DIR="$HOME" [ -e "$HOME"/conf/ejabberd.yml ] && HOME_DIR="$HOME"
else else
EXEC_CMD="as_current_user" EXEC_CMD="as_current_user"
echo "WARNING: This is not recommended to run ejabberd as root" >&2 echo "WARNING: This is not recommended to run ejabberd as root" >&2
@ -61,15 +62,15 @@ for arg; do
done done
# define ejabberd variables if not already defined from the command line # define ejabberd variables if not already defined from the command line
: "${ETC_DIR:="$HOME_DIR/config"}" : "${ETC_DIR:="$HOME_DIR/conf"}"
: "${LOGS_DIR:="$HOME_DIR/log"}" : "${LOGS_DIR:="$HOME_DIR/logs"}"
: "${SPOOL_DIR:="$HOME_DIR/db"}"
: "${EJABBERD_CONFIG_PATH:="$ETC_DIR/ejabberd.yml"}" : "${EJABBERD_CONFIG_PATH:="$ETC_DIR/ejabberd.yml"}"
: "${EJABBERDCTL_CONFIG_PATH:="$ETC_DIR/ejabberdctl.cfg"}" : "${EJABBERDCTL_CONFIG_PATH:="$ETC_DIR/ejabberdctl.cfg"}"
[ -f "$EJABBERDCTL_CONFIG_PATH" ] && . "$EJABBERDCTL_CONFIG_PATH" [ -f "$EJABBERDCTL_CONFIG_PATH" ] && . "$EJABBERDCTL_CONFIG_PATH"
[ -n "$ERLANG_NODE_ARG" ] && ERLANG_NODE="$ERLANG_NODE_ARG" [ -n "$ERLANG_NODE_ARG" ] && ERLANG_NODE="$ERLANG_NODE_ARG"
[ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] && S="-s" [ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] && S="-s"
: "${EJABBERD_LOG_PATH:="$LOGS_DIR/ejabberd.log"}" : "${EJABBERD_LOG_PATH:="$LOGS_DIR/ejabberd.log"}"
: "${SPOOL_DIR:="$HOME_DIR/database/$ERLANG_NODE"}"
# define erl parameters # define erl parameters
ERLANG_OPTS="+K $POLL -smp $SMP +P $ERL_PROCESSES $ERL_OPTIONS" ERLANG_OPTS="+K $POLL -smp $SMP +P $ERL_PROCESSES $ERL_OPTIONS"
@ -289,11 +290,13 @@ case $1 in
;; ;;
debug) debug)
debugwarning debugwarning
exec_erl "$(uid debug)" -hidden -remsh "$ERLANG_NODE" exec_erl "$(uid debug)" -hidden -remsh "$ERLANG_NODE" \
-boot start_clean
;; ;;
etop) etop)
exec_erl "$(uid top)" -hidden -node "$ERLANG_NODE" -s etop \ exec_erl "$(uid top)" -hidden -node "$ERLANG_NODE" -s etop \
-s erlang halt -output text -s erlang halt -output text \
-boot start_clean
;; ;;
iexdebug) iexdebug)
debugwarning debugwarning
@ -308,7 +311,8 @@ case $1 in
[ "$PEER" = "${PEER%.*}" ] && PS="-s" [ "$PEER" = "${PEER%.*}" ] && PS="-s"
exec_cmd "$ERL" ${PS:--}name "$(uid ping "$(hostname $PS)")" $ERLANG_OPTS \ exec_cmd "$ERL" ${PS:--}name "$(uid ping "$(hostname $PS)")" $ERLANG_OPTS \
-noinput -hidden -eval 'io:format("~p~n",[net_adm:ping('"'$PEER'"')])' \ -noinput -hidden -eval 'io:format("~p~n",[net_adm:ping('"'$PEER'"')])' \
-s erlang halt -output text -s erlang halt -output text \
-boot start_clean
;; ;;
started) started)
wait_status 0 30 2 # wait 30x2s before timeout wait_status 0 30 2 # wait 30x2s before timeout
@ -317,8 +321,8 @@ case $1 in
wait_status 3 30 2 && stop_epmd # wait 30x2s before timeout wait_status 3 30 2 && stop_epmd # wait 30x2s before timeout
;; ;;
*) *)
exec_erl "$(uid ctl)" -hidden -noinput -s ejabberd_ctl \ exec_erl "$(uid ctl)" -hidden -noinput -boot start_clean \
-extra "$ERLANG_NODE" $NO_TIMEOUT "$@" -s ejabberd_ctl -extra "$ERLANG_NODE" $NO_TIMEOUT "$@"
result=$? result=$?
case $result in case $result in
2|3) help;; 2|3) help;;

@ -0,0 +1,12 @@
#!/bin/sh
set -e
ROOTDIR=/home/ejabberd
BINDIR=$ROOTDIR/bin
EMU=beam
PROGNAME=`echo $0 | sed 's/.*\\///'`
export EMU
export ROOTDIR
export BINDIR
export PROGNAME
exec "$BINDIR/erlexec" ${1+"$@"}

@ -1,50 +0,0 @@
#!/bin/bash
VERSION=${1:-HEAD}
REF=$VERSION
[ "$VERSION" = "HEAD" ] && VERSION=latest
ARCHIVE=ejabberd-${VERSION}.tar.gz
GREEN='\033[0;32m'
NC='\033[0m' # No Color]]'
docker images | grep -q "ejabberd/mix" || {
echo -e "${GREEN}Pulling ejabberd build Docker image${NC}"
docker pull ejabberd/mix
}
if [ ! -d ejbuild ]; then
echo -e "${GREEN}Cloning ejabberd${NC}"
git clone https://github.com/processone/ejabberd.git ejbuild
else
echo -e "${GREEN}Fetch ejabberd${NC}"
(cd ejbuild; git checkout master && git pull)
fi
cat > ejbuild/vars.config <<EOF
{mysql, true}.
{pgsql, true}.
{sqlite, true}.
{zlib, true}.
{redis, true}.
{elixir, true}.
{iconv, true}.
EOF
if [ ! -e ${ARCHIVE} ]; then
echo -e "${GREEN}Checkout ejabberd ${REF}${NC}"
(cd ejbuild; git checkout $REF)
echo -e "${GREEN}Building ejabberd release${NC}"
# Copy release configuration
cp rel/*.exs ejbuild/rel/
# Force clock resync ?
#docker run -it --rm --privileged --entrypoint="/sbin/hwclock" ejabberd/mix -s
# Build ejabberd and generate release
docker run -it -v $(pwd)/ejbuild:$(pwd)/ejbuild -w $(pwd)/ejbuild -e "MIX_ENV=prod" ejabberd/mix do clean, deps.get, deps.compile, compile, release.init, release --env=prod
# Copy generated ejabberd release archive
relvsn=$(grep version ejbuild/mix.exs | cut -d'"' -f2)
cp ejbuild/_build/prod/rel/ejabberd/releases/$relvsn/ejabberd.tar.gz ${ARCHIVE}
fi
# Build ejabberd base container
echo -e "${GREEN}Building ejabberd Community Edition container${NC}"
docker build --build-arg VERSION=${VERSION} -t ejabberd/ecs:${VERSION} .

@ -111,29 +111,33 @@ hosts:
## automatically by ejabberd. ## automatically by ejabberd.
## ##
certfiles: certfiles:
- "/home/ejabberd/config/server.pem" - "/home/ejabberd/conf/server.pem"
## - "/etc/letsencrypt/live/example.org/*.pem" ## - "/etc/letsencrypt/live/example.org/*.pem"
## - "/etc/letsencrypt/live/example.com/*.pem" ## - "/etc/letsencrypt/live/example.com/*.pem"
ca_file: "/home/ejabberd/config/cacert.pem" ca_file: "/home/ejabberd/conf/cacert.pem"
###. ================= ###. =================
###' TLS configuration ###' TLS configuration
define_macro: ## Note that the following configuration is the default
'TLS_CIPHERS': "HIGH:!aNULL:!eNULL:!3DES:@STRENGTH" ## configuration of the TLS driver, so you don't need to
'TLS_OPTIONS': ## uncomment it.
- "no_sslv3" ##
- "cipher_server_preference" ## define_macro:
- "no_compression" ## 'TLS_CIPHERS': "HIGH:!aNULL:!eNULL:!3DES:@STRENGTH"
'DH_FILE': "/home/ejabberd/config/dhparams.pem" # generated with: openssl dhparam -out dhparams.pem 2048 ## 'TLS_OPTIONS':
## - "no_sslv3"
c2s_dhfile: 'DH_FILE' ## - "cipher_server_preference"
s2s_dhfile: 'DH_FILE' ## - "no_compression"
c2s_ciphers: 'TLS_CIPHERS' ## 'DH_FILE': "/home/ejabberd/conf/dhparams.pem" # generated with: openssl dhparam -out dhparams.pem 2048
s2s_ciphers: 'TLS_CIPHERS' ##
c2s_protocol_options: 'TLS_OPTIONS' ## c2s_dhfile: 'DH_FILE'
s2s_protocol_options: 'TLS_OPTIONS' ## s2s_dhfile: 'DH_FILE'
## c2s_ciphers: 'TLS_CIPHERS'
## s2s_ciphers: 'TLS_CIPHERS'
## c2s_protocol_options: 'TLS_OPTIONS'
## s2s_protocol_options: 'TLS_OPTIONS'
###. =============== ###. ===============
###' LISTENING PORTS ###' LISTENING PORTS
@ -647,7 +651,7 @@ language: "en"
## ##
## Full path to a script that generates the image. ## Full path to a script that generates the image.
## ##
## captcha_cmd: "/home/ejabberd/lib/ejabberd-17.12/priv/bin/captcha.sh" ## captcha_cmd: "/home/ejabberd/lib/ejabberd-xx.yy/priv/bin/captcha.sh"
## ##
## Host for the URL and port where ejabberd listens for CAPTCHA requests. ## Host for the URL and port where ejabberd listens for CAPTCHA requests.
@ -712,7 +716,7 @@ modules:
mod_bosh: {} mod_bosh: {}
## mod_http_fileserver: ## mod_http_fileserver:
## docroot: "/var/www" ## docroot: "/var/www"
## accesslog: "/var/log/ejabberd/access.log" ## accesslog: "/home/ejabberd/logs/access.log"
## mod_http_upload: ## mod_http_upload:
## # docroot: "@HOME@/upload" ## # docroot: "@HOME@/upload"
## put_url: "https://@HOST@:5444" ## put_url: "https://@HOST@:5444"

@ -0,0 +1,3 @@
{lookup,["file","native"]}.
{host,{127,0,0,1}, ["localhost","@@HOSTNAME@@"]}.
{file, resolv, "/etc/resolv.conf"}.

File diff suppressed because it is too large Load Diff

@ -1,12 +0,0 @@
#!/bin/sh
set -e
if [ "${1:0:1}" = '-' ]; then
set -- ejabberd "$@"
fi
case "$1" in
'ejabberd') exec $HOME/bin/ejabberd foreground ;;
'console') exec $HOME/bin/ejabberd console ;;
*) exit ;;
esac

@ -23,7 +23,7 @@ end
environment :prod do environment :prod do
set include_erts: true set include_erts: true
set include_src: false set include_src: false
set config: "rel/docker.exs" set config: "rel/prod.exs"
set cookie: :"HmewW_sUao={>LXTD8,g;xBu`.i]tq7Dz.m2?ZqO<g1Iz}?L(36T%w,Zz,)gHp$^" set cookie: :"HmewW_sUao={>LXTD8,g;xBu`.i]tq7Dz.m2?ZqO<g1Iz}?L(36T%w,Zz,)gHp$^"
end end

@ -2,9 +2,9 @@ use Mix.Config
# This is standard path in the context of ejabberd release # This is standard path in the context of ejabberd release
config :ejabberd, config :ejabberd,
file: "/home/p1/ejabberd/config/ejabberd.yml", file: "/home/ejabberd/conf/ejabberd.yml",
log_path: '/home/p1/ejabberd/log/ejabberd.log' log_path: '/home/ejabberd/logs/ejabberd.log'
# Customize Mnesia directory: # Customize Mnesia directory:
config :mnesia, config :mnesia,
dir: '/home/p1/ejabberd/database/' dir: '/home/ejabberd/database/'

@ -2,9 +2,9 @@ use Mix.Config
# This is standard path in the context of ejabberd release # This is standard path in the context of ejabberd release
config :ejabberd, config :ejabberd,
file: "/home/p1/ejabberd/config/ejabberd.yml", file: "/home/ejabberd/conf/ejabberd.yml",
log_path: '/home/p1/ejabberd/log/ejabberd.log' log_path: '/home/ejabberd/logs/ejabberd.log'
# Customize Mnesia directory: # Customize Mnesia directory:
config :mnesia, config :mnesia,
dir: '/home/p1/ejabberd/database/' dir: '/home/ejabberd/database/'

@ -0,0 +1,5 @@
{mysql, true}.
{pgsql, true}.
{sqlite, true}.
{zlib, true}.
{elixir, true}.

@ -10,13 +10,14 @@ You can build ejabberd from source with all dependencies, with the following com
```bash ```bash
git clone https://github.com/processone/ejabberd.git git clone https://github.com/processone/ejabberd.git
cd ejabberd
docker run --rm -v $(pwd):$(pwd) -w $(pwd) ejabberd/mix do deps.get, deps.compile, compile docker run --rm -v $(pwd):$(pwd) -w $(pwd) ejabberd/mix do deps.get, deps.compile, compile
``` ```
Alternatively if you do not have Git installed, you can do: Alternatively if you do not have Git installed, you can do:
```bash ```bash
wget https://github.com/processone/ejabberd/archive/master.zip wget https://github.com/processone/ejabberd/archive/master.zip
unzip ejabberd-master unzip master.zip
cd ejabberd-master cd ejabberd-master
docker run --rm -v $(pwd):$(pwd) -w $(pwd) ejabberd/mix do deps.get, deps.compile, compile docker run --rm -v $(pwd):$(pwd) -w $(pwd) ejabberd/mix do deps.get, deps.compile, compile
``` ```

Loading…
Cancel
Save