feat(host-agent): Windows-Build aus Docker (Wine) + setup.exe (Dienst)

Dockerfile.win (tobix/pywine): baut die Windows-.exe via Wine+PyInstaller und
per NSIS ein setup.exe, das den Agent via nssm als Autostart-Windows-Dienst
einrichtet und die .env aus %ProgramData%\ARIA-Host-Agent liest (AppDirectory).
build-win.sh als Einstieg; release_agent.sh baut Windows jetzt mit (SKIP_WINDOWS=1
ueberspringt). _load_dotenv haertet: sucht .env auch neben sys.executable (onefile-
.exe-Ort), nicht nur __file__/CWD. README: Windows-Build + Dienst + Release.
macOS bleibt self-build (nicht aus Docker moeglich).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-09-24 20:39:08 +02:00
co-authored by Claude Opus 4.8
parent 4f8e65b76b
commit d32e7e59c3
6 changed files with 207 additions and 7 deletions
+36
View File
@@ -0,0 +1,36 @@
# Baut die Windows-.exe des Host-Agents AUF LINUX — via Wine + Windows-Python +
# PyInstaller. tobix/pywine bringt Wine + Windows-Python 3.11 mit (PyInstaller
# kann NICHT cross-compilen, deshalb der Wine-Umweg).
#
# Zusaetzlich baut NSIS ein setup.exe, das die Agent-.exe installiert, eine .env
# in C:\ProgramData\ARIA-Host-Agent anlegt (falls keine da ist) und den Agent als
# automatisch startenden Windows-Dienst (via nssm) einrichtet.
FROM tobix/pywine:3.11
ARG VERSION=0.0.0
# NSIS (Installer-Compiler, laeuft nativ auf Linux) + Tools fuer nssm.
RUN apt-get update \
&& apt-get install -y --no-install-recommends nsis curl unzip ca-certificates \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /work
# nssm — Non-Sucking Service Manager (public domain): macht aus der Konsolen-.exe
# einen sauberen Windows-Dienst (die .exe selbst spricht das SCM nicht).
RUN curl -fsSL https://nssm.cc/release/nssm-2.24.zip -o /tmp/nssm.zip \
&& unzip -q /tmp/nssm.zip -d /tmp \
&& cp /tmp/nssm-2.24/win64/nssm.exe ./nssm.exe \
&& rm -rf /tmp/nssm*
COPY requirements.txt host_agent.py ./
COPY windows/installer.nsi ./
# Windows-Python-Deps + PyInstaller, dann die Onefile-.exe bauen.
RUN wine pip install --no-cache-dir -r requirements.txt pyinstaller
RUN wine pyinstaller --onefile --name aria-host-agent --collect-all psutil host_agent.py \
&& cp dist/aria-host-agent.exe ./aria-host-agent.exe \
&& makensis -DVERSION=${VERSION} installer.nsi
# Beide Artefakte rausreichen (dist/ wird vom build-win.sh als Volume gemountet).
CMD ["bash","-lc","cp dist/aria-host-agent.exe /out/ && cp aria-host-agent-setup.exe /out/ && echo 'OK -> /out/aria-host-agent.exe + /out/aria-host-agent-setup.exe'"]
+43 -1
View File
@@ -46,11 +46,23 @@ PyInstaller kann **nicht cross-kompilieren** — jede Binary wird auf ihrem OS g
./build-native.sh # -> dist/aria-host-agent
```
**Windows** — PyInstaller (Python 3 im PATH nötig):
**Windows — nativ** (auf einem Windows-Rechner, Python 3 im PATH nötig):
```bat
build-native.bat REM -> dist\aria-host-agent.exe
```
**Windows — aus Docker heraus (auf Linux!), inkl. Installer** — Wine baut die
`.exe`, NSIS packt ein `setup.exe`, das den Agent als Windows-Dienst einrichtet:
```bash
./build-win.sh [version]
# -> dist/aria-host-agent.exe (Konsolen-Binary)
# -> dist/aria-host-agent-setup.exe (Installer: Dienst + .env in ProgramData)
```
Der erste Lauf zieht das `tobix/pywine`-Image (~1–2 GB) und richtet die Wine-
Python-Umgebung ein — das dauert; Folge-Builds sind schnell. PyInstaller kann
nicht cross-compilen, deshalb der Wine-Umweg. **macOS geht so NICHT** (Apple
lässt sich nicht legal aus Docker bauen) — dort `./build-native.sh` auf einem Mac.
### Docker scheitert? (Live-ISO / overlayfs-Root)
Wenn `build.sh` mit `failed to mount … overlayfs … invalid argument` abbricht,
@@ -96,6 +108,36 @@ Danach: `systemctl status aria-host-agent` · `journalctl -u aria-host-agent -f`
Die Binary sucht er unter `dist/aria-host-agent` bzw. `./aria-host-agent` (oder
2. Argument). Braucht `dialog` für den Dateibrowser (bietet die Installation an).
### Windows-Dienst (setup.exe)
`aria-host-agent-setup.exe` (aus `build-win.sh` oder dem Gitea-Release) als
Administrator ausführen. Der Installer:
- kopiert die `.exe` nach `%ProgramFiles%\ARIA Host-Agent`,
- legt `%ProgramData%\ARIA-Host-Agent\.env` an (nur falls noch keine da ist),
- richtet über **nssm** den Dienst **ARIA Host-Agent** ein (Autostart) und startet ihn.
Danach die `.env` unter `%ProgramData%\ARIA-Host-Agent\` mit RVS-Zugang +
`CONTROL_ENABLED=true` füllen und den Dienst neu starten (`services.msc` →
*ARIA Host-Agent*, oder `nssm restart ARIAHostAgent`). Deinstallation über
*Apps & Features* → *ARIA Host-Agent* (die `.env` in ProgramData bleibt erhalten).
## Release (Binaries als Gitea-Assets)
`release_agent.sh <version>` baut alles Docker-Baubare und hängt es als
**Release-Asset** an den Tag `agent-v<version>` — nichts landet im Git-Tree:
```bash
./release_agent.sh 0.2.0 # Linux + Android + Windows (Wine)
SKIP_WINDOWS=1 ./release_agent.sh 0.2.0 # ohne Windows (schneller)
```
Assets: `aria-host-agent-linux-x64`, `aria-host-agent-android-agent-v<v>.apk`,
`aria-host-agent-windows.exe`, `aria-host-agent-windows-setup.exe`. **macOS** ist
nicht Docker-baubar — auf einem Mac `./build-native.sh` laufen lassen und das
Ergebnis vor dem Release nach `dist/aria-host-agent-macos` legen, dann nimmt das
Skript es automatisch mit. Gitea-Zugang via `.env`/Umgebung (`GITEA_URL`,
`GITEA_REPO`, `GITEA_USER`), Kennwort wird abgefragt.
## TLS / SNI — Agent im selben Netz wie der RVS
Steht der Rechner im **selben Netz wie der RVS** (z.B. Rechenzentrum) und soll
+17
View File
@@ -0,0 +1,17 @@
#!/usr/bin/env bash
# Baut die Windows-.exe + setup.exe des Host-Agents AUF LINUX (Wine im Docker).
# ./build-win.sh [version]
# Ergebnis:
# dist/aria-host-agent.exe (Konsolen-Binary)
# dist/aria-host-agent-setup.exe (Installer: richtet Windows-Dienst ein)
#
# Hinweis: Der erste Lauf zieht das tobix/pywine-Image (~1-2 GB) + baut die
# Wine-Umgebung — das dauert. Folge-Builds sind schnell.
set -euo pipefail
cd "$(dirname "$0")"
VERSION="${1:-0.0.0}"
mkdir -p dist
docker build -f Dockerfile.win --build-arg VERSION="$VERSION" -t aria-host-agent-win .
docker run --rm -v "$(pwd)/dist:/out" aria-host-agent-win
echo
echo "Fertig: dist/aria-host-agent.exe + dist/aria-host-agent-setup.exe"
+7 -1
View File
@@ -64,7 +64,13 @@ def _load_dotenv() -> None:
"""Laedt eine .env neben der Binary/dem Script (oder im CWD) in os.environ.
Bereits gesetzte Werte gewinnen. Kein python-dotenv noetig."""
here = os.path.dirname(os.path.abspath(__file__))
for path in (os.path.join(here, ".env"), os.path.join(os.getcwd(), ".env")):
# PyInstaller-onefile: __file__ liegt im Temp-Extract-Dir, NICHT beim .exe/
# Binary — deshalb zusaetzlich sys.executable-Ordner (echter Binary-Ort) und
# das CWD (z.B. der ProgramData-Ordner, den der Windows-Dienst als AppDir nutzt).
exe_dir = os.path.dirname(os.path.abspath(sys.executable))
seen = set()
candidates = [os.path.join(d, ".env") for d in (exe_dir, here, os.getcwd())]
for path in [p for p in candidates if not (p in seen or seen.add(p))]:
if not os.path.isfile(path):
continue
try:
+21 -5
View File
@@ -78,17 +78,33 @@ echo -e " ${GREEN}✓${NC} Linux-Binary ($(du -h "$LINUX_BIN" | cut -f1))"
( cd android && ./build.sh >/dev/null )
APK="$SCRIPT_DIR/android/dist/aria-android-agent.apk"
[ -f "$APK" ] || { echo -e "${RED}APK fehlt: $APK${NC}"; exit 1; }
echo -e " ${GREEN}✓${NC} Android-APK ($(du -h "$APK" | cut -f1))\n"
echo -e " ${GREEN}✓${NC} Android-APK ($(du -h "$APK" | cut -f1))"
# Windows (.exe + setup.exe) via Wine im Docker. Dauert (pywine-Image ~1-2 GB) —
# mit SKIP_WINDOWS=1 ./release_agent.sh <v> ueberspringbar.
if [ "${SKIP_WINDOWS:-0}" = "1" ]; then
echo -e " ${YELLOW}Windows-Build uebersprungen (SKIP_WINDOWS=1)${NC}"
else
echo -e " ${CYAN}…${NC} Windows-.exe + setup.exe bauen (Wine, kann dauern)"
if ./build-win.sh "$VERSION" >/dev/null 2>&1; then
echo -e " ${GREEN}✓${NC} Windows-.exe + setup.exe"
else
echo -e " ${YELLOW}Windows-Build fehlgeschlagen — Release laeuft ohne Windows weiter.${NC}"
echo -e " ${YELLOW}(Einzeln testen: ./build-win.sh $VERSION)${NC}"
fi
fi
echo ""
# Asset-Liste aufbauen: "lokaler_pfad::asset-name"
ASSETS=(
"$LINUX_BIN::aria-host-agent-linux-x64"
"$APK::aria-host-agent-android-$TAG.apk"
)
# Optionale, vorgebaute native Artefakte (nur wenn vorhanden):
[ -f "$SCRIPT_DIR/dist/aria-host-agent-macos" ] && ASSETS+=("$SCRIPT_DIR/dist/aria-host-agent-macos::aria-host-agent-macos")
[ -f "$SCRIPT_DIR/dist/aria-host-agent.exe" ] && ASSETS+=("$SCRIPT_DIR/dist/aria-host-agent.exe::aria-host-agent-windows.exe")
[ -f "$SCRIPT_DIR/dist/aria-host-agent-setup.exe" ] && ASSETS+=("$SCRIPT_DIR/dist/aria-host-agent-setup.exe::aria-host-agent-setup.exe")
# Native Artefakte (nur wenn vorhanden): Windows aus build-win.sh, macOS
# vorgebaut (build-native.sh auf einem Mac -> dist/aria-host-agent-macos legen).
[ -f "$SCRIPT_DIR/dist/aria-host-agent-macos" ] && ASSETS+=("$SCRIPT_DIR/dist/aria-host-agent-macos::aria-host-agent-macos")
[ -f "$SCRIPT_DIR/dist/aria-host-agent.exe" ] && ASSETS+=("$SCRIPT_DIR/dist/aria-host-agent.exe::aria-host-agent-windows.exe")
[ -f "$SCRIPT_DIR/dist/aria-host-agent-setup.exe" ] && ASSETS+=("$SCRIPT_DIR/dist/aria-host-agent-setup.exe::aria-host-agent-windows-setup.exe")
# ── Git-Tag ──────────────────────────────────────────────────────────
echo -e "${GREEN}[3/5] Git-Tag $TAG...${NC}"
+83
View File
@@ -0,0 +1,83 @@
; ARIA Host-Agent — Windows-Installer (NSIS, auf Linux mit makensis gebaut).
;
; Installiert die Agent-.exe nach %ProgramFiles%\ARIA Host-Agent, legt eine .env
; in %ProgramData%\ARIA-Host-Agent an (nur falls noch keine da ist — User-Config
; bleibt erhalten) und richtet einen automatisch startenden Windows-Dienst via
; nssm ein. Der Dienst laeuft mit AppDirectory = ProgramData-Ordner, damit der
; Agent die .env von dort (aus dem CWD) liest.
!ifndef VERSION
!define VERSION "0.0.0"
!endif
!define SVC "ARIAHostAgent"
Name "ARIA Host-Agent ${VERSION}"
OutFile "aria-host-agent-setup.exe"
InstallDir "$PROGRAMFILES64\ARIA Host-Agent"
RequestExecutionLevel admin
Unicode true
ShowInstDetails show
ShowUninstDetails show
Var DataDir
Page directory
Page instfiles
UninstPage uninstConfirm
UninstPage instfiles
Section "Install"
SetOutPath "$INSTDIR"
File "aria-host-agent.exe"
File "nssm.exe"
; ProgramData-Ordner fuer die .env bestimmen
ReadEnvStr $0 "ProgramData"
StrCmp $0 "" 0 +2
StrCpy $0 "$PROFILE" ; Fallback, falls %ProgramData% fehlt
StrCpy $DataDir "$0\ARIA-Host-Agent"
CreateDirectory "$DataDir"
; .env nur schreiben, wenn noch keine existiert (User-Config nicht ueberschreiben)
IfFileExists "$DataDir\.env" env_done 0
FileOpen $1 "$DataDir\.env" w
FileWrite $1 "# ARIA Host-Agent — Konfiguration (dieser Windows-Dienst liest diese Datei).$\r$\n"
FileWrite $1 "# Nach dem Aendern den Dienst neu starten: services.msc -> ARIA Host-Agent.$\r$\n"
FileWrite $1 "RVS_HOST=rvs.example.de$\r$\n"
FileWrite $1 "RVS_PORT=443$\r$\n"
FileWrite $1 "RVS_TLS=true$\r$\n"
FileWrite $1 "RVS_TLS_FALLBACK=true$\r$\n"
FileWrite $1 "RVS_TOKEN=$\r$\n"
FileWrite $1 "RVS_SNI=$\r$\n"
FileWrite $1 "HOST_NAME=$\r$\n"
FileWrite $1 "CONTROL_ENABLED=true$\r$\n"
FileClose $1
env_done:
; Dienst (neu) einrichten — evtl. alten sauber entfernen, dann installieren
nsExec::ExecToLog '"$INSTDIR\nssm.exe" stop ${SVC}'
nsExec::ExecToLog '"$INSTDIR\nssm.exe" remove ${SVC} confirm'
nsExec::ExecToLog '"$INSTDIR\nssm.exe" install ${SVC} "$INSTDIR\aria-host-agent.exe"'
nsExec::ExecToLog '"$INSTDIR\nssm.exe" set ${SVC} AppDirectory "$DataDir"'
nsExec::ExecToLog '"$INSTDIR\nssm.exe" set ${SVC} DisplayName "ARIA Host-Agent"'
nsExec::ExecToLog '"$INSTDIR\nssm.exe" set ${SVC} Description "ARIA-Fernsteuerung dieses Rechners (RVS-Agent)."'
nsExec::ExecToLog '"$INSTDIR\nssm.exe" set ${SVC} Start SERVICE_AUTO_START'
nsExec::ExecToLog '"$INSTDIR\nssm.exe" start ${SVC}'
; Uninstaller + Eintrag unter "Apps & Features"
WriteUninstaller "$INSTDIR\uninstall.exe"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\ARIAHostAgent" "DisplayName" "ARIA Host-Agent"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\ARIAHostAgent" "DisplayVersion" "${VERSION}"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\ARIAHostAgent" "UninstallString" '"$INSTDIR\uninstall.exe"'
SectionEnd
Section "Uninstall"
nsExec::ExecToLog '"$INSTDIR\nssm.exe" stop ${SVC}'
nsExec::ExecToLog '"$INSTDIR\nssm.exe" remove ${SVC} confirm'
Delete "$INSTDIR\aria-host-agent.exe"
Delete "$INSTDIR\nssm.exe"
Delete "$INSTDIR\uninstall.exe"
RMDir "$INSTDIR"
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\ARIAHostAgent"
; Die .env in %ProgramData%\ARIA-Host-Agent bleibt bewusst erhalten (User-Config).
SectionEnd