Release Healer Man 0.1.6
This commit is contained in:
@@ -15,6 +15,7 @@ import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
import webbrowser
|
||||
from dataclasses import dataclass
|
||||
@@ -226,6 +227,67 @@ def current_commit() -> str:
|
||||
return ""
|
||||
|
||||
|
||||
def validate_release_candidates(project_root: Path = PROJECT_ROOT) -> list[str]:
|
||||
"""Check tracked and untracked release files without changing the real index."""
|
||||
with tempfile.TemporaryDirectory(prefix="healer-man-release-index-") as temp_dir:
|
||||
environment = os.environ.copy()
|
||||
environment["GIT_INDEX_FILE"] = str(Path(temp_dir) / "index")
|
||||
|
||||
def run_temporary_index_command(*args: str) -> subprocess.CompletedProcess[bytes]:
|
||||
result = subprocess.run(
|
||||
["git", *args],
|
||||
cwd=project_root,
|
||||
env=environment,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
check=False,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
output = result.stdout.decode("utf-8", errors="replace").strip()
|
||||
raise ReleaseError(
|
||||
f"Release-candidate Git check failed ({result.returncode}): "
|
||||
f"git {' '.join(args)}\n{output}"
|
||||
)
|
||||
return result
|
||||
|
||||
run_temporary_index_command("read-tree", "HEAD")
|
||||
run_temporary_index_command("add", "-A")
|
||||
raw_paths = run_temporary_index_command(
|
||||
"diff", "--cached", "--name-only", "-z"
|
||||
).stdout
|
||||
staged_paths = [
|
||||
path.decode("utf-8", errors="replace")
|
||||
for path in raw_paths.split(b"\0")
|
||||
if path
|
||||
]
|
||||
|
||||
whitespace_result = subprocess.run(
|
||||
["git", "diff", "--cached", "--check"],
|
||||
cwd=project_root,
|
||||
env=environment,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
errors="replace",
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
check=False,
|
||||
)
|
||||
if whitespace_result.returncode != 0:
|
||||
detail = whitespace_result.stdout.strip()
|
||||
raise ReleaseError(
|
||||
"Release-candidate whitespace check failed. This check includes "
|
||||
f"tracked and untracked files:\n{detail}"
|
||||
)
|
||||
|
||||
dangerous = find_dangerous_staged_paths(staged_paths)
|
||||
if dangerous:
|
||||
raise ReleaseError(
|
||||
"Refusing to release forbidden output or a possible secret:\n"
|
||||
+ "\n".join(f" - {path}" for path in dangerous)
|
||||
)
|
||||
return staged_paths
|
||||
|
||||
|
||||
def windows_powershell_environment(powershell: str) -> dict[str, str]:
|
||||
environment = os.environ.copy()
|
||||
if Path(powershell).name.lower() == "powershell.exe":
|
||||
@@ -1003,6 +1065,15 @@ def launch_gui(*, smoke_test: bool = False) -> int:
|
||||
credentials: dict[str, str],
|
||||
) -> None:
|
||||
self._preflight_git(require_clean=False)
|
||||
self._log_event(
|
||||
"\n--- Early release-candidate check ---\n"
|
||||
"$ npm run release:check"
|
||||
)
|
||||
candidate_paths = validate_release_candidates()
|
||||
self._log_event(
|
||||
f"✓ Early release-candidate check passed "
|
||||
f"({len(candidate_paths)} changed path(s), including untracked files)"
|
||||
)
|
||||
npm = shutil.which("npm")
|
||||
powershell = shutil.which("powershell.exe") or shutil.which("powershell")
|
||||
if not npm or not powershell:
|
||||
@@ -1034,7 +1105,6 @@ def launch_gui(*, smoke_test: bool = False) -> int:
|
||||
)
|
||||
for command, label in checks:
|
||||
self._run_command(command, label)
|
||||
self._run_command(["git", "diff", "--check"], "Whitespace check")
|
||||
self._run_command(["git", "add", "-A"], "Stage release")
|
||||
staged = True
|
||||
|
||||
@@ -1371,12 +1441,28 @@ def main(argv: Sequence[str] | None = None) -> int:
|
||||
action="store_true",
|
||||
help="print release/version/Git readiness without opening the GUI",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--check-release",
|
||||
action="store_true",
|
||||
help="check all release candidates, including untracked files",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--smoke-test",
|
||||
action="store_true",
|
||||
help=argparse.SUPPRESS,
|
||||
)
|
||||
args = parser.parse_args(argv)
|
||||
if args.check_release:
|
||||
try:
|
||||
paths = validate_release_candidates()
|
||||
print(
|
||||
f"Release candidate check passed: {len(paths)} changed path(s), "
|
||||
"including untracked files."
|
||||
)
|
||||
except Exception as exc:
|
||||
print(f"Release candidate check failed: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
return 0
|
||||
if args.check:
|
||||
try:
|
||||
print(json.dumps(check_summary(), indent=2))
|
||||
|
||||
Reference in New Issue
Block a user