Splits the runtime apt installs (gdal-bin, snmp, libsnmp40, locales,
BEAM runtime libs) into k8s/Dockerfile.base, hosted at
codeberg.org/gmcintire/towerops-base:latest. The app Dockerfile now
does FROM that base instead of re-installing gdal (~500 MB) on every
push.
The new build-base workflow rebuilds the base image only when
k8s/Dockerfile.base or the workflow itself changes, weekly via cron
(Sundays 06:00 UTC, with CACHE_BUST=<ISO week> to force apt-get update
on a week boundary), or via workflow_dispatch.
Production workflow now uses buildx + does docker login before the
build so it can pull the private base image.
Catalog-only system for LIDAR-derived 1m DEMs covering Texas. We do not
mirror raster data — the DB stores tile metadata (URL + footprint
geometry) and elevation values are streamed on-demand from public USGS
3DEP / TNRIS Cloud-Optimized GeoTIFFs via GDAL /vsicurl/ HTTP byte-range
reads. Used for future tower coverage line-of-sight calculations.
- Enable PostGIS extension; tile footprints indexed via GiST
- Catalog sync from USGS 3DEP (primary) and TNRIS (fallback for gaps)
- Tile dedup by ST_Contains so TNRIS doesn't duplicate 3DEP coverage
- Single-point reads via gdallocationinfo with tile fallthrough on nodata
- Grid retrieval via gdal_translate AAIGrid with multi-tile mosaicing
- Monthly Oban cron worker keeps catalog fresh
- gdal-bin added to runtime image; geo_postgis dep added
Incremental compilation can miss schema field removals in dependent
modules, causing undefined_column errors in production.
Reviewed-on: graham/towerops-web#119
Set up mix_gleam archive, gleam_stdlib, and gleeunit deps. Add Gleam
compiler to all Dockerfiles, CI workflows, nix shell, and .tool-versions.
Rewrite Numeric, QueryHelpers, and URLValidator from Elixir to Gleam with
full ExUnit test coverage. Update all callers to use the Gleam modules
directly via :towerops@module syntax. Remove duplicate sanitize_like/1
private functions in snmp.ex and trace.ex.
Reviewed-on: graham/towerops-web#98
Reverted k8s/Dockerfile back to using official hexpm/elixir and debian
images directly instead of custom gmcintire/towerops-base images.
This restores the original Dockerfile behavior where all dependencies
are installed during each build. The base image work in k8s/base-image/
remains available but is not used by the main application build.
Changed image naming scheme from separate builder/runtime images to a
single image name with different tags:
- docker.io/gmcintire/towerops-base:builder
- docker.io/gmcintire/towerops-base:runtime
- docker.io/gmcintire/towerops-base:latest (alias for runtime)
This simplifies the naming and makes it clearer that both images are
part of the same towerops-base image family.
Changes:
- build.sh: Use IMAGE_NAME with BUILDER_TAG/RUNTIME_TAG
- Makefile: Update all targets to use new naming
- k8s/Dockerfile: Point to gmcintire/towerops-base:builder and :runtime
Instead of installing system packages and tools on every deploy, we now
have two base images that pre-bake all the slow setup steps.
Base Images Created:
1. elixir-builder:latest - Builder stage base
- hexpm/elixir with build-essential and git pre-installed
- hex and rebar pre-installed
- Saves ~40 seconds per deploy
2. debian-runtime:latest - Runtime stage base
- Debian with all runtime packages pre-installed
- Locale pre-configured (en_US.UTF-8)
- /app directory pre-created
- Saves ~60 seconds per deploy
Main Dockerfile Changes:
- Use elixir-builder:latest instead of hexpm/elixir
- Use debian-runtime:latest instead of debian:trixie-slim
- Remove apt-get install steps (now in base images)
- Remove hex/rebar install (now in builder base)
- Remove locale setup (now in runtime base)
Build System:
- build.sh builds both images with podman/docker auto-detect
- Pushes to both GitLab registry and Docker Hub
- Makefile targets for build/test/push/clean
Total Time Saved Per Deploy: ~100 seconds (1m40s)
Benefits:
- Faster CI/CD builds (no repeated apt-get install)
- Faster local development builds
- Consistent build environment across all deploys
- Security updates centralized in base image rebuilds
- Rewrote Ping module to use system ping binary instead of raw ICMP
sockets which required CAP_NET_RAW privileges
- Added iputils-ping package to Docker images for production
- Added proper IP address validation before executing ping command
- Updated tests to account for 1 second minimum timeout of system ping
- System ping binary is setuid root and works without elevated privileges