Files
duckbrain/Makefile

97 lines
4.6 KiB
Makefile

# DuckBrain development harness — Makefile
#
# This file (along with tests/, phpunit.xml, composer.*, .gitignore) lives
# ONLY on the develop branch. master is a publish-only vitrine that contains
# nothing but the readable artifact: nobody commits to master directly, and
# no file outside the WHITELIST below ever reaches it.
#
# Daily loop:
# make test run the unit suite (sqlite :memory:, no containers, no
# network; installs dev dependencies first if missing)
# ...then develop on develop and commit there as usual.
#
# Publishing ritual (human only — AI agents must never run it; it commits):
# 1. make publish MSG="feat: ..." (MSG optional, defaults to "sync: <date>")
# 2. review the new master commit (git log -1 refs/heads/master); if the
# generated message needs polish, rewrite it BEFORE pushing, e.g.
# from Emacs
# 3. git push origin master (manual on purpose: pushing is a
# decision, never a side effect)
#
# How publish builds master — wipe-and-rebuild mirror, no merges, no
# cherry-picks (see design decisions behind establish-dev-publish-workflow):
# - refuses to run off develop or with a dirty working tree
# - checks master out into a disposable worktree under .publish/
# (your current working tree is never touched)
# - for each WHITELIST path: removes master's copy and checks out develop's,
# so deleted/renamed artifact files never survive as ghosts in master
# - BLACKLIST then subtracts noise found INSIDE whitelisted paths
# - creates the commit only when the artifact actually changed; its body
# lists the develop commits (after the last-sync tag) that touched
# WHITELIST paths — pure-noise commits are filtered out automatically
# - moves the last-sync tag to develop's tip, removes the worktree, and
# prints any root entry left out of WHITELIST (add it if it belongs to
# the artifact)
SHELL := /bin/bash
.PHONY: test publish
PHPUNIT := vendor/bin/phpunit
# --- publish configuration -------------------------------------------------
WHITELIST := src config.php index.php autoload.php .htaccess readme.org
BLACKLIST :=
DEVELOP_BRANCH := develop
DEVELOP_REF := refs/heads/$(DEVELOP_BRANCH)
MASTER_BRANCH := master
MASTER_REF := refs/heads/$(MASTER_BRANCH)
LAST_SYNC_TAG := last-sync
MSG ?= sync: $(shell date +%Y-%m-%d)
test: $(PHPUNIT)
./$(PHPUNIT)
$(PHPUNIT): composer.json composer.lock
composer install --no-interaction
publish:
@if [ "$$(git branch --show-current)" != "$(DEVELOP_BRANCH)" ]; then \
echo "ABORT: publish must be invoked from $(DEVELOP_BRANCH) (current: '$$(git branch --show-current)')"; \
exit 1; \
fi
@if [ -n "$$(git status --porcelain)" ]; then \
echo "ABORT: working tree is dirty; commit your changes on $(DEVELOP_BRANCH) before publishing:"; \
git status --short; \
exit 1; \
fi
git worktree remove --force .publish/master 2>/dev/null || true
git worktree add --quiet .publish/master $(MASTER_BRANCH)
@test "$$(git -C .publish/master symbolic-ref HEAD)" = "$(MASTER_REF)" || { \
echo "ABORT: publish worktree HEAD is not attached to $(MASTER_REF)"; \
git worktree remove --force .publish/master; exit 1; }
@for w in $(WHITELIST); do git -C .publish/master rm -rf --ignore-unmatch --quiet -- $$w; done
git -C .publish/master checkout $(DEVELOP_REF) -- $(WHITELIST)
@for b in $(BLACKLIST); do git -C .publish/master rm -rf --ignore-unmatch --quiet -- $$b; done
git -C .publish/master diff --quiet $(DEVELOP_REF) -- $(WHITELIST)
@if git -C .publish/master diff --cached --quiet; then \
echo "nothing to publish: master already mirrors $(DEVELOP_BRANCH)"; \
else \
set -e; \
if git rev-parse -q --verify tags/$(LAST_SYNC_TAG) >/dev/null; then \
RANGE=$(LAST_SYNC_TAG)..$(DEVELOP_REF); \
else \
echo "note: no $(LAST_SYNC_TAG) tag yet; summarizing full $(DEVELOP_BRANCH) history"; \
RANGE=$(DEVELOP_REF); \
fi; \
BODY=$$(git log $$RANGE --oneline -- $(WHITELIST)); \
{ printf '%s\n' "$(MSG)"; printf '\n'; printf '%s\n' "$$BODY"; } > .publish/msg.txt; \
git -C .publish/master commit --quiet -F "$$(pwd)/.publish/msg.txt"; \
echo "publish commit created; review/edit it (before pushing) with your git tool of choice"; \
fi
git tag -f $(LAST_SYNC_TAG) $(DEVELOP_REF) >/dev/null
git worktree remove .publish/master
@echo "root entries left behind in $(DEVELOP_BRANCH) (add to WHITELIST if they belong to the artifact):"; \
comm -23 <(git ls-tree --name-only $(DEVELOP_REF) | sort) \
<(for x in $(WHITELIST) $(BLACKLIST); do echo $$x; done | sort) | sed 's/^/ - /'