Add FF-only Gitea to GitHub sync job #1

Merged
slh merged 1 commits from chore/git-bridge-forward-sync into main 2026-07-17 10:23:05 +08:00
5 changed files with 120 additions and 0 deletions

View File

@ -117,6 +117,34 @@ services:
networks: networks:
- n8n_default - n8n_default
git-bridge:
container_name: git-bridge
profiles: ["git-bridge"]
image: ${GITEA_IMAGE:-gitea/gitea:1.23}
restart: "no"
user: "0:0"
environment:
HOME: /state/home
HTTP_PROXY: http://192.168.0.4:7890
HTTPS_PROXY: http://192.168.0.4:7890
ALL_PROXY: http://192.168.0.4:7890
NO_PROXY: localhost,127.0.0.1,::1,gitea,postgres,litellm
entrypoint: ["/bin/sh", "/app/bin/sync-main-forward.sh"]
volumes:
- ./git-bridge/bin:/app/bin:ro
- ./git-bridge/config:/app/config:ro
- ./git-bridge/state:/state
- ./git-bridge/secrets/gitea_token:/run/secrets/gitea_token:ro
- ./git-bridge/secrets/github_token:/run/secrets/github_token:ro
read_only: true
tmpfs:
- /tmp
cap_drop: ["ALL"]
security_opt:
- no-new-privileges:true
networks:
- git_forge
networks: networks:
git_forge: git_forge:
name: git_forge name: git_forge

13
git-bridge/README.md Normal file
View File

@ -0,0 +1,13 @@
# Git Bridge
Phase 1 provides a deterministic, one-shot forward sync from Gitea Main to
GitHub Main. The job refuses divergence and never force-pushes.
Run after this change is merged and deployed:
```bash
docker compose --profile git-bridge run --rm --no-deps git-bridge
```
GitHub remains a development workspace. Production deployment continues to
use only Gitea Main.

8
git-bridge/bin/git-askpass.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/sh
case "$1" in
*Username*github.com*|*github.com*Username*) printf '%s\n' 'RX-Penguin' ;;
*github.com*) cat /run/secrets/github_token ;;
*Username*gitea*|*gitea*Username*) printf '%s\n' 'git-bridge' ;;
*gitea*) cat /run/secrets/gitea_token ;;
*) exit 1 ;;
esac

View File

@ -0,0 +1,68 @@
#!/bin/sh
set -eu
. /app/config/repositories.conf
state_repo=/state/infra.git
lock_dir=/state/forward-sync.lock
mkdir -p /state
mkdir -p "$HOME"
if ! mkdir "$lock_dir" 2>/dev/null; then
echo "forward sync is already running" >&2
exit 75
fi
trap 'rmdir "$lock_dir" 2>/dev/null || true' EXIT INT TERM
export GIT_TERMINAL_PROMPT=0
export GIT_ASKPASS=/app/bin/git-askpass.sh
if [ ! -d "$state_repo" ]; then
git init --bare "$state_repo" >/dev/null
fi
if git -C "$state_repo" remote get-url gitea >/dev/null 2>&1; then
[ "$(git -C "$state_repo" remote get-url gitea)" = "$GITEA_REMOTE" ] || {
echo "gitea remote mismatch" >&2
exit 2
}
else
git -C "$state_repo" remote add gitea "$GITEA_REMOTE"
fi
if git -C "$state_repo" remote get-url github >/dev/null 2>&1; then
[ "$(git -C "$state_repo" remote get-url github)" = "$GITHUB_REMOTE" ] || {
echo "github remote mismatch" >&2
exit 2
}
else
git -C "$state_repo" remote add github "$GITHUB_REMOTE"
fi
git -C "$state_repo" fetch --no-tags gitea \
"+refs/heads/$MAIN_BRANCH:refs/remotes/gitea/$MAIN_BRANCH"
gitea_sha=$(git -C "$state_repo" rev-parse "refs/remotes/gitea/$MAIN_BRANCH")
if git -C "$state_repo" ls-remote --exit-code --heads github \
"refs/heads/$MAIN_BRANCH" >/dev/null 2>&1; then
git -C "$state_repo" fetch --no-tags github \
"+refs/heads/$MAIN_BRANCH:refs/remotes/github/$MAIN_BRANCH"
github_sha=$(git -C "$state_repo" rev-parse "refs/remotes/github/$MAIN_BRANCH")
if ! git -C "$state_repo" merge-base --is-ancestor "$github_sha" "$gitea_sha"; then
echo "sync refused: GitHub Main is not an ancestor of Gitea Main" >&2
exit 42
fi
fi
git -C "$state_repo" push github \
"refs/remotes/gitea/$MAIN_BRANCH:refs/heads/$MAIN_BRANCH"
verified_sha=$(git -C "$state_repo" ls-remote github \
"refs/heads/$MAIN_BRANCH" | awk 'NR == 1 { print $1 }')
[ "$verified_sha" = "$gitea_sha" ] || {
echo "sync verification failed" >&2
exit 1
}
echo "forward sync completed"
echo "branch=$MAIN_BRANCH"
echo "commit=$gitea_sha"
echo "force_push=false"

View File

@ -0,0 +1,3 @@
GITEA_REMOTE=http://gitea:3000/slh/infra.git
GITHUB_REMOTE=https://github.com/RX-Penguin/infra.git
MAIN_BRANCH=main