diff --git a/containers/teslamate/Dockerfile b/containers/teslamate/Dockerfile deleted file mode 100644 index 70c6d71..0000000 --- a/containers/teslamate/Dockerfile +++ /dev/null @@ -1,84 +0,0 @@ -# TeslaMate - Tesla data logger -# Based on upstream Dockerfile - -ARG CONTAINER_APP_VERSION=v3.0.0 -ARG TESLAMATE_VERSION=${CONTAINER_APP_VERSION} - -FROM elixir:1.19.5-otp-26 AS builder - -ARG TESLAMATE_VERSION - -SHELL ["/bin/bash", "-o", "pipefail", "-c"] - -RUN apt-get update \ - && apt-get install -y ca-certificates curl gnupg git zstd brotli \ - && mkdir -p /etc/apt/keyrings \ - && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \ - | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ - && NODE_MAJOR=22 \ - && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" \ - | tee /etc/apt/sources.list.d/nodesource.list \ - && apt-get update \ - && apt-get install nodejs -y \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -RUN mix local.rebar --force && \ - mix local.hex --force - -# Clone specific version -RUN git clone --depth 1 --branch ${TESLAMATE_VERSION} \ - https://forge.ops.eblu.me/mirrors/teslamate.git /opt/app - -ENV MIX_ENV=prod -WORKDIR /opt/app - -RUN mix deps.get --only $MIX_ENV -RUN mix deps.compile - -RUN npm ci --prefix ./assets --progress=false --no-audit --loglevel=error -RUN mix assets.deploy - -RUN mix compile -RUN SKIP_LOCALE_DOWNLOAD=true mix release --path /opt/built - -# Runtime image -FROM debian:trixie-slim AS app - -ARG CONTAINER_APP_VERSION -LABEL org.opencontainers.image.title="TeslaMate" -LABEL org.opencontainers.image.description="Tesla data logger and visualization" -LABEL org.opencontainers.image.version="${CONTAINER_APP_VERSION}" -LABEL org.opencontainers.image.source="https://forge.eblu.me/eblume/blumeops" -LABEL org.opencontainers.image.vendor="blumeops" - -ENV LANG=C.UTF-8 \ - SRTM_CACHE=/opt/app/.srtm_cache \ - HOME=/opt/app - -WORKDIR $HOME - -RUN apt-get update && apt-get install -y --no-install-recommends \ - libodbc2 \ - libsctp1 \ - libssl3t64 \ - libstdc++6 \ - netcat-openbsd \ - tini \ - tzdata \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* \ - && groupadd --gid 10001 --system nonroot \ - && useradd --uid 10000 --system --gid nonroot --home-dir /home/nonroot --shell /sbin/nologin nonroot \ - && chown -R nonroot:nonroot . - -COPY --chown=nonroot:nonroot --chmod=555 entrypoint.sh / -COPY --from=builder --chown=nonroot:nonroot /opt/built . -RUN mkdir $SRTM_CACHE - -USER nonroot:nonroot - -EXPOSE 4000 - -ENTRYPOINT ["tini", "--", "/bin/dash", "/entrypoint.sh"] -CMD ["bin/teslamate", "start"] diff --git a/containers/teslamate/container.py b/containers/teslamate/container.py new file mode 100644 index 0000000..519d77d --- /dev/null +++ b/containers/teslamate/container.py @@ -0,0 +1,104 @@ +"""TeslaMate — Tesla data logger. + +Two-stage build: Elixir+Node (builder), Debian slim (runtime). +Source cloned from forge mirror. +""" + +import dagger +from dagger import dag + +from blumeops.containers import clone_from_forge, oci_labels + +VERSION = "v3.0.0" + + +async def build(src: dagger.Directory) -> dagger.Container: + source = clone_from_forge("teslamate", VERSION) + + # Stage 1: Build Elixir release with Node.js assets + builder = ( + dag.container() + .from_("elixir:1.19.5-otp-26") + .with_exec( + [ + "bash", + "-c", + "apt-get update" + " && apt-get install -y ca-certificates curl gnupg git zstd brotli" + " && mkdir -p /etc/apt/keyrings" + " && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key" + " | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg" + ' && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg]' + ' https://deb.nodesource.com/node_22.x nodistro main"' + " > /etc/apt/sources.list.d/nodesource.list" + " && apt-get update" + " && apt-get install -y nodejs" + " && apt-get clean" + " && rm -rf /var/lib/apt/lists/*", + ] + ) + .with_exec(["mix", "local.rebar", "--force"]) + .with_exec(["mix", "local.hex", "--force"]) + .with_directory("/opt/app", source) + .with_workdir("/opt/app") + .with_env_variable("MIX_ENV", "prod") + .with_exec(["mix", "deps.get", "--only", "prod"]) + .with_exec(["mix", "deps.compile"]) + .with_exec( + [ + "npm", + "ci", + "--prefix", + "./assets", + "--progress=false", + "--no-audit", + "--loglevel=error", + ] + ) + .with_exec(["mix", "assets.deploy"]) + .with_exec(["mix", "compile"]) + .with_exec( + ["bash", "-c", "SKIP_LOCALE_DOWNLOAD=true mix release --path /opt/built"] + ) + ) + + # Stage 2: Debian slim runtime + entrypoint = src.file("containers/teslamate/entrypoint.sh") + + runtime = ( + dag.container() + .from_("debian:trixie-slim") + .with_exec( + [ + "bash", + "-c", + "apt-get update && apt-get install -y --no-install-recommends" + " libodbc2 libsctp1 libssl3t64 libstdc++6" + " netcat-openbsd tini tzdata" + " && apt-get clean" + " && rm -rf /var/lib/apt/lists/*" + " && groupadd --gid 10001 --system nonroot" + " && useradd --uid 10000 --system --gid nonroot" + " --home-dir /home/nonroot --shell /sbin/nologin nonroot", + ] + ) + ) + runtime = oci_labels( + runtime, + title="TeslaMate", + description="Tesla data logger and visualization", + version=VERSION, + ) + return ( + runtime.with_env_variable("LANG", "C.UTF-8") + .with_env_variable("SRTM_CACHE", "/opt/app/.srtm_cache") + .with_env_variable("HOME", "/opt/app") + .with_workdir("/opt/app") + .with_directory("/opt/app", builder.directory("/opt/built"), owner="nonroot") + .with_exec(["mkdir", "-p", "/opt/app/.srtm_cache"]) + .with_file("/entrypoint.sh", entrypoint, permissions=0o555, owner="nonroot") + .with_user("nonroot") + .with_exposed_port(4000) + .with_entrypoint(["tini", "--", "/bin/dash", "/entrypoint.sh"]) + .with_default_args(args=["bin/teslamate", "start"]) + ) diff --git a/docs/changelog.d/teslamate-dagger-migration.infra.md b/docs/changelog.d/teslamate-dagger-migration.infra.md new file mode 100644 index 0000000..7938365 --- /dev/null +++ b/docs/changelog.d/teslamate-dagger-migration.infra.md @@ -0,0 +1 @@ +Migrate teslamate container build from legacy Dockerfile to native Dagger container.py. diff --git a/docs/how-to/deployment/build-container-image.md b/docs/how-to/deployment/build-container-image.md index a0e7d03..ce66a46 100644 --- a/docs/how-to/deployment/build-container-image.md +++ b/docs/how-to/deployment/build-container-image.md @@ -129,7 +129,7 @@ Existing containers demonstrate several build approaches: | Native Dagger (Go + Node) | [[#navidrome]] | `container.py` with helper functions — preferred for new containers | | Alpine package install | [[#transmission]] | Simplest Dockerfile — install from apk | | Go from source | [[#miniflux]] | Dockerfile: clone upstream, `go build` | -| Multi-stage Elixir | [[#teslamate]] | Dockerfile: Elixir release with Node assets | +| Native Dagger (Elixir + Node) | [[#teslamate]] | `container.py` with Debian runtime — Elixir release with Node assets | | Runtime tarball download | [[#kiwix-serve]] | Dockerfile: download pre-built binary with arch detection | | Nix `dockerTools` | [[#ntfy-nix]] | `buildLayeredImage` with nix-built app (ringtail runner) | @@ -147,7 +147,7 @@ Existing containers demonstrate several build approaches: ### teslamate -`containers/teslamate/Dockerfile` — Two-stage Elixir build with Node.js asset compilation. Uses Debian-based images due to Elixir/OTP dependencies. (Legacy Dockerfile — migrate to `container.py` during review.) +`containers/teslamate/container.py` — Native Dagger build. Two-stage pipeline: Elixir builder with Node.js for asset compilation, Debian slim runtime. Uses Debian-based images (not Alpine) due to Elixir/OTP dependencies. Includes entrypoint script for pg-wait and migrations. ### kiwix-serve diff --git a/service-versions.yaml b/service-versions.yaml index ffb1e0f..e7e2ac9 100644 --- a/service-versions.yaml +++ b/service-versions.yaml @@ -190,7 +190,7 @@ services: - name: teslamate type: argocd - last-reviewed: 2026-03-03 + last-reviewed: 2026-04-14 current-version: "v3.0.0" upstream-source: https://github.com/teslamate-org/teslamate/releases