diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a03cecf --- /dev/null +++ b/.dockerignore @@ -0,0 +1,18 @@ +.dockerignore +# there are valid reasons to keep the .git, namely so that you can get the +# current commit hash +#.git +.log +tmp + +# Mix artifacts +_build +deps +*.ez +releases + +# Generate on crash by the VM +erl_crash.dump + +# Static artifacts +node_modules diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a9489ca --- /dev/null +++ b/Dockerfile @@ -0,0 +1,89 @@ +# 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:1.13.4-erlang-24.3.3-debian-bullseye-20210902-slim +# +ARG BUILDER_IMAGE="hexpm/elixir:1.13.4-erlang-24.3.3-debian-bullseye-20210902-slim" +ARG RUNNER_IMAGE="debian:bullseye-20210902-slim" + +FROM ${BUILDER_IMAGE} as builder + +# install build dependencies +RUN apt-get update -y && apt-get install -y build-essential git \ + && apt-get clean && rm -f /var/lib/apt/lists/*_* + +# prepare build dir +WORKDIR /app + +# install hex + rebar +RUN mix local.hex --force && \ + mix local.rebar --force + +# set build ENV +ENV MIX_ENV="prod" + +# install mix dependencies +COPY mix.exs mix.lock ./ +RUN mix deps.get --only $MIX_ENV +RUN mkdir config + +# copy compile-time config files before we compile dependencies +# to ensure any relevant config change will trigger the dependencies +# to be re-compiled. +COPY config/config.exs config/${MIX_ENV}.exs config/ +RUN mix deps.compile + +COPY priv priv + +# note: if your project uses a tool like https://purgecss.com/, +# which customizes asset compilation based on what it finds in +# your Elixir templates, you will need to move the asset compilation +# step down so that `lib` is available. +COPY assets assets + +# compile assets +RUN mix assets.deploy + +# Compile the release +COPY lib lib + +RUN mix compile + +# Changes to config/runtime.exs don't require recompiling the code +COPY config/runtime.exs config/ + +COPY rel rel +RUN mix release + +# start a new build stage so that the final image will only contain +# the compiled release and other runtime necessities +FROM ${RUNNER_IMAGE} + +RUN apt-get update -y && apt-get install -y libstdc++6 openssl libncurses5 locales \ + && apt-get clean && rm -f /var/lib/apt/lists/*_* + +# Set the locale +RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen + +ENV LANG en_US.UTF-8 +ENV LANGUAGE en_US:en +ENV LC_ALL en_US.UTF-8 + +WORKDIR "/app" +RUN chown nobody /app + +# Only copy the final release from the build stage +COPY --from=builder --chown=nobody:root /app/_build/prod/rel/aggiedit ./ + +USER nobody + +CMD ["/app/bin/server"] diff --git a/config/config.exs b/config/config.exs index 0ffcbbd..8be792c 100644 --- a/config/config.exs +++ b/config/config.exs @@ -27,7 +27,7 @@ config :aggiedit, AggieditWeb.Endpoint, config :aggiedit, Aggiedit.Mailer, adapter: Swoosh.Adapters.Local # Swoosh API client is needed for adapters other than SMTP. -config :swoosh, :api_client, false +# config :swoosh, :api_client, false # Configure esbuild (the version is required) config :esbuild, diff --git a/config/runtime.exs b/config/runtime.exs index 50fa43f..84b1f03 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -32,6 +32,16 @@ if config_env() == :prod do pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"), socket_options: maybe_ipv6 + config :aggiedit, Aggiedit.Mailer, + adapter: Swoosh.Adapters.SMTP, + relay: System.get_env("SMTP_SERVER"), + port: 587, + username: System.get_env("CONTACT_EMAIL"), + password: System.get_env("EMAIL_PASSWORD"), + tls: :always, + auth: :always, + no_mx_lookups: true + # The secret key base is used to sign/encrypt cookies and other secrets. # A default value is used in config/dev.exs and config/test.exs but you # want to use a different value for prod and you most likely don't want diff --git a/lib/aggiedit/release.ex b/lib/aggiedit/release.ex new file mode 100644 index 0000000..c1f6c7e --- /dev/null +++ b/lib/aggiedit/release.ex @@ -0,0 +1,28 @@ +defmodule Aggiedit.Release do + @moduledoc """ + Used for executing DB release tasks when run in production without Mix + installed. + """ + @app :aggiedit + + def migrate do + load_app() + + for repo <- repos() do + {:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :up, all: true)) + end + end + + def rollback(repo, version) do + load_app() + {:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :down, to: version)) + end + + defp repos do + Application.fetch_env!(@app, :ecto_repos) + end + + defp load_app do + Application.load(@app) + end +end diff --git a/rel/overlays/bin/migrate b/rel/overlays/bin/migrate new file mode 100755 index 0000000..e984762 --- /dev/null +++ b/rel/overlays/bin/migrate @@ -0,0 +1,3 @@ +#!/bin/sh +cd -P -- "$(dirname -- "$0")" +exec ./aggiedit eval Aggiedit.Release.migrate \ No newline at end of file diff --git a/rel/overlays/bin/migrate.bat b/rel/overlays/bin/migrate.bat new file mode 100755 index 0000000..6ff2128 --- /dev/null +++ b/rel/overlays/bin/migrate.bat @@ -0,0 +1 @@ +call "%~dp0\aggiedit" eval Aggiedit.Release.migrate \ No newline at end of file diff --git a/rel/overlays/bin/server b/rel/overlays/bin/server new file mode 100755 index 0000000..236ff06 --- /dev/null +++ b/rel/overlays/bin/server @@ -0,0 +1,3 @@ +#!/bin/sh +cd -P -- "$(dirname -- "$0")" +PHX_SERVER=true exec ./aggiedit start diff --git a/rel/overlays/bin/server.bat b/rel/overlays/bin/server.bat new file mode 100755 index 0000000..da0d4fd --- /dev/null +++ b/rel/overlays/bin/server.bat @@ -0,0 +1,2 @@ +set PHX_SERVER=true +call "%~dp0\aggiedit" start \ No newline at end of file