#!/usr/bin/env bash # Push the current topic branch to origin — the ONLY sanctioned way out of the # container. # # Why a wrapper instead of plain `git push`: # # * **`main` and shared branches are refused.** The agent commits to # `auto/`; a human merges. A token that can push anywhere is one # confused iteration away from rewriting the consolidated line. # * **Force-push is refused**, always. Nothing here needs it, and history # rewriting is the one mistake that cannot be undone by merging. # * It pushes the CURRENT branch only, by name, so a stray `--all` cannot # publish another agent's worktree branch mid-experiment. # # Credentials come from a file mounted read-only at ~/.git-credentials (see # `sylph-agent`). They are never printed, never logged, and never passed on a # command line. # # push-work push the current branch, and any annotated tags on it # push-work --dry-run say what it would do set -euo pipefail DRY=0 [ "${1:-}" = "--dry-run" ] && DRY=1 repo_root=$(git rev-parse --show-toplevel 2>/dev/null) || { echo "push-work: not inside a git repository" >&2; exit 1; } cd "$repo_root" branch=$(git rev-parse --abbrev-ref HEAD) if [ "$branch" = "HEAD" ]; then echo "push-work: detached HEAD — check out a branch first" >&2; exit 1 fi case "$branch" in auto/*) ;; *) echo "push-work: refusing to push '$branch'." >&2 echo " Only auto/* topic branches may leave the container; a human merges" >&2 echo " them into main. Move your work: git switch -c auto/" >&2 exit 1 ;; esac if [ ! -s "$HOME/.git-credentials" ]; then echo "push-work: no credentials mounted at ~/.git-credentials." >&2 echo " The host must start the container with SYLPH_GIT_CREDENTIALS pointing" >&2 echo " at a file containing one line:" >&2 echo " https://:@git.mc02.dev" >&2 exit 1 fi # Applied to THIS COMMAND ONLY, via `-c`, never `git config --local`. # # Writing it to --local config persists it in the repository, and this repo is a # bind mount the host also uses -- so the host's git inherited # `store --file=/sylph-home/port/.git-credentials`, a path that exists only # inside the container, and every host push then failed with # `unable to get credential storage lock: No such file or directory`. # # A tool that configures a shared repository to suit itself breaks every other # user of that repository. Keep it to the invocation. CRED_HELPER="store --file=$HOME/.git-credentials" ahead=$(git rev-list --count "origin/$branch..$branch" 2>/dev/null || git rev-list --count HEAD) echo "push-work: $branch — $ahead commit(s) to publish" if [ "$DRY" = 1 ]; then echo "push-work: --dry-run, stopping here" exit 0 fi # --force-with-lease is deliberately NOT offered. If this is rejected as # non-fast-forward, someone else moved the branch: fetch and merge, do not # overwrite. git -c "credential.helper=$CRED_HELPER" push --follow-tags --set-upstream origin "$branch" echo "push-work: pushed $branch"