Skip to content

Commit 9abb50d

Browse files
committed
Prepare for Release
* Add container-based release setup. Launch `mix podman.relase` to build a release container. * Add missing production deps. Where missing a package so swoosh can send mail via `sendmail`. * Fix syntax errors in proc config file. * Minimize app JS bundle by making pdfjs a dev dependency. * Auto-create upload dirs so an empty folder for all uploads mounted into the production container suffices to launch the app.
1 parent 456a6dc commit 9abb50d

16 files changed

Lines changed: 266 additions & 31 deletions

File tree

.dockerignore

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# This file excludes paths from the Docker build context.
2+
#
3+
# By default, Docker's build context includes all files (and folders) in the
4+
# current directory. Even if a file isn't copied into the container it is still sent to
5+
# the Docker daemon.
6+
#
7+
# There are multiple reasons to exclude files from the build context:
8+
#
9+
# 1. Prevent nested folders from being copied into the container (ex: exclude
10+
# /assets/node_modules when copying /assets)
11+
# 2. Reduce the size of the build context and improve build time (ex. /build, /deps, /doc)
12+
# 3. Avoid sending files containing sensitive information
13+
#
14+
# More information on using .dockerignore is available here:
15+
# https://docs.docker.com/engine/reference/builder/#dockerignore-file
16+
17+
.dockerignore
18+
19+
# Ignore git, but keep git HEAD and refs to access current commit hash if needed:
20+
#
21+
# $ cat .git/HEAD | awk '{print ".git/"$2}' | xargs cat
22+
# d0b8727759e1e0e7aa3d41707d12376e373d5ecc
23+
.git
24+
!.git/HEAD
25+
!.git/refs
26+
27+
# Common development/test artifacts
28+
/cover/
29+
/doc/
30+
/test/
31+
/tmp/
32+
.elixir_ls
33+
34+
# Mix artifacts
35+
/_build/
36+
/deps/
37+
*.ez
38+
39+
# Generated on crash by the VM
40+
erl_crash.dump
41+
42+
# Static artifacts - These should be fetched and built inside the Docker image
43+
# https://hexdocs.pm/phoenix/Mix.Tasks.Phx.Gen.Release.html#module-docker
44+
/assets/node_modules/
45+
/priv/static/assets/
46+
/priv/static/cache_manifest.json

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ dev_round-*.tar
3131
# Ignore digested assets cache.
3232
/priv/static/cache_manifest.json
3333

34+
# Ignore uploaded files
35+
/priv/uploads
36+
3437
# In case you use Node.js/npm, you want to ignore these.
3538
npm-debug.log
3639
/assets/node_modules/
@@ -41,3 +44,6 @@ npm-debug.log
4144

4245
# VSCode stuff
4346
/.vscode
47+
48+
# Vim swap files
49+
*.swp

Dockerfile

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Find eligible builder and runner images on Docker Hub. We use Ubuntu/Debian
2+
# instead of Alpine to avoid DNS resolution issues in production.
3+
#
4+
# https://hub.docker.com/r/hexpm/elixir/tags?name=ubuntu
5+
# https://hub.docker.com/_/ubuntu/tags
6+
#
7+
# This file is based on these images:
8+
#
9+
# - https://hub.docker.com/r/hexpm/elixir/tags - for the build image
10+
# - https://hub.docker.com/_/debian/tags?name=trixie-20260406-slim - for the release image
11+
# - https://pkgs.org/ - resource for finding needed packages
12+
# - Ex: docker.io/hexpm/elixir:1.19.4-erlang-28.1-debian-trixie-20260406-slim
13+
#
14+
ARG ELIXIR_VERSION=1.19.4
15+
ARG OTP_VERSION=28.1
16+
ARG DEBIAN_VERSION=trixie-20260406-slim
17+
18+
ARG BUILDER_IMAGE="docker.io/hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}"
19+
ARG RUNNER_IMAGE="docker.io/debian:${DEBIAN_VERSION}"
20+
21+
FROM ${BUILDER_IMAGE} AS builder
22+
23+
# install build dependencies
24+
RUN apt-get update \
25+
&& apt-get install -y --no-install-recommends build-essential git npm\
26+
&& rm -rf /var/lib/apt/lists/*
27+
28+
# prepare build dir
29+
WORKDIR /app
30+
31+
# install hex + rebar
32+
RUN mix local.hex --force \
33+
&& mix local.rebar --force
34+
35+
# set build ENV
36+
ENV MIX_ENV="prod"
37+
38+
# install mix dependencies
39+
COPY mix.exs mix.lock ./
40+
RUN mix deps.get --only $MIX_ENV
41+
RUN mkdir config
42+
43+
# copy compile-time config files before we compile dependencies
44+
# to ensure any relevant config change will trigger the dependencies
45+
# to be re-compiled.
46+
COPY config/config.exs config/${MIX_ENV}.exs config/
47+
RUN mix deps.compile
48+
49+
RUN mix assets.setup
50+
51+
COPY priv priv
52+
53+
COPY lib lib
54+
55+
# Compile the release
56+
RUN mix compile
57+
58+
COPY assets assets
59+
60+
# compile assets
61+
RUN mix assets.deploy
62+
63+
# Changes to config/runtime.exs don't require recompiling the code
64+
COPY config/runtime.exs config/
65+
66+
COPY rel rel
67+
RUN mix release
68+
69+
# start a new build stage so that the final image will only contain
70+
# the compiled release and other runtime necessities
71+
FROM ${RUNNER_IMAGE} AS final
72+
73+
RUN apt-get update \
74+
&& apt-get install -y --no-install-recommends libstdc++6 openssl libncurses6 locales ca-certificates\
75+
&& rm -rf /var/lib/apt/lists/*
76+
77+
# Set the locale
78+
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen \
79+
&& locale-gen
80+
81+
ENV LANG=en_US.UTF-8
82+
ENV LANGUAGE=en_US:en
83+
ENV LC_ALL=en_US.UTF-8
84+
85+
WORKDIR "/app"
86+
RUN chown nobody /app
87+
88+
# set runner ENV
89+
ENV MIX_ENV="prod"
90+
91+
# Only copy the final release from the build stage
92+
COPY --from=builder --chown=nobody:root /app/_build/${MIX_ENV}/rel/dev_round ./
93+
94+
USER nobody
95+
96+
# If using an environment that doesn't automatically reap zombie processes, it is
97+
# advised to add an init process such as tini via `apt-get install`
98+
# above and adding an entrypoint. See https://github.com/krallin/tini for details
99+
# ENTRYPOINT ["/tini", "--"]
100+
101+
CMD ["/app/bin/server"]

assets/package-lock.json

Lines changed: 16 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
},
66
"devDependencies": {
77
"@tailwindcss/typography": "^0.5.19",
8+
"pdfjs-dist": "^5.4.624",
89
"daisyui": "^5.5.19"
910
},
1011
"dependencies": {
11-
"pdfjs-dist": "^5.4.624",
1212
"prismjs": "^1.30.0"
1313
}
1414
}

config/config.exs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ config :exldap, :settings,
8181
sslopts: [verify: :verify_peer, cacerts: :public_key.cacerts_get()],
8282
search_timeout: 10_000
8383

84+
# Disable swoosh api client (we use SMTP)
85+
config :swoosh, :api_client, false
86+
8487
# Import environment specific config. This must remain at the bottom
8588
# of this file so it overrides the configuration defined above.
8689
import_config "#{config_env()}.exs"

config/dev.exs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,3 @@ config :phoenix_live_view,
8787
debug_attributes: true,
8888
# Enable helpful, but potentially expensive runtime checks
8989
enable_expensive_runtime_checks: true
90-
91-
# Disable swoosh api client as it is only required for production adapters.
92-
config :swoosh, :api_client, false

config/runtime.exs

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -61,20 +61,28 @@ if config_env() == :prod do
6161

6262
config :dev_round,
6363
:mail_from,
64-
System.get_env(MAIL_FROM) ||
64+
System.get_env("MAIL_FROM") ||
6565
raise("""
6666
environment variable MAIL_FROM is missing.
6767
For example: devround@localhost
6868
""")
6969

7070
config :dev_round,
7171
:mail_from,
72-
System.get_env(LDAP_USER_GROUP) ||
72+
System.get_env("LDAP_USER_GROUP") ||
7373
raise("""
7474
environment variable LDAP_USER_GROUP is missing.
7575
For example: devround_users
7676
""")
7777

78+
config :dev_round,
79+
:mail_from,
80+
System.get_env("LDAP_ADMIN_GROUP") ||
81+
raise("""
82+
environment variable LDAP_ADMIN_GROUP is missing.
83+
For example: devround_users
84+
""")
85+
7886
config :dev_round, DevRoundWeb.Endpoint,
7987
url: [host: host, port: 443, scheme: "https"],
8088
http: [
@@ -96,6 +104,13 @@ if config_env() == :prod do
96104
password: System.fetch_env!("LDAP_BIND_PASSWORD"),
97105
sslopts: [verify: :verify_peer, cacerts: :public_key.cacerts_get()]
98106

107+
# Configuring the mailer
108+
109+
config :dev_round, DevRound.Mailer,
110+
adapter: Swoosh.Adapters.Sendmail,
111+
cmd_path: "/usr/sbin/sendmail",
112+
args: ["-t", "-i"]
113+
99114
# ## SSL Support
100115
#
101116
# To get SSL working, you will need to add the `https` key
@@ -127,22 +142,4 @@ if config_env() == :prod do
127142
# force_ssl: [hsts: true]
128143
#
129144
# Check `Plug.SSL` for all available options in `force_ssl`.
130-
131-
# ## Configuring the mailer
132-
#
133-
# In production you need to configure the mailer to use a different adapter.
134-
# Here is an example configuration for Mailgun:
135-
#
136-
# config :sample_app, SampleApp.Mailer,
137-
# adapter: Swoosh.Adapters.Mailgun,
138-
# api_key: System.get_env("MAILGUN_API_KEY"),
139-
# domain: System.get_env("MAILGUN_DOMAIN")
140-
#
141-
# Most non-SMTP adapters require an API client. Swoosh supports Req, Hackney,
142-
# and Finch out-of-the-box. This configuration is typically done at
143-
# compile-time in your config/prod.exs:
144-
#
145-
# config :swoosh, :api_client, Swoosh.ApiClient.Req
146-
#
147-
# See https://hexdocs.pm/swoosh/Swoosh.html#module-installation for details.
148145
end

0 commit comments

Comments
 (0)