# Moss pole configurator: a Phoenix LiveView page that renders mosspole.scad
# and mosspole-pipe.scad in the browser, with OpenSCAD compiled to
# WebAssembly. Build from the repository root:
#
#   docker build -f web/Dockerfile -t mosspole-web .
#   docker run --rm -p 4000:4000 mosspole-web

ARG ALPINE=3.22.7
ARG ELIXIR_IMAGE=hexpm/elixir:1.19.7-erlang-28.4.3-alpine-${ALPINE}

# OpenSCAD for the browser, built with Emscripten on OpenSCAD's own base
# image, which holds its libraries already compiled to WebAssembly. The image
# exists for amd64 only; Docker emulates it elsewhere, and the result runs on
# any platform.
FROM --platform=linux/amd64 openscad/wasm-base@sha256:f73d33d5f2fd4c7ae4d3aaacb1e2e2deb193b878b38bb80c8235c933ac340c66 AS wasm
ARG OPENSCAD_REF=21780e0987e72f6ea9ceaa5a8e2d41f58fe66b96
# Each compile job can take well over a gigabyte with CGAL; set this to the
# number of CPUs where Docker has about 2 GB of memory for each.
ARG BUILD_JOBS=4
WORKDIR /src
RUN git init -q openscad && cd openscad \
 && git remote add origin https://github.com/openscad/openscad.git \
 && git fetch -q --depth 1 origin ${OPENSCAD_REF} && git checkout -q FETCH_HEAD \
 && git submodule update -q --init --depth 1 \
      submodules/manifold submodules/Clipper2 submodules/sanitizers-cmake
RUN emcmake cmake -S openscad -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \
      -DWASM_BUILD_TYPE=web -DENABLE_TESTS=OFF -DEXPERIMENTAL=OFF -DUSE_CCACHE=OFF \
 && cmake --build build --parallel ${BUILD_JOBS} \
 && mkdir /out && cp build/openscad.js build/openscad.wasm /out/
# The page sends openscad.wasm to every visitor, so it offers the source of
# what is in it from the same place, for the GPL and LGPL (see the licences
# page and ADR 13): OpenSCAD as built, without the tests and the parts of
# Manifold its build leaves out, and CGAL's headers from the base image,
# which are its source.
RUN mkdir -p /out/openscad /out/source && mv /out/openscad.js /out/openscad.wasm /out/openscad/ \
 && tar --exclude=.git --exclude=openscad/tests \
      --exclude=openscad/submodules/manifold/samples --exclude=openscad/submodules/manifold/test \
      --exclude=openscad/submodules/manifold/extras --exclude=openscad/submodules/manifold/docs \
      -cJf /out/source/openscad-${OPENSCAD_REF}.tar.xz openscad \
 && tar -cJf /out/source/cgal-6.0.1-headers.tar.xz -C /emsdk/upstream/emscripten/cache/sysroot/include CGAL

# Just the two files, for running the app outside Docker:
#   docker build -f web/Dockerfile --target openscad-wasm --output web/priv/static/openscad .
FROM scratch AS openscad-wasm
COPY --from=wasm /out/openscad/ /

# The sources of the other copyleft libraries in openscad.wasm, and the
# recipe of the base image (openscad/openscad-wasm, as of the image's build),
# checked against their checksums.
FROM alpine:${ALPINE} AS sources
WORKDIR /out
RUN apk add --no-cache curl \
 && curl -fsSLO https://gmplib.org/download/gmp/gmp-6.3.0.tar.xz \
 && curl -fsSLO https://www.mpfr.org/mpfr-4.2.1/mpfr-4.2.1.tar.xz \
 && curl -fsSL -o glib-17a1183996f85be8814f4cccc2400c93a2e53cbf.tar.gz \
      https://github.com/kleisauke/glib/archive/17a1183996f85be8814f4cccc2400c93a2e53cbf.tar.gz \
 && curl -fsSL -o openscad-wasm-501694addd7a7c9bd307b38d8591a4faad5381e8.tar.gz \
      https://github.com/openscad/openscad-wasm/archive/501694addd7a7c9bd307b38d8591a4faad5381e8.tar.gz \
 && printf '%s\n' \
      "a3c2b80201b89e68616f4ad30bc66aee4927c3ce50e33929ca819d5c43538898  gmp-6.3.0.tar.xz" \
      "277807353a6726978996945af13e52829e3abd7a9a5b7fb2793894e18f1fcbb2  mpfr-4.2.1.tar.xz" \
      "23bcb242533deef021a10b6a313fc4f977fe9f951295804a60149f8cad55c638  glib-17a1183996f85be8814f4cccc2400c93a2e53cbf.tar.gz" \
      "de15ea60d9caa05c76be3d5e8557ce823b75fcc905e06006811c0483526da088  openscad-wasm-501694addd7a7c9bd307b38d8591a4faad5381e8.tar.gz" \
    | sha256sum -c -

# The tests, as a stage of their own: the deploy job builds it before the
# release (--target test), so a failing test stops the deploy. Nothing of it
# goes into the image.
FROM ${ELIXIR_IMAGE} AS test
ENV MIX_ENV=test MOSSPOLE_MODELS=/model
WORKDIR /app
RUN mix local.hex --force && mix local.rebar --force
COPY web/mix.exs web/mix.lock ./
RUN mix deps.get && mix deps.compile
COPY mosspole.scad mosspole.json mosspole-pipe.scad mosspole-pipe.json \
     mosspole-lid.scad mosspole-lid.json /model/
COPY fonts /model/fonts
COPY web/config config
COPY web/lib lib
COPY web/priv priv
COPY web/test test
RUN mix compile --warnings-as-errors && mix test

# The Phoenix release, with OpenSCAD among its static files.
FROM ${ELIXIR_IMAGE} AS app
ENV MIX_ENV=prod
WORKDIR /app
RUN mix local.hex --force && mix local.rebar --force
COPY web/mix.exs web/mix.lock ./
RUN mix deps.get --only prod
COPY web/config config
RUN mix deps.compile
COPY web/lib lib
COPY web/priv priv
COPY --from=wasm /out/openscad/ priv/static/openscad/
COPY --from=wasm /out/source/ priv/static/source/
COPY --from=sources /out/ priv/static/source/
COPY web/Dockerfile priv/static/source/Dockerfile
RUN gzip -9 -k priv/static/openscad/* priv/static/*.js priv/static/*.css \
 && (cd priv/static/source && sha256sum *.tar.* Dockerfile > SHA256SUMS) \
 && mix compile && mix release

FROM alpine:${ALPINE}
RUN apk add --no-cache libstdc++ libgcc ncurses-libs openssl \
 && adduser -D -h /app app
COPY --from=app --chown=app /app/_build/prod/rel/mosspole /app
COPY mosspole.scad mosspole.json mosspole-pipe.scad mosspole-pipe.json \
     mosspole-lid.scad mosspole-lid.json /app/model/
COPY fonts /app/model/fonts
USER app
WORKDIR /app
# The commit the image was built from, for the build_info figure.
ARG GIT_COMMIT=unknown
ENV PHX_SERVER=true PORT=4000 MOSSPOLE_MODELS=/app/model MOSSPOLE_COMMIT=${GIT_COMMIT}
EXPOSE 4000
CMD ["/app/bin/mosspole", "start"]
