Python-Integration von Gitlab, Jira und Confluence zur Automatisierung von Release-Builds

Kürzlich hat ein Kollege im Stand-up-Modus einen rationalen Vorschlag gemacht : Die Automatisierung des Release-Builds basiert auf bereits vorgefertigten Methoden für die Interaktion mit Jira, die in Python geschrieben wurden.

Der Bereitstellungsprozess sieht wie folgt aus: Wenn sich eine ausreichende Anzahl getesteter Aufgaben ansammelt, geben sie in jedem von den Aufgaben betroffenen Projekt einen Kandidatenkandidaten (RC) frei, und die Aufgaben werden als Teil von RC getestet. Danach wird der RC auf den Staging-Server gegossen, wo er in unmittelbarer Nähe der Kampfumgebung noch getestet und eine vollständige Regression durchgeführt wird. Nach den erforderlichen Bereitstellungsaktionen wird die neue Version in den Master gegossen.

Bis vor kurzem wurde der gesamte Montageprozess von einem der Entwickler manuell durchgeführt. Das dauerte eine Stunde, zwei oder mehr Mal und schien mir kein sehr interessanter Beruf zu sein. Wenn nun fast alles fertig ist, dauert die Freigabe von 20 Aufgaben, die 5 Projekte betreffen, weniger als eine Minute. Es bleibt natürlich Konfliktlösung, Ausführen verpasster Tests und mehr, aber selbst wenn man dies berücksichtigt, wird die Zeit der Entwickler und Tester, die warten müssen, bis sich jemand als erster befreit und RC erstellt, viel gespart.

Im Allgemeinen habe ich mich an die Aufgabe gemacht, und es hat sich als sehr interessant und faszinierend herausgestellt. Und was braucht man sonst noch zum Vergnügen der Arbeit, wenn nicht spannende Projekte?

Ich habe Legacy studiert: Es stellte sich heraus, dass die Jira-API direkt verwendet wurde, und es wurde geschrieben, es schien mir nicht optimal. Die Liste der Release-Aufgaben wurde beispielsweise wie folgt erstellt: Alle vorhandenen Releases wurden von Jira heruntergeladen, und dann wurde jedes nach Namen mit dem Namen unseres Releases verglichen, bis das gewünschte gefunden wurde:

def get_release_info(config):

   try:

       release_input = sys.argv[1]

   except IndexError:

       raise Exception('Enter release name')

   releases_json = requests.get(url=RELEASES_LIST_URL, auth=(login, jira_password).json()

   for release in releases_json:

       if release['name'] == release_input:

                ...


Im Allgemeinen führt die direkte Interaktion mit der API zu nicht sehr lesbarem Code. Und ich wollte kein Fahrrad erfinden. Meine erste Suche in Github führte mich zur JIRA Python Library, einer ziemlich einfachen und leistungsstarken Bibliothek. Ich hatte ursprünglich vor, Marge-Anfragen mit der GitPython-Bibliothek zu erstellen, die auch auf Github zu finden ist. Eine weitere Untersuchung des Problems führte jedoch sofort zu der Idee, etwas zu finden, das nicht mit Git, sondern sofort mit Gitlab zu tun hat. Aus diesem Grund habe ich mich für die bekannteste Lösung entschieden: Python GitLab.

Ich begann damit, eine Liste der Aufgaben im entsprechenden Status für die Version zu erhalten . Ich habe mich entschlossen, die vorherige Lösung nicht stark zu ändern, sondern sie effektiver zu gestalten, indem ich sofort die API der Aufgabe der gewünschten Version anfordere:

fix_issues = jira.search_issues(f'fixVersion={release_input}')

fix_id = jira.issue(fix_issues.iterable[0]).fields.fixVersions[0].id

Obwohl höchstwahrscheinlich dasselbe unter der Haube passiert, stellte sich heraus, dass es schöner und optimaler war, aber immer noch nicht sehr gut lesbar. Ferner ist es aus den empfangenen Aufgaben erforderlich, Links zu sammeln, um Anforderungen zusammenzuführen. Ich habe beschlossen, die gefundenen Zusammenführungsanforderungen in namedtuple zu speichern. Sie eignen sich hervorragend dafür:

Merge_request = namedtuple('Merge_request', ['url', 'iid', 'project', 'issue'])

Zusammenführungsanfragen wurden auch über die Jira-API empfangen:

projects = set()

links_json = requests.get(url=REMOTE_LINK.format(issue_number),

                            auth=login,jira_password).json()

for link in links_json:

   url_parts = link['object']['url'].split('/')

   project = f'{url_parts[4]}'

   iid = url_parts[6]

   projects.add(project)   

Danach entschied ich, wo ich die gefundenen Bibliotheken verwenden könnte . Dann werde ich vielleicht diese Stücke umgestalten.

Als nächstes müssen Sie überprüfen, ob plötzlich bereits die richtigen RC-Zweige vorhanden sind. Wenn bereits Versuche zum Erstellen vorhanden waren, müssen diese gelöscht und neue erstellt werden. Ich habe dies bereits mit der Python GitLab-Bibliothek gemacht:

gl = gitlab.Gitlab('https://gitlab...ru/', private_token=GITLAB_PRIVATE_TOKEN)

pr = gl.projects.get(project)

try:

   rc = pr.branches.get(f'{RC_name}')

   rc.delete()

   pr.branches.create({'branch': f'{RC_name}', 'ref': 'master'})

except gitlab.GitlabError:

   pr.branches.create({'branch': f'{RC_name}', 'ref': 'master'})

Danach können Sie die Tabelle in der Jira-Montageaufgabe ausfüllen . Die Informationen in der Tabelle sind in den folgenden Spalten enthalten: Nr., Aufgabe, Priorität, Zusammenführungsanforderungen von der Aufgabe in RC, Status der Zusammenführungsanforderung (ob die in Gitlab bestandenen Tests, ob es Konflikte gibt oder nicht, wird gegossen / nicht gegossen).

Bei diesem Schritt stieß ich auf einen unangenehmen Gitlab-Fehler: Wenn die Änderungen zuvor im Zielzweig der Zusammenführungsanforderung eingefroren wurden, gibt die Gitlab-API beim Anfordern des Status der Zusammenführungsanforderung eine Antwort auf das Vorhandensein von Konflikten. Im Prinzip kann dies verstanden werden: Wenn es nichts zu gießen gibt, wird es nicht funktionieren, aber warum sollte man sagen, dass es an diesem Ort einen Konflikt gibt? In Gitlab-Foren werden seit einigen Jahren Leute gefragt, aber es gibt noch keine Antworten.

In meinem Fall führt dies zu einem falschen Konfliktstatus und zur manuellen Überprüfung der Zusammenführungsanforderung. Ich habe mir noch nichts ausgedacht.

Die Logik beim Erstellen von Zusammenführungsanforderungen lautet wie folgt: Wenn Tests nicht bestanden werden oder ein Konflikt vorliegt, werden Zusammenführungsanforderungen erstellt, die jedoch nicht in RC fließen. Die entsprechenden Status werden in die Tabelle aufgenommen.

Um die Ausführung von Tests zu überprüfen, suchen wir nach einer Liste geeigneter Pipelines. Gitlab gibt sie sortiert nach Datum in absteigender Reihenfolge aus, genau wie wir es brauchen. Wir nehmen das erste - es wird das sein, was benötigt wird:

pipelines = project.pipelines.list(ref=f'{issue}')

if pipelines:

   pipelines = pipelines[0]

   if pipelines.attributes['status'] != 'success':

       status = '(x)   !, '

Erstellen Sie dann die Zusammenführungsanforderungen selbst . Wir suchen nach offenen, falls dies nicht der erste Versuch ist, etwas zu bauen. Wenn die erforderliche Zusammenführungsanforderung nicht vorhanden ist, erstellen Sie Folgendes:

mr = project.mergerequests.list(

 state='opened',

source_branch=source_branch,         target_branch=target_branch)

if mr:

   mr = mr[0]

else:

   mr = project.mergerequests.create(

{'source_branch': source_branch,

           'target_branch': target_branch,

           'title': f"{(MR.issue).replace('-', '_')} -> {RC_name}",

           'target_project_id': PROJECTS_NAMES[MR.project],})

status = mr.attributes['merge_status']

url = mr.attributes['web_url']

return status, url, mr

MR.issue.replace ('-', '_') - Ändern Sie den Namen der Aufgabe, um Gitlabas nicht informative Kommentare zur Aufgabe in Jira zu entfernen.

Als nächstes betrachten wir den Status der empfangenen Zusammenführungsanforderung : Wenn kein Konflikt vorliegt, gießen wir ihn in RC ein. Wenn es einen Konflikt gibt, notieren Sie die entsprechenden Status und lassen Sie sie manuell überprüfen.

Analog dazu erstellen wir für alle Projekte Zusammenführungsanforderungen von RC zu Staging und von Staging zu Master. Sie werden nach Überprüfung des gesamten Release-Builds in RC-Zweigen gegossen.

Der Jira-Aufgabenvorlage wurden zwei Felder hinzugefügt: für Pre-Pre-Post- und Post-De-Post-Aktionen. Für alle beteiligten Aufgaben sammelt der Algorithmus eine Liste solcher Aktionen und gibt die Montageaufgabe ein, um nichts zu vergessen.

Und schließlich ist die Ausgabe in Jira die Erstellung einer Montageaufgabe :

existing_issue = jira.search_issues(

f'project=PROJ AND summary ~ " {release_name}"')

if existing_issue:

   existing_issue = existing_issue[0]

   existing_issue.update(fields={

       'description': message,

       'customfield_xxx': message_before_deploy,

       'customfield_yyy': message_post_deploy,})

else:

   issue_dict = {

       "fixVersions": [{"name": release_name,}],

       'project': {'key': 'PROJ'},

       'summary': f" {release_name}",

       'description': message,

       'issuetype': {'name': 'RC'},  #      

       'customfield_xxx': message_before_deploy,

       'customfield_yyy': message_post_deploy,}

   new_issue = jira.create_issue(fields=issue_dict)

Weitere Gedanken sind wie folgt : Ausführen eines Skripts auf Gitlab mit einem Web-Hook von Jira. Sie können beispielsweise einen Webhook erstellen, um eine Montageaufgabe zu erstellen (einen speziellen Aufgabentyp dafür erstellen), oder eine Aufgabe mit dem Wort "Baugruppe" im Titel erstellen. Und Gitlab an diesem Hook führt entweder ein Bash-Skript aus, das den gesamten Prozess startet, oder es erstellt ein Docker-Image mit Python und führt ein Skript darauf aus. Obwohl die zweite Option bereits zu kompliziert ist. Ja, und Sie benötigen technische Konten in Jira und Gitlab. Im Allgemeinen gibt es noch keine endgültige Entscheidung.

Nach dem Erstellen von RC-Zweigen können Sie auch einen Prüfstand für die erforderlichen Zweige bereitstellen und Regressionstests ausführen. Jenkins kann damit umgehen. Dies ist aber auch vorerst geplant.

Dies alles, um die Arbeit der Tester zu beschleunigen und Entwickler von Routinearbeiten zu befreien. Der wirtschaftliche Sinn ist hier jedoch auch sehr spezifisch: Nehmen Sie einen durchschnittlichen Entwickler (im luftleeren Raum) mit einem hypothetischen Gehalt von 150.000 Rubel für 8 Stunden Arbeitszeit pro Tag. Zwei Jahre lang hatten wir ungefähr 700 Veröffentlichungen - dies ist ungefähr die Veröffentlichung pro Tag. Einige mehr, andere weniger, aber im Durchschnitt, glaube ich, war mindestens eine Stunde der Zeit des Entwicklers für die Erstellung der Version vergangen. Das heißt, die Automatisierung dieses Prozesses spart dem Unternehmen mindestens 150.000 / 8 = 18.750 Rubel pro Monat.

Unterwegs habe ich ein separates Skript für mich erstellt, in dem Statistiken zur kommenden Version angezeigt werden: Wie viele Aufgaben, welche Projekte sind betroffen usw. Wenn ich in keinem der Projekte in Gitlab den Entwicklerstatus habe, wird beim Erstellen von Zusammenführungsanforderungen der Zugriff verweigert. Außerdem ist es praktisch, im Voraus über Bereitstellungsaktionen Bescheid zu wissen oder eine Aufgabe zu erfassen, die versehentlich in diese Version fällt. Die Verteilung von Versionsbenachrichtigungen wurde mithilfe der SMTP- und E-Mail-Module behoben.

Ich war mit der Lösung dieses Problems zufrieden: Es ist immer interessant, etwas Neues zu lernen und in die Praxis umzusetzen. Es wird schön sein, wenn diese Erfahrung für jemanden nützlich ist.

All Articles