refactor(git): migrate repository creation to core.git commands
Some checks failed
Mark stable commit / test-unit (push) Has been cancelled
Mark stable commit / test-integration (push) Has been cancelled
Mark stable commit / test-env-virtual (push) Has been cancelled
Mark stable commit / test-env-nix (push) Has been cancelled
Mark stable commit / test-e2e (push) Has been cancelled
Mark stable commit / test-virgin-user (push) Has been cancelled
Mark stable commit / test-virgin-root (push) Has been cancelled
Mark stable commit / lint-shell (push) Has been cancelled
Mark stable commit / lint-python (push) Has been cancelled
Mark stable commit / mark-stable (push) Has been cancelled

- Replace direct subprocess git calls with core.git commands (init, add_all, commit, branch_move, push_upstream)
- Introduce add_all, init, and branch_move command wrappers with preview support
- Use git config queries via get_config_value instead of shell access
- Preserve main → master fallback logic with explicit error handling
- Improve error transparency while keeping previous non-fatal behavior

https://chatgpt.com/share/69414b77-b4d4-800f-a189-463b489664b3
This commit is contained in:
Kevin Veen-Birkenbach
2025-12-16 13:05:42 +01:00
parent 9c22c7dbb4
commit 019aa4b0d9
6 changed files with 115 additions and 44 deletions

View File

@@ -1,8 +1,8 @@
# src/pkgmgr/actions/repository/create.py
from __future__ import annotations from __future__ import annotations
import os import os
import re import re
import subprocess
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any, Dict, Optional, Tuple from typing import Any, Dict, Optional, Tuple
from urllib.parse import urlparse from urllib.parse import urlparse
@@ -14,6 +14,16 @@ from pkgmgr.actions.mirror.setup_cmd import setup_mirrors
from pkgmgr.actions.repository.scaffold import render_default_templates from pkgmgr.actions.repository.scaffold import render_default_templates
from pkgmgr.core.command.alias import generate_alias from pkgmgr.core.command.alias import generate_alias
from pkgmgr.core.config.save import save_user_config from pkgmgr.core.config.save import save_user_config
from pkgmgr.core.git.commands import (
GitCommitError,
GitPushUpstreamError,
add_all,
branch_move,
commit,
init,
push_upstream,
)
from pkgmgr.core.git.queries import get_config_value
Repository = Dict[str, Any] Repository = Dict[str, Any]
@@ -28,27 +38,6 @@ class RepoParts:
name: str name: str
def _run(cmd: str, cwd: str, preview: bool) -> None:
if preview:
print(f"[Preview] Would run in {cwd}: {cmd}")
return
subprocess.run(cmd, cwd=cwd, shell=True, check=True)
def _git_get(key: str) -> str:
try:
out = subprocess.run(
f"git config --get {key}",
shell=True,
check=False,
capture_output=True,
text=True,
)
return (out.stdout or "").strip()
except Exception:
return ""
def _split_host_port(host_with_port: str) -> Tuple[str, Optional[str]]: def _split_host_port(host_with_port: str) -> Tuple[str, Optional[str]]:
if ":" in host_with_port: if ":" in host_with_port:
host, port = host_with_port.split(":", 1) host, port = host_with_port.split(":", 1)
@@ -116,28 +105,27 @@ def _write_default_mirrors(repo_dir: str, primary: str, name: str, preview: bool
def _git_init_and_initial_commit(repo_dir: str, preview: bool) -> None: def _git_init_and_initial_commit(repo_dir: str, preview: bool) -> None:
_run("git init", cwd=repo_dir, preview=preview) init(cwd=repo_dir, preview=preview)
_run("git add -A", cwd=repo_dir, preview=preview) add_all(cwd=repo_dir, preview=preview)
if preview: try:
print(f'[Preview] Would run in {repo_dir}: git commit -m "Initial commit"') commit("Initial commit", cwd=repo_dir, preview=preview)
return except GitCommitError as exc:
print(f"[WARN] Initial commit failed (continuing): {exc}")
subprocess.run('git commit -m "Initial commit"', cwd=repo_dir, shell=True, check=False)
def _git_push_main_or_master(repo_dir: str, preview: bool) -> None: def _git_push_main_or_master(repo_dir: str, preview: bool) -> None:
_run("git branch -M main", cwd=repo_dir, preview=preview)
try: try:
_run("git push -u origin main", cwd=repo_dir, preview=preview) branch_move("main", cwd=repo_dir, preview=preview)
push_upstream("origin", "main", cwd=repo_dir, preview=preview)
return return
except subprocess.CalledProcessError: except GitPushUpstreamError:
pass pass
try: try:
_run("git branch -M master", cwd=repo_dir, preview=preview) branch_move("master", cwd=repo_dir, preview=preview)
_run("git push -u origin master", cwd=repo_dir, preview=preview) push_upstream("origin", "master", cwd=repo_dir, preview=preview)
except subprocess.CalledProcessError as exc: except GitPushUpstreamError as exc:
print(f"[WARN] Push failed: {exc}") print(f"[WARN] Push failed: {exc}")
@@ -157,8 +145,8 @@ def create_repo(
base_dir = os.path.expanduser(str(directories.get("repositories", "~/Repositories"))) base_dir = os.path.expanduser(str(directories.get("repositories", "~/Repositories")))
repo_dir = os.path.join(base_dir, parts.host, parts.owner, parts.name) repo_dir = os.path.join(base_dir, parts.host, parts.owner, parts.name)
author_name = _git_get("user.name") or "Unknown Author" author_name = get_config_value("user.name") or "Unknown Author"
author_email = _git_get("user.email") or "unknown@example.invalid" author_email = get_config_value("user.email") or "unknown@example.invalid"
homepage = _repo_homepage(parts.host, parts.owner, parts.name) homepage = _repo_homepage(parts.host, parts.owner, parts.name)
primary_url = _build_default_primary_url(parts) primary_url = _build_default_primary_url(parts)

View File

@@ -1,27 +1,31 @@
# src/pkgmgr/core/git/commands/__init__.py
from __future__ import annotations from __future__ import annotations
from .add import GitAddError, add from .add import GitAddError, add
from .add_all import GitAddAllError, add_all
from .add_remote import GitAddRemoteError, add_remote
from .add_remote_push_url import GitAddRemotePushUrlError, add_remote_push_url
from .branch_move import GitBranchMoveError, branch_move
from .checkout import GitCheckoutError, checkout from .checkout import GitCheckoutError, checkout
from .clone import GitCloneError, clone
from .commit import GitCommitError, commit from .commit import GitCommitError, commit
from .create_branch import GitCreateBranchError, create_branch from .create_branch import GitCreateBranchError, create_branch
from .delete_local_branch import GitDeleteLocalBranchError, delete_local_branch from .delete_local_branch import GitDeleteLocalBranchError, delete_local_branch
from .delete_remote_branch import GitDeleteRemoteBranchError, delete_remote_branch from .delete_remote_branch import GitDeleteRemoteBranchError, delete_remote_branch
from .fetch import GitFetchError, fetch from .fetch import GitFetchError, fetch
from .init import GitInitError, init
from .merge_no_ff import GitMergeError, merge_no_ff from .merge_no_ff import GitMergeError, merge_no_ff
from .pull import GitPullError, pull from .pull import GitPullError, pull
from .pull_ff_only import GitPullFfOnlyError, pull_ff_only from .pull_ff_only import GitPullFfOnlyError, pull_ff_only
from .push import GitPushError, push from .push import GitPushError, push
from .push_upstream import GitPushUpstreamError, push_upstream from .push_upstream import GitPushUpstreamError, push_upstream
from .add_remote import GitAddRemoteError, add_remote
from .add_remote_push_url import GitAddRemotePushUrlError, add_remote_push_url
from .set_remote_url import GitSetRemoteUrlError, set_remote_url from .set_remote_url import GitSetRemoteUrlError, set_remote_url
from .tag_annotated import GitTagAnnotatedError, tag_annotated from .tag_annotated import GitTagAnnotatedError, tag_annotated
from .tag_force_annotated import GitTagForceAnnotatedError, tag_force_annotated from .tag_force_annotated import GitTagForceAnnotatedError, tag_force_annotated
from .clone import GitCloneError, clone
__all__ = [ __all__ = [
"add", "add",
"add_all",
"fetch", "fetch",
"checkout", "checkout",
"pull", "pull",
@@ -39,7 +43,10 @@ __all__ = [
"tag_annotated", "tag_annotated",
"tag_force_annotated", "tag_force_annotated",
"clone", "clone",
"init",
"branch_move",
"GitAddError", "GitAddError",
"GitAddAllError",
"GitFetchError", "GitFetchError",
"GitCheckoutError", "GitCheckoutError",
"GitPullError", "GitPullError",
@@ -57,4 +64,6 @@ __all__ = [
"GitTagAnnotatedError", "GitTagAnnotatedError",
"GitTagForceAnnotatedError", "GitTagForceAnnotatedError",
"GitCloneError", "GitCloneError",
"GitInitError",
"GitBranchMoveError",
] ]

View File

@@ -0,0 +1,22 @@
# src/pkgmgr/core/git/commands/add_all.py
from __future__ import annotations
from ..errors import GitError, GitCommandError
from ..run import run
class GitAddAllError(GitCommandError):
"""Raised when `git add -A` fails."""
def add_all(*, cwd: str = ".", preview: bool = False) -> None:
"""
Stage all changes (tracked + untracked).
Equivalent to:
git add -A
"""
try:
run(["add", "-A"], cwd=cwd, preview=preview)
except GitError as exc:
raise GitAddAllError("Failed to stage all changes with `git add -A`.", cwd=cwd) from exc

View File

@@ -0,0 +1,22 @@
# src/pkgmgr/core/git/commands/branch_move.py
from __future__ import annotations
from ..errors import GitError, GitCommandError
from ..run import run
class GitBranchMoveError(GitCommandError):
"""Raised when renaming/moving a branch fails."""
def branch_move(branch: str, *, cwd: str = ".", preview: bool = False) -> None:
"""
Rename the current branch to `branch`, creating it if needed.
Equivalent to:
git branch -M <branch>
"""
try:
run(["branch", "-M", branch], cwd=cwd, preview=preview)
except GitError as exc:
raise GitBranchMoveError(f"Failed to move/rename current branch to {branch!r}.", cwd=cwd) from exc

View File

@@ -0,0 +1,22 @@
# src/pkgmgr/core/git/commands/init.py
from __future__ import annotations
from ..errors import GitError, GitCommandError
from ..run import run
class GitInitError(GitCommandError):
"""Raised when `git init` fails."""
def init(*, cwd: str = ".", preview: bool = False) -> None:
"""
Initialize a repository.
Equivalent to:
git init
"""
try:
run(["init"], cwd=cwd, preview=preview)
except GitError as exc:
raise GitInitError("Failed to initialize git repository.", cwd=cwd) from exc

View File

@@ -1,3 +1,4 @@
# src/pkgmgr/core/git/commands/push_upstream.py
from __future__ import annotations from __future__ import annotations
from ..errors import GitError, GitCommandError from ..errors import GitError, GitCommandError
@@ -8,14 +9,21 @@ class GitPushUpstreamError(GitCommandError):
"""Raised when pushing a branch with upstream tracking fails.""" """Raised when pushing a branch with upstream tracking fails."""
def push_upstream(remote: str, branch: str, cwd: str = ".") -> None: def push_upstream(
remote: str,
branch: str,
*,
cwd: str = ".",
preview: bool = False,
) -> None:
""" """
Push a branch and set upstream tracking. Push a branch and set upstream tracking.
Equivalent to: git push -u <remote> <branch> Equivalent to:
git push -u <remote> <branch>
""" """
try: try:
run(["push", "-u", remote, branch], cwd=cwd) run(["push", "-u", remote, branch], cwd=cwd, preview=preview)
except GitError as exc: except GitError as exc:
raise GitPushUpstreamError( raise GitPushUpstreamError(
f"Failed to push branch {branch!r} to {remote!r} with upstream tracking.", f"Failed to push branch {branch!r} to {remote!r} with upstream tracking.",