The forgejo-sandbox / reforge harness, lifted out of the machine config into a host-agnostic, generic engine anyone can consume with Nix. Two layers: - engine (this repo) — nixosModules.reforge stands up the sandbox forge, provisions role accounts + tokens, enforces branch protection, and puts the reforge-* CLI + forgejo-mcp on PATH. Carries no project specifics. - run config — per-project manifest/charter/agenda/issues an adopter fills in; scaffold one with the `reforge` flake template. Portability fixes vs the in-config version: - forgejo-mcp resolved from $REFORGE_MCP_BIN or PATH, never a named host (kills the nixosConfigurations.omni hardcode). - all instance data + paths parameterized via REFORGE_* env, baked into the reforge-scripts wrappers from module options (configDir, agentsDir, refsDir, org, port, tokenOwner, ...). - option namespace neutral (reforge.* not omni.packs.*); settings policies carry no absolute /etc/nixos paths. - role briefs + orchestrator playbook genericized: all project specifics point at the charter; refs corpus optional. Validated: nix flake check (eval) + builds of forgejo-mcp, reforge-scripts, and a module-eval check. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
73 lines
2.5 KiB
Bash
73 lines
2.5 KiB
Bash
#!/usr/bin/env bash
|
|
# Reforge acceptance check (docs/reforge.md): byte-for-byte TREE diff of
|
|
# each sandbox repo's `main` against its declared target — the known
|
|
# working state — from $REFORGE_CONFIG_DIR/manifest.txt.
|
|
#
|
|
# Targets are fetched shallow from their real remotes (may require your ssh
|
|
# key, depending on the target URL); nothing is pushed or written anywhere.
|
|
# History is deliberately ignored: the run re-derives the tree through its
|
|
# own PR history, so only content parity counts.
|
|
#
|
|
# Exit 0 = every repo with a declared target is IDENTICAL.
|
|
#
|
|
# reforge-compare
|
|
set -euo pipefail
|
|
|
|
FORGE_URL=${REFORGE_FORGE_URL:-http://localhost:3030}
|
|
ORG=${REFORGE_ORG:-sandbox-team}
|
|
ADMIN_USER=${REFORGE_ADMIN_USER:-sandbox-admin}
|
|
TOKENS_DIR=${REFORGE_TOKENS_DIR:-/var/lib/forgejo-sandbox/tokens}
|
|
CONFIG_DIR=${REFORGE_CONFIG_DIR:?set REFORGE_CONFIG_DIR to your run config dir}
|
|
TOKEN_FILE=${REFORGE_ADMIN_TOKEN_FILE:-$TOKENS_DIR/${ADMIN_USER}.token}
|
|
TOKEN=$(cat "$TOKEN_FILE")
|
|
|
|
MANIFEST="$CONFIG_DIR/manifest.txt"
|
|
[ -r "$MANIFEST" ] || { echo "compare: no readable manifest at $MANIFEST" >&2; exit 1; }
|
|
|
|
WORK=$(mktemp -d)
|
|
trap 'rm -rf "$WORK"' EXIT
|
|
|
|
auth_url() { # name
|
|
echo "http://$ADMIN_USER:$TOKEN@${FORGE_URL#http://}/$ORG/$1.git"
|
|
}
|
|
|
|
fail=0
|
|
mapfile -t ROWS < <(grep -Ev '^[[:space:]]*(#|$)' "$MANIFEST")
|
|
for row in "${ROWS[@]}"; do
|
|
IFS='|' read -r name _kind _upstream _base_ref target_url target_ref <<<"$row"
|
|
|
|
if [ "$target_url" = "-" ] || [ -z "$target_url" ]; then
|
|
echo "SKIP $name — no target declared in the manifest"
|
|
continue
|
|
fi
|
|
|
|
d="$WORK/$name"
|
|
git init --quiet "$d"
|
|
|
|
if ! git -C "$d" fetch --quiet --depth 1 "$(auth_url "$name")" main 2>/dev/null; then
|
|
echo "MISSING $name — no main in the sandbox (not seeded / not scaffolded yet)"
|
|
fail=1
|
|
continue
|
|
fi
|
|
sandbox_sha=$(git -C "$d" rev-parse FETCH_HEAD)
|
|
|
|
git -C "$d" fetch --quiet --depth 1 "$target_url" "$target_ref"
|
|
target_sha=$(git -C "$d" rev-parse FETCH_HEAD)
|
|
|
|
if git -C "$d" diff --quiet "$sandbox_sha" "$target_sha"; then
|
|
echo "IDENTICAL $name — matches $target_url @ $target_ref"
|
|
else
|
|
n=$(git -C "$d" diff --name-only "$sandbox_sha" "$target_sha" | wc -l)
|
|
summary=$(git -C "$d" diff --shortstat "$sandbox_sha" "$target_sha")
|
|
echo "DIFFERS $name — $n files vs $target_ref:$summary"
|
|
fail=1
|
|
fi
|
|
done
|
|
|
|
echo
|
|
if [ "$fail" = 0 ]; then
|
|
echo "compare: every targeted repo is byte-for-byte identical to its target."
|
|
else
|
|
echo "compare: divergence remains (DIFFERS/MISSING above)."
|
|
fi
|
|
exit "$fail"
|