Möglicher Ansatz ohne GIT mit einem eigenen Webserver.
Der Webserver hostet 2 Dateien. Eine Datei enthält den aktuellen Versionsstring und die Download-URL zur aktuellen Version auf dem gleichen Server. Die andere Datei ein Archiv mit der aktuellen Version. Das Programm holt sich die Versionsinfo vom Webserver und wenn der Versionsstring vom Webserver abweicht, wird das Update geladen, es entpackt und dann im gleichen Prozess durch os.execv das Python-Programm erneut gestartet.
Das Beispiel enthält keine Exceptions, um den Code nicht weiter aufzublähen.
Mögliche Fehler:
- Kein Internetzugang
- Webserver ist offline
- DNS-Provider nicht bezahlt und die Domain ist offline
- Webserver liefert unsinnige Antworten
- Falscher Versionsstring
- SSL-Zertifikat ist ungültig (z.B. weil es abgelaufen ist). Hierbei kann z.B. der Webserver Caddy helfen. Ich habe den einfach installiert und seit einem Jahr mittels certbot (let's encrypt) nichts mehr aktualisiert. Zuvor habe ich das manuell gemacht, da ich zu faul war das richtig zu konfigurieren.
- Festplatte ist voll.
- Datei existiert, gehört aber einem anderen User, d.h. sie kann nicht überschrieben werden.
- Namensauflösung geht nicht.
main.py
Code: Alles auswählen
import os
import sys
import json
from urllib.request import urlopen
from shutil import unpack_archive
VERSION = "1.3.3.8"
def get_version():
return json.load(urlopen("http://localhost:8888/version"))
def update():
if (new_version := get_version())["version"] != VERSION:
print("Info vom Server:", new_version)
response = urlopen(new_version["url"])
with open("download", "wb") as fd:
while chunk := response.read(1024):
fd.write(chunk)
unpack_archive("download", format="gztar")
print("Starte Anwendung erneut, um mit der neuen Version fortzufahren.")
# zuviel Automatismus ist nicht immer gut, vor allem dann, wenn man Fehler macht
# hier wird der laufende Prozess genutzt, um das Programm erneut zu starten
os.execv(sys.executable, [sys.executable, *sys.argv])
else:
print("Laut Server ist die Anwendung aktuell. Kann aber auch falsch sein, wenn der Admin einen Fehler gemacht hat.")
def main():
print("Anwendung ...")
if __name__ == "__main__":
update()
main()
Webserver realisiert mit FastAPI, kann aber auch genauso gut ein normaler Webserver sein, der einfach Dateien ausliefert. z.B. Nginx, Apache2 oder mein Favorit Caddy:
Code: Alles auswählen
from fastapi import FastAPI
from fastapi.responses import FileResponse
api = FastAPI()
@api.get("/version")
async def version():
return {"version": "1.3.3.8", "url": "http://localhost:8888/update"}
@api.get("/update")
async def update():
return FileResponse("data")
Wenn die Anwendung aktuell ist:
Anwendung:
Laut Server ist die Anwendung aktuell
Anwendung ...
Webserver:
[deadeye@nexus updater]$ uvicorn web:api --port 8888
INFO: Started server process [95562]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on
http://127.0.0.1:8888 (Press CTRL+C to quit)
INFO: 127.0.0.1:51448 - "GET /version HTTP/1.1" 200 OK
Wenn die Anwendung nicht aktuell ist:
Anwendung:
Info vom Server: {'version': '1.3.3.8', 'url': '
http://localhost:8888/update'}
Starte Anwendung erneut, um mit der neuen Version fortzufahren.
Laut Server ist die Anwendung aktuell
Anwendung ...
Webserver:
[deadeye@nexus updater]$ uvicorn web:api --port 8888
INFO: Started server process [95920]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on
http://127.0.0.1:8888 (Press CTRL+C to quit)
INFO: 127.0.0.1:41918 - "GET /version HTTP/1.1" 200 OK
INFO: 127.0.0.1:41920 - "GET /update HTTP/1.1" 200 OK
INFO: 127.0.0.1:41936 - "GET /version HTTP/1.1" 200 OK
Für das Beispiel habe ich nur die main.py gepackt.
Danach habe ich den Versionsstring in der main.py auf 1.3.3.7 geändert, um ein Update zu erzwingen.
Das ist halt nur ein Beispiel, wie man es machen könnte.
Was man aber beachten muss, dass die Anwendung den richtigen Versionsstring hat, da man sich ansonsten eine hässliche Endlosschleife einfängt. Anwendung startet, server meldet ist zu alt, Anwendung wird aktualisiert und erneut gepfüft, weicht aber ab und wird dann erneut heruntergeladen usw..
Besser ist es, sich für sowas Scripte zu schreiben, die das Projekt automatisch packen und auch den Versionsstring auf dem Webserver aktualisiert und ggf. den Downloadlink anpasst, sofern sich der Dateiname des Downloads ändert.
Wenn das Programm aus mehreren Dateien besteht, kann man den Versionsstring auch in einer Datei abspeichern. Selbst das ließe sich z.B. mit Github automatisieren. Ich mage diese Abhängigkeit aber nicht, seit dem MS das Unternehmen gekauft hat.