sync: root v0.4

This commit is contained in:
2025-10-29 17:28:32 +01:00
parent 93cbd4eb01
commit deb1cc55fa
3 changed files with 377 additions and 179 deletions
+356
View File
@@ -0,0 +1,356 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
manage_releases.py
Automates synchronization of Git tags with GitHub releases. On each run it:
* Verifies the repository is clean and the current branch is fully pushed.
* Ensures HEAD is tagged by comparing the root version.nfo with existing
release tags and bumping/committing when required.
* Iterates over every tag in the repository, creating GitHub releases for any
missing entries and attaching per-directory ZIP archives (excluding
version.nfo files) that reflect the repository state for that tag.
Dependencies:
- gh CLI (https://cli.github.com/)
- git available in PATH
"""
from __future__ import annotations
import argparse
import io
import os
import re
import shutil
import subprocess
import tarfile
import tempfile
import textwrap
from pathlib import Path
from typing import Dict, Iterable, List, Optional, Sequence, Tuple
from zipfile import ZIP_DEFLATED, ZipFile
def sh(args: Sequence[str], cwd: Path, check: bool = True) -> subprocess.CompletedProcess[str]:
"""Run a subprocess and return the completed process."""
result = subprocess.run(
list(args),
cwd=str(cwd),
text=True,
capture_output=True,
)
if check and result.returncode != 0:
raise RuntimeError(
f"Command failed ({' '.join(args)}):\n"
f"{result.stdout}{result.stderr}"
)
return result
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Synchronize Git tags with GitHub releases.")
parser.add_argument(
"--repo",
default=".",
help="Ścieżka do repozytorium git (domyślnie: bieżący katalog).",
)
parser.add_argument(
"--remote",
default="origin",
help="Nazwa zdalnego repozytorium do pushowania commitów oraz tagów (domyślnie: origin).",
)
parser.add_argument(
"--output-dir",
default="Release packs",
help="Katalog wyjściowy, w którym powstaną ZIP-y uploadowane do release (domyślnie: 'Release packs').",
)
parser.add_argument(
"--notes-template",
default="Automated release for {tag}",
help="Szablon notatek release (dostępne pola: {tag}).",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Tylko plan bez commitów, tagowania i publikacji release.",
)
return parser.parse_args()
def read_text_if_exists(path: Path) -> Optional[str]:
if not path.is_file():
return None
return path.read_text(encoding="utf-8").strip()
def parse_version(version: str) -> Optional[Tuple[int, ...]]:
if not version:
return None
if not re.fullmatch(r"\d+(?:\.\d+)*", version):
return None
return tuple(int(part) for part in version.split("."))
def bump_version(version: str) -> str:
parts = parse_version(version)
if parts is None:
# fallback: append .1 to non-numeric strings
return f"{version}.1"
bumped = list(parts)
bumped[-1] += 1
return ".".join(str(piece) for piece in bumped)
def sanitize_component(value: str) -> str:
value = value.strip()
if not value:
return "unknown"
return re.sub(r"[\\s/\\\\]+", "-", value)
class ReleaseManager:
def __init__(self, args: argparse.Namespace):
self.args = args
self.repo = Path(args.repo).resolve()
self.output_dir = (self.repo / args.output_dir).resolve()
# ------------------------------------------------------------------ helpers
def git(self, *args: str, check: bool = True) -> subprocess.CompletedProcess[str]:
return sh(args, cwd=self.repo, check=check)
def ensure_tooling(self) -> None:
sh(["gh", "--version"], cwd=self.repo, check=True)
def ensure_repo(self) -> None:
if not (self.repo / ".git").exists():
raise RuntimeError(f"{self.repo} nie jest repozytorium git.")
def ensure_clean_state(self) -> None:
status = self.git("git", "status", "--porcelain", check=True).stdout.strip()
if status:
raise RuntimeError(
"Wykryto niezakomitowane zmiany. Ukończ commit lub stash przed publikacją release.\n"
"Release podpisany z nieczystego drzewa może wymagać resetu HEAD do taga."
)
upstream = self.git("git", "rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}", check=False)
if upstream.returncode != 0:
raise RuntimeError(
"Brak skonfigurowanego upstreamu dla bieżącej gałęzi nie można zweryfikować, "
"czy wszystkie commity zostały wypchnięte."
)
ahead = self.git("git", "rev-list", "--count", "@{u}..HEAD", check=True).stdout.strip()
if ahead and int(ahead) > 0:
raise RuntimeError(
"Bieżąca gałąź zawiera niewypchnięte commity. Wypchnij zmiany zanim utworzysz release."
)
def get_head_tag(self) -> Optional[str]:
result = self.git("git", "describe", "--tags", "--exact-match", "HEAD", check=False)
tag = result.stdout.strip()
if result.returncode == 0 and tag:
return tag
return None
def list_tags(self) -> List[str]:
tags = self.git("git", "tag", "--sort=creatordate", check=True).stdout.splitlines()
return [tag.strip() for tag in tags if tag.strip()]
def list_version_tags(self) -> List[str]:
tags = []
for tag in self.list_tags():
if parse_version(tag):
tags.append(tag)
return tags
def read_root_version(self) -> Optional[str]:
return read_text_if_exists(self.repo / "version.nfo")
def write_root_version(self, version: str) -> None:
target = self.repo / "version.nfo"
target.write_text(f"{version}\n", encoding="utf-8")
def commit_root_version(self, version: str) -> None:
if self.args.dry_run:
print(f"[DRY-RUN] git add version.nfo && git commit -m 'release: bump root to v{version}'")
return
self.write_root_version(version)
self.git("git", "add", "version.nfo", check=True)
self.git("git", "commit", "-m", f"release: bump root to v{version}", check=True)
self.git("git", "push", self.args.remote, check=True)
def tag_exists(self, tag: str) -> bool:
result = self.git("git", "rev-parse", "-q", "--verify", f"refs/tags/{tag}", check=False)
return result.returncode == 0
def create_tag(self, tag: str, message: str) -> None:
if self.tag_exists(tag):
print(f"[INFO] Tag {tag} już istnieje pomijam tworzenie.")
return
if self.args.dry_run:
print(f"[DRY-RUN] git tag -a {tag} -m \"{message}\"")
return
self.git("git", "tag", "-a", tag, "-m", message, check=True)
self.git("git", "push", self.args.remote, tag, check=True)
def ensure_head_tagged(self) -> str:
head_tag = self.get_head_tag()
if head_tag:
return head_tag
root_version = self.read_root_version()
root_tuple = parse_version(root_version) if root_version else None
version_tags = self.list_version_tags()
latest_version = version_tags[-1] if version_tags else None
latest_tuple = parse_version(latest_version) if latest_version else None
if root_version and (root_tuple and (latest_tuple is None or root_tuple > latest_tuple)):
new_version = root_version
else:
base_version = latest_version or root_version or "0.0"
new_version = bump_version(base_version)
if not self.args.dry_run:
print(f"[INFO] Bump root version.nfo -> {new_version}")
self.commit_root_version(new_version)
tag_message = f"release {new_version}"
self.create_tag(new_version, tag_message)
return new_version
def gather_directories_for_tag(self, tag: str) -> List[str]:
result = self.git("git", "ls-tree", "-d", "--name-only", tag, check=True)
candidates = []
for line in result.stdout.splitlines():
name = line.strip()
if not name:
continue
candidates.append(name)
return candidates
def version_for_directory(self, tag: str, directory: str) -> str:
show = self.git("git", "show", f"{tag}:{directory}/version.nfo", check=False)
if show.returncode == 0:
version = show.stdout.strip()
if version:
return version
return tag
def build_assets_for_tag(self, tag: str) -> List[Path]:
assets: List[Path] = []
directories = self.gather_directories_for_tag(tag)
if not directories:
print(f"[WARN] Tag {tag} nie zawiera katalogów do zzipowania.")
return assets
tag_output_dir = self.output_dir / sanitize_component(tag)
if not self.args.dry_run:
if tag_output_dir.exists():
shutil.rmtree(tag_output_dir)
tag_output_dir.mkdir(parents=True, exist_ok=True)
for directory in directories:
version = self.version_for_directory(tag, directory)
safe_version = sanitize_component(version)
safe_dir = sanitize_component(directory)
zip_name = f"{safe_dir}-v{safe_version}.zip"
zip_path = tag_output_dir / zip_name
if self.args.dry_run:
print(f"[DRY-RUN] Przygotuj ZIP {zip_path} z taga {tag}:{directory} (pomijając version.nfo)")
assets.append(zip_path)
continue
# Re-run capturing binary properly.
archive_proc = subprocess.run(
["git", "archive", "--format=tar", tag, directory],
cwd=str(self.repo),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
if archive_proc.returncode != 0:
raise RuntimeError(
f"git archive nie powiódł się dla {tag}:{directory}:\n"
f"{archive_proc.stderr.decode('utf-8', errors='ignore')}"
)
with tempfile.TemporaryDirectory() as tmp_dir:
tmp_path = Path(tmp_dir)
with tarfile.open(fileobj=io.BytesIO(archive_proc.stdout)) as tar:
tar.extractall(tmp_path)
extracted_root = tmp_path / directory
for extra in extracted_root.rglob("version.nfo"):
if extra.is_file():
extra.unlink()
with ZipFile(zip_path, "w", ZIP_DEFLATED) as zipf:
for root, _, files in os.walk(extracted_root):
for filename in files:
file_path = Path(root) / filename
rel_path = file_path.relative_to(tmp_path)
zipf.write(file_path, rel_path.as_posix())
assets.append(zip_path)
return assets
def release_exists(self, tag: str) -> bool:
result = self.git("gh", "release", "view", tag, check=False)
return result.returncode == 0
def create_release(self, tag: str, assets: List[Path]) -> None:
notes = self.args.notes_template.format(tag=tag)
wrapped = "\n".join(textwrap.wrap(notes, width=100))
if self.args.dry_run:
print(f"[DRY-RUN] gh release create {tag} {' '.join(str(a) for a in assets)}")
print(f"[DRY-RUN] Notatki:\n{wrapped}")
return
cmd = ["gh", "release", "create", tag, "--title", tag, "--notes", wrapped]
cmd.extend(str(asset) for asset in assets)
result = self.git(*cmd, check=False)
if result.returncode != 0:
raise RuntimeError(
f"Nie udało się utworzyć release {tag}:\n{result.stdout}{result.stderr}"
)
def sync_releases(self) -> None:
tags = self.list_tags()
if not tags:
print("[INFO] Brak tagów do synchronizacji.")
return
self.output_dir.mkdir(parents=True, exist_ok=True)
for tag in tags:
if self.release_exists(tag):
print(f"[INFO] Release {tag} już istnieje pomijam.")
continue
print(f"[INFO] Przygotowuję release dla taga {tag}...")
assets = self.build_assets_for_tag(tag)
self.create_release(tag, assets)
def run(self) -> None:
self.ensure_repo()
self.ensure_tooling()
self.ensure_clean_state()
self.ensure_head_tagged()
self.sync_releases()
def main() -> int:
args = parse_args()
manager = ReleaseManager(args)
try:
manager.run()
except RuntimeError as exc:
print(f"[BŁĄD] {exc}")
return 1
return 0
if __name__ == "__main__":
raise SystemExit(main())
+20 -178
View File
@@ -58,10 +58,6 @@ ZIP_MODE: str = "ALL" # "ALL" | "MODULE_ONLY" | "OFF"
# Katalog wyjściowy na archiwa .zip; jeśli None, użyje SOURCE_BASE_DIR/_zips # Katalog wyjściowy na archiwa .zip; jeśli None, użyje SOURCE_BASE_DIR/_zips
ZIP_OUTPUT_DIR: Optional[str] = r"C:\Release packs" ZIP_OUTPUT_DIR: Optional[str] = r"C:\Release packs"
CHECK_MODE: bool = False # tylko walidacja brak zmian, wyjście !=0 jeśli są akcje CHECK_MODE: bool = False # tylko walidacja brak zmian, wyjście !=0 jeśli są akcje
ENABLE_GH_RELEASE: bool = False # publikowanie release przez gh CLI
RELEASE_TAG_PREFIX: Optional[str] = None # prefiks taga release (np. "module/")
RELEASE_NOTES_TEMPLATE: str = "Automated release for {name} v{version}"
GIT_REMOTE_NAME: str = "origin"
# ========================== # ==========================
# KONIEC KONFIGURACJI # KONIEC KONFIGURACJI
@@ -128,87 +124,6 @@ def git_changed_top_level_dirs(repo_dir: Path) -> List[str]:
return sorted(changed) return sorted(changed)
def git_current_head(repo_dir: Path) -> str:
rc, out = sh(["git", "rev-parse", "HEAD"], repo_dir)
if rc != 0:
raise RuntimeError(f"Nie mogę ustalić HEAD w {norm(repo_dir)}:\n{out}")
return out.strip()
def git_tag_exists(repo_dir: Path, tag: str) -> bool:
rc, _ = sh(["git", "rev-parse", "-q", "--verify", f"refs/tags/{tag}"], repo_dir)
return rc == 0
def create_git_tag(repo_dir: Path, tag: str, message: str, commit: Optional[str]) -> None:
if git_tag_exists(repo_dir, tag):
print(f"[INFO] Tag {tag} już istnieje pomijam tworzenie.")
return
cmd = ["git", "tag", "-a", tag]
if commit:
cmd.append(commit)
cmd += ["-m", message]
rc, out = sh(cmd, repo_dir)
if rc != 0:
raise RuntimeError(f"Nie udało się utworzyć taga {tag}:\n{out}")
if out.strip():
print(out)
def push_git_tag(repo_dir: Path, tag: str) -> None:
remote = getattr(ARGS, "git_remote", GIT_REMOTE_NAME)
rc, out = sh(["git", "push", remote, tag], repo_dir)
if rc != 0:
raise RuntimeError(f"Nie udało się wypchnąć taga {tag}:\n{out}")
if out.strip():
print(out)
def ensure_gh_available(repo_dir: Path) -> None:
rc, out = sh(["gh", "--version"], repo_dir)
if rc != 0:
raise RuntimeError("Polecenie 'gh' nie jest dostępne w PATH:\n" + out)
def github_release_exists(repo_dir: Path, tag: str) -> bool:
rc, _ = sh(["gh", "release", "view", tag], repo_dir)
return rc == 0
def publish_github_release(repo_dir: Path, tag: str, title: str, notes: str, assets: List[Path]) -> None:
ensure_gh_available(repo_dir)
assets_str = [str(p) for p in assets]
if github_release_exists(repo_dir, tag):
if not assets_str:
print(f"[INFO] Release {tag} już istnieje brak nowych assetów do przesłania.")
return
cmd = ["gh", "release", "upload", tag, *assets_str, "--clobber"]
rc, out = sh(cmd, repo_dir)
if rc != 0:
raise RuntimeError(f"Nie udało się zaktualizować release {tag}:\n{out}")
if out.strip():
print(out)
return
cmd = ["gh", "release", "create", tag, *assets_str, "--title", title, "--notes", notes]
rc, out = sh(cmd, repo_dir)
if rc != 0:
raise RuntimeError(f"Nie udało się utworzyć release {tag}:\n{out}")
if out.strip():
print(out)
def build_release_tag(name: str, version: str) -> str:
safe_name = name.replace(" ", "-").replace("/", "-")
base = f"{safe_name}-v{version}"
prefix = getattr(ARGS, "release_tag_prefix", RELEASE_TAG_PREFIX)
if prefix:
return f"{prefix}{base}"
return base
def find_modules_dir_for_user(user: str, def find_modules_dir_for_user(user: str,
relative_pattern: str, relative_pattern: str,
appdata_kind: Optional[str]) -> Path: appdata_kind: Optional[str]) -> Path:
@@ -392,12 +307,12 @@ def write_json_atomic(path: Path, data: dict) -> None:
tmp.replace(path) tmp.replace(path)
def run_git_commands(repo_dir: Path, commit_msg: str, dry_run: bool) -> Optional[str]: def run_git_commands(repo_dir: Path, commit_msg: str, dry_run: bool) -> None:
if dry_run: if dry_run:
print("[DRY-RUN] git add -A") print("[DRY-RUN] git add -A")
print(f"[DRY-RUN] git commit -m {commit_msg!r}") print(f"[DRY-RUN] git commit -m {commit_msg!r}")
print("[DRY-RUN] git push") print("[DRY-RUN] git push")
return None return
def run(cmd: List[str]) -> None: def run(cmd: List[str]) -> None:
rc, out = sh(cmd, repo_dir) rc, out = sh(cmd, repo_dir)
@@ -413,12 +328,10 @@ def run_git_commands(repo_dir: Path, commit_msg: str, dry_run: bool) -> Optional
if out.strip(): if out.strip():
print(out) print(out)
run(["git", "push"]) run(["git", "push"])
head = git_current_head(repo_dir)
return head
def preflight_module(src_dir: Path, dry_run: bool) -> Tuple[str, str, Optional[str]]: def preflight_module(src_dir: Path, dry_run: bool) -> Tuple[str, str]:
""" """
Moduł: odczyt module.json, bump, zapis (jeśli nie dry_run), commit i push. Moduł: odczyt module.json, bump, zapis (jeśli nie dry_run), commit i push.
Zwraca (module_id, new_version, commit_sha lub None przy dry-run). Zwraca (module_id, new_version, commit_sha lub None przy dry-run).
@@ -435,20 +348,18 @@ def preflight_module(src_dir: Path, dry_run: bool) -> Tuple[str, str, Optional[s
print(f"[PRE][MODULE] {src_dir.name}: id={module_id}, version={old_version} -> {new_version}") print(f"[PRE][MODULE] {src_dir.name}: id={module_id}, version={old_version} -> {new_version}")
commit_sha: Optional[str] = None
if dry_run: if dry_run:
print("[DRY-RUN] Nie zapisuję module.json i nie wykonuję git.") print("[DRY-RUN] Nie zapisuję module.json i nie wykonuję git.")
else: else:
manifest["version"] = new_version manifest["version"] = new_version
write_json_atomic(manifest_path, manifest) write_json_atomic(manifest_path, manifest)
commit_msg = f"sync: {module_id} v{new_version}" commit_msg = f"sync: {module_id} v{new_version}"
commit_sha = run_git_commands(src_dir, commit_msg, dry_run=False) run_git_commands(src_dir, commit_msg, dry_run=False)
return module_id, new_version, commit_sha return module_id, new_version
def preflight_non_module(src_dir: Path, dry_run: bool) -> Tuple[str, Optional[str]]: def preflight_non_module(src_dir: Path, dry_run: bool) -> str:
""" """
Niemoduł: version.nfo w formacie '0.x' (x int). Niemoduł: version.nfo w formacie '0.x' (x int).
- jeśli brak -> '0.1' - jeśli brak -> '0.1'
@@ -457,7 +368,6 @@ def preflight_non_module(src_dir: Path, dry_run: bool) -> Tuple[str, Optional[st
Zwraca (new_version, commit_sha lub None przy dry-run). Zwraca (new_version, commit_sha lub None przy dry-run).
""" """
nfo = src_dir / "version.nfo" nfo = src_dir / "version.nfo"
commit_sha: Optional[str] = None
if not nfo.exists(): if not nfo.exists():
new_version = "0.1" new_version = "0.1"
print(f"[PRE][FOLDER] {src_dir.name}: version.nfo brak -> {new_version}") print(f"[PRE][FOLDER] {src_dir.name}: version.nfo brak -> {new_version}")
@@ -465,8 +375,8 @@ def preflight_non_module(src_dir: Path, dry_run: bool) -> Tuple[str, Optional[st
with nfo.open("w", encoding="utf-8") as f: with nfo.open("w", encoding="utf-8") as f:
f.write(new_version + "\n") f.write(new_version + "\n")
commit_msg = f"sync: {src_dir.name} v{new_version}" commit_msg = f"sync: {src_dir.name} v{new_version}"
commit_sha = run_git_commands(src_dir, commit_msg, dry_run=False) run_git_commands(src_dir, commit_msg, dry_run=False)
return new_version, commit_sha return new_version
# istnieje -> wczytaj i bumpnij # istnieje -> wczytaj i bumpnij
content = nfo.read_text(encoding="utf-8").strip() content = nfo.read_text(encoding="utf-8").strip()
@@ -486,15 +396,13 @@ def preflight_non_module(src_dir: Path, dry_run: bool) -> Tuple[str, Optional[st
with nfo.open("w", encoding="utf-8") as f: with nfo.open("w", encoding="utf-8") as f:
f.write(new_version + "\n") f.write(new_version + "\n")
commit_msg = f"sync: {src_dir.name} v{new_version}" commit_msg = f"sync: {src_dir.name} v{new_version}"
commit_sha = run_git_commands(src_dir, commit_msg, dry_run=False) run_git_commands(src_dir, commit_msg, dry_run=False)
return new_version, commit_sha return new_version
# ====== ROOT version.nfo (globalna wersja repo) ====== # ====== ROOT version.nfo (globalna wersja repo) ======
def maybe_bump_root_version( def maybe_bump_root_version(repo_dir: Path, changed_any: bool, dry_run: bool) -> Optional[str]:
repo_dir: Path, changed_any: bool, dry_run: bool
) -> Optional[Tuple[str, Optional[str]]]:
""" """
Jeśli: Jeśli:
- istnieją zmiany do przetworzenia (changed_any == True) - istnieją zmiany do przetworzenia (changed_any == True)
@@ -530,26 +438,11 @@ def maybe_bump_root_version(
if dry_run: if dry_run:
print("[DRY-RUN][ROOT] Zapis version.nfo i commit/push zostaną pominięte.") print("[DRY-RUN][ROOT] Zapis version.nfo i commit/push zostaną pominięte.")
return new_version, None return new_version
nfo.write_text(new_version + "\n", encoding="utf-8") nfo.write_text(new_version + "\n", encoding="utf-8")
commit_sha = run_git_commands(repo_dir, f"sync: root v{new_version}", dry_run=False) run_git_commands(repo_dir, f"sync: root v{new_version}", dry_run=False)
return new_version, commit_sha return new_version
def maybe_publish_root_release(repo_dir: Path, version: str, commit_sha: Optional[str], dry_run: bool) -> None:
if not ARGS.gh_release or dry_run:
return
name = repo_dir.name or "root"
tag = build_release_tag(name, version)
title = f"{name} v{version}"
template = getattr(ARGS, "release_notes_template", RELEASE_NOTES_TEMPLATE)
notes = template.format(name=name, version=version, tag=tag)
if commit_sha is None:
commit_sha = git_current_head(repo_dir)
create_git_tag(repo_dir, tag, title, commit_sha)
push_git_tag(repo_dir, tag)
publish_github_release(repo_dir, tag, title, notes, [])
# ====== ZIP utilities ====== # ====== ZIP utilities ======
@@ -636,22 +529,6 @@ def parse_args() -> argparse.Namespace:
help="Tylko sprawdzenie zmian (wymusza dry-run, kod wyjścia 1 przy wymaganych akcjach).") help="Tylko sprawdzenie zmian (wymusza dry-run, kod wyjścia 1 przy wymaganych akcjach).")
p.set_defaults(check=CHECK_MODE) p.set_defaults(check=CHECK_MODE)
# GitHub Release options
release_group = p.add_mutually_exclusive_group()
release_group.add_argument("--gh-release", dest="gh_release", action="store_true",
help="Po zakończeniu commitów utwórz release na GitHubie (wymaga gh CLI).")
release_group.add_argument("--no-gh-release", dest="gh_release", action="store_false",
help="Wyłącz publikowanie release (domyślne).")
p.set_defaults(gh_release=ENABLE_GH_RELEASE)
p.add_argument("--release-tag-prefix", dest="release_tag_prefix",
default=RELEASE_TAG_PREFIX,
help="Opcjonalny prefiks dodawany przed tagiem release (np. 'module/').")
p.add_argument("--release-notes-template", dest="release_notes_template",
default=RELEASE_NOTES_TEMPLATE,
help="Szablon notatek release (format: name, version, tag).")
p.add_argument("--git-remote", dest="git_remote",
default=GIT_REMOTE_NAME,
help="Nazwa zdalnego repozytorium do wypychania tagów (domyślnie 'origin').")
return p.parse_args() return p.parse_args()
@@ -667,7 +544,7 @@ def resolve_modules_base(user: str, relative_pattern: str, appdata_kind: Optiona
def process_module(src_dir: Path, modules_base: Path, confirm_delete: bool, dry_run: bool) -> None: def process_module(src_dir: Path, modules_base: Path, confirm_delete: bool, dry_run: bool) -> None:
module_id, new_ver, commit_sha = preflight_module(src_dir, dry_run=dry_run) module_id, new_ver = preflight_module(src_dir, dry_run=dry_run)
target_dir = modules_base / src_dir.name target_dir = modules_base / src_dir.name
print(f"[INFO][MODULE] Cel modułu: {norm(target_dir)}") print(f"[INFO][MODULE] Cel modułu: {norm(target_dir)}")
@@ -700,21 +577,8 @@ def process_module(src_dir: Path, modules_base: Path, confirm_delete: bool, dry_
print(f"[DONE][MODULE] {src_dir.name} -> commit 'sync: {module_id} v{new_ver}' wykonany." if not dry_run print(f"[DONE][MODULE] {src_dir.name} -> commit 'sync: {module_id} v{new_ver}' wykonany." if not dry_run
else f"[DRY-RUN][MODULE] {src_dir.name} -> commit planowany.") else f"[DRY-RUN][MODULE] {src_dir.name} -> commit planowany.")
if ARGS.gh_release and not dry_run:
tag = build_release_tag(module_id, new_ver)
title = f"{module_id} v{new_ver}"
template = getattr(ARGS, "release_notes_template", RELEASE_NOTES_TEMPLATE)
notes = template.format(name=module_id, version=new_ver, tag=tag)
assets = [zip_path] if zip_path else []
if commit_sha is None:
commit_sha = git_current_head(src_dir)
create_git_tag(src_dir, tag, title, commit_sha)
push_git_tag(src_dir, tag)
publish_github_release(src_dir, tag, title, notes, assets)
def process_non_module(src_dir: Path, dry_run: bool) -> None: def process_non_module(src_dir: Path, dry_run: bool) -> None:
new_ver, commit_sha = preflight_non_module(src_dir, dry_run=dry_run) new_ver = preflight_non_module(src_dir, dry_run=dry_run)
# ZIP po commicie (jeśli ustawienie pozwala) # ZIP po commicie (jeśli ustawienie pozwala)
zip_path: Optional[Path] = None zip_path: Optional[Path] = None
if should_zip(is_module=False, zip_mode=ARGS.zip_mode): if should_zip(is_module=False, zip_mode=ARGS.zip_mode):
@@ -726,20 +590,6 @@ def process_non_module(src_dir: Path, dry_run: bool) -> None:
print(f"[DONE][FOLDER] {src_dir.name} -> version.nfo = {new_ver}; commit wykonany." if not dry_run print(f"[DONE][FOLDER] {src_dir.name} -> version.nfo = {new_ver}; commit wykonany." if not dry_run
else f"[DRY-RUN][FOLDER] {src_dir.name} -> version.nfo będzie {new_ver}; commit planowany.") else f"[DRY-RUN][FOLDER] {src_dir.name} -> version.nfo będzie {new_ver}; commit planowany.")
if ARGS.gh_release and not dry_run:
name = src_dir.name
tag = build_release_tag(name, new_ver)
title = f"{name} v{new_ver}"
template = getattr(ARGS, "release_notes_template", RELEASE_NOTES_TEMPLATE)
notes = template.format(name=name, version=new_ver, tag=tag)
assets = [zip_path] if zip_path else []
if commit_sha is None:
commit_sha = git_current_head(src_dir)
create_git_tag(src_dir, tag, title, commit_sha)
push_git_tag(src_dir, tag)
publish_github_release(src_dir, tag, title, notes, assets)
def main() -> int: def main() -> int:
global ARGS global ARGS
ARGS = parse_args() ARGS = parse_args()
@@ -748,10 +598,6 @@ def main() -> int:
if check_mode: if check_mode:
ARGS.dry_run = True ARGS.dry_run = True
ARGS.release_tag_prefix = (ARGS.release_tag_prefix or "").strip() or None
ARGS.release_notes_template = ARGS.release_notes_template or RELEASE_NOTES_TEMPLATE
ARGS.git_remote = ARGS.git_remote or GIT_REMOTE_NAME
mode = ARGS.mode mode = ARGS.mode
user = ARGS.user user = ARGS.user
appdata_kind = ARGS.appdata_kind if ARGS.appdata_kind != "None" else None appdata_kind = ARGS.appdata_kind if ARGS.appdata_kind != "None" else None
@@ -777,11 +623,9 @@ def main() -> int:
if not src_dir.is_dir(): if not src_dir.is_dir():
raise FileNotFoundError(f"[SINGLE] Brak katalogu: {norm(src_dir)}") raise FileNotFoundError(f"[SINGLE] Brak katalogu: {norm(src_dir)}")
root_info = maybe_bump_root_version(source_base, changed_any=True, dry_run=dry_run) root_bumped = maybe_bump_root_version(source_base, changed_any=True, dry_run=dry_run)
if root_info: if root_bumped:
pending_actions = True pending_actions = True
root_version, root_commit = root_info
maybe_publish_root_release(source_base, root_version, root_commit, dry_run)
pending_actions = True pending_actions = True
@@ -802,11 +646,9 @@ def main() -> int:
if changed: if changed:
pending_actions = True pending_actions = True
root_info = maybe_bump_root_version(source_base, changed_any=bool(changed), dry_run=dry_run) root_bumped = maybe_bump_root_version(source_base, changed_any=bool(changed), dry_run=dry_run)
if root_info: if root_bumped:
pending_actions = True pending_actions = True
root_version, root_commit = root_info
maybe_publish_root_release(source_base, root_version, root_commit, dry_run)
if not changed: if not changed:
print("[INFO] Brak zmian do przetworzenia.") print("[INFO] Brak zmian do przetworzenia.")
+1 -1
View File
@@ -1 +1 @@
0.3 0.4