From 4e2d74e21cf47ce4c0165cd1906b5fbbba5f3db0 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Tue, 31 Mar 2026 08:55:09 -0500 Subject: [PATCH] Build wgrib2 in Docker with cached build stage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a separate wgrib2-builder stage that compiles wgrib2 from source. Docker layer caching means this only builds once — subsequent deploys reuse the cached layer. The binary is copied into the final runtime image. Also fixed wgrib2 path lookup to be runtime instead of compile-time so it works in the Docker build pipeline. --- Dockerfile | 33 ++++++++++++++++++++--- lib/microwaveprop/weather/grib2/wgrib2.ex | 9 +++---- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index 87f9b787..8c627aaa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,29 @@ ARG DEBIAN_VERSION=trixie-20260316-slim ARG BUILDER_IMAGE="docker.io/hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}" ARG RUNNER_IMAGE="docker.io/debian:${DEBIAN_VERSION}" +# ---- wgrib2 build stage (cached independently) ---- +FROM ${RUNNER_IMAGE} AS wgrib2-builder + +ARG WGRIB2_VERSION=3.6.1 + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + build-essential gfortran wget ca-certificates \ + zlib1g-dev libaec-dev libpng-dev \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /tmp/wgrib2 + +RUN wget -q "https://www.ftp.cpc.ncep.noaa.gov/wd51we/wgrib2/wgrib2.tgz.v${WGRIB2_VERSION}" \ + -O wgrib2.tgz \ + && tar xzf wgrib2.tgz \ + && cd grib2 \ + && export CC=gcc FC=gfortran \ + && make \ + && cp wgrib2/wgrib2 /usr/local/bin/wgrib2 \ + && strip /usr/local/bin/wgrib2 + +# ---- Elixir build stage ---- FROM ${BUILDER_IMAGE} AS builder # install build dependencies @@ -66,12 +89,13 @@ 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 +# ---- Final runtime stage ---- FROM ${RUNNER_IMAGE} AS final RUN apt-get update \ - && apt-get install -y --no-install-recommends libstdc++6 openssl libncurses6 locales ca-certificates snmp \ + && apt-get install -y --no-install-recommends \ + libstdc++6 openssl libncurses6 locales ca-certificates snmp \ + libgfortran5 libaec0 zlib1g libpng16-16t64 \ && rm -rf /var/lib/apt/lists/* # Set the locale @@ -88,6 +112,9 @@ RUN chown nobody /app # set runner ENV ENV MIX_ENV="prod" +# Copy wgrib2 binary from the build stage +COPY --from=wgrib2-builder /usr/local/bin/wgrib2 /usr/local/bin/wgrib2 + # Only copy the final release from the build stage COPY --from=builder --chown=nobody:root /app/_build/${MIX_ENV}/rel/microwaveprop ./ diff --git a/lib/microwaveprop/weather/grib2/wgrib2.ex b/lib/microwaveprop/weather/grib2/wgrib2.ex index 67e43b0d..63e457e9 100644 --- a/lib/microwaveprop/weather/grib2/wgrib2.ex +++ b/lib/microwaveprop/weather/grib2/wgrib2.ex @@ -9,7 +9,6 @@ defmodule Microwaveprop.Weather.Grib2.Wgrib2 do require Logger - @wgrib2_path System.find_executable("wgrib2") @undefined_value 9.999e20 @doc """ @@ -29,9 +28,9 @@ defmodule Microwaveprop.Weather.Grib2.Wgrib2 do end @doc "Check if wgrib2 is available on the system." - def available? do - @wgrib2_path != nil - end + def available?, do: wgrib2_path() != nil + + defp wgrib2_path, do: System.find_executable("wgrib2") defp extract_with_wgrib2(grib_binary, match_pattern, grid_spec) do %{ @@ -68,7 +67,7 @@ defmodule Microwaveprop.Weather.Grib2.Wgrib2 do "bin" ] - case System.cmd(@wgrib2_path, args, stderr_to_stdout: true) do + case System.cmd(wgrib2_path(), args, stderr_to_stdout: true) do {output, 0} -> # Parse message inventory from stdout to know which vars were extracted messages = parse_wgrib2_inventory(output)