Version / Datum eines Skriptes ermitteln

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
drnicolas
User
Beiträge: 105
Registriert: Sonntag 24. Juli 2016, 10:32

Wie kann ich eine Art Versionsnummer und das Datum eines Skriptes ermitteln?

hintergrund: Ich muss ein Skript auf mehreren Rechnern aktualisieren. Das soll zwar mittelfristig geändert werden, aber aktuell ist es halt so.

Das Aktualisieren mache ich über ein privates Git-Repo - gibt's was besseres?

trotzdem würde ich gerne Informationen zum Versionsstand in meine Logs integrieren

Gibt's da was cleveres?
Sirius3
User
Beiträge: 18253
Registriert: Sonntag 21. Oktober 2012, 17:20

Wenn Du schon git benutzt, dann benutze git-Tags für die Versionierung.
Zusätzlich hat man eine pyproject.toml-Datei mit der man via pip + setuptools ein installierbares Paket baut.
https://packaging.python.org/en/latest/ ... ject-toml/
https://realpython.com/python-pyproject-toml/
imonbln
User
Beiträge: 190
Registriert: Freitag 3. Dezember 2021, 17:07

Eigentlich kann man auf alle deine Fragen, wie ein geschickter Anwalt antworten, es kommt darauf an!

Bei der Frage nach der Versionsnummer/Datum, kommt es auf deine Arbeisweise an. Wenn du die Aktualisierung mit git clone gemacht hast und immer brav alles committet hast, sagt dir der Commit Hash welche Version du hast. Wenn das Skript aber einfach kopiert wurde und da so liegt, wird es schwer zu sagen welche Version es ist. Solltest du wie @Sirius3 vorgeschlagen hat, ein installierbares Paket gebaut haben, hast du da deine Versionierung.

Bei der frage, ob es was Besseres, zum Aktualisieren gibt, als ein privates Git-Repository, lautet das, es kommt darauf an, was gibt es denn noch so im Netzwerk? Wenn du zum Beispiel mit Linux Servern hantierst du die regelmäßig Updatest, könnte es sinnvoll sein das du dein Skript in das Paketformat deiner Distribution verpackst und mit den Normalen Systemupdates verteilst.

Die Frage wie du die Information der Version mit in die Logs schreibst, hängt wieder stark davon ab, wie du die anderen zwei Fragen für dich beantwortet hast.

Aus einer Python-only Sichtweise, hat @Sirius3 recht und du solltest diesen Weg gehen. Aber im Gesamtkonzept können auch andere Lösungen besser zum System passen, wie ich versucht habe aufzuzeigen.
drnicolas
User
Beiträge: 105
Registriert: Sonntag 24. Juli 2016, 10:32

Vielen Dank.
Das sind erstmal gute Stichworte zum Googeln.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1224
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

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.

Code: Alles auswählen

tar -czf data main.py
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.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Antworten