mirror of
git://f0xx.org/android_cast
synced 2026-07-29 06:18:42 +03:00
gitea sync
This commit is contained in:
@@ -331,6 +331,40 @@ GITEA_SUBMODULES_CONF=/root/my-submodules.list sudo ./gitea_mirror_submodules.sh
|
||||
sudo -u gitea gitea -c /var/lib/gitea/custom/conf/app.ini admin repo-sync-mirrors
|
||||
```
|
||||
|
||||
## Branch protection (`master`, `next` only)
|
||||
|
||||
Server-side rules per [docs/GIT_FLOW.md](../../../../docs/GIT_FLOW.md) §7. **Two places:**
|
||||
|
||||
| Where | Tool |
|
||||
|-------|------|
|
||||
| **Gitea** `AndroidCast/android_cast` | `gitea_protect_branches.sh` |
|
||||
| **Canonical FE** `git://10.7.0.10/android_cast` | `examples/git/hooks/pre-receive.android_cast.example` |
|
||||
|
||||
**Gitea (BE):**
|
||||
|
||||
```bash
|
||||
sudo ./examples/crash_reporter/backend/scripts/gitea/gitea_protect_branches.sh
|
||||
sudo ./examples/crash_reporter/backend/scripts/gitea/gitea_protect_branches.sh --list
|
||||
```
|
||||
|
||||
- `master` and `next` only (no `*` wildcard — `feature/*` stays free)
|
||||
- Force-push **disabled for everyone**
|
||||
- Push/merge allowlist: **admin**, **foxx**, org team **Owners**
|
||||
- Admins cannot bypass merge rules
|
||||
|
||||
**FE bare repo** (primary enforcement for `git push origin`):
|
||||
|
||||
```bash
|
||||
# on FE, in bare repo hooks/
|
||||
cp examples/git/hooks/pre-receive.android_cast.example \
|
||||
/path/to/android_cast.git/hooks/pre-receive
|
||||
chmod +x /path/to/android_cast.git/hooks/pre-receive
|
||||
```
|
||||
|
||||
Rejects on `master` / `next`: branch delete, non-fast-forward (force-push). Other branches unchanged.
|
||||
|
||||
`gitea_polish_project.sh` runs branch protection automatically unless `GITEA_SKIP_BRANCH_PROTECT=1`.
|
||||
|
||||
## Polish (finish line — safe BE run)
|
||||
|
||||
```bash
|
||||
|
||||
@@ -200,7 +200,12 @@ if command -v "$SCRIPT_DIR/gitea_set_default_branch.sh" >/dev/null 2>&1; then
|
||||
sh "$SCRIPT_DIR/gitea_set_default_branch.sh" || warn "default branch set failed (sync first)"
|
||||
fi
|
||||
|
||||
# 7) Summary
|
||||
# 7) Branch protection (master, next)
|
||||
if [ "${GITEA_SKIP_BRANCH_PROTECT:-0}" -eq 0 ]; then
|
||||
sh "$SCRIPT_DIR/gitea_protect_branches.sh" || warn "branch protection failed — run manually"
|
||||
fi
|
||||
|
||||
# 8) Summary
|
||||
log "=== done ==="
|
||||
cat <<EOF
|
||||
|
||||
@@ -208,6 +213,8 @@ UI checklist:
|
||||
• foxx/admin: open ${GITEA_ROOT_URL%/}/$GITEA_ORG (org page — not only "Your repositories")
|
||||
• Expect: android_cast, libvpx, opus, speex under $GITEA_ORG
|
||||
• If android_cast still stale: sudo ./gitea_diagnose_mirror_sync.sh
|
||||
• Branch protection: master/next on Gitea; install FE hook:
|
||||
examples/git/hooks/pre-receive.android_cast.example
|
||||
• Email/2FA later: app.ini REGISTER_EMAIL_CONFIRM=false; add SMTP when ready
|
||||
|
||||
EOF
|
||||
|
||||
172
examples/crash_reporter/backend/scripts/gitea/gitea_protect_branches.sh
Executable file
172
examples/crash_reporter/backend/scripts/gitea/gitea_protect_branches.sh
Executable file
@@ -0,0 +1,172 @@
|
||||
#!/bin/ash
|
||||
#
|
||||
# Server-side protection for master and next on Gitea (android_cast).
|
||||
# Blocks force-push for everyone; push/merge limited to org Owners (admin, foxx).
|
||||
# Side branches (feature/*, fix/*) are unaffected — no wildcard rule.
|
||||
#
|
||||
# Usage (BE as root):
|
||||
# sudo ./gitea_protect_branches.sh
|
||||
# GITEA_OWNER=AndroidCast GITEA_REPO=android_cast sudo ./gitea_protect_branches.sh
|
||||
# sudo ./gitea_protect_branches.sh --list
|
||||
#
|
||||
# Note: canonical git on FE (git://10.7.0.10/android_cast) needs its own hook —
|
||||
# see examples/git/hooks/pre-receive.android_cast.example
|
||||
#
|
||||
set -eu
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=gitea_ini.sh
|
||||
. "$SCRIPT_DIR/gitea_ini.sh"
|
||||
# shellcheck source=gitea_mirror_lib.sh
|
||||
. "$SCRIPT_DIR/gitea_mirror_lib.sh"
|
||||
|
||||
GITEA_OWNER="${GITEA_OWNER:-${GITEA_ORG:-AndroidCast}}"
|
||||
GITEA_REPO="${GITEA_REPO:-android_cast}"
|
||||
# Space-separated branches to protect (exact names only).
|
||||
GITEA_PROTECT_BRANCHES="${GITEA_PROTECT_BRANCHES:-master next}"
|
||||
# Users allowed to push (fast-forward / merge) to protected branches.
|
||||
GITEA_PROTECT_PUSH_USERS="${GITEA_PROTECT_PUSH_USERS:-admin foxx}"
|
||||
# Org team with push access (AndroidCast Owners).
|
||||
GITEA_PROTECT_PUSH_TEAM="${GITEA_PROTECT_PUSH_TEAM:-Owners}"
|
||||
|
||||
log() { printf '[gitea-protect] %s\n' "$*"; }
|
||||
warn() { printf '[gitea-protect] WARN: %s\n' "$*" >&2; }
|
||||
die() { printf '[gitea-protect] ERROR: %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
gitea_api_token() {
|
||||
_t="${GITEA_ADMIN_TOKEN:-}"
|
||||
[ -n "$_t" ] || _t="$(gitea_resolve_push_token)" || return 1
|
||||
printf '%s' "$_t"
|
||||
}
|
||||
|
||||
gitea_list_branch_protections() {
|
||||
command -v curl >/dev/null 2>&1 || return 1
|
||||
_token="$(gitea_api_token)" || return 1
|
||||
_api="$(gitea_api_base)"
|
||||
curl -sS "$_api/repos/$GITEA_OWNER/$GITEA_REPO/branch_protections" \
|
||||
-H "Authorization: token $_token"
|
||||
}
|
||||
|
||||
gitea_protection_exists() {
|
||||
_branch="$1"
|
||||
gitea_list_branch_protections 2>/dev/null | grep -q "\"branch_name\":\"$_branch\""
|
||||
}
|
||||
|
||||
gitea_protect_payload() {
|
||||
_branch="$1"
|
||||
_prio="$2"
|
||||
_users_json=""
|
||||
for _u in $GITEA_PROTECT_PUSH_USERS; do
|
||||
[ -n "$_u" ] || continue
|
||||
_users_json="$_users_json\"$_u\","
|
||||
done
|
||||
_users_json="${_users_json%,}"
|
||||
|
||||
cat <<EOF
|
||||
{
|
||||
"rule_name": "$_branch",
|
||||
"branch_name": "$_branch",
|
||||
"priority": $_prio,
|
||||
"enable_push": true,
|
||||
"enable_push_whitelist": true,
|
||||
"push_whitelist_usernames": [$_users_json],
|
||||
"push_whitelist_teams": ["$GITEA_PROTECT_PUSH_TEAM"],
|
||||
"enable_force_push": false,
|
||||
"enable_force_push_allowlist": false,
|
||||
"block_admin_merge_override": true
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
gitea_apply_protection() {
|
||||
_branch="$1"
|
||||
_prio="$2"
|
||||
command -v curl >/dev/null 2>&1 || return 1
|
||||
_token="$(gitea_api_token)" || return 1
|
||||
_api="$(gitea_api_base)"
|
||||
_payload="$(gitea_protect_payload "$_branch" "$_prio")"
|
||||
_resp="$(mktemp /tmp/gitea-protect-XXXXXX.json)"
|
||||
|
||||
if gitea_protection_exists "$_branch"; then
|
||||
log "update protection: $GITEA_OWNER/$GITEA_REPO::$_branch"
|
||||
_http="$(curl -sS -o "$_resp" -w '%{http_code}' \
|
||||
-X PATCH "$_api/repos/$GITEA_OWNER/$GITEA_REPO/branch_protections/$_branch" \
|
||||
-H "Authorization: token $_token" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$_payload")"
|
||||
else
|
||||
log "create protection: $GITEA_OWNER/$GITEA_REPO::$_branch"
|
||||
_http="$(curl -sS -o "$_resp" -w '%{http_code}' \
|
||||
-X POST "$_api/repos/$GITEA_OWNER/$GITEA_REPO/branch_protections" \
|
||||
-H "Authorization: token $_token" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$_payload")"
|
||||
fi
|
||||
|
||||
case "$_http" in
|
||||
200|201|204) gitea_rm_path "$_resp"; return 0 ;;
|
||||
*)
|
||||
warn "HTTP $_http for $_branch:"
|
||||
head -c 500 "$_resp" >&2 2>/dev/null || true
|
||||
printf '\n' >&2
|
||||
gitea_rm_path "$_resp"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
list_protections() {
|
||||
gitea_load_config || exit 1
|
||||
gitea_ensure_cli_access || exit 1
|
||||
log "branch protections: $GITEA_OWNER/$GITEA_REPO"
|
||||
gitea_list_branch_protections | tr ',' '\n' | grep -E 'branch_name|enable_force_push|enable_push' || true
|
||||
}
|
||||
|
||||
# --- main ---
|
||||
|
||||
case "${1:-}" in
|
||||
-h|--help|help)
|
||||
sed -n '2,16p' "$0"
|
||||
exit 0
|
||||
;;
|
||||
--list|list)
|
||||
list_protections
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
gitea_load_config || exit 1
|
||||
gitea_ensure_cli_access || exit 1
|
||||
|
||||
if ! gitea_repo_exists_api "$GITEA_OWNER" "$GITEA_REPO"; then
|
||||
die "repo $GITEA_OWNER/$GITEA_REPO not found — run gitea_polish_project.sh first"
|
||||
fi
|
||||
|
||||
log "repo=$GITEA_OWNER/$GITEA_REPO branches=$GITEA_PROTECT_BRANCHES"
|
||||
log "push allowlist: users=$GITEA_PROTECT_PUSH_USERS team=$GITEA_PROTECT_PUSH_TEAM"
|
||||
log "force-push: disabled for all | side branches: unprotected"
|
||||
|
||||
_prio=1
|
||||
_fail=0
|
||||
for _b in $GITEA_PROTECT_BRANCHES; do
|
||||
gitea_apply_protection "$_b" "$_prio" || _fail=1
|
||||
_prio=$((_prio + 1))
|
||||
done
|
||||
|
||||
cat <<EOF
|
||||
|
||||
Gitea rules applied. Verify in UI:
|
||||
$GITEA_OWNER/$GITEA_REPO → Settings → Branches
|
||||
|
||||
Protected: master, next only
|
||||
• No force-push (anyone)
|
||||
• Push/merge: $GITEA_PROTECT_PUSH_USERS + team $GITEA_PROTECT_PUSH_TEAM
|
||||
• Admins cannot bypass (block_admin_merge_override)
|
||||
|
||||
IMPORTANT — canonical origin on FE is separate:
|
||||
Install examples/git/hooks/pre-receive.android_cast.example on the bare repo
|
||||
at git://10.7.0.10/android_cast (hooks/pre-receive).
|
||||
|
||||
EOF
|
||||
|
||||
[ "$_fail" -eq 0 ]
|
||||
70
examples/git/hooks/pre-receive.android_cast.example
Normal file
70
examples/git/hooks/pre-receive.android_cast.example
Normal file
@@ -0,0 +1,70 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Canonical bare repo hook: git://10.7.0.10/android_cast
|
||||
# Install on FE (or wherever the bare repo lives):
|
||||
# cp pre-receive.android_cast.example /path/to/android_cast.git/hooks/pre-receive
|
||||
# chmod +x /path/to/android_cast.git/hooks/pre-receive
|
||||
#
|
||||
# Protects master and next only:
|
||||
# - reject branch deletion (git push origin :master)
|
||||
# - reject non-fast-forward / force-push
|
||||
# Side branches (feature/*, fix/*, hotfix/*) are not listed — any push allowed.
|
||||
#
|
||||
# Optional: restrict who may update master/next (SSH user name in AUTH_USER
|
||||
# or GL_USERNAME depending on git daemon setup). Uncomment ALLOWED_USERS block.
|
||||
#
|
||||
set -eu
|
||||
|
||||
PROTECTED_BRANCHES="master next"
|
||||
# ALLOWED_USERS="foxx admin git"
|
||||
|
||||
zero="0000000000000000000000000000000000000000"
|
||||
|
||||
is_protected() {
|
||||
for _p in $PROTECTED_BRANCHES; do
|
||||
[ "$1" = "$_p" ] && return 0
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# Optional user gate (uncomment ALLOWED_USERS above to enable)
|
||||
check_user() {
|
||||
[ -z "${ALLOWED_USERS:-}" ] && return 0
|
||||
_u="${GL_USERNAME:-${AUTH_USER:-${USER:-}}}"
|
||||
for _a in $ALLOWED_USERS; do
|
||||
[ "$_u" = "$_a" ] && return 0
|
||||
done
|
||||
echo "hooks: user '$_u' not allowed to update protected branches" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
while read -r oldrev newrev refname; do
|
||||
case "$refname" in
|
||||
refs/heads/*) ;;
|
||||
*) continue ;;
|
||||
esac
|
||||
branch="${refname#refs/heads/}"
|
||||
is_protected "$branch" || continue
|
||||
|
||||
check_user || exit 1
|
||||
|
||||
# Deletion
|
||||
if [ "$newrev" = "$zero" ]; then
|
||||
echo "hooks: refusing to delete protected branch '$branch'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# New branch creation on protected name (allow only if empty repo edge case)
|
||||
if [ "$oldrev" = "$zero" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# Non-fast-forward => force-push or history rewrite
|
||||
if ! git merge-base --is-ancestor "$oldrev" "$newrev" 2>/dev/null; then
|
||||
echo "hooks: refusing non-fast-forward push to protected branch '$branch'" >&2
|
||||
echo "hooks: use feature/* branch + merge (see docs/GIT_FLOW.md)" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user