fix: bug: migrate_ops_repo seeds canonical structure in host path but agents container uses a Docker named volume — migration is orphaned (#586)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0612bb25d0
commit
d60a3da1b1
2 changed files with 127 additions and 0 deletions
30
bin/disinto
30
bin/disinto
|
|
@ -698,6 +698,36 @@ p.write_text(text)
|
||||||
# This brings pre-#407 deployments up to date with the canonical structure
|
# This brings pre-#407 deployments up to date with the canonical structure
|
||||||
migrate_ops_repo "$ops_root" "$branch"
|
migrate_ops_repo "$ops_root" "$branch"
|
||||||
|
|
||||||
|
# In compose mode, sync the migration into running agents containers (#586).
|
||||||
|
# migrate_ops_repo pushes to forgejo, so we pull inside each container
|
||||||
|
# that has the ops repo on a Docker named volume.
|
||||||
|
if is_compose_mode; then
|
||||||
|
local container
|
||||||
|
for container in $(docker compose -f "${FACTORY_ROOT}/docker-compose.yml" \
|
||||||
|
ps --format '{{.Names}}' 2>/dev/null || true); do
|
||||||
|
# Only target agents containers (disinto-agents, disinto-agents-llama, etc.)
|
||||||
|
case "$container" in
|
||||||
|
*agents*) ;;
|
||||||
|
*) continue ;;
|
||||||
|
esac
|
||||||
|
local container_ops="/home/agent/repos/${project_name}-ops"
|
||||||
|
if docker exec "$container" test -d "${container_ops}/.git" 2>/dev/null; then
|
||||||
|
# Ensure remote is configured, then pull
|
||||||
|
echo "Syncing ops repo migration into container: ${container}"
|
||||||
|
docker exec -u agent "$container" bash -c "
|
||||||
|
cd '${container_ops}' || exit 0
|
||||||
|
if ! git remote get-url origin >/dev/null 2>&1; then
|
||||||
|
git remote add origin 'http://forgejo:3000/${ops_slug}.git'
|
||||||
|
fi
|
||||||
|
git fetch origin '${branch}' --quiet 2>/dev/null || true
|
||||||
|
git reset --hard 'origin/${branch}' --quiet 2>/dev/null || true
|
||||||
|
" 2>/dev/null || echo " (container ${container} not reachable — will sync on next startup)"
|
||||||
|
else
|
||||||
|
echo " Container ${container}: ops repo not yet cloned — will bootstrap on next startup"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
# Set up vault branch protection on ops repo (#77)
|
# Set up vault branch protection on ops repo (#77)
|
||||||
# This ensures admin-only merge to main, blocking bots from merging vault PRs
|
# This ensures admin-only merge to main, blocking bots from merging vault PRs
|
||||||
# Use HUMAN_TOKEN (disinto-admin) or FORGE_TOKEN (dev-bot) for admin operations
|
# Use HUMAN_TOKEN (disinto-admin) or FORGE_TOKEN (dev-bot) for admin operations
|
||||||
|
|
|
||||||
|
|
@ -125,10 +125,107 @@ else
|
||||||
log "Run 'claude auth login' on the host, or set ANTHROPIC_API_KEY in .env"
|
log "Run 'claude auth login' on the host, or set ANTHROPIC_API_KEY in .env"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Bootstrap ops repos for each project TOML (#586).
|
||||||
|
# In compose mode the ops repo lives on a Docker named volume at
|
||||||
|
# /home/agent/repos/<project>-ops. If init ran migrate_ops_repo on the host
|
||||||
|
# the container never saw those changes. This function clones from forgejo
|
||||||
|
# when the repo is missing, or configures the remote and pulls when it exists
|
||||||
|
# but has no remote (orphaned local-only checkout).
|
||||||
|
bootstrap_ops_repos() {
|
||||||
|
local repos_dir="/home/agent/repos"
|
||||||
|
mkdir -p "$repos_dir"
|
||||||
|
chown agent:agent "$repos_dir"
|
||||||
|
|
||||||
|
for toml in "${DISINTO_DIR}"/projects/*.toml; do
|
||||||
|
[ -f "$toml" ] || continue
|
||||||
|
|
||||||
|
# Extract project name and ops repo slug from TOML
|
||||||
|
local project_name ops_slug primary_branch
|
||||||
|
project_name=$(python3 -c "
|
||||||
|
import tomllib, sys
|
||||||
|
with open(sys.argv[1], 'rb') as f:
|
||||||
|
cfg = tomllib.load(f)
|
||||||
|
print(cfg.get('name', ''))
|
||||||
|
" "$toml" 2>/dev/null || true)
|
||||||
|
[ -n "$project_name" ] || continue
|
||||||
|
|
||||||
|
ops_slug=$(python3 -c "
|
||||||
|
import tomllib, sys
|
||||||
|
with open(sys.argv[1], 'rb') as f:
|
||||||
|
cfg = tomllib.load(f)
|
||||||
|
print(cfg.get('ops_repo', ''))
|
||||||
|
" "$toml" 2>/dev/null || true)
|
||||||
|
|
||||||
|
primary_branch=$(python3 -c "
|
||||||
|
import tomllib, sys
|
||||||
|
with open(sys.argv[1], 'rb') as f:
|
||||||
|
cfg = tomllib.load(f)
|
||||||
|
print(cfg.get('primary_branch', 'main'))
|
||||||
|
" "$toml" 2>/dev/null || true)
|
||||||
|
primary_branch="${primary_branch:-main}"
|
||||||
|
|
||||||
|
# Fall back to convention if ops_repo not in TOML
|
||||||
|
if [ -z "$ops_slug" ]; then
|
||||||
|
local repo_slug
|
||||||
|
repo_slug=$(python3 -c "
|
||||||
|
import tomllib, sys
|
||||||
|
with open(sys.argv[1], 'rb') as f:
|
||||||
|
cfg = tomllib.load(f)
|
||||||
|
print(cfg.get('repo', ''))
|
||||||
|
" "$toml" 2>/dev/null || true)
|
||||||
|
if [ -n "$repo_slug" ]; then
|
||||||
|
ops_slug="${repo_slug}-ops"
|
||||||
|
else
|
||||||
|
ops_slug="disinto-admin/${project_name}-ops"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
local ops_root="${repos_dir}/${project_name}-ops"
|
||||||
|
local remote_url="${FORGE_URL}/${ops_slug}.git"
|
||||||
|
|
||||||
|
if [ ! -d "${ops_root}/.git" ]; then
|
||||||
|
# Clone ops repo from forgejo
|
||||||
|
log "Ops bootstrap: cloning ${ops_slug} -> ${ops_root}"
|
||||||
|
if gosu agent git clone --quiet "$remote_url" "$ops_root" 2>/dev/null; then
|
||||||
|
log "Ops bootstrap: ${ops_slug} cloned successfully"
|
||||||
|
else
|
||||||
|
# Remote may not exist yet (first run before init); create empty repo
|
||||||
|
log "Ops bootstrap: clone failed for ${ops_slug} — initializing empty repo"
|
||||||
|
gosu agent bash -c "
|
||||||
|
mkdir -p '${ops_root}' && \
|
||||||
|
git -C '${ops_root}' init --initial-branch='${primary_branch}' -q && \
|
||||||
|
git -C '${ops_root}' remote add origin '${remote_url}'
|
||||||
|
"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Repo exists — ensure remote is configured and pull latest
|
||||||
|
local current_remote
|
||||||
|
current_remote=$(git -C "$ops_root" remote get-url origin 2>/dev/null || true)
|
||||||
|
if [ -z "$current_remote" ]; then
|
||||||
|
log "Ops bootstrap: adding missing remote to ${ops_root}"
|
||||||
|
gosu agent git -C "$ops_root" remote add origin "$remote_url"
|
||||||
|
elif [ "$current_remote" != "$remote_url" ]; then
|
||||||
|
log "Ops bootstrap: fixing remote URL in ${ops_root}"
|
||||||
|
gosu agent git -C "$ops_root" remote set-url origin "$remote_url"
|
||||||
|
fi
|
||||||
|
# Pull latest from forgejo to pick up any host-side migrations
|
||||||
|
log "Ops bootstrap: pulling latest for ${project_name}-ops"
|
||||||
|
gosu agent bash -c "
|
||||||
|
cd '${ops_root}' && \
|
||||||
|
git fetch origin '${primary_branch}' --quiet 2>/dev/null && \
|
||||||
|
git reset --hard 'origin/${primary_branch}' --quiet 2>/dev/null
|
||||||
|
" || log "Ops bootstrap: pull failed for ${ops_slug} (remote may not exist yet)"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
# Configure git and tea once at startup (as root, then drop to agent)
|
# Configure git and tea once at startup (as root, then drop to agent)
|
||||||
configure_git_creds
|
configure_git_creds
|
||||||
configure_tea_login
|
configure_tea_login
|
||||||
|
|
||||||
|
# Bootstrap ops repos from forgejo into container volumes (#586)
|
||||||
|
bootstrap_ops_repos
|
||||||
|
|
||||||
# Initialize state directory for check_active guards
|
# Initialize state directory for check_active guards
|
||||||
init_state_dir
|
init_state_dir
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue