fix: [nomad-prep] P7 — make disinto init idempotent + add --dry-run (#800)
Make `disinto init` safe to re-run on the same box: - Store admin token as FORGE_ADMIN_TOKEN in .env; preserve on re-run (previously deleted and recreated every run, churning DB state) - Fix human token creation: use admin_pass for basic-auth since human_user == admin_user (previously used a random password that never matched the actual user password, so HUMAN_TOKEN was never created successfully) - Preserve HUMAN_TOKEN in .env on re-run (same pattern as bot tokens) - Bot tokens were already idempotent (preserved unless --rotate-tokens) Add --dry-run flag that reports every intended action (file writes, API calls, docker commands) based on current state, then exits 0 without touching state. Useful for CI gating and cutover confidence. Update smoke test: - Add dry-run test (verifies exit 0 and no .env modification) - Add idempotency state diff (verifies .env is unchanged on re-run) - Verify FORGE_ADMIN_TOKEN and HUMAN_TOKEN are stored in .env Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
55cce66468
commit
9d8f322005
3 changed files with 212 additions and 58 deletions
84
bin/disinto
84
bin/disinto
|
|
@ -85,6 +85,7 @@ Init options:
|
|||
--build Use local docker build instead of registry images (dev mode)
|
||||
--yes Skip confirmation prompts
|
||||
--rotate-tokens Force regeneration of all bot tokens/passwords (idempotent by default)
|
||||
--dry-run Print every intended action without executing
|
||||
|
||||
Hire an agent options:
|
||||
--formula <path> Path to role formula TOML (default: formulas/<role>.toml)
|
||||
|
|
@ -653,7 +654,7 @@ disinto_init() {
|
|||
shift
|
||||
|
||||
# Parse flags
|
||||
local branch="" repo_root="" ci_id="0" auto_yes=false forge_url_flag="" bare=false rotate_tokens=false use_build=false
|
||||
local branch="" repo_root="" ci_id="0" auto_yes=false forge_url_flag="" bare=false rotate_tokens=false use_build=false dry_run=false
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--branch) branch="$2"; shift 2 ;;
|
||||
|
|
@ -664,6 +665,7 @@ disinto_init() {
|
|||
--build) use_build=true; shift ;;
|
||||
--yes) auto_yes=true; shift ;;
|
||||
--rotate-tokens) rotate_tokens=true; shift ;;
|
||||
--dry-run) dry_run=true; shift ;;
|
||||
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
|
@ -740,6 +742,86 @@ p.write_text(text)
|
|||
fi
|
||||
fi
|
||||
|
||||
# ── Dry-run mode: report intended actions and exit ─────────────────────────
|
||||
if [ "$dry_run" = true ]; then
|
||||
echo ""
|
||||
echo "── Dry-run: intended actions ────────────────────────────"
|
||||
local env_file="${FACTORY_ROOT}/.env"
|
||||
local rr="${repo_root:-/home/${USER}/${project_name}}"
|
||||
|
||||
if [ "$bare" = false ]; then
|
||||
[ -f "${FACTORY_ROOT}/docker-compose.yml" ] \
|
||||
&& echo "[skip] docker-compose.yml (exists)" \
|
||||
|| echo "[create] docker-compose.yml"
|
||||
fi
|
||||
|
||||
[ -f "$env_file" ] \
|
||||
&& echo "[exists] .env" \
|
||||
|| echo "[create] .env"
|
||||
|
||||
# Report token state from .env
|
||||
if [ -f "$env_file" ]; then
|
||||
local _var
|
||||
for _var in FORGE_ADMIN_TOKEN HUMAN_TOKEN FORGE_TOKEN FORGE_REVIEW_TOKEN \
|
||||
FORGE_PLANNER_TOKEN FORGE_GARDENER_TOKEN FORGE_VAULT_TOKEN \
|
||||
FORGE_SUPERVISOR_TOKEN FORGE_PREDICTOR_TOKEN FORGE_ARCHITECT_TOKEN; do
|
||||
if grep -q "^${_var}=" "$env_file" 2>/dev/null; then
|
||||
echo "[keep] ${_var} (preserved)"
|
||||
else
|
||||
echo "[create] ${_var}"
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo "[create] all tokens and passwords"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "[ensure] Forgejo admin user 'disinto-admin'"
|
||||
echo "[ensure] 8 bot users: dev-bot, review-bot, planner-bot, gardener-bot, vault-bot, supervisor-bot, predictor-bot, architect-bot"
|
||||
echo "[ensure] 2 llama bot users: dev-qwen, dev-qwen-nightly"
|
||||
echo "[ensure] .profile repos for all bots"
|
||||
echo "[ensure] repo ${forge_repo} on Forgejo with collaborators"
|
||||
echo "[run] preflight checks"
|
||||
|
||||
[ -d "${rr}/.git" ] \
|
||||
&& echo "[skip] clone ${rr} (exists)" \
|
||||
|| echo "[clone] ${repo_url} -> ${rr}"
|
||||
|
||||
echo "[push] to local Forgejo"
|
||||
echo "[ensure] ops repo disinto-admin/${project_name}-ops"
|
||||
echo "[ensure] branch protection on ${forge_repo}"
|
||||
|
||||
[ "$toml_exists" = true ] \
|
||||
&& echo "[skip] ${toml_path} (exists)" \
|
||||
|| echo "[create] ${toml_path}"
|
||||
|
||||
if [ "$bare" = false ]; then
|
||||
echo "[ensure] Woodpecker OAuth2 app"
|
||||
echo "[ensure] Chat OAuth2 app"
|
||||
echo "[ensure] WOODPECKER_AGENT_SECRET in .env"
|
||||
fi
|
||||
|
||||
echo "[ensure] labels on ${forge_repo}"
|
||||
|
||||
[ -f "${rr}/VISION.md" ] \
|
||||
&& echo "[skip] VISION.md (exists)" \
|
||||
|| echo "[create] VISION.md"
|
||||
|
||||
echo "[copy] issue templates"
|
||||
echo "[ensure] scheduling (cron or compose polling)"
|
||||
|
||||
if [ "$bare" = false ]; then
|
||||
echo "[start] docker compose stack"
|
||||
echo "[ensure] Woodpecker token + repo activation"
|
||||
fi
|
||||
|
||||
echo "[ensure] CLAUDE_CONFIG_DIR"
|
||||
echo "[ensure] state files (.dev-active, .reviewer-active, .gardener-active)"
|
||||
echo ""
|
||||
echo "Dry run complete — no changes made."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Generate compose files (unless --bare)
|
||||
if [ "$bare" = false ]; then
|
||||
local forge_port
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue