GitLab erlaubt es automatisch nach einem Commit Befehle auszuführen. Dadurch ist es möglich, automatisiert Tests auszuführen, um beispielsweise die Funktionsfähigkeit einer Anwendung zu überprüfen.

Kürzlich war es für ein Projekt notwendig, in regelmäßigen Abständen – zum Beispiel monatlich – einen Release zu erstellen, der auf GitLab veröffentlicht werden soll. An und für sich wird diese Funktionalität von GitLab (noch) nicht unterstützt (https://gitlab.com/gitlab-org/gitlab-ce/issues/63858)

Über die GitLab API ist es allerdings möglich, einen Release zu erstellen. Um es im CI/CD-Prozess zu ermöglichen, muss lediglich diese API angesprochen werden. Dazu gibt es bereits ein Docker-Image, dass die Arbeit wesentlich erleichtert (https://github.com/inetprocess/gitlab-release)

Doch wie kann eine konkrete Implementierung des Prozesses aussehen? Wie kann zu bestimmten Zeitpunkten automatisch ein Release erstellt werden?

Ein Release kann grundsätzlich nur aus einem Tag erstellt werden. Daher ist der erste Schritt, automatisiert solch einen zu erstellen. Dazu erstellt man in der Datei .gitlab-ci.yml einen Abschnitt, der nur ausgeführt werden soll, wenn er vom sogenannten Scheduler ausgeführt wird.

YAML
.gitlab-ci.yml

Hier wird – mit einem Access-Token als Benutzer-Authentifizierung – ein Tag erstellt. Dazu muss allerdings dieser Token erstellt und hinterlegt werden. Unter den Benutzereinstellungen kann unter Access Tokens ein Schlüssel erstellt werden. Die notwendigen Berechtigungen sind api und write_repository.

Danach wird einmalig ein Access Token angezeigt – dieser muss unter den Projekteinstellungen CI / CD im Bereich Variables hinterlegt werden. Der Name soll in diesem Fall GITLAB_ACCESS_TOKEN lauten:

Wichtig – vor allem in öffentlichen Repositories – ist, dass unbedingt Protected sowie Masked aktiviert ist. Ansonsten scheint der Access Token auch für unberechtigte Benutzer auf.
Da selbst mit der Protected-Einstellung sogenannte Projekt-Maintainer den Wert auslesen können – und somit Aktionen als anderer Benutzer ausführen könnten – wurde in unserem Projekt ein eigener, eingeschränkter, Benutzer angelegt, der nur für diese Aufgabe erstellt wurde.

Um im automatisch angelegten Tag auf diese Variable zugreifen zu können, muss der Tag geschützt werden. Im obigen Codeausschnitt haben wir festgelegt, dass die Tags die vom Scheduler erstellt werden, mit „Release“ beginnen. Somit schützen wir alle Tags, die diesem Muster entsprechen:

Ist dieser Schritt erfolgt, kann bereits der Scheduler konfiguriert werden:

Hier kann konfiguriert werden, wie oft der zukünftiger Release automatisch erstellt werden soll. Entspricht keines der vorgegebenen Intervalle den Anforderungen, kann das Intervall im Format von CRON vorgegeben werden (https://de.wikipedia.org/wiki/Cron#Beispiele). Nachdem in .gitlab-ci.yml hinterlegt wurde, dass für einen Scheduler-Aufruf ein Tag erstellt werden soll, ist auch dieser Schritt erledigt.

Um nun daraus einen Release zu erstellen, wird in .gitlab-ci.yml noch ein Abschnitt erstellt, der nur für die erstellten Tags ausgeführt wird:

YAML
.gitlab-ci.yml

Danach wird jedes Mal wenn der Scheduler ausgeführt wird, ein Release vom aktuellen Stand des Master-Branches erstellt.

Zum Abschluss noch eine vollständige Version einer möglichen .gitlab-ci.yml-Datei:

YAML
.gitlab-ci.yml

Beteilige dich an der Unterhaltung

7 Kommentare

  1. Super Beitrag! Aber bei Connection zu einer Gitlab SSL-URL fliegt das Script mit Fehler raus, obwohl das entsprechende Root-Zertifikat im System-Store vorhanden ist.

      1. Nein, der Link hat denke ich nichts damit zu tun.
        Hier der Error Trace:
        root@aa54c6e4de35:/build/java-app# gitlab-release
        Traceback (most recent call last):
        File „/usr/local/lib/python3.6/dist-packages/urllib3/connectionpool.py“, line 672, in urlopen
        chunked=chunked,
        File „/usr/local/lib/python3.6/dist-packages/urllib3/connectionpool.py“, line 376, in _make_request
        self._validate_conn(conn)
        File „/usr/local/lib/python3.6/dist-packages/urllib3/connectionpool.py“, line 994, in _validate_conn
        conn.connect()
        File „/usr/local/lib/python3.6/dist-packages/urllib3/connection.py“, line 360, in connect
        ssl_context=context,
        File „/usr/local/lib/python3.6/dist-packages/urllib3/util/ssl_.py“, line 370, in ssl_wrap_socket
        return context.wrap_socket(sock, server_hostname=server_hostname)
        File „/usr/lib/python3.6/ssl.py“, line 407, in wrap_socket
        _context=self, _session=session)
        File „/usr/lib/python3.6/ssl.py“, line 817, in __init__
        self.do_handshake()
        File „/usr/lib/python3.6/ssl.py“, line 1077, in do_handshake
        self._sslobj.do_handshake()
        File „/usr/lib/python3.6/ssl.py“, line 689, in do_handshake
        self._sslobj.do_handshake()
        ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:852)

        During handling of the above exception, another exception occurred:

        Traceback (most recent call last):
        File „/usr/local/lib/python3.6/dist-packages/requests/adapters.py“, line 449, in send
        timeout=timeout
        File „/usr/local/lib/python3.6/dist-packages/urllib3/connectionpool.py“, line 720, in urlopen
        method, url, error=e, _pool=self, _stacktrace=sys.exc_info()[2]
        File „/usr/local/lib/python3.6/dist-packages/urllib3/util/retry.py“, line 436, in increment
        raise MaxRetryError(_pool, url, error or ResponseError(cause))
        urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host=’gitlab‘, port=443): Max retries exceeded with url: /api/v4/projects/152/repository/tags/0.0.3 (Caused by SSLError(SSLError(1, ‚[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:852)‘),))

        During handling of the above exception, another exception occurred:

        Traceback (most recent call last):
        File „/usr/bin/gitlab-release“, line 150, in
        info = release.create_release(args.message, args.files)
        File „/usr/bin/gitlab-release“, line 92, in create_release
        existing_release = self.fetch_release()
        File „/usr/bin/gitlab-release“, line 72, in fetch_release
        res = self.request(‚get‘, url)
        File „/usr/bin/gitlab-release“, line 83, in request
        res = requests.request(method, url, **kwargs)
        File „/usr/local/lib/python3.6/dist-packages/requests/api.py“, line 61, in request
        return session.request(method=method, url=url, **kwargs)
        File „/usr/local/lib/python3.6/dist-packages/requests/sessions.py“, line 530, in request
        resp = self.send(prep, **send_kwargs)
        File „/usr/local/lib/python3.6/dist-packages/requests/sessions.py“, line 643, in send
        r = adapter.send(request, **kwargs)
        File „/usr/local/lib/python3.6/dist-packages/requests/adapters.py“, line 514, in send
        raise SSLError(e, request=request)
        requests.exceptions.SSLError: HTTPSConnectionPool(host=’gitlab‘, port=443): Max retries exceeded with url: /api/v4/projects/152/repository/tags/0.0.3 (Caused by SSLError(SSLError(1, ‚[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:852)‘),))

        1. Im Beispiel-Code wurde im Schritt publish das Docker-Image inetprocess/gitlab-release verwendet. Wird hier ein eigenes Image (mit hinterlegtem Root-Zertifikat) verwendet bzw. im publish-Schritt vor dem gitlab-release das Zertifikat hinterlegt?

          Falls nicht vielleicht mal das probieren. Das Docker-Image hätte nämlich grundsätzlich keinen Zugriff auf den System-Store des Servers. (Nachdem die Docker-Container getrennt vom eigentlichem Dateisystem sind)

          1. Sowohl in der .toml als auch in dem Image existiert das Zertifikat. Curl oder wget werfen keinen Fehler.

          2. PS: Fehler tritt bei Gitlab-CE und eigener CA auf.

            Wahrscheinlich ist die Library ‚urllib3′ welche (völlig unverständlicherweise‘) auf ein eigenes CA-Bundle (‚certifi‘) setzt, falls das Zertifikat nicht explizit übergeben wird.

Schreib einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.