Git Scripting-Schnittstelle verwenden
CODESYS Git stellt eine Scripting-Schnittstelle für Git zur Verfügung. Im Folgenden werden Ihnen Beispiele zur Verwendung der Schnittstelle aufgezeigt. Weiter unten finden Sie außerdem Informationen zur textbasierten Ausgabe der Meldungen, die bei vielen Git-Operationen erzeugt werden.
Für weitere Informationen zum Arbeiten mit der Scripting-Schnittstelle siehe: Script Engine API Documentation
Voraussetzungen
Für die Ausführung der folgenden Beispiele wird vorausgesetzt:
CODESYS 3.5.19.30 oder höher
Zusätzlich werden die folgenden Komponenten benötigt:
CODESYS Library Documentation Support (für das Erstellen der Compiled Library)
CODESYS Git 1.6.0.0 oder höher
Eine lokale Git-Installation
Wichtig
Verwenden Sie möglichst SecureString-Passwörter
Für eine erhöhte Sicherheit sollten Passwörter als .NET-SecureString übergeben werden.
Die davon betroffenen git-Operationen sind: clone
, fetch
, pull
, push
SecureStrings können in IronPython wie folgt erstellt werden. Das "Passwort“ sollte dabei selbst aus einem sicheren String stammen und nicht, wie hier zu Demonstrationszwecken, im Klartext im Skript stehen. Intern wird jedes übergebene Passwort sicher behandelt:
from System.Security import SecureString sec_str_password = SecureString() for c in "Passwort": sec_str_password.AppendChar(c)
Für weitere Security-Maßnahmen bei der Verwendung von CODESYS Git siehe: Security für CODESYS Git
Vorbereitung
CODESYS Bibliothek
Da aktuell keine CODESYS-Bibliotheken in Git verwaltet werden, werden die Quellen einer CODESYS-Bibliothek benötigt. In den Beispielen wird die Bibliothek String Functions.library
des Produkts CODESYS String Libraries verwendet.
Remote-Git-Repository
Für das vorliegende Beispiel wird ein Git-Bare-Repository im Dateisystem als Remote-Repository verwendet.
Zur Vorbereitung wird das entsprechende Verzeichnis zunächst gelöscht und dann neu angelegt.
import shutil import os def prepare_empty_dir(empty_dir_path): print("Prepare empty directory at", empty_dir_path) shutil.rmtree(empty_dir_path, ignore_errors=True) if not(os.path.exists(empty_dir_path) and os.path.isdir(empty_dir_path)): os.makedirs(empty_dir_path)
Anschließend wird ein leeres Bare-Git-Repository angelegt.
import subprocess def create_bare_git_repository(bare_repository_path): print("Create bare git repository at", bare_repository_path) create_bare_repository_cmd = 'cmd /c "git -C \"' + bare_repository_path + '\" init --bare"' try: retcode = subprocess.call(create_bare_repository_cmd, shell=True) if retcode < 0: raise Exception("Creating bare git repository at " + bare_repository_path + " failed: ", -retcode) else: print("Creating bare git repository at " + bare_repository_path + " succeeded.") except Exception as e: print("[ERROR] Creating bare git repository failed: ", e) raise
Das angelegte leere Bare-Git-Repository wird mit dem Inhalt der CODESYS-Bibliothek befüllt.
def initialize_bare_git_repository(library_path, local_repository_path, bare_repository_path): print("Open library:", library_path) project = projects.open(library_path) print("Initiate local git repository") project.git.init(local_repository_path) project.git.commit_complete("Create git repo for lib", "user", "mail@mail") print("Push to remote git repository") origin_remote = project.git.remote_add("origin", bare_repository_path) project.git.branch_set_upstream_to(origin_remote) project.git.push() project.git.de_init(cleanUpFileSystem=True) project.close()
Das folgende Skript führt die beschriebenen Funktionen aus.
import os def main(): if projects.primary: projects.primary.close() basepath = "C:\\CODESYS_Projects\\Git_Scripting_Tutorial\\" project_basepath = os.path.join(basepath, "projects\\") library_file_name = "ExampleLib1.library" library_path = os.path.join(project_basepath, library_file_name) remote_repo_basepath = os.path.join(basepath, "remotes\\") remote_repo_directory_name = "ExampleLib1RemoteRepo" remote_repo_path = os.path.join(remote_repo_basepath, remote_repo_directory_name) local_repo_basepath = os.path.join(basepath, "repos\\") local_repo_directory_name = "ExampleLib1LocalRepo" local_repo_path = os.path.join(local_repo_basepath, local_repo_directory_name) print("Create and push library to remote git repository") prepare_empty_dir(remote_repo_path) create_bare_git_repository(remote_repo_path) initialize_bare_git_repository(library_path, local_repo_path, remote_repo_path) print("[Success] All done") if __name__ == '__main__': main()
Das auf diese Weise erstellte und mit Inhalt versehene Bare-Git-Repository wird für die weiteren Bespiele genutzt.
Clonen eines Git-Remote-Repository
Die folgende Funktion führt git clone
für ein Git-Remote-Repository aus.
def clone_git_repository(project_basepath, project_file_name, remote_repo_url_or_path, local_repo_path): update_flags = VersionUpdateFlags.UpdateAll | VersionUpdateFlags.SilentMode project = git.clone(project_basepath, project_file_name, remote_repo_url_or_path, local_repo_path, update_flags=update_flags) project.save() return project
Erstellen und Mergen eines neuen Branches
Die folgende Hilfsfunktion erzeugt exemplarisch einige neue Objekte in einem CODESYS-Projekt.
def add_dut(project): ST_STRUCT_STR = """\ a : BOOL; b : BIT; c : BIT; """ ST_UNION_STR = """\ TYPE ExampleUnion : UNION Zahl : INT; Prozent : ExampleAlias; Bits : ExampleStruct; END_UNION END_TYPE """ # Create a struct DUT and insert the list of variables just into the right # place in line two, row 0 (line numbering starts with line 0) example_dut_struct = project.create_dut('ExampleStruct') # DutType.Structure is the default example_dut_struct.textual_declaration.insert(2, 0, ST_STRUCT_STR) # Alias types get their "content" via the base type, which will just end up # as one line in the declaration part: # TYPE MyAlias : INT (0..100); END_TYPE example_dut_alias = project.create_dut('ExampleAlias', DutType.Alias, "INT (0..100)") # Instead of injecting the variables into the existing declaration, # one can also just replace the complete declaration part, including the # boilerplate code. example_dut_union = project.create_dut('ExampleUnion', DutType.Union) example_dut_union.textual_declaration.replace(ST_UNION_STR)
Die folgende Hilfsfunktion inkrementiert die Build-Version in den Projektinformationen eines CODESYS-Projekts.
def increment_build_version(project): """ Increment build version in project info. """ info = project.get_project_info() old_version = info.version info.version = (old_version.Major, old_version.Minor, old_version.Build + 1, 0) project.save()
Die folgende Funktion erstellt zunächst einen neuen Branch, führt in diesem Branch Änderungen aus und merget anschließend diese Änderungen zurück in den Hauptbranch.
def copy_branch_and_merge(project): current_branch = project.git.branch_show_current() print("Current branch: ", current_branch.friendly_name) project, current_branch = project.git.branch_copy(current_branch, "new_branch", checkout=True) print("Current branch: ", current_branch.friendly_name) add_dut(project) project.git.commit_complete("Added DUT", "user", "mail@mail") increment_build_version(project) project.git.commit_complete("Incremented build version", "user", "mail@mail") project, current_branch = project.git.checkout("master") print("Current branch: ", current_branch.friendly_name) project, merge_result = project.git.merge("new_branch") print("Merged: ", merge_result.ToString()) project.save() return project
Das folgende Skript führt git clone
für ein Git-Remote-Repository aus, führt Änderungen im Projekt durch und pusht die Änderungen anschließend in das Git-Remote-Repository (CopyBranchAndMerge.py).
def main(): if projects.primary: projects.primary.close() basepath = "C:\\CODESYS_Projects\\Git_Scripting_Tutorial\\" project_basepath = os.path.join(basepath, "projects\\") library_file_name = "ExampleLib1Cloned.library" remote_repo_basepath = os.path.join(basepath, "remotes\\") remote_repo_directory_name = "ExampleLib1RemoteRepo" remote_repo_path = os.path.join(remote_repo_basepath, remote_repo_directory_name) local_repo_basepath = os.path.join(basepath, "repos\\") local_repo_directory_name = "ExampleLib1LocalRepo" local_repo_path = os.path.join(local_repo_basepath, local_repo_directory_name) print("Clone project") project = clone_git_repository(project_basepath, library_file_name, remote_repo_path, local_repo_path) project = copy_branch_and_merge(project) project.git.push() project.save() project.git.de_init(cleanUpFileSystem=True) project.save() project.close() print("[Success] All done") if __name__ == '__main__': main())
Erzeugen einer Compiled Library
Das folgende Skript führt git clone
für eine CODESYS-Quellbibliothek aus einem Git-Remote-Repository aus und erstellt anschließend daraus eine compilierte Bibliothek (CreateCompiledLibrary.py).
import os class CompileError(Exception): pass def clone_git_repository(project_basepath, project_file_name, remote_repo_url_or_path, local_repo_path): update_flags = VersionUpdateFlags.UpdateAll | VersionUpdateFlags.SilentMode project = git.clone(project_basepath, project_file_name, remote_repo_url_or_path, local_repo_path, update_flags=update_flags) project.save() return project def create_compiled_library(project): # requires the CODESYS Library Documentation Support Package! project.check_all_pool_objects() compile_result_message = system.get_messages(category='{97F48D64-A2A3-4856-B640-75C046E37EA9}')[-1] if "0 errors" in compile_result_message: project.save_as_compiled_library(destination_name=None) else: raise CompileError("Compile failed: " + compile_result_message) return project basepath = "D:\\JiraTickets\\GIT-145\\" project_basepath = os.path.join(basepath, "projects\\") remote_repo_basepath = os.path.join(basepath, "remotes\\") remote_repo_directory_name = "StringFunctions.git" remote_repo_path = os.path.join(remote_repo_basepath, remote_repo_directory_name) local_repo_basepath = os.path.join(basepath, "repos\\") local_repo_path = os.path.join(local_repo_basepath, "StringFunctions.git") print("Clone project") project = clone_git_repository(project_basepath, "String Functions Cloned.library", remote_repo_path, local_repo_path) project = create_compiled_library(project) project.git.de_init(cleanUpFileSystem=True) project.close() print("[Success] All done")
Installieren einer Bibliothek aus einem Git Remote Repository
Das folgende Skript führt git clone
für eine CODESYS-Quellbibliothek aus einem Git-Remote-Repository aus und installiert diese Bibliothek in der aktuellen CODESYS-Instanz (InstallLibrary.py).
import os def clone_git_repository(project_directory_path, project_file_name, remote_repo_url_or_path, local_repo_path): update_flags = VersionUpdateFlags.UpdateAll | VersionUpdateFlags.SilentMode project = git.clone(project_directory_path, project_file_name, remote_repo_url_or_path, local_repo_path, update_flags=update_flags) project.save() return project def install_library(project): library_repo = librarymanager.repositories[0] librarymanager.install_library(project.path, library_repo, True) def main(): if projects.primary: projects.primary.close() basepath = "C:\\CODESYS_Projects\\Git_Scripting_Tutorial\\" project_basepath = os.path.join(basepath, "projects\\") library_file_name = "ExampleLib1Cloned2.library" remote_repo_basepath = os.path.join(basepath, "remotes\\") remote_repo_directory_name = "ExampleLib1RemoteRepo" remote_repo_path = os.path.join(remote_repo_basepath, remote_repo_directory_name) local_repo_basepath = os.path.join(basepath, "repos\\") local_repo_directory_name = "ExampleLib1LocalRepo" local_repo_path = os.path.join(local_repo_basepath, local_repo_directory_name) print("Clone project") project = clone_git_repository(project_basepath, library_file_name, remote_repo_path, local_repo_path) print("Install library") install_library(project) project.git.de_init(cleanUpFileSystem=True) project.close() print("[Success] All done") if __name__ == '__main__': main()
Meldungsausgaben bei Git-Operationen
Für die Nutzung von CODESYS Git liefern die meisten Befehle eine textbasierte Ausgabe. Diese wird im Betrieb von CODESYS in der Kommandozeile automatisch bei der Ausführung von CODESYS Git-Befehlen über den Script-Treiber ausgegeben. Beim Verwenden von CODESYS Git in CODESYS Development System erscheinen die Ausgaben auch im Meldungsfenster.
Aufbau der Meldungen: Git: <severity>: [<time>] <text>
In der Benutzeroberfläche von CODESYS Git reduziert sich die Ausgabe auf: [<time>] <text>
severity
: Meldungskategorie. Die Kategorien reichen von reiner Information bis zu kritischem Fehlertime
: Genauer Zeitpunkt der Meldung. FormatHH:MM:SS
text
: Inhalt der Meldung. Bei Standard-Git-Befehlen entspricht der Inhalt dem Kommandozeilen-Befehl, der dasselbe Resultat liefern würde wie der getätigte Script-Treiber-Aufruf. Bei CODESYS Git-Befehlen, die keinem Standard-Git-Befehl entsprechen, beispielsweise Projekt aus Repository neu erstellen, erklärt der Meldungstext die ausgeführte Aktion.
Befehle mit mehreren Meldungen:
Für manche Befehle (beispielsweise git log
wird die Ausgabe auf mehrere Meldungen aufgeteilt. Im Fall von git log
wird jeder angezeigte Commit in einer eigenen Meldung angezeigt. Um in diesem Fall zu verdeutlichen, dass diese Meldungen Teil des git log
-Befehls sind, wird in den Meldungen auf den ursprünglichem Befehl verwiesen.