Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cee64b9f74 | ||
|
|
e16513f195 | ||
|
|
792516919a |
@@ -26,11 +26,23 @@ firehose/
|
|||||||
│ ├── lib/
|
│ ├── lib/
|
||||||
│ └── mix.exs
|
│ └── mix.exs
|
||||||
├── mise.toml # Runtime versions (Elixir, Erlang, Node)
|
├── mise.toml # Runtime versions (Elixir, Erlang, Node)
|
||||||
|
├── Dockerfile # Deploy build; ARG pins must match mise.toml
|
||||||
|
├── scripts/check-pins.sh # Verifies the pin sync; runs in `make check`
|
||||||
└── Makefile # make (test, check)
|
└── Makefile # make (test, check)
|
||||||
```
|
```
|
||||||
|
|
||||||
Two blogs are configured:
|
Two blogs are configured:
|
||||||
|
|
||||||
|
# Toolchain pins
|
||||||
|
|
||||||
|
`mise.toml` and the `Dockerfile` ARG pins must agree; `scripts/check-pins.sh`
|
||||||
|
(runs as part of `make check`) enforces this. When bumping versions, change
|
||||||
|
both files in one commit. Upstream drift (newer Elixir/OTP/Debian releases)
|
||||||
|
is reported by `dokku-updates firehose check` (dokku-maintenance repo).
|
||||||
|
Upgrading pins or mix packages is largely unscripted: for such tasks, a Pi
|
||||||
|
sub agent can be started in this directory — it reads this AGENTS.md and
|
||||||
|
follows the zero-defects policy above.
|
||||||
|
|
||||||
| Blog | Route | Description |
|
| Blog | Route | Description |
|
||||||
|------|-------|-------------|
|
|------|-------|-------------|
|
||||||
| Engineering | `/blog/engineering` | Main blog |
|
| Engineering | `/blog/engineering` | Main blog |
|
||||||
|
|||||||
+3
-3
@@ -1,9 +1,9 @@
|
|||||||
# Dockerfile for Dokku deployment
|
# Dockerfile for Dokku deployment
|
||||||
# Multi-stage build for Phoenix/Elixir app with monorepo layout
|
# Multi-stage build for Phoenix/Elixir app with monorepo layout
|
||||||
|
|
||||||
ARG ELIXIR_VERSION=1.18.3
|
ARG ELIXIR_VERSION=1.20.4
|
||||||
ARG OTP_VERSION=27.2.4
|
ARG OTP_VERSION=27.3.4.19
|
||||||
ARG DEBIAN_VERSION=bookworm-20260316-slim
|
ARG DEBIAN_VERSION=bookworm-20261020-slim
|
||||||
|
|
||||||
ARG BUILDER_IMAGE="docker.io/hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}"
|
ARG BUILDER_IMAGE="docker.io/hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}"
|
||||||
ARG RUNNER_IMAGE="docker.io/debian:${DEBIAN_VERSION}"
|
ARG RUNNER_IMAGE="docker.io/debian:${DEBIAN_VERSION}"
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
check:
|
check:
|
||||||
@echo "Running static analysis..."
|
@echo "Running static analysis..."
|
||||||
@make -C app MISE_BIN=mise check
|
@make -C app MISE_BIN=mise check
|
||||||
|
@bash scripts/check-pins.sh
|
||||||
|
|
||||||
# Precommit target for CI/pre-commit hooks
|
# Precommit target for CI/pre-commit hooks
|
||||||
precommit: check
|
precommit: check
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ firehose/
|
|||||||
├── blogex/ # Blogex library (multi-blog engine)
|
├── blogex/ # Blogex library (multi-blog engine)
|
||||||
│ ├── lib/
|
│ ├── lib/
|
||||||
│ └── mix.exs
|
│ └── mix.exs
|
||||||
└── mise.toml # Runtime versions (Elixir, Erlang, Node)
|
├── Dockerfile # Deploy build; ARG pins must match mise.toml
|
||||||
|
├── mise.toml # Runtime versions (Elixir, Erlang, Node)
|
||||||
|
└── scripts/check-pins.sh # Verifies the pin sync; runs in `make check`
|
||||||
```
|
```
|
||||||
|
|
||||||
Two blogs are configured:
|
Two blogs are configured:
|
||||||
@@ -51,6 +53,15 @@ mise install
|
|||||||
|
|
||||||
This installs Elixir, Erlang, and Node at the versions specified in `mise.toml`.
|
This installs Elixir, Erlang, and Node at the versions specified in `mise.toml`.
|
||||||
|
|
||||||
|
## Toolchain pins
|
||||||
|
|
||||||
|
`mise.toml` (local dev toolchain) and the `Dockerfile` `ARG` pins (deploy
|
||||||
|
builder image) must agree — local builds and deployed images should run the
|
||||||
|
same Elixir/OTP. `make check` runs `scripts/check-pins.sh` to verify this;
|
||||||
|
when it reports a MISMATCH, fix the file it names. Upstream drift of these
|
||||||
|
pins (newer Elixir/OTP/Debian releases) is reported separately by
|
||||||
|
`dokku-updates firehose check` from the dokku-maintenance repo.
|
||||||
|
|
||||||
### 2. Start PostgreSQL
|
### 2. Start PostgreSQL
|
||||||
|
|
||||||
You need PostgreSQL running locally with a `postgres` user (password: `postgres`).
|
You need PostgreSQL running locally with a `postgres` user (password: `postgres`).
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[tools]
|
[tools]
|
||||||
elixir = "latest"
|
elixir = "1.20.4-otp-27"
|
||||||
erlang = "latest"
|
erlang = "27.3.4.19"
|
||||||
node = "latest"
|
node = "latest"
|
||||||
go = "1.26"
|
go = "1.27"
|
||||||
|
|||||||
Executable
+86
@@ -0,0 +1,86 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# check-pins.sh - keep mise.toml and the Dockerfile base-image pins in sync.
|
||||||
|
#
|
||||||
|
# The Phoenix build has two sources of truth for toolchain versions:
|
||||||
|
# - mise.toml [tools]: local dev toolchain (what `mise install` gives you)
|
||||||
|
# - Dockerfile ARG pins: the hexpm/elixir builder image used for deploys
|
||||||
|
# They must agree, otherwise local builds and deployed images run different
|
||||||
|
# Elixir/OTP combinations. Run standalone, or via `make check`.
|
||||||
|
#
|
||||||
|
# Also verifies that the BUILDER_IMAGE / RUNNER_IMAGE ARGs still expand to the
|
||||||
|
# tags composed from the pins (catches hand-edited image references).
|
||||||
|
#
|
||||||
|
# Pure shell + sed: no mise, docker or elixir needed, so it runs in CI and in
|
||||||
|
# sandboxes before the toolchain is installed.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||||
|
mise_toml="$root/mise.toml"
|
||||||
|
dockerfile="$root/Dockerfile"
|
||||||
|
status=0
|
||||||
|
|
||||||
|
die() { echo "MISMATCH: $*" >&2; status=1; }
|
||||||
|
|
||||||
|
# sed-extract a `key = "value"` (or `ARG KEY=value`) line; dies when absent.
|
||||||
|
grab() { # grab <description> <key-regex> <file>
|
||||||
|
local value
|
||||||
|
value=$(sed -n "s/^$2[[:space:]]*=[[:space:]]*//p" "$3")
|
||||||
|
if [ -z "$value" ]; then
|
||||||
|
die "$1 not found in $3 (expected key = value)"
|
||||||
|
fi
|
||||||
|
value=${value%\"}; value=${value#\"} # strip optional surrounding quotes
|
||||||
|
value=$(printf '%s' "$value" | sed 's/[[:space:]]*$//') # trim trailing ws
|
||||||
|
printf '%s' "$value"
|
||||||
|
}
|
||||||
|
|
||||||
|
mise_elixir=$(grab 'mise elixir' 'elixir' "$mise_toml")
|
||||||
|
mise_erlang=$(grab 'mise erlang' 'erlang' "$mise_toml")
|
||||||
|
d_elixir=$(grab 'Dockerfile ELIXIR_VERSION' 'ARG[[:space:]]\+ELIXIR_VERSION' "$dockerfile")
|
||||||
|
d_otp=$(grab 'Dockerfile OTP_VERSION' 'ARG[[:space:]]\+OTP_VERSION' "$dockerfile")
|
||||||
|
d_debian=$(grab 'Dockerfile DEBIAN_VERSION' 'ARG[[:space:]]\+DEBIAN_VERSION' "$dockerfile")
|
||||||
|
|
||||||
|
if [ $status -eq 0 ]; then
|
||||||
|
mise_elixir_ver=${mise_elixir%%-otp-*}
|
||||||
|
mise_elixir_otp=${mise_elixir##*-otp-}
|
||||||
|
d_otp_major=${d_otp%%.*}
|
||||||
|
|
||||||
|
# Elixir: full version must match exactly.
|
||||||
|
[ "$mise_elixir_ver" = "$d_elixir" ] ||
|
||||||
|
die "Elixir: mise.toml has $mise_elixir_ver, Dockerfile ELIXIR_VERSION is $d_elixir"
|
||||||
|
|
||||||
|
# mise elixir builds embed their OTP major (…-otp-27); must match the
|
||||||
|
# major of the Dockerfile OTP_VERSION.
|
||||||
|
[ "$mise_elixir_otp" = "$d_otp_major" ] ||
|
||||||
|
die "OTP major: mise.toml elixir built for otp-$mise_elixir_otp, Dockerfile OTP_VERSION major is $d_otp_major"
|
||||||
|
|
||||||
|
# mise `erlang` IS OTP: must match the Dockerfile pin exactly.
|
||||||
|
[ "$mise_erlang" = "$d_otp" ] ||
|
||||||
|
die "OTP: mise.toml erlang is $mise_erlang, Dockerfile OTP_VERSION is $d_otp"
|
||||||
|
|
||||||
|
# BUILDER_IMAGE must expand to the tag composed from the pins.
|
||||||
|
expected_builder="docker.io/hexpm/elixir:${d_elixir}-erlang-${d_otp}-debian-${d_debian}"
|
||||||
|
d_builder=$(grab 'Dockerfile BUILDER_IMAGE' 'ARG[[:space:]]\+BUILDER_IMAGE' "$dockerfile")
|
||||||
|
expanded_builder=$(printf '%s' "$d_builder" |
|
||||||
|
sed -e "s/\${ELIXIR_VERSION}/$d_elixir/" \
|
||||||
|
-e "s/\${OTP_VERSION}/$d_otp/" \
|
||||||
|
-e "s/\${DEBIAN_VERSION}/$d_debian/")
|
||||||
|
[ "$expanded_builder" = "$expected_builder" ] ||
|
||||||
|
die "BUILDER_IMAGE expands to $expanded_builder, expected $expected_builder"
|
||||||
|
|
||||||
|
# RUNNER_IMAGE must expand to the Debian pin.
|
||||||
|
expected_runner="docker.io/debian:${d_debian}"
|
||||||
|
d_runner=$(grab 'Dockerfile RUNNER_IMAGE' 'ARG[[:space:]]\+RUNNER_IMAGE' "$dockerfile")
|
||||||
|
expanded_runner=${d_runner/\$\{DEBIAN_VERSION\}/$d_debian}
|
||||||
|
[ "$expanded_runner" = "$expected_runner" ] ||
|
||||||
|
die "RUNNER_IMAGE expands to $expanded_runner, expected $expected_runner"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $status -eq 0 ]; then
|
||||||
|
echo "pins ok: Elixir $d_elixir, OTP $d_otp, Debian $d_debian (mise and Dockerfile agree)"
|
||||||
|
echo " builder: $expected_builder"
|
||||||
|
echo " runner: $expected_runner"
|
||||||
|
echo " (upstream drift of these pins is reported by dokku-updates 'firehose check')"
|
||||||
|
else
|
||||||
|
echo >&2 "fix mise.toml and/or the Dockerfile ARG pins so they agree, then re-run."
|
||||||
|
fi
|
||||||
|
exit $status
|
||||||
Reference in New Issue
Block a user