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:
@@ -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
@@ -46,11 +46,23 @@ PyInstaller kann **nicht cross-kompilieren** — jede Binary wird auf ihrem OS g
|
|||||||
./build-native.sh # -> dist/aria-host-agent
|
./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
|
```bat
|
||||||
build-native.bat REM -> dist\aria-host-agent.exe
|
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)
|
### Docker scheitert? (Live-ISO / overlayfs-Root)
|
||||||
|
|
||||||
Wenn `build.sh` mit `failed to mount … overlayfs … invalid argument` abbricht,
|
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
|
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).
|
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
|
## TLS / SNI — Agent im selben Netz wie der RVS
|
||||||
|
|
||||||
Steht der Rechner im **selben Netz wie der RVS** (z.B. Rechenzentrum) und soll
|
Steht der Rechner im **selben Netz wie der RVS** (z.B. Rechenzentrum) und soll
|
||||||
|
|||||||
Executable
+17
@@ -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"
|
||||||
@@ -64,7 +64,13 @@ def _load_dotenv() -> None:
|
|||||||
"""Laedt eine .env neben der Binary/dem Script (oder im CWD) in os.environ.
|
"""Laedt eine .env neben der Binary/dem Script (oder im CWD) in os.environ.
|
||||||
Bereits gesetzte Werte gewinnen. Kein python-dotenv noetig."""
|
Bereits gesetzte Werte gewinnen. Kein python-dotenv noetig."""
|
||||||
here = os.path.dirname(os.path.abspath(__file__))
|
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):
|
if not os.path.isfile(path):
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -78,17 +78,33 @@ echo -e " ${GREEN}✓${NC} Linux-Binary ($(du -h "$LINUX_BIN" | cut -f1))"
|
|||||||
( cd android && ./build.sh >/dev/null )
|
( cd android && ./build.sh >/dev/null )
|
||||||
APK="$SCRIPT_DIR/android/dist/aria-android-agent.apk"
|
APK="$SCRIPT_DIR/android/dist/aria-android-agent.apk"
|
||||||
[ -f "$APK" ] || { echo -e "${RED}APK fehlt: $APK${NC}"; exit 1; }
|
[ -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"
|
# Asset-Liste aufbauen: "lokaler_pfad::asset-name"
|
||||||
ASSETS=(
|
ASSETS=(
|
||||||
"$LINUX_BIN::aria-host-agent-linux-x64"
|
"$LINUX_BIN::aria-host-agent-linux-x64"
|
||||||
"$APK::aria-host-agent-android-$TAG.apk"
|
"$APK::aria-host-agent-android-$TAG.apk"
|
||||||
)
|
)
|
||||||
# Optionale, vorgebaute native Artefakte (nur wenn vorhanden):
|
# 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-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.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")
|
[ -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 ──────────────────────────────────────────────────────────
|
# ── Git-Tag ──────────────────────────────────────────────────────────
|
||||||
echo -e "${GREEN}[3/5] Git-Tag $TAG...${NC}"
|
echo -e "${GREEN}[3/5] Git-Tag $TAG...${NC}"
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in New Issue
Block a user