diff --git a/.tangled/workflows/deps-canary.yml b/.tangled/workflows/deps-canary.yml new file mode 100644 index 0000000..904a92e --- /dev/null +++ b/.tangled/workflows/deps-canary.yml @@ -0,0 +1,27 @@ +# push-to-main only: no pull_request trigger. This check is expected to go +# red the day the newest allowed dependency pair regresses (see below), and a +# deliberately-red check on every PR trains everyone to ignore checks. No +# schedule trigger either: Tangled's workflow syntax doesn't support one yet. +when: + - event: ["push"] + branch: ["main"] + +engine: nixery + +# Separate workflow from test.yml on purpose: this one is expected to go red +# whenever the newest allowed atproto_client/atproto_codegen pair regresses, +# and that must never block the real test pipeline. +dependencies: + nixpkgs/nixpkgs-unstable: + - gleam + - erlang + - rebar3 + - nodejs + - bun + - gnumake + - git + +steps: + - name: newest allowed dependency horizon + command: | + make deps-canary diff --git a/Makefile b/Makefile index 82156c7..8744bda 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ LEXICON_CHECK_REF ?= origin/main WEB_VENDOR_DIR := server/priv/static/vendor .PHONY: help gen css build vendor dev run test format check clean docker-build up down logs \ - lexicon-check lexicon-vendor \ + lexicon-check lexicon-vendor deps-canary \ e2e-setup e2e-up e2e-down e2e help: ## list available targets @@ -66,6 +66,9 @@ format: ## format all gleam source check: ## verify all gleam source is formatted gleam format --check $(GLEAM_DIRS) +deps-canary: ## fail if the newest allowed dependency horizon breaks gen or tests + ./scripts/deps-canary.sh + lexicon-check: ## fail if any dev.mokkenstorm.* lexicon has a BREAKING change vs LEXICON_CHECK_REF (default origin/main) @ref="$(LEXICON_CHECK_REF)"; \ if ! git rev-parse --verify --quiet "$$ref^{tree}" > /dev/null; then \ diff --git a/scripts/deps-canary.sh b/scripts/deps-canary.sh new file mode 100755 index 0000000..24c1205 --- /dev/null +++ b/scripts/deps-canary.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +# Widens the deliberate atproto_client/atproto_codegen upper-bound pins to the +# full un-pinned horizon for one run, then builds and tests against whatever +# that resolves to. The point is to notice the day the newest allowed release +# pair stops breaking us, without carrying the pins any longer than needed. +# +# Locally this requires a clean tracked tree (untracked files are fine) and +# restores the whole tracked tree on exit via a trap, success or failure (and +# on INT/TERM/HUP, so a Ctrl-C mid-run doesn't leave the widened pins or a +# half-finished `make gen` behind). In CI (CI=1) the clone is disposable, so +# the clean-tree gate is skipped; the trap still runs but nothing depends on +# it. +set -euo pipefail +cd "$(dirname "${BASH_SOURCE[0]}")/.." + +log() { printf '\033[36m[deps-canary]\033[0m %s\n' "$*"; } + +if [ "${CI:-}" != "1" ] && [ -n "$(git status --porcelain --untracked-files=no)" ]; then + echo "deps-canary: tracked working tree is dirty; commit or stash before running locally" >&2 + exit 1 +fi + +restore() { + local status=$? + log "restoring tracked tree" + # `make gen` (run below) rewrites generated sources under + # shared/src/at_record/gen/** too, not just the manifests; the clean-tree + # gate above already guarantees the tracked tree was pristine, so checking + # out everything is safe and leaves no gen drift in a developer's checkout. + git checkout -- . + rm -f shared/gleam.toml.bak server/gleam.toml.bak + exit "$status" +} +trap restore EXIT INT TERM HUP + +# A sed that silently fails to match (e.g. a pinned line with a trailing +# comment the pattern didn't expect) would leave the file untouched and this +# canary would go on to test the pinned versions instead of the widened ones +# -- worse than no canary, since it reports green for the wrong thing. +assert_widened() { + local file="$1" expected="$2" + if ! grep -qF "$expected" "$file"; then + echo "deps-canary: expected to find '$expected' in $file after widening, but it's not there" >&2 + exit 1 + fi +} + +log "widening atproto_client to >= 0.1.0 and < 1.0.0 (shared, server)" +sed -i.bak -E 's/^atproto_client = "[^"]*"( #.*)?$/atproto_client = ">= 0.1.0 and < 1.0.0"/' shared/gleam.toml +sed -i.bak -E 's/^atproto_client = "[^"]*"( #.*)?$/atproto_client = ">= 0.1.0 and < 1.0.0"/' server/gleam.toml +assert_widened shared/gleam.toml 'atproto_client = ">= 0.1.0 and < 1.0.0"' +assert_widened server/gleam.toml 'atproto_client = ">= 0.1.0 and < 1.0.0"' + +log "widening atproto_codegen to >= 0.2.0 and < 1.0.0 (shared)" +sed -i.bak -E 's/^atproto_codegen = "[^"]*"( #.*)?$/atproto_codegen = ">= 0.2.0 and < 1.0.0"/' shared/gleam.toml +assert_widened shared/gleam.toml 'atproto_codegen = ">= 0.2.0 and < 1.0.0"' + +rm -f shared/gleam.toml.bak server/gleam.toml.bak + +log "resolving the newest allowed dependency horizon" +( cd shared && gleam update ) +( cd server && gleam update ) +( cd web && gleam update ) + +log "resolved atproto_* versions:" +for dir in shared server web; do + grep -oE 'name = "atproto_[a-z_]+", version = "[^"]+"' "$dir/manifest.toml" | + sed -E "s/^/ $dir: /; s/name = \"([^\"]+)\", version = \"([^\"]+)\"/\\1 \\2/" +done + +make gen +make test