feat: add --update flag to trigger in-place upgrade on demand

Refactors the upgrade logic into _run_upgrade() so it can be called
both from the startup version check prompt and directly via --update,
bypassing the version comparison entirely.
This commit is contained in:
Justin Bollinger
2026-03-19 17:49:23 -04:00
parent 9fba3f7bf6
commit 8483a17242
2 changed files with 92 additions and 28 deletions
+41 -28
View File
@@ -811,6 +811,38 @@ def ascii_art():
)
def _run_upgrade():
"""Run `git pull && make clean && make && make install` in the repo root."""
import subprocess
print()
# Find the actual git repo root - _repo_root may point to
# site-packages when installed rather than the source checkout.
git_root_result = subprocess.run(
["git", "rev-parse", "--show-toplevel"],
cwd=_repo_root,
capture_output=True,
text=True,
)
if git_root_result.returncode != 0:
print(
"\n Could not find a git repository to upgrade from."
"\n Run manually: git pull && make clean && make && make install\n"
)
raise SystemExit(1)
repo_root = git_root_result.stdout.strip()
result = subprocess.run(
"git pull && make clean && make && make install",
shell=True,
cwd=repo_root,
)
if result.returncode == 0:
print("\n Upgrade complete. Please restart hate_crack.\n")
else:
print("\n Upgrade failed. Check the output above for errors.\n")
raise SystemExit(0)
def check_for_updates():
"""Check GitHub for a newer release and print a notice if one exists."""
try:
@@ -842,34 +874,7 @@ def check_for_updates():
print()
return
if answer == "y":
import subprocess
print()
# Find the actual git repo root - _repo_root may point to
# site-packages when installed rather than the source checkout.
git_root_result = subprocess.run(
["git", "rev-parse", "--show-toplevel"],
cwd=_repo_root,
capture_output=True,
text=True,
)
if git_root_result.returncode != 0:
print(
"\n Could not find a git repository to upgrade from."
"\n Run manually: git pull && make clean && make && make install\n"
)
raise SystemExit(1)
repo_root = git_root_result.stdout.strip()
result = subprocess.run(
"git pull && make clean && make && make install",
shell=True,
cwd=repo_root,
)
if result.returncode == 0:
print("\n Upgrade complete. Please restart hate_crack.\n")
else:
print("\n Upgrade failed. Check the output above for errors.\n")
raise SystemExit(0)
_run_upgrade()
except Exception:
pass
@@ -4159,6 +4164,11 @@ def main():
action="store_true",
help="Cleanup .out files, torrents, and extract or remove .7z archives",
)
parser.add_argument(
"--update",
action="store_true",
help="Pull latest changes and reinstall (git pull && make clean && make && make install)",
)
parser.add_argument("--debug", action="store_true", help="Enable debug mode")
parser.add_argument(
"--potfile-path",
@@ -4341,6 +4351,9 @@ def main():
maxruntime = config.maxruntime
bandrelbasewords = config.bandrelbasewords
if args.update:
_run_upgrade()
if args.download_torrent:
download_weakpass_torrent(
download_torrent=download_torrent_file,
+51
View File
@@ -212,6 +212,57 @@ class TestCheckForUpdates:
output = capsys.readouterr().out
assert "Run manually" in output
class TestRunUpgrade:
"""Tests for _run_upgrade() called directly via --update flag."""
def test_run_upgrade_success(self, hc_module, capsys):
git_root_proc = MagicMock()
git_root_proc.returncode = 0
git_root_proc.stdout = "/fake/repo\n"
make_proc = MagicMock()
make_proc.returncode = 0
with patch("subprocess.run", side_effect=[git_root_proc, make_proc]) as mock_run, pytest.raises(SystemExit) as exc:
hc_module._run_upgrade()
assert exc.value.code == 0
assert mock_run.call_count == 2
make_cmd = mock_run.call_args_list[1][0][0]
assert "git pull" in make_cmd
assert "make install" in make_cmd
assert mock_run.call_args_list[1][1]["cwd"] == "/fake/repo"
output = capsys.readouterr().out
assert "Upgrade complete" in output
def test_run_upgrade_make_failure(self, hc_module, capsys):
git_root_proc = MagicMock()
git_root_proc.returncode = 0
git_root_proc.stdout = "/fake/repo\n"
make_proc = MagicMock()
make_proc.returncode = 1
with patch("subprocess.run", side_effect=[git_root_proc, make_proc]), pytest.raises(SystemExit) as exc:
hc_module._run_upgrade()
assert exc.value.code == 0
output = capsys.readouterr().out
assert "Upgrade failed" in output
def test_run_upgrade_no_git_repo(self, hc_module, capsys):
git_root_proc = MagicMock()
git_root_proc.returncode = 128
git_root_proc.stdout = ""
with patch("subprocess.run", return_value=git_root_proc), pytest.raises(SystemExit) as exc:
hc_module._run_upgrade()
assert exc.value.code == 1
output = capsys.readouterr().out
assert "Run manually" in output
def test_upgrade_prompt_ctrl_c_continues(self, hc_module, capsys):
mock_resp = MagicMock()
mock_resp.json.return_value = {"tag_name": "v99.0.0"}