[projects/git-slug] Respektowanie --quiet w 'git pld update --new' i 'git pld init'
arekm
arekm at pld-linux.org
Tue May 12 15:09:15 CEST 2026
commit 8a14c43c11c70be0bb82607c7d5380649f06649f
Author: Arkadiusz Miśkiewicz <arekm at maven.pl>
Date: Tue Apr 21 13:58:29 2026 +0200
Respektowanie --quiet w 'git pld update --new' i 'git pld init'
Dwie ścieżki omijały kontrakt trybu cichego, bo odpalały podproces
z dziedziczonym stderr rodzica zamiast przez PIPE:
* GitRepo.init_gitdir drukowało 'Initialized empty Git repository in
...' przy każdym pakiecie zainicjalizowanym przez --new.
* createpackage wywoływało ssh host create bez przechwytywania, więc
komunikat zwrotny serwera ('Repository created: ...') pojawiał się
niezależnie od --quiet.
init_gitdir/init dostają teraz quiet=... analogicznie do fetch(), a
initpackage przekazuje options.quiet dalej. createpackage zawsze
przechwytuje wyjście ssh i odtwarza je tylko gdy nie jesteśmy w trybie
cichym albo gdy serwer zwrócił błąd — sukces w --quiet jest cichy,
ale porażka nadal pokazuje co powiedział serwer.
Przy okazji uzupełnione testy pokrywające luki ujawnione w audycie:
--no-new, 'update --depth' i 'clone --depth'.
git_slug/gitrepo.py | 8 ++--
slug.py | 14 ++++++-
tests/test_failure_paths.py | 98 ++++++++++++++++++++++++++++++++++++++++++++-
tests/test_gitrepo.py | 50 ++++++++++++++++++++++-
tests/test_slug_commands.py | 49 +++++++++++++++++++++++
5 files changed, 211 insertions(+), 8 deletions(-)
---
diff --git a/git_slug/gitrepo.py b/git_slug/gitrepo.py
index 349dcf7..a4e5b8f 100644
--- a/git_slug/gitrepo.py
+++ b/git_slug/gitrepo.py
@@ -83,8 +83,10 @@ class GitRepo:
clist += [remotename] + fetchlist
return self.commandexc(clist)
- def init_gitdir(self):
+ def init_gitdir(self, quiet=False):
clist = ['git', 'init']
+ if quiet:
+ clist.append('-q')
if os.path.dirname(self.gdir) == self.wtree:
clist.append(self.wtree)
else:
@@ -92,10 +94,10 @@ class GitRepo:
if subprocess.call(clist, preexec_fn=_reset_sigint):
raise GitRepoError(self.gdir)
- def init(self, remotepull, remotepush = None, remotename=REMOTE_NAME):
+ def init(self, remotepull, remotepush = None, remotename=REMOTE_NAME, quiet=False):
if os.path.isdir(self.gdir):
print("warning: directory {} already existed".format(self.gdir), file=sys.stderr)
- self.init_gitdir()
+ self.init_gitdir(quiet=quiet)
self.commandio(['remote', 'add', remotename, remotepull])
if remotepush is not None:
self.commandio(['remote', 'set-url', '--push', remotename, remotepush])
diff --git a/slug.py b/slug.py
index b5f54c6..94c8ef6 100755
--- a/slug.py
+++ b/slug.py
@@ -608,7 +608,8 @@ def initpackage(name, options):
try:
repo = GitRepo(os.path.join(options.packagesdir, name))
remotepush = os.path.join(GIT_REPO_PUSH, name)
- repo.init(os.path.join(GIT_REPO, name), remotepush)
+ repo.init(os.path.join(GIT_REPO, name), remotepush,
+ quiet=options.quiet)
return repo
except GitRepoError as e:
print('error: failed to init {}: {}'.format(name, e),
@@ -620,8 +621,17 @@ def createpackage(name, options):
"""Create a new package repository on the server and init locally.
Returns True on success, False on failure.
+
+ Always capture ssh output, then relay it unless we're in quiet mode
+ and the remote succeeded. That way normal mode shows server feedback
+ ("Repository created: ...") and quiet mode stays silent on success
+ but still surfaces server-side errors for diagnosis.
"""
- result = subprocess.run(['ssh', GITLOGIN + GITSERVER, 'create', name])
+ result = subprocess.run(['ssh', GITLOGIN + GITSERVER, 'create', name],
+ capture_output=True)
+ if result.returncode != 0 or not options.quiet:
+ sys.stderr.buffer.write(result.stderr)
+ sys.stdout.buffer.write(result.stdout)
if result.returncode != 0:
print('error: failed to create {} on server'.format(name),
file=sys.stderr)
diff --git a/tests/test_failure_paths.py b/tests/test_failure_paths.py
index 67d60ce..92803ed 100644
--- a/tests/test_failure_paths.py
+++ b/tests/test_failure_paths.py
@@ -71,7 +71,7 @@ def test_initpackage_returns_none_on_repo_init_error(monkeypatch, make_options,
def __init__(self, path):
self.path = path
- def init(self, remotepull, remotepush):
+ def init(self, remotepull, remotepush, **kwargs):
raise slug.GitRepoError("init failed")
monkeypatch.setattr(slug, "GitRepo", BrokenGitRepo)
@@ -82,17 +82,111 @@ def test_initpackage_returns_none_on_repo_init_error(monkeypatch, make_options,
assert "error: failed to init perl-Broken: init failed" in capsys.readouterr().err
+def test_initpackage_propagates_quiet_to_repo_init(monkeypatch, make_options):
+ """Regresja: options.quiet musi dotrzeć do GitRepo.init(), inaczej
+ 'git init' wypisze 'Initialized empty Git repository...' na stderr
+ pomimo --quiet (bo subprocess.call dziedziczy stderr rodzica)."""
+ captured = {}
+
+ class CapturingGitRepo:
+ def __init__(self, path):
+ self.path = path
+
+ def init(self, remotepull, remotepush, **kwargs):
+ captured["remotepull"] = remotepull
+ captured["remotepush"] = remotepush
+ captured["kwargs"] = kwargs
+
+ monkeypatch.setattr(slug, "GitRepo", CapturingGitRepo)
+
+ slug.initpackage("perl-Quiet", make_options(quiet=1))
+
+ assert captured["kwargs"] == {"quiet": 1}
+
+
def test_createpackage_returns_false_when_server_create_fails(monkeypatch, make_options, capsys):
monkeypatch.setattr(
slug.subprocess,
"run",
- lambda argv: SimpleNamespace(returncode=1),
+ lambda argv, **kwargs: SimpleNamespace(returncode=1, stdout=b"", stderr=b""),
)
assert slug.createpackage("perl-Broken", make_options()) is False
assert "error: failed to create perl-Broken on server" in capsys.readouterr().err
+def test_createpackage_relays_server_output_in_normal_mode(
+ monkeypatch, make_options, capsys):
+ """Bez --quiet komunikat zwrotny serwera ('Repository created: ...')
+ musi trafić do operatora — przechwytujemy wyjście ssh i odtwarzamy
+ je 1:1 na lokalny stdout/stderr."""
+ captured = {}
+
+ def fake_run(argv, **kwargs):
+ captured["argv"] = argv
+ captured["kwargs"] = kwargs
+ return SimpleNamespace(
+ returncode=0,
+ stdout=b"Repository created: perl-OK\n",
+ stderr=b"server note\n",
+ )
+
+ monkeypatch.setattr(slug.subprocess, "run", fake_run)
+ monkeypatch.setattr(slug, "initpackage", lambda name, options: object())
+
+ assert slug.createpackage("perl-OK", make_options(quiet=0)) is True
+ assert captured["argv"][0] == "ssh"
+ assert captured["argv"][-1] == "perl-OK"
+ # Always capture so the relay decision is uniform; correctness is
+ # measured by what ends up on stdout/stderr, not by the kwargs.
+ assert captured["kwargs"].get("capture_output") is True
+ out = capsys.readouterr()
+ assert "Repository created: perl-OK" in out.out
+ assert "server note" in out.err
+
+
+def test_createpackage_captures_ssh_output_when_quiet(monkeypatch, make_options, capsys):
+ """Z --quiet ssh musi zostać przechwycony, inaczej komunikat serwera
+ trafi bezpośrednio na terminal omijając kontrakt trybu cichego."""
+ captured = {}
+
+ def fake_run(argv, **kwargs):
+ captured["kwargs"] = kwargs
+ return SimpleNamespace(returncode=0, stdout=b"Repository created\n", stderr=b"")
+
+ monkeypatch.setattr(slug.subprocess, "run", fake_run)
+ monkeypatch.setattr(slug, "initpackage", lambda name, options: object())
+
+ assert slug.createpackage("perl-Quiet", make_options(quiet=1)) is True
+ assert captured["kwargs"].get("capture_output") is True
+ # Sukces w trybie cichym = zero output
+ out = capsys.readouterr()
+ assert out.out == ""
+ assert out.err == ""
+
+
+def test_createpackage_replays_captured_ssh_output_on_failure_when_quiet(
+ monkeypatch, make_options, capsys):
+ """Z --quiet sukces jest cichy, ale porażka musi odtworzyć
+ przechwycony stdout/stderr serwera — bez tego operator nie ma jak
+ zdiagnozować, dlaczego 'ssh create' się nie powiódł."""
+
+ def fake_run(argv, **kwargs):
+ return SimpleNamespace(
+ returncode=2,
+ stdout=b"partial progress\n",
+ stderr=b"create failed: exists\n",
+ )
+
+ monkeypatch.setattr(slug.subprocess, "run", fake_run)
+
+ assert slug.createpackage("perl-Exists", make_options(quiet=1)) is False
+ out = capsys.readouterr()
+ assert "create failed: exists" in out.err
+ assert "partial progress" in out.out
+ assert "error: failed to create perl-Exists on server" in out.err
+
+
def test_getrefs_exits_on_remote_refs_error(monkeypatch, capsys):
def fake_refs(*args):
raise slug.RemoteRefsError("heads", "git://repo")
diff --git a/tests/test_gitrepo.py b/tests/test_gitrepo.py
index d5bfb46..5887bd4 100644
--- a/tests/test_gitrepo.py
+++ b/tests/test_gitrepo.py
@@ -78,17 +78,49 @@ def test_init_gitdir_uses_bare_mode_for_external_gitdir(monkeypatch):
assert calls == [["git", "init", "--bare", "/srv/git/pkg.git"]]
+def test_init_gitdir_passes_q_when_quiet(monkeypatch):
+ """init_gitdir(quiet=True) must pass -q so 'git init' doesn't print
+ the 'Initialized empty Git repository in ...' banner via the
+ inherited stderr. Regression for the --quiet/--new flow."""
+ calls = []
+ monkeypatch.setattr(gitrepo_module.subprocess, "call",
+ lambda clist, **kwargs: calls.append(clist) or 0)
+
+ GitRepo("/work/pkg").init_gitdir(quiet=True)
+ GitRepo("/work/pkg", "/srv/git/pkg.git").init_gitdir(quiet=True)
+
+ assert calls == [
+ ["git", "init", "-q", "/work/pkg"],
+ ["git", "init", "-q", "--bare", "/srv/git/pkg.git"],
+ ]
+
+
+def test_init_gitdir_passes_q_for_any_truthy_quiet(monkeypatch):
+ """quiet threading uses options.quiet (an int: 0/1/2), so any truthy
+ value — including -qq (2) — must add -q."""
+ calls = []
+ monkeypatch.setattr(gitrepo_module.subprocess, "call",
+ lambda clist, **kwargs: calls.append(clist) or 0)
+
+ GitRepo("/work/pkg").init_gitdir(quiet=2)
+
+ assert calls == [["git", "init", "-q", "/work/pkg"]]
+
+
def test_init_warns_when_gitdir_exists_and_sets_remote_config(monkeypatch, capsys):
repo = GitRepo("/work/pkg")
commands = []
+ gitdir_kwargs = {}
monkeypatch.setattr(gitrepo_module.os.path, "isdir", lambda path: True)
- monkeypatch.setattr(repo, "init_gitdir", lambda: None)
+ monkeypatch.setattr(repo, "init_gitdir",
+ lambda **kwargs: gitdir_kwargs.update(kwargs))
monkeypatch.setattr(repo, "commandio", lambda clist: commands.append(clist) or (b"", b""))
repo.init("git://pull/pkg", "ssh://push/pkg")
assert "warning: directory /work/pkg/.git already existed" in capsys.readouterr().err
+ assert gitdir_kwargs == {"quiet": False}
assert commands == [
["remote", "add", "origin", "git://pull/pkg"],
["remote", "set-url", "--push", "origin", "ssh://push/pkg"],
@@ -96,6 +128,22 @@ def test_init_warns_when_gitdir_exists_and_sets_remote_config(monkeypatch, capsy
]
+def test_init_threads_quiet_to_init_gitdir(monkeypatch):
+ """init(quiet=...) must forward to init_gitdir so the -q flag actually
+ reaches the subprocess."""
+ repo = GitRepo("/work/pkg")
+ gitdir_kwargs = {}
+
+ monkeypatch.setattr(gitrepo_module.os.path, "isdir", lambda path: False)
+ monkeypatch.setattr(repo, "init_gitdir",
+ lambda **kwargs: gitdir_kwargs.update(kwargs))
+ monkeypatch.setattr(repo, "commandio", lambda clist: (b"", b""))
+
+ repo.init("git://pull/pkg", "ssh://push/pkg", quiet=1)
+
+ assert gitdir_kwargs == {"quiet": 1}
+
+
def test_check_remote_reads_loose_ref(tmp_path):
repo_dir = tmp_path / "pkg"
gitdir = repo_dir / ".git" / "refs" / "remotes" / "origin"
diff --git a/tests/test_slug_commands.py b/tests/test_slug_commands.py
index faccd76..77eb37f 100644
--- a/tests/test_slug_commands.py
+++ b/tests/test_slug_commands.py
@@ -186,6 +186,55 @@ def test_init_command_succeeds_when_all_packages_are_created(monkeypatch, make_o
assert calls == ["pkg-a", "pkg-b"]
+def test_update_command_no_new_disables_newpkgs(monkeypatch, make_options):
+ """--no-new neguje --new: uopts.new=False → options.newpkgs=False."""
+ captured = {}
+
+ def fake_fetch_packages(options):
+ captured["newpkgs"] = options.newpkgs
+ options.had_errors = False
+ return []
+
+ monkeypatch.setattr(slug, "fetch_packages", fake_fetch_packages)
+
+ slug.update_command(make_options(), ["--new", "--no-new"])
+
+ assert captured["newpkgs"] is False
+
+
+def test_update_command_passes_depth_to_fetch_packages(monkeypatch, make_options):
+ """--depth musi trafić do options.depth — inaczej shallow fetch nie zadziała."""
+ captured = {}
+
+ def fake_fetch_packages(options):
+ captured["depth"] = options.depth
+ options.had_errors = False
+ return []
+
+ monkeypatch.setattr(slug, "fetch_packages", fake_fetch_packages)
+
+ slug.update_command(make_options(), ["--depth", "10"])
+
+ assert captured["depth"] == 10
+
+
+def test_clone_command_passes_depth_to_fetch_packages(monkeypatch, make_options):
+ """clone --depth również musi ustawić options.depth."""
+ captured = {}
+
+ def fake_fetch_packages(options):
+ captured["depth"] = options.depth
+ options.had_errors = False
+ return []
+
+ monkeypatch.setattr(slug, "fetch_packages", fake_fetch_packages)
+ monkeypatch.setattr(slug, "run_worker", lambda *a, **kw: [])
+
+ slug.clone_command(make_options(), ["--depth", "5"])
+
+ assert captured["depth"] == 5
+
+
def test_clone_package_returns_none_after_successful_checkout():
calls = []
================================================================
---- gitweb:
http://git.pld-linux.org/gitweb.cgi/projects/git-slug.git/commitdiff/4a7e426b8f1a3571094b5dc89412bc49b8f29666
More information about the pld-cvs-commit
mailing list