Create Thüringen branding edition on shared core

This commit is contained in:
pan
2026-07-05 22:38:59 +02:00
commit 4e6f544ba5
19 changed files with 359 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
__pycache__/
*.py[cod]
*.egg-info/
.pytest_cache/
.venv/
build/
dist/
.env
+3
View File
@@ -0,0 +1,3 @@
[submodule "vendor/synadm-tui"]
path = vendor/synadm-tui
url = https://git.blackwall.ipv64.de/pan/synadm-tui.git
+19
View File
@@ -0,0 +1,19 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2026 synadm-tui contributors
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. The complete license text is available at:
https://www.gnu.org/licenses/gpl-3.0.txt
SPDX-License-Identifier: GPL-3.0-or-later
+44
View File
@@ -0,0 +1,44 @@
# synadm TUI Thüringen Edition
Die Thüringen Edition ergänzt den unveränderten Anwendungskern von [`synadm-tui`](https://git.blackwall.ipv64.de/pan/synadm-tui) um das Thüringen-Thema, das Wappen und getrennte Release-Pakete.
Programmlogik, Navigation, Assistenten und Sicherheitsfunktionen werden nicht kopiert. Das Repository bindet einen geprüften `synadm-tui`-Stand als Git-Submodul ein; dadurch bleiben beide Editionen funktional identisch.
## Quellcode testen
```bash
git clone --recurse-submodules https://git.blackwall.ipv64.de/pan/synadm-tui-thueringen.git
cd synadm-tui-thueringen
PYTHONPATH=vendor/synadm-tui python3 -m unittest discover -v
PYTHONPATH=vendor/synadm-tui python3 -m synadm_tui_thueringen --version
```
## Native Datei und Pakete bauen
```bash
python3 -m pip install pyinstaller
python3 scripts/build_native.py
python3 scripts/build_packages.py
cd dist/packages && sha256sum --check SHA256SUMS
```
## Pakete ohne Runner veröffentlichen
```bash
python3 scripts/publish_packages.py --tea-login Building --dry-run
python3 scripts/publish_packages.py --tea-login Building
```
DEB-Pakete werden in die Distribution `thueringen`, RPM-Pakete in die Gruppe `thueringen` veröffentlicht. Dadurch bleiben auch die Paketquellen von der Standard Edition getrennt.
## Voraussetzungen
- Linux x86-64 oder ARM64
- Python 3.10+ für den Quellstart
- PyInstaller für native Builds
- `dpkg-deb` und `rpmbuild` für Systempakete
- separat installiertes `synadm`, wahlweise über den integrierten Installationsassistenten
## Lizenz
GPL-3.0-or-later, siehe `LICENSE`.
+29
View File
@@ -0,0 +1,29 @@
[build-system]
requires = ["setuptools>=68"]
build-backend = "setuptools.build_meta"
[project]
name = "synadm-tui-thueringen"
version = "0.15"
description = "Thüringen-Edition der Terminal-Oberfläche für synadm"
readme = "README.md"
requires-python = ">=3.10"
license = { text = "GPL-3.0-or-later" }
authors = [{ name = "synadm-tui contributors" }]
dependencies = ["synadm-tui==0.15"]
classifiers = [
"Environment :: Console :: Curses",
"Programming Language :: Python :: 3",
]
[project.scripts]
synadm-tui-thueringen = "synadm_tui_thueringen.cli:main"
[tool.setuptools.packages.find]
include = ["synadm_tui_thueringen*"]
[tool.setuptools.package-data]
synadm_tui_thueringen = ["assets/*.png", "assets/*.block.json"]
[tool.unittest]
start-directory = "tests"
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env python3
"""Regenerate the portable 256-color crest data."""
from __future__ import annotations
import json
import re
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
ASSETS = ROOT / "synadm_tui_thueringen" / "assets"
PIXEL = re.compile(r"#([0-9A-Fa-f]{8})\s")
def main() -> int:
process = subprocess.run(
["convert", str(ASSETS / "thueringen-wappen.png"), "-filter", "Lanczos", "-resize", "18x20!", "txt:-"],
check=True,
text=True,
stdout=subprocess.PIPE,
)
pixels = [match.group(1).lower() for line in process.stdout.splitlines() if (match := PIXEL.search(line))]
if len(pixels) != 18 * 20:
raise RuntimeError(f"360 Pixel erwartet, {len(pixels)} erhalten")
target = ASSETS / "thuringia.block.json"
target.write_text(
json.dumps({"width": 18, "height": 20, "pixels": pixels}, separators=(",", ":")) + "\n",
encoding="utf-8",
)
print(target)
return 0
if __name__ == "__main__":
raise SystemExit(main())
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env python3
"""Build the Thüringen binary against the pinned synadm-tui core."""
from __future__ import annotations
import hashlib
import os
from pathlib import Path
import PyInstaller.__main__
ROOT = Path(__file__).resolve().parent.parent
CORE = Path(os.environ.get("SYNADM_TUI_CORE", ROOT / "vendor" / "synadm-tui")).resolve()
def main() -> int:
if not (CORE / "synadm_tui" / "app.py").is_file():
raise SystemExit("synadm-tui-Core fehlt; git submodule update --init ausführen.")
arguments = [
str(ROOT / "scripts" / "standalone_entry.py"),
"--name=synadm-tui-thueringen",
"--onefile",
"--clean",
f"--paths={ROOT}",
f"--paths={CORE}",
f"--distpath={ROOT / 'dist'}",
f"--workpath={ROOT / 'build' / 'pyinstaller'}",
f"--specpath={ROOT / 'build'}",
]
for asset in (
"retro-cyberspace.png",
"hacker-terminal.png",
"cyberspace.block.json",
"hacker.block.json",
):
arguments.append(f"--add-data={CORE / 'synadm_tui' / 'assets' / asset}:synadm_tui/assets")
for asset in ("thueringen-wappen.png", "thuringia.block.json"):
arguments.append(
f"--add-data={ROOT / 'synadm_tui_thueringen' / 'assets' / asset}:synadm_tui_thueringen/assets"
)
PyInstaller.__main__.run(arguments)
artifact = ROOT / "dist" / "synadm-tui-thueringen"
checksum = hashlib.sha256(artifact.read_bytes()).hexdigest()
(ROOT / "dist" / "SHA256SUMS").write_text(
f"{checksum} synadm-tui-thueringen\n", encoding="utf-8"
)
return 0
if __name__ == "__main__":
raise SystemExit(main())
+34
View File
@@ -0,0 +1,34 @@
#!/usr/bin/env python3
"""Build DEB and RPM packages by reusing the pinned core's packaging code."""
from __future__ import annotations
import importlib.util
import os
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
CORE = Path(os.environ.get("SYNADM_TUI_CORE", ROOT / "vendor" / "synadm-tui")).resolve()
SPEC = importlib.util.spec_from_file_location("synadm_core_build_packages", CORE / "scripts" / "build_packages.py")
if not SPEC or not SPEC.loader:
raise SystemExit("Paketierungsmodul des synadm-tui-Core fehlt.")
module = importlib.util.module_from_spec(SPEC)
sys.modules[SPEC.name] = module
SPEC.loader.exec_module(module)
module.ROOT = ROOT
module.DIST = ROOT / "dist"
module.OUTPUT = module.DIST / "packages"
module.HOMEPAGE = "https://git.blackwall.ipv64.de/pan/synadm-tui-thueringen"
module.EDITIONS = (
module.Edition(
"synadm-tui-thueringen",
"synadm-tui-thueringen",
"Thüringen-Edition der synadm Terminal-Oberfläche",
"Regionales Branding auf dem unveränderten Anwendungskern von synadm-tui.",
),
)
raise SystemExit(module.main())
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/env python3
"""Publish Thüringen packages via the pinned core's secure uploader."""
from __future__ import annotations
import importlib.util
import os
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
CORE = Path(os.environ.get("SYNADM_TUI_CORE", ROOT / "vendor" / "synadm-tui")).resolve()
SPEC = importlib.util.spec_from_file_location("synadm_core_publish_packages", CORE / "scripts" / "publish_packages.py")
if not SPEC or not SPEC.loader:
raise SystemExit("Uploadmodul des synadm-tui-Core fehlt.")
module = importlib.util.module_from_spec(SPEC)
sys.modules[SPEC.name] = module
SPEC.loader.exec_module(module)
module.ROOT = ROOT
module.PACKAGE_DIR = ROOT / "dist" / "packages"
module.DEFAULT_DISTRIBUTION = "thueringen"
module.DEFAULT_RPM_GROUP = "thueringen"
raise SystemExit(module.main())
+6
View File
@@ -0,0 +1,6 @@
"""PyInstaller entry point for the Thüringen edition."""
from synadm_tui_thueringen.cli import main
raise SystemExit(main())
+3
View File
@@ -0,0 +1,3 @@
"""Branding extension for the Thüringen edition of synadm TUI."""
__version__ = "0.15"
+4
View File
@@ -0,0 +1,4 @@
from .cli import main
raise SystemExit(main())
Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

@@ -0,0 +1 @@
{"width":18,"height":20,"pixels":["15678fff","1875a4ff","18729fff","1774a2ff","076899ff","106f9fff","1874a4ff","206c8bff","1b6e96ff","296e80ff","1f6f91ff","277087ff","1072a4ff","076796ff","1472a1ff","1775a4ff","1678a8ff","166891ff","1878a7ff","1c89c0ff","1c87beff","1e81b3ff","86a9bbff","4b90b2ff","1081c0ff","318893ff","66853eff","97a32bff","6c873bff","33858aff","3187b9ff","96b1bdff","3186b1ff","1e7bacff","217fb0ff","177bacff","1a739fff","1d85b4ff","0f80b0ff","267aa0ff","d4ceceff","759bafff","0083c1ff","566075ff","d03c06ff","dd470cff","ce3a05ff","5d4958ff","3f8cb0ff","dad2ceff","2290bfff","7c4546ff","cb3010ff","10719fff","1276a4ff","505c5cff","8d341cff","206887ff","0d7bb3ff","1d7babff","37607dff","902f1eff","9d1200ff","d92607ff","f32000ff","78494aff","0687c4ff","136995ff","64505bff","bc311bff","665a68ff","1178a9ff","176f93ff","5f7473ff","c69f9aff","8facbdff","3187b2ff","1281b8ff","275f84ff","4f6779ff","8a959bff","d5bdb8ff","e7c4baff","7c94a4ff","127cb0ff","a4abb1ff","636777ff","286e94ff","1386bcff","1877a7ff","1774a3ff","257993ff","5a8691ff","f7eeecff","d5d5d4ff","6f95a7ff","5fa2c1ff","4891b2ff","a2bdc9ff","f0f8f9ff","f4fbfbff","6c9cb2ff","91a4afff","94bacbff","0076b4ff","cdccccff","a1b3beff","096a9aff","1976a5ff","0b80bbff","186691ff","a34635ff","b93f25ff","ca4527ff","ea512eff","de4b2aff","d6482aff","d2482bff","e24422ff","795051ff","3281a8ff","a73f2cff","30698dff","549cbdff","5091b2ff","1272a2ff","106c9aff","83a6b8ff","74a3baff","145d86ff","a52006ff","e12300ff","e42500ff","e42500ff","e62500ff","e52800ff","f52100ff","744749ff","0086c8ff","375876ff","b0331bff","2a5d7fff","0e81b9ff","1976a4ff","0e6a99ff","9fb9c9ff","95b0bdff","006ea6ff","868d97ff","c6a39bff","c6aca7ff","ceaba2ff","ceb0aaff","bda39eff","cfa79dff","53829dff","4a8bacff","87b4c7ff","2e698bff","afa1a1ff","b4c1c7ff","3f7895ff","19749dff","257a9aff","1c78a3ff","89acbeff","eff3f3ff","e7e9e9ff","dadadaff","a4c8d7ff","ccdadeff","e0ebeeff","ebefeeff","4f91afff","5398b9ff","d0d1d0ff","097db2ff","93b7c7ff","4f8cabff","1d719aff","147093ff","7a4d37ff","d43b1fff","b6523cff","b6462fff","ae4f3eff","426b84ff","435f7aff","795554ff","c54a30ff","d75033ff","ce472bff","525769ff","1e6f9aff","605766ff","864943ff","0f86bfff","1777a7ff","1d728fff","5e5247ff","78412fff","37565eff","235e81ff","235171ff","9b2e1eff","ee2300ff","ef2900ff","ce1f00ff","d11c00ff","eb2200ff","e92600ff","a03a2bff","834441ff","346c8eff","1c84b7ff","1976a4ff","1e739dfd","1285bbff","107499ff","097eb7ff","4391b6ff","cdbbb7ff","e8b4a7ff","d4b4aeff","7f7b83ff","988b8dff","ddbdb6ff","dabab3ff","b19b9aff","1679abff","0e81b7ff","d4d4d3ff","8caabaff","0a6b9cfe","1974a1e3","2a80abff","cbc9ccfe","5d93aeff","1278acff","659fbbff","ecfcffff","bfd9e2ff","007bb9ff","579cbdff","fff5f0ff","fcfcfbff","b8d5dfff","107cafff","117bafff","6694acfe","458eb2ff","176f9ae6","1d688da4","237fadff","a1b4bdfc","4c8eb2ff","048acbff","236e97ff","cb462aff","da4a2bff","8b3f37ff","326281ff","845957ff","9a564dff","dc4f30ff","bf3d22ff","8d392eff","0a74a9fc","1382b9ff","23698ca7","304e5e37","1a79a9fe","097ab4fc","227490fe","34647dff","7a3e3fff","fc2300ff","dd2600ff","685059ff","1682b7ff","1181baff","156d9cff","ca1e00ff","fd2500ff","82423dfe","1785bafc","1e7ba9ff","2b4c5d3a","1286b400","24627f93","1683beff","4a7569f9","cc9579fe","e6b1a3ff","95817eff","266082ff","0b7bafff","1880b5ff","1d82b2ff","4289acff","d5b8b6ff","9e8170fe","1173a4f9","1a86bbff","22618297","0b8dbe00","12749e03","5d000006","1c678fb5","237895ff","64928dff","6799a4fc","298298fd","197fb5ff","d6ccc7ff","6e9ebbff","1b6a7cff","c9b596fd","e7e4dbfc","4d8d92ff","147eb0ff","22678bb9","41000007","12739d03","2b4a5b01","00ffff00","8d000003","1c5e8483","116e9df0","0e79b0ff","1883bbff","1e80b0ff","85a9bbff","4d92b6ff","147bacff","487e85ff","337686ff","1472aaf1","235d7a85","6f000004","00ffff00","2a4a5c01","244e6400","363e4101","00ffff00","0b85c900","39383926","265f7c84","1f6c92ca","1c74a1f0","0a6a99fe","1170a0fe","1e75a0f1","116999cb","145e8985","38362d27","1483b800","ff000000","2f404a01","244f6600"]}
+11
View File
@@ -0,0 +1,11 @@
"""Entry point for the Thüringen edition."""
from __future__ import annotations
from synadm_tui.cli import main as core_main
from .theme import THURINGIA_EDITION, THURINGIA_THEME
def main(argv: list[str] | None = None) -> int:
return core_main(argv, edition=THURINGIA_EDITION, extra_themes=(THURINGIA_THEME,))
+52
View File
@@ -0,0 +1,52 @@
"""Thüringen profile and visual assets; application behavior comes from synadm-tui."""
from __future__ import annotations
import curses
from pathlib import Path
from synadm_tui.app import Theme
from synadm_tui.edition import Edition
ASSETS = Path(__file__).resolve().parent / "assets"
THURINGIA_CREST = (
" .-============-.",
" / * * * * \\",
" | /\\_/\\ |",
" | ___/ o o \\___ |",
" | / |==^==| \\ |",
" | /|===|\\ |",
" \\ * * * * /",
" '============'",
)
THURINGIA_THEME = Theme(
"thuringia",
"Freistaat Thüringen",
"THÜRINGEN",
(
(curses.COLOR_RED, -1),
(curses.COLOR_WHITE, curses.COLOR_RED),
(curses.COLOR_CYAN, -1),
(curses.COLOR_RED, -1),
(curses.COLOR_YELLOW, -1),
(curses.COLOR_WHITE, -1),
(curses.COLOR_BLUE, -1),
(curses.COLOR_WHITE, curses.COLOR_BLUE),
(curses.COLOR_RED, curses.COLOR_WHITE),
),
THURINGIA_CREST,
"FREISTAAT THÜRINGEN // SYSTEM BEREIT",
image_path=ASSETS / "thueringen-wappen.png",
block_path=ASSETS / "thuringia.block.json",
)
THURINGIA_EDITION = Edition(
key="thuringia",
name="Thüringen Edition",
binary_name="synadm-tui-thueringen",
default_theme="thuringia",
theme_keys=("thuringia", "cyberspace", "matrix", "hacker", "high-contrast", "monochrome"),
)
+1
View File
@@ -0,0 +1 @@
"""Tests for the Thüringen branding overlay."""
+28
View File
@@ -0,0 +1,28 @@
from __future__ import annotations
import unittest
import synadm_tui
from synadm_tui.app import App
from synadm_tui.runner import SynadmRunner
from synadm_tui_thueringen import __version__
from synadm_tui_thueringen.theme import THURINGIA_EDITION, THURINGIA_THEME
class EditionTests(unittest.TestCase):
def test_core_and_overlay_versions_match(self) -> None:
self.assertEqual(__version__, synadm_tui.__version__)
def test_theme_assets_are_external_and_complete(self) -> None:
self.assertTrue(THURINGIA_THEME.image_path.is_file())
self.assertTrue(THURINGIA_THEME.block_path.is_file())
self.assertEqual(len(THURINGIA_THEME.palette), 9)
def test_core_accepts_external_edition(self) -> None:
app = App(SynadmRunner("not-installed"), THURINGIA_EDITION, (THURINGIA_THEME,))
self.assertEqual(app.theme.key, "thuringia")
self.assertEqual(app.edition.binary_name, "synadm-tui-thueringen")
if __name__ == "__main__":
unittest.main()
Vendored Submodule
+1
Submodule vendor/synadm-tui added at c1483aae71