forked from phenom/healer-man
Update Healer Man server
This commit is contained in:
1 parent
9c5fb4c614
commit
bd073fb00f
113 files changed
+4902
-273
No files matched your search
+245
-115
@@ -555,13 +555,13 @@ def launch_gui(*, smoke_test: bool = False) -> int:
|
||||
from tkinter import filedialog, messagebox, scrolledtext, ttk
|
||||
|
||||
class ReleaseManagerApp:
|
||||
BG = "#0c1515"
|
||||
PANEL = "#122222"
|
||||
PANEL_2 = "#172b2a"
|
||||
TEXT = "#e9f4ef"
|
||||
MUTED = "#91aaa2"
|
||||
ACCENT = "#63d9a4"
|
||||
ACCENT_DARK = "#183b31"
|
||||
BG = "#0b1220"
|
||||
PANEL = "#172337"
|
||||
PANEL_2 = "#22344d"
|
||||
TEXT = "#f2f6fc"
|
||||
MUTED = "#aabbd0"
|
||||
ACCENT = "#63dfb5"
|
||||
ACCENT_DARK = "#214a47"
|
||||
WARNING = "#f2c66d"
|
||||
ERROR = "#ff8b83"
|
||||
|
||||
@@ -580,63 +580,66 @@ def launch_gui(*, smoke_test: bool = False) -> int:
|
||||
|
||||
def _configure_window(self) -> None:
|
||||
self.root.title("Healer Man Release Manager")
|
||||
self.root.geometry("1120x820")
|
||||
self.root.minsize(960, 700)
|
||||
width = min(1100, max(700, self.root.winfo_screenwidth() - 60))
|
||||
height = min(740, max(500, self.root.winfo_screenheight() - 100))
|
||||
self.root.geometry(f"{width}x{height}")
|
||||
self.root.minsize(700, 500)
|
||||
self.root.configure(bg=self.BG)
|
||||
style = ttk.Style(self.root)
|
||||
try:
|
||||
style.theme_use("clam")
|
||||
except tk.TclError:
|
||||
pass
|
||||
style.configure(".", background=self.BG, foreground=self.TEXT)
|
||||
style.configure(".", background=self.BG, foreground=self.TEXT, font=("Segoe UI", 10))
|
||||
style.configure("TFrame", background=self.BG)
|
||||
style.configure("Panel.TFrame", background=self.PANEL)
|
||||
style.configure(
|
||||
"Title.TLabel",
|
||||
background=self.BG,
|
||||
foreground=self.TEXT,
|
||||
font=("Segoe UI Semibold", 20),
|
||||
)
|
||||
style.configure(
|
||||
"Sub.TLabel",
|
||||
background=self.BG,
|
||||
foreground=self.MUTED,
|
||||
font=("Segoe UI", 10),
|
||||
)
|
||||
style.configure(
|
||||
"Panel.TLabel", background=self.PANEL, foreground=self.TEXT
|
||||
)
|
||||
style.configure(
|
||||
"Muted.Panel.TLabel", background=self.PANEL, foreground=self.MUTED
|
||||
)
|
||||
style.configure(
|
||||
"Section.Panel.TLabel",
|
||||
background=self.PANEL,
|
||||
foreground=self.TEXT,
|
||||
font=("Segoe UI Semibold", 13),
|
||||
)
|
||||
style.configure(
|
||||
"Accent.TButton",
|
||||
background=self.ACCENT,
|
||||
foreground="#06110d",
|
||||
foreground="#082019",
|
||||
font=("Segoe UI Semibold", 10),
|
||||
padding=(13, 9),
|
||||
padding=(16, 11),
|
||||
)
|
||||
style.map("Accent.TButton", background=[("active", "#84e7bb")])
|
||||
style.configure("TButton", padding=(10, 7))
|
||||
style.map("Accent.TButton", background=[("active", "#8af1cc"), ("disabled", self.PANEL_2)])
|
||||
style.configure("TButton", background=self.PANEL_2, foreground=self.TEXT, padding=(12, 8))
|
||||
style.map("TButton", background=[("active", "#314866")], foreground=[("disabled", self.MUTED)])
|
||||
style.configure("Panel.TCheckbutton", background=self.PANEL, foreground=self.TEXT)
|
||||
style.map("Panel.TCheckbutton", background=[("active", self.PANEL)])
|
||||
style.configure("Panel.TRadiobutton", background=self.PANEL, foreground=self.TEXT)
|
||||
style.map("Panel.TRadiobutton", background=[("active", self.PANEL)])
|
||||
style.configure(
|
||||
"TEntry",
|
||||
fieldbackground="#0a1716",
|
||||
fieldbackground="#101b2b",
|
||||
foreground=self.TEXT,
|
||||
insertcolor=self.TEXT,
|
||||
bordercolor="#31504a",
|
||||
bordercolor="#3b5068",
|
||||
padding=6,
|
||||
)
|
||||
style.configure(
|
||||
"TCombobox",
|
||||
fieldbackground="#0a1716",
|
||||
fieldbackground="#101b2b",
|
||||
foreground=self.TEXT,
|
||||
arrowcolor=self.TEXT,
|
||||
)
|
||||
style.configure("Horizontal.TProgressbar", background=self.ACCENT, troughcolor=self.PANEL_2)
|
||||
style.configure("TNotebook", background=self.PANEL, borderwidth=0)
|
||||
style.configure(
|
||||
"TNotebook.Tab",
|
||||
background=self.PANEL_2,
|
||||
foreground=self.MUTED,
|
||||
padding=(12, 7),
|
||||
padding=(17, 9),
|
||||
)
|
||||
style.map(
|
||||
"TNotebook.Tab",
|
||||
@@ -645,33 +648,81 @@ def launch_gui(*, smoke_test: bool = False) -> int:
|
||||
)
|
||||
|
||||
def _build_ui(self) -> None:
|
||||
shell = ttk.Frame(self.root, padding=18)
|
||||
shell = ttk.Frame(self.root, padding=10)
|
||||
shell.pack(fill="both", expand=True)
|
||||
shell.columnconfigure(0, weight=3)
|
||||
shell.columnconfigure(1, weight=2)
|
||||
shell.columnconfigure(0, weight=1)
|
||||
shell.rowconfigure(2, weight=1)
|
||||
|
||||
title = ttk.Frame(shell)
|
||||
title.grid(row=0, column=0, columnspan=2, sticky="ew", pady=(0, 14))
|
||||
title.columnconfigure(0, weight=1)
|
||||
ttk.Label(title, text="Healer Man Release Manager", style="Title.TLabel").grid(
|
||||
row=0, column=0, sticky="w"
|
||||
mode_panel = ttk.Frame(shell, style="Panel.TFrame", padding=(10, 6))
|
||||
mode_panel.grid(row=0, column=0, sticky="ew", pady=(0, 8))
|
||||
mode_panel.columnconfigure(3, weight=1)
|
||||
ttk.Label(mode_panel, text="Mode", style="Muted.Panel.TLabel").grid(
|
||||
row=0, column=0, sticky="w", padx=(0, 12)
|
||||
)
|
||||
ttk.Label(
|
||||
title,
|
||||
text="Validate → inspect → commit → push → deploy, with optional signed APK",
|
||||
style="Sub.TLabel",
|
||||
).grid(row=1, column=0, sticky="w", pady=(2, 0))
|
||||
self.refresh_button = ttk.Button(title, text="Refresh", command=self.refresh)
|
||||
self.refresh_button.grid(row=0, column=1, rowspan=2, sticky="e")
|
||||
self.git_truenas_only_var = tk.BooleanVar(value=DEFAULT_GIT_TRUENAS_ONLY)
|
||||
self.release_mode_toggle = ttk.Radiobutton(
|
||||
mode_panel,
|
||||
text="Git + TrueNAS",
|
||||
variable=self.git_truenas_only_var,
|
||||
value=True,
|
||||
command=self._update_release_mode,
|
||||
style="Panel.TRadiobutton",
|
||||
)
|
||||
self.release_mode_toggle.grid(row=0, column=1, sticky="w", padx=(0, 18))
|
||||
self.apk_mode_toggle = ttk.Radiobutton(
|
||||
mode_panel,
|
||||
text="Include signed APK",
|
||||
variable=self.git_truenas_only_var,
|
||||
value=False,
|
||||
command=self._update_release_mode,
|
||||
style="Panel.TRadiobutton",
|
||||
)
|
||||
self.apk_mode_toggle.grid(row=0, column=2, sticky="w")
|
||||
self.refresh_button = ttk.Button(mode_panel, text="Refresh", command=self.refresh)
|
||||
self.refresh_button.grid(row=0, column=4, sticky="e")
|
||||
self.git_summary_var = tk.StringVar(value="Loading Git status…")
|
||||
|
||||
release_panel = ttk.Frame(shell, style="Panel.TFrame", padding=15)
|
||||
release_panel.grid(row=1, column=0, sticky="nsew", padx=(0, 8))
|
||||
setup_host = ttk.Frame(shell)
|
||||
setup_host.grid(row=1, column=0, sticky="nsew")
|
||||
setup_host.columnconfigure(0, weight=1)
|
||||
setup_host.rowconfigure(0, weight=1)
|
||||
self.setup_canvas = tk.Canvas(
|
||||
setup_host, bg=self.BG, highlightthickness=0, borderwidth=0, height=220
|
||||
)
|
||||
self.setup_canvas.grid(row=0, column=0, sticky="nsew")
|
||||
setup_scrollbar = ttk.Scrollbar(
|
||||
setup_host, orient="vertical", command=self.setup_canvas.yview
|
||||
)
|
||||
setup_scrollbar.grid(row=0, column=1, sticky="ns", padx=(4, 0))
|
||||
self.setup_canvas.configure(yscrollcommand=setup_scrollbar.set)
|
||||
self.setup_content = ttk.Frame(self.setup_canvas)
|
||||
self.setup_content.columnconfigure(0, weight=3)
|
||||
self.setup_content.columnconfigure(1, weight=2)
|
||||
self.setup_window = self.setup_canvas.create_window(
|
||||
(0, 0), window=self.setup_content, anchor="nw"
|
||||
)
|
||||
self.setup_canvas.bind(
|
||||
"<Configure>",
|
||||
lambda event: self.setup_canvas.itemconfigure(self.setup_window, width=event.width),
|
||||
)
|
||||
self.setup_content.bind(
|
||||
"<Configure>",
|
||||
lambda _event: self.setup_canvas.configure(
|
||||
scrollregion=self.setup_canvas.bbox("all")
|
||||
),
|
||||
)
|
||||
self.setup_canvas.bind(
|
||||
"<MouseWheel>",
|
||||
self._scroll_setup,
|
||||
)
|
||||
|
||||
release_panel = ttk.Frame(self.setup_content, style="Panel.TFrame", padding=12)
|
||||
release_panel.grid(row=0, column=0, sticky="nsew")
|
||||
self.release_panel = release_panel
|
||||
release_panel.columnconfigure(1, weight=1)
|
||||
ttk.Label(
|
||||
release_panel, text="Release details", style="Panel.TLabel",
|
||||
font=("Segoe UI Semibold", 12),
|
||||
).grid(row=0, column=0, columnspan=4, sticky="w", pady=(0, 11))
|
||||
release_panel, text="Release details", style="Section.Panel.TLabel",
|
||||
).grid(row=0, column=0, columnspan=4, sticky="w", pady=(0, 8))
|
||||
|
||||
self.current_version_var = tk.StringVar(value="—")
|
||||
self.current_code_var = tk.StringVar(value="—")
|
||||
@@ -679,11 +730,7 @@ def launch_gui(*, smoke_test: bool = False) -> int:
|
||||
self.next_version_var = tk.StringVar()
|
||||
self.next_code_var = tk.StringVar()
|
||||
self.commit_message_var = tk.StringVar()
|
||||
self.git_truenas_only_var = tk.BooleanVar(
|
||||
value=DEFAULT_GIT_TRUENAS_ONLY
|
||||
)
|
||||
self.run_tests_var = tk.BooleanVar(value=DEFAULT_RUN_TESTS)
|
||||
self.release_mode_hint_var = tk.StringVar()
|
||||
self.test_mode_hint_var = tk.StringVar()
|
||||
|
||||
ttk.Label(release_panel, text="Current", style="Muted.Panel.TLabel").grid(
|
||||
@@ -700,7 +747,7 @@ def launch_gui(*, smoke_test: bool = False) -> int:
|
||||
)
|
||||
|
||||
ttk.Label(release_panel, text="Bump", style="Muted.Panel.TLabel").grid(
|
||||
row=2, column=0, sticky="w", pady=(12, 0)
|
||||
row=2, column=0, sticky="w", pady=(7, 0)
|
||||
)
|
||||
self.bump_box = ttk.Combobox(
|
||||
release_panel,
|
||||
@@ -709,7 +756,7 @@ def launch_gui(*, smoke_test: bool = False) -> int:
|
||||
state="readonly",
|
||||
width=10,
|
||||
)
|
||||
self.bump_box.grid(row=2, column=1, sticky="w", padx=(8, 20), pady=(12, 0))
|
||||
self.bump_box.grid(row=2, column=1, sticky="w", padx=(8, 20), pady=(7, 0))
|
||||
self.bump_box.bind("<<ComboboxSelected>>", lambda _event: self._suggest_versions())
|
||||
ttk.Label(release_panel, text="Next version", style="Muted.Panel.TLabel").grid(
|
||||
row=3, column=0, sticky="w", pady=(9, 0)
|
||||
@@ -740,9 +787,9 @@ def launch_gui(*, smoke_test: bool = False) -> int:
|
||||
)
|
||||
self.release_notes = tk.Text(
|
||||
release_panel,
|
||||
height=4,
|
||||
height=3,
|
||||
wrap="word",
|
||||
bg="#0a1716",
|
||||
bg="#101b2b",
|
||||
fg=self.TEXT,
|
||||
insertbackground=self.TEXT,
|
||||
relief="flat",
|
||||
@@ -753,48 +800,40 @@ def launch_gui(*, smoke_test: bool = False) -> int:
|
||||
self.release_notes.grid(
|
||||
row=5, column=1, columnspan=3, sticky="nsew", padx=(8, 0), pady=(9, 0)
|
||||
)
|
||||
self.release_mode_toggle = ttk.Checkbutton(
|
||||
release_panel,
|
||||
text="Git + TrueNAS only (skip Android version bump and APK publication)",
|
||||
variable=self.git_truenas_only_var,
|
||||
command=self._update_release_mode,
|
||||
)
|
||||
self.release_mode_toggle.grid(
|
||||
row=6, column=0, columnspan=4, sticky="w", pady=(12, 0)
|
||||
)
|
||||
ttk.Label(
|
||||
release_panel,
|
||||
textvariable=self.release_mode_hint_var,
|
||||
style="Muted.Panel.TLabel",
|
||||
wraplength=610,
|
||||
).grid(row=7, column=0, columnspan=4, sticky="w", pady=(4, 0))
|
||||
self.run_tests_toggle = ttk.Checkbutton(
|
||||
release_panel,
|
||||
text="Run test suites (recommended)",
|
||||
text="Run all test suites before release",
|
||||
variable=self.run_tests_var,
|
||||
command=self._update_test_mode,
|
||||
style="Panel.TCheckbutton",
|
||||
)
|
||||
self.run_tests_toggle.grid(
|
||||
row=8, column=0, columnspan=4, sticky="w", pady=(10, 0)
|
||||
row=6, column=0, columnspan=4, sticky="w", pady=(10, 0)
|
||||
)
|
||||
ttk.Label(
|
||||
release_panel,
|
||||
textvariable=self.test_mode_hint_var,
|
||||
style="Muted.Panel.TLabel",
|
||||
wraplength=610,
|
||||
).grid(row=9, column=0, columnspan=4, sticky="w", pady=(4, 0))
|
||||
wraplength=620,
|
||||
).grid(row=7, column=0, columnspan=4, sticky="w", pady=(4, 0))
|
||||
self.apk_detail_widgets = tuple(
|
||||
widget
|
||||
for row in (2, 3, 5)
|
||||
for widget in release_panel.grid_slaves(row=row)
|
||||
)
|
||||
|
||||
secrets_panel = ttk.Frame(shell, style="Panel.TFrame", padding=15)
|
||||
secrets_panel.grid(row=1, column=1, sticky="nsew", padx=(8, 0))
|
||||
secrets_panel = ttk.Frame(self.setup_content, style="Panel.TFrame", padding=12)
|
||||
secrets_panel.grid(row=0, column=1, sticky="nsew", padx=(8, 0))
|
||||
self.secrets_panel = secrets_panel
|
||||
secrets_panel.columnconfigure(1, weight=1)
|
||||
ttk.Label(
|
||||
secrets_panel, text="Signing and Gitea (APK only)", style="Panel.TLabel",
|
||||
font=("Segoe UI Semibold", 12),
|
||||
secrets_panel, text="Signing and Gitea", style="Section.Panel.TLabel",
|
||||
).grid(row=0, column=0, columnspan=3, sticky="w")
|
||||
ttk.Label(
|
||||
secrets_panel,
|
||||
text="Signing password and saved token are Windows-encrypted outside Git.",
|
||||
style="Muted.Panel.TLabel",
|
||||
wraplength=260,
|
||||
).grid(row=1, column=0, columnspan=3, sticky="w", pady=(2, 10))
|
||||
|
||||
self.keystore_var = tk.StringVar(value=str(DEFAULT_KEYSTORE_FILE))
|
||||
@@ -829,12 +868,13 @@ def launch_gui(*, smoke_test: bool = False) -> int:
|
||||
secrets_panel,
|
||||
text="Remember Gitea token on this PC",
|
||||
variable=self.remember_token_var,
|
||||
style="Panel.TCheckbutton",
|
||||
).grid(row=7, column=0, columnspan=2, sticky="w", pady=(7, 0))
|
||||
ttk.Button(
|
||||
secrets_panel,
|
||||
text="Forget saved token",
|
||||
command=self._forget_saved_token,
|
||||
).grid(row=7, column=2, sticky="e", pady=(7, 0))
|
||||
).grid(row=8, column=0, columnspan=3, sticky="w", pady=(5, 0))
|
||||
ttk.Label(
|
||||
secrets_panel,
|
||||
text=(
|
||||
@@ -842,16 +882,17 @@ def launch_gui(*, smoke_test: bool = False) -> int:
|
||||
"enter the Gitea token once and it will load automatically."
|
||||
),
|
||||
style="Muted.Panel.TLabel",
|
||||
wraplength=360,
|
||||
).grid(row=8, column=0, columnspan=3, sticky="w", pady=(7, 0))
|
||||
wraplength=260,
|
||||
).grid(row=9, column=0, columnspan=3, sticky="w", pady=(7, 0))
|
||||
|
||||
lower = ttk.Panedwindow(shell, orient="horizontal")
|
||||
lower.grid(row=2, column=0, columnspan=2, sticky="nsew", pady=(16, 0))
|
||||
workspace = ttk.Notebook(shell)
|
||||
workspace.grid(row=2, column=0, sticky="nsew", pady=(8, 0))
|
||||
self.workspace_tabs = workspace
|
||||
|
||||
log_panel = ttk.Frame(lower, style="Panel.TFrame", padding=12)
|
||||
commands_panel = ttk.Frame(lower, style="Panel.TFrame", padding=12)
|
||||
lower.add(log_panel, weight=3)
|
||||
lower.add(commands_panel, weight=2)
|
||||
log_panel = ttk.Frame(workspace, style="Panel.TFrame", padding=14)
|
||||
commands_panel = ttk.Frame(workspace, style="Panel.TFrame", padding=14)
|
||||
workspace.add(log_panel, text="Activity")
|
||||
workspace.add(commands_panel, text="TrueNAS commands")
|
||||
log_panel.rowconfigure(2, weight=1)
|
||||
log_panel.columnconfigure(0, weight=1)
|
||||
commands_panel.rowconfigure(1, weight=1)
|
||||
@@ -867,28 +908,27 @@ def launch_gui(*, smoke_test: bool = False) -> int:
|
||||
).grid(row=0, column=0, sticky="w")
|
||||
self.progress = ttk.Progressbar(status_row, mode="indeterminate", length=150)
|
||||
self.progress.grid(row=0, column=1, sticky="e")
|
||||
self.git_summary_var = tk.StringVar(value="Loading Git status…")
|
||||
ttk.Label(
|
||||
log_panel,
|
||||
textvariable=self.git_summary_var,
|
||||
style="Muted.Panel.TLabel",
|
||||
wraplength=600,
|
||||
wraplength=1060,
|
||||
).grid(row=1, column=0, sticky="ew", pady=(4, 8))
|
||||
self.log = scrolledtext.ScrolledText(
|
||||
log_panel,
|
||||
wrap="word",
|
||||
bg="#071110",
|
||||
fg="#cfe2dc",
|
||||
bg="#101b2b",
|
||||
fg=self.TEXT,
|
||||
insertbackground=self.TEXT,
|
||||
relief="flat",
|
||||
font=("Cascadia Mono", 9),
|
||||
font=("Cascadia Mono", 10),
|
||||
state="disabled",
|
||||
)
|
||||
self.log.grid(row=2, column=0, sticky="nsew")
|
||||
|
||||
ttk.Label(
|
||||
commands_panel,
|
||||
text="TrueNAS shell commands",
|
||||
text="Deploy from TrueNAS after the Git push",
|
||||
style="Panel.TLabel",
|
||||
font=("Segoe UI Semibold", 11),
|
||||
).grid(row=0, column=0, sticky="w", pady=(0, 7))
|
||||
@@ -908,7 +948,8 @@ def launch_gui(*, smoke_test: bool = False) -> int:
|
||||
)
|
||||
|
||||
actions = ttk.Frame(shell)
|
||||
actions.grid(row=3, column=0, columnspan=2, sticky="ew", pady=(14, 0))
|
||||
self.actions = actions
|
||||
actions.grid(row=3, column=0, sticky="ew", pady=(8, 0))
|
||||
actions.columnconfigure(0, weight=1)
|
||||
self.release_button = ttk.Button(
|
||||
actions,
|
||||
@@ -927,25 +968,79 @@ def launch_gui(*, smoke_test: bool = False) -> int:
|
||||
self.cancel_button.grid(row=0, column=2, padx=(7, 0))
|
||||
self._update_release_mode()
|
||||
self._update_test_mode()
|
||||
self._bind_setup_scroll(self.setup_content)
|
||||
self.root.bind("<Configure>", self._fit_setup_height)
|
||||
self.root.after_idle(self._fit_setup_height)
|
||||
|
||||
def _bind_setup_scroll(self, widget: tk.Misc) -> None:
|
||||
if not isinstance(widget, (tk.Text, ttk.Combobox)):
|
||||
widget.bind("<MouseWheel>", self._scroll_setup, add="+")
|
||||
for child in widget.winfo_children():
|
||||
self._bind_setup_scroll(child)
|
||||
|
||||
def _scroll_setup(self, event: tk.Event[tk.Misc]) -> str:
|
||||
if event.delta:
|
||||
self.setup_canvas.yview_scroll(-1 if event.delta > 0 else 1, "units")
|
||||
return "break"
|
||||
|
||||
def _fit_setup_height(self, event: tk.Event[tk.Misc] | None = None) -> None:
|
||||
if event is not None and event.widget is not self.root:
|
||||
return
|
||||
self._layout_setup_panels()
|
||||
available = max(140, int(self.root.winfo_height() * 0.43))
|
||||
desired = min(self.setup_content.winfo_reqheight(), available)
|
||||
if int(self.setup_canvas.cget("height")) != desired:
|
||||
self.setup_canvas.configure(height=desired)
|
||||
|
||||
def _layout_setup_panels(self) -> None:
|
||||
apk_mode = not self.git_truenas_only_var.get()
|
||||
stacked = apk_mode and self.root.winfo_width() < 940
|
||||
layout = (apk_mode, stacked)
|
||||
if layout == getattr(self, "_setup_layout", None):
|
||||
return
|
||||
self._setup_layout = layout
|
||||
if not apk_mode:
|
||||
self.release_panel.grid_configure(
|
||||
row=0, column=0, columnspan=2, padx=0, pady=0
|
||||
)
|
||||
self.secrets_panel.grid_remove()
|
||||
elif stacked:
|
||||
self.release_panel.grid_configure(
|
||||
row=0, column=0, columnspan=2, padx=0, pady=0
|
||||
)
|
||||
self.secrets_panel.grid_configure(
|
||||
row=1, column=0, columnspan=2, padx=0, pady=(8, 0)
|
||||
)
|
||||
else:
|
||||
self.release_panel.grid_configure(
|
||||
row=0, column=0, columnspan=1, padx=(0, 4), pady=0
|
||||
)
|
||||
self.secrets_panel.grid_configure(
|
||||
row=0, column=1, columnspan=1, padx=(4, 0), pady=0
|
||||
)
|
||||
self.root.after_idle(self._fit_setup_height)
|
||||
|
||||
def _update_release_mode(self) -> None:
|
||||
git_truenas_only = self.git_truenas_only_var.get()
|
||||
if git_truenas_only:
|
||||
self.retry_button.grid_remove()
|
||||
for widget in self.apk_detail_widgets:
|
||||
widget.grid_remove()
|
||||
else:
|
||||
self.retry_button.grid()
|
||||
for widget in self.apk_detail_widgets:
|
||||
widget.grid()
|
||||
self._layout_setup_panels()
|
||||
self.setup_canvas.yview_moveto(0)
|
||||
self.root.after_idle(self._fit_setup_height)
|
||||
self.bump_box.configure(state="disabled" if git_truenas_only else "readonly")
|
||||
entry_state = "disabled" if git_truenas_only else "normal"
|
||||
self.next_version_entry.configure(state=entry_state)
|
||||
self.next_code_entry.configure(state=entry_state)
|
||||
if git_truenas_only:
|
||||
self.release_button.configure(text="Validate and Push Git + TrueNAS Update")
|
||||
self.release_mode_hint_var.set(
|
||||
"Default mode: keeps the current Android version, skips Android sync and APK "
|
||||
"publication, and prepares the pinned TrueNAS deployment commands after push."
|
||||
)
|
||||
else:
|
||||
self.release_button.configure(text="Validate, Push, and Publish APK")
|
||||
self.release_mode_hint_var.set(
|
||||
"APK mode: bumps the app and Android versions, runs Android sync, and publishes "
|
||||
"the signed APK after the Git push."
|
||||
)
|
||||
self._suggest_versions()
|
||||
current_notes = self.release_notes.get("1.0", "end").strip()
|
||||
server_default = "Healer Man Git and TrueNAS server update."
|
||||
@@ -980,15 +1075,18 @@ def launch_gui(*, smoke_test: bool = False) -> int:
|
||||
frame.columnconfigure(0, weight=1)
|
||||
widget = tk.Text(
|
||||
frame,
|
||||
wrap="none",
|
||||
bg="#071110",
|
||||
fg="#cfe2dc",
|
||||
wrap="word",
|
||||
bg="#101b2b",
|
||||
fg=self.TEXT,
|
||||
relief="flat",
|
||||
padx=8,
|
||||
pady=8,
|
||||
font=("Cascadia Mono", 8),
|
||||
padx=12,
|
||||
pady=10,
|
||||
font=("Cascadia Mono", 10),
|
||||
)
|
||||
widget.grid(row=0, column=0, sticky="nsew")
|
||||
scrollbar = ttk.Scrollbar(frame, orient="vertical", command=widget.yview)
|
||||
scrollbar.grid(row=0, column=1, sticky="ns")
|
||||
widget.configure(yscrollcommand=scrollbar.set)
|
||||
return widget
|
||||
|
||||
def _browse_keystore(self) -> None:
|
||||
@@ -1265,11 +1363,13 @@ def launch_gui(*, smoke_test: bool = False) -> int:
|
||||
self.busy = True
|
||||
self.cancel_event.clear()
|
||||
self.status_var.set(label)
|
||||
self.workspace_tabs.select(0)
|
||||
self.progress.start(12)
|
||||
self.release_button.configure(state="disabled")
|
||||
self.retry_button.configure(state="disabled")
|
||||
self.refresh_button.configure(state="disabled")
|
||||
self.release_mode_toggle.configure(state="disabled")
|
||||
self.apk_mode_toggle.configure(state="disabled")
|
||||
self.run_tests_toggle.configure(state="disabled")
|
||||
self.cancel_button.configure(state="normal")
|
||||
self._append_log(f"\n=== {label} ===")
|
||||
@@ -1774,6 +1874,7 @@ def launch_gui(*, smoke_test: bool = False) -> int:
|
||||
self.retry_button.configure(state="normal")
|
||||
self.refresh_button.configure(state="normal")
|
||||
self.release_mode_toggle.configure(state="normal")
|
||||
self.apk_mode_toggle.configure(state="normal")
|
||||
self.run_tests_toggle.configure(state="normal")
|
||||
self.cancel_button.configure(state="disabled")
|
||||
self.keystore_password_var.set("")
|
||||
@@ -1781,21 +1882,24 @@ def launch_gui(*, smoke_test: bool = False) -> int:
|
||||
if self.remember_token_var.get() and SAVED_GITEA_TOKEN_FILE.is_file():
|
||||
self.token_var.set(read_saved_gitea_token())
|
||||
if result == "success":
|
||||
self.status_var.set("Workflow completed")
|
||||
self._append_log("\n✓ Workflow completed successfully.")
|
||||
if self.git_truenas_only_var.get():
|
||||
self.workspace_tabs.select(1)
|
||||
messagebox.showinfo(
|
||||
"Workflow complete",
|
||||
detail,
|
||||
parent=self.root,
|
||||
)
|
||||
elif result == "cancelled":
|
||||
self.status_var.set("Workflow cancelled")
|
||||
self._append_log(f"\nCancelled: {detail}")
|
||||
else:
|
||||
self.status_var.set("Workflow failed")
|
||||
self._append_log(f"\nERROR: {detail}")
|
||||
messagebox.showerror("Workflow failed", detail, parent=self.root)
|
||||
self.refresh()
|
||||
self.status_var.set({
|
||||
"success": "Workflow completed",
|
||||
"cancelled": "Workflow cancelled",
|
||||
}.get(result, "Workflow failed"))
|
||||
|
||||
def _close(self) -> None:
|
||||
if self.busy:
|
||||
@@ -1809,11 +1913,37 @@ def launch_gui(*, smoke_test: bool = False) -> int:
|
||||
self.root.destroy()
|
||||
|
||||
root = tk.Tk()
|
||||
ReleaseManagerApp(root)
|
||||
app = ReleaseManagerApp(root)
|
||||
if smoke_test:
|
||||
root.withdraw()
|
||||
root.update_idletasks()
|
||||
root.update()
|
||||
app.git_truenas_only_var.set(False)
|
||||
app._update_release_mode()
|
||||
root.update_idletasks()
|
||||
assert app.secrets_panel.winfo_manager() == "grid"
|
||||
assert app.release_panel.grid_info()["columnspan"] == 1
|
||||
app.git_truenas_only_var.set(True)
|
||||
app._update_release_mode()
|
||||
root.update_idletasks()
|
||||
assert app.release_panel.grid_info()["columnspan"] == 2
|
||||
assert not app.secrets_panel.winfo_manager()
|
||||
root.geometry("700x500")
|
||||
root.deiconify()
|
||||
root.update()
|
||||
app.git_truenas_only_var.set(False)
|
||||
app._update_release_mode()
|
||||
root.update()
|
||||
app._fit_setup_height()
|
||||
assert app.secrets_panel.grid_info()["row"] == 1
|
||||
assert app.release_panel.grid_info()["columnspan"] == 2
|
||||
assert int(app.setup_canvas.cget("height")) <= 215
|
||||
assert app.setup_canvas.yview()[1] < 1
|
||||
app.setup_canvas.yview_moveto(1)
|
||||
root.update_idletasks()
|
||||
assert app.setup_canvas.yview()[0] > 0
|
||||
assert app.release_button.winfo_manager() == "grid"
|
||||
assert app.actions.winfo_y() + app.actions.winfo_height() <= root.winfo_height()
|
||||
root.destroy()
|
||||
return 0
|
||||
root.mainloop()
|
||||
|
||||
Reference in new issue
Block a user