set gitea as push/fetch and github as push

This commit is contained in:
SowinskiBraeden committed 2026-09-08 20:09:16 -07:00
1 parent 87cc983cb2
commit 58700886a6
1 file changed
+129 -67
Regular → Executable
+129 -67
View File
@@ -5,7 +5,9 @@ import subprocess
from pathlib import Path from pathlib import Path
PROJECTS_DIR = Path.home() / "Projects" PROJECTS_DIR = Path.home() / "Projects"
GITHUB_USER = "SowinskiBraeden"
OWNER = "SowinskiBraeden"
GITHUB_HOST = "github.com"
GITEA_HOST = "git.sowinski.dev" GITEA_HOST = "git.sowinski.dev"
@@ -18,41 +20,46 @@ def git(repo: Path, *args: str, check: bool = True) -> subprocess.CompletedProce
) )
def find_git_repositories(root: Path): def find_git_repositories(root: Path) -> list[Path]:
"""Find all Git repositories recursively below root."""
repositories = set()
for git_path in root.rglob(".git"):
if git_path.is_dir() or git_path.is_file():
repositories.add(git_path.parent)
return sorted(repositories)
def parse_repo_name(url: str) -> str | None:
""" """
Find repositories recursively under ~/Projects. Extract the repository name if the URL belongs to OWNER on either
GitHub or our Gitea instance.
Once a .git directory/file is found, don't recurse further into that Supported examples:
repository looking for nested repositories.
"""
repositories = []
for path in root.rglob(".git"):
if path.is_dir() or path.is_file():
repositories.append(path.parent)
return sorted(set(repositories))
def github_repo_name(origin: str):
"""
Recognize origins such as:
git@github.com:SowinskiBraeden/example.git git@github.com:SowinskiBraeden/example.git
https://github.com/SowinskiBraeden/example.git https://github.com/SowinskiBraeden/example.git
ssh://git@github.com/SowinskiBraeden/example.git
Returns "example" or None. git@git.sowinski.dev:SowinskiBraeden/example.git
https://git.sowinski.dev/SowinskiBraeden/example.git
""" """
hosts = [
re.escape(GITHUB_HOST),
re.escape(GITEA_HOST),
]
host_pattern = "(?:" + "|".join(hosts) + ")"
patterns = [ patterns = [
rf"^git@github\.com:{re.escape(GITHUB_USER)}/([^/]+?)(?:\.git)?$", rf"^git@{host_pattern}:{re.escape(OWNER)}/([^/]+?)(?:\.git)?$",
rf"^https?://github\.com/{re.escape(GITHUB_USER)}/([^/]+?)(?:\.git)?/?$", rf"^https?://{host_pattern}/{re.escape(OWNER)}/([^/]+?)(?:\.git)?/?$",
rf"^ssh://git@github\.com/{re.escape(GITHUB_USER)}/([^/]+?)(?:\.git)?/?$", rf"^ssh://git@{host_pattern}/{re.escape(OWNER)}/([^/]+?)(?:\.git)?/?$",
] ]
for pattern in patterns: for pattern in patterns:
match = re.match(pattern, origin) match = re.match(pattern, url)
if match: if match:
return match.group(1) return match.group(1)
@@ -60,50 +67,65 @@ def github_repo_name(origin: str):
return None return None
def configure_repository(repo: Path): def get_origin(repo: Path) -> str | None:
result = git(repo, "remote", "get-url", "origin", check=False) result = git(
repo,
"remote",
"get-url",
"origin",
check=False,
)
if result.returncode != 0: if result.returncode != 0:
return None
return result.stdout.strip()
def configure_repository(repo: Path) -> bool:
origin = get_origin(repo)
if origin is None:
print(f"SKIP {repo}") print(f"SKIP {repo}")
print(" No origin remote") print(" No origin remote")
return return False
origin = result.stdout.strip() repo_name = parse_repo_name(origin)
repo_name = github_repo_name(origin)
if not repo_name: if repo_name is None:
print(f"SKIP {repo}") print(f"SKIP {repo}")
print(f" Origin is not {GITHUB_USER}'s GitHub: {origin}") print(f" Unrecognized/non-{OWNER} origin:")
return print(f" {origin}")
return False
github_url = f"git@github.com:{GITHUB_USER}/{repo_name}.git" gitea_url = f"git@{GITEA_HOST}:{OWNER}/{repo_name}.git"
gitea_url = f"git@{GITEA_HOST}:{GITHUB_USER}/{repo_name}.git" github_url = f"git@{GITHUB_HOST}:{OWNER}/{repo_name}.git"
existing_push_urls = git( print(f"UPDATE {repo}")
repo, print(f" Repository: {OWNER}/{repo_name}")
"remote",
"get-url",
"--all",
"--push",
"origin",
).stdout.splitlines()
print(f"FOUND {repo}")
print(f" Repository: {GITHUB_USER}/{repo_name}")
# Explicitly establish GitHub as the first push destination.
# #
# If no separate pushurl exists yet, this creates one without changing # FETCH
# the fetch URL. #
if github_url not in existing_push_urls: # Gitea is authoritative.
git(repo, "remote", "set-url", "--push", "origin", github_url) #
print(f" + push -> {github_url}") git(
else: repo,
print(" = GitHub push already configured") "remote",
"set-url",
"origin",
gitea_url,
)
# Re-read because set-url --push may have replaced the implicit print(f" fetch -> {gitea_url}")
# origin push URL.
existing_push_urls = git( #
# PUSH
#
# Reset the push URL list so running this utility repeatedly cannot
# create duplicate push destinations.
#
current_push_urls = git(
repo, repo,
"remote", "remote",
"get-url", "get-url",
@@ -112,36 +134,76 @@ def configure_repository(repo: Path):
"origin", "origin",
).stdout.splitlines() ).stdout.splitlines()
if gitea_url not in existing_push_urls: # Delete existing explicit push URLs.
#
# Git returns an error if there are no explicit push URLs matching,
# which is harmless here.
for push_url in current_push_urls:
git( git(
repo, repo,
"remote", "remote",
"set-url", "set-url",
"--add", "--delete",
"--push", "--push",
"origin", "origin",
gitea_url, re.escape(push_url),
check=False,
) )
print(f" + push -> {gitea_url}")
else:
print(" = Gitea push already configured")
# Establish Gitea as the primary push destination.
git(
repo,
"remote",
"set-url",
"--push",
"origin",
gitea_url,
)
# GitHub remains the public/legacy destination.
git(
repo,
"remote",
"set-url",
"--add",
"--push",
"origin",
github_url,
)
print(f" push -> {gitea_url}")
print(f" push -> {github_url}")
print() print()
return True
def main(): def main():
if not PROJECTS_DIR.exists(): if not PROJECTS_DIR.exists():
raise SystemExit(f"Projects directory does not exist: {PROJECTS_DIR}") raise SystemExit(
f"Projects directory does not exist: {PROJECTS_DIR}"
)
repositories = find_git_repositories(PROJECTS_DIR) repositories = find_git_repositories(PROJECTS_DIR)
print(f"Searching {PROJECTS_DIR}") print(f"Searching: {PROJECTS_DIR}")
print(f"Found {len(repositories)} Git repositories.\n") print(f"Found {len(repositories)} Git repositories.")
print()
configured = 0
skipped = 0
for repo in repositories: for repo in repositories:
configure_repository(repo) if configure_repository(repo):
configured += 1
else:
skipped += 1
print("Done.") print("=" * 60)
print("Finished")
print(f"Configured: {configured}")
print(f"Skipped: {skipped}")
print(f"Total: {len(repositories)}")
if __name__ == "__main__": if __name__ == "__main__":