fix(security): replace broken branch protection scripts with Gitea-native sync (#1098)
Some checks failed
Deploy Nexus / deploy (push) Has been cancelled

This commit is contained in:
2026-04-07 14:56:31 +00:00
parent c7468a3c6a
commit 60bd9a05ff
2 changed files with 86 additions and 43 deletions

View File

@@ -1,44 +1,6 @@
#!/bin/bash
# Apply branch protections to all repositories
# Requires GITEA_TOKEN env var
REPOS=("hermes-agent" "the-nexus" "timmy-home" "timmy-config")
for repo in "${REPOS[@]}"
do
curl -X POST "https://forge.alexanderwhitestone.com/api/v1/repos/Timmy_Foundation/$repo/branches/main/protection" \
-H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"required_reviews": 1,
"dismiss_stale_reviews": true,
"block_force_push": true,
"block_deletions": true
}'
done
#!/bin/bash
# Gitea API credentials
GITEA_TOKEN="your-personal-access-token"
GITEA_API="https://forge.alexanderwhitestone.com/api/v1"
# Repos to protect
REPOS=("hermes-agent" "the-nexus" "timmy-home" "timmy-config")
for REPO in "${REPO[@]}"; do
echo "Configuring branch protection for $REPO..."
curl -X POST -H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "main",
"require_pull_request": true,
"required_approvals": 1,
"dismiss_stale_approvals": true,
"required_status_checks": '"$(test "$REPO" = "hermes-agent" && echo "true" || echo "false")"',
"block_force_push": true,
"block_delete": true
}' \
"$GITEA_API/repos/Timmy_Foundation/$REPO/branch_protection"
done
# Wrapper for the canonical branch-protection sync script.
# Usage: ./gitea-branch-protection.sh
set -euo pipefail
cd "$(dirname "$0")"
python3 scripts/sync_branch_protection.py

View File

@@ -0,0 +1,81 @@
#!/usr/bin/env python3
"""
Sync branch protection rules from .gitea/branch-protection/*.yml to Gitea.
Correctly uses the Gitea 1.25+ API (not GitHub-style).
"""
import os
import sys
import json
import urllib.request
import yaml
GITEA_URL = os.getenv("GITEA_URL", "https://forge.alexanderwhitestone.com")
GITEA_TOKEN = os.getenv("GITEA_TOKEN", "")
ORG = "Timmy_Foundation"
CONFIG_DIR = ".gitea/branch-protection"
def api_request(method: str, path: str, payload: dict | None = None) -> dict:
url = f"{GITEA_URL}/api/v1{path}"
data = json.dumps(payload).encode() if payload else None
req = urllib.request.Request(url, data=data, method=method, headers={
"Authorization": f"token {GITEA_TOKEN}",
"Content-Type": "application/json",
})
with urllib.request.urlopen(req, timeout=30) as resp:
return json.loads(resp.read().decode())
def apply_protection(repo: str, rules: dict) -> bool:
branch = rules.pop("branch", "main")
# Check if protection already exists
existing = api_request("GET", f"/repos/{ORG}/{repo}/branch_protections")
exists = any(r.get("branch_name") == branch for r in existing)
payload = {
"branch_name": branch,
"rule_name": branch,
"required_approvals": rules.get("required_approvals", 1),
"block_on_rejected_reviews": rules.get("block_on_rejected_reviews", True),
"dismiss_stale_approvals": rules.get("dismiss_stale_approvals", True),
"block_deletions": rules.get("block_deletions", True),
"block_force_push": rules.get("block_force_push", True),
"block_admin_merge_override": rules.get("block_admin_merge_override", True),
"enable_status_check": rules.get("require_ci_to_merge", False),
"status_check_contexts": rules.get("status_check_contexts", []),
}
try:
if exists:
api_request("PATCH", f"/repos/{ORG}/{repo}/branch_protections/{branch}", payload)
else:
api_request("POST", f"/repos/{ORG}/{repo}/branch_protections", payload)
print(f"{repo}:{branch} synced")
return True
except Exception as e:
print(f"{repo}:{branch} failed: {e}")
return False
def main() -> int:
if not GITEA_TOKEN:
print("ERROR: GITEA_TOKEN not set")
return 1
ok = 0
for fname in os.listdir(CONFIG_DIR):
if not fname.endswith(".yml"):
continue
repo = fname[:-4]
with open(os.path.join(CONFIG_DIR, fname)) as f:
cfg = yaml.safe_load(f)
if apply_protection(repo, cfg.get("rules", {})):
ok += 1
print(f"\nSynced {ok} repo(s)")
return 0
if __name__ == "__main__":
raise SystemExit(main())