set gitea as push/fetch and github as push
This commit is contained in:
1 file changed
+128
-66
Regular → Executable
+128
-66
@@ -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,7 +134,33 @@ 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(
|
||||||
|
repo,
|
||||||
|
"remote",
|
||||||
|
"set-url",
|
||||||
|
"--delete",
|
||||||
|
"--push",
|
||||||
|
"origin",
|
||||||
|
re.escape(push_url),
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Establish Gitea as the primary push destination.
|
||||||
|
git(
|
||||||
|
repo,
|
||||||
|
"remote",
|
||||||
|
"set-url",
|
||||||
|
"--push",
|
||||||
|
"origin",
|
||||||
|
gitea_url,
|
||||||
|
)
|
||||||
|
|
||||||
|
# GitHub remains the public/legacy destination.
|
||||||
git(
|
git(
|
||||||
repo,
|
repo,
|
||||||
"remote",
|
"remote",
|
||||||
@@ -120,28 +168,42 @@ def configure_repository(repo: Path):
|
|||||||
"--add",
|
"--add",
|
||||||
"--push",
|
"--push",
|
||||||
"origin",
|
"origin",
|
||||||
gitea_url,
|
github_url,
|
||||||
)
|
)
|
||||||
print(f" + push -> {gitea_url}")
|
|
||||||
else:
|
|
||||||
print(" = Gitea push already configured")
|
|
||||||
|
|
||||||
|
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__":
|
||||||
|
|||||||
Reference in new issue
Block a user