In der schnelllebigen Welt der Webentwicklung sind kontinuierliche Integration und kontinuierliche Bereitstellung (CI/CD) zu unverzichtbaren Praktiken für die effiziente Bereitstellung hochwertiger Software geworden. CI/CD ermöglicht es Entwicklern, den Prozess des Erstellens, Testens und Verteilens von Codeänderungen zu automatisieren, das Risiko menschlicher Fehler zu verringern und schnellere Iterationen zu ermöglichen. In diesem Artikel erfährst du, warum CI/CD so wichtig ist, wie du eine CI-Pipeline erstellst und wie du die kontinuierliche Bereitstellung in deiner CI-Pipeline mit der Kinsta-API programmatisch einrichtest – alles mit GitHub Actions in deinem GitHub-Repository.
Warum CI/CD?
Die Anwendungs-Hosting-Plattform von Kinsta bietet seit jeher eine Option für die automatische Bereitstellung, die immer dann ausgelöst wird, wenn eine Änderung an einem bestimmten Branch in deinem gehosteten Git-Repository vorgenommen wird. Für große Projekte mit mehreren Teammitgliedern ist dies jedoch nicht unbedingt ideal. Viele Entwickler/innen neigen dazu, die automatische Bereitstellung aus verschiedenen Gründen nicht zu aktivieren.
Ein Grund dafür ist, dass in einer kollaborativen Umgebung, in der mehrere Entwickler/innen am selben Projekt arbeiten, automatische Verteilungen, die durch die Änderung eines/r Entwicklers/in am Repository ausgelöst werden, zu Instabilität und unvorhergesehenen Problemen führen können. Ohne eine ordnungsgemäße Prüfung und Validierung kann selbst eine kleine Codeänderung die Live-Site stören, was zu Ausfallzeiten und negativen Nutzererfahrungen führen kann.
An dieser Stelle kommt eine CI/CD-Pipeline ins Spiel. Mit einem sorgfältig orchestrierten CI/CD-Workflow können Entwickler/innen sicherstellen, dass Codeänderungen getestet und validiert werden, bevor sie auf der Live-Site eingesetzt werden. Es gibt viele Tools für die Implementierung von CI/CD in der Softwareentwicklung. In diesem Tutorial werden wir GitHub Actions verwenden.
Was ist GitHub Actions?
GitHub Actions ist ein leistungsstarkes Automatisierungstool von GitHub. Es bietet Entwicklern die Möglichkeit, verschiedene Aufgaben, Prozesse und Arbeitsabläufe innerhalb ihrer Softwareentwicklungsprojekte zu automatisieren. Es lässt sich in die GitHub-Repositories integrieren und ist daher einfach zu bedienen.
Mit GitHub Actions und der Kinsta-API kannst du benutzerdefinierte Workflows definieren, die zu deinen Projektanforderungen passen. Du kannst eine CI-Pipeline einrichten, die deine Anwendung testet und die Bereitstellung auf Kinsta anstößt.
Erste Schritte mit GitHub Actions
GitHub Actions basiert auf dem Konzept der Workflows, d.h. auf automatisierten Aufgaben, die durch bestimmte Ereignisse ausgelöst oder in regelmäßigen Abständen geplant werden. Zu diesen Ereignissen können Code-Pushes, Pull-Requests, das Erstellen von Problemen und vieles mehr gehören. Wenn eines dieser Ereignisse eintritt, führt GitHub Actions automatisch einen zugehörigen Workflow aus, der eine Reihe von vordefinierten Schritten ausführt.
Jeder Schritt im Workflow steht für eine bestimmte Aktion, z. B. das Erstellen des Codes, das Ausführen von Tests, das Verteilen oder das Senden von Benachrichtigungen. Lass uns einen Workflow mit drei Aufgaben erstellen:
- Syntax mit ESLint prüfen
- Tests ausführen
- Anwendung erneut bereitstellen
Schritt 1: Richte dein GitHub-Repository ein
Um mit GitHub Actions loszulegen, brauchst du ein GitHub Repository.
Hier verwenden wir dieses GitHub-Repository, das für das Tutorial How To Build and Deploy a ChatGPT Clone Application With React and OpenAI API entwickelt wurde.
Du kannst das Repository selbst verwenden, indem du auf GitHub dorthin navigierst und auswählst: Diese Vorlage verwenden > Ein neues Repository erstellen.
In dieser React-Anwendung werden Unit-Tests erstellt, um jede Komponente zu testen. ESLint wird auch verwendet, um eine perfekte Syntax und Codeformatierung zu gewährleisten. Die CI-Pipeline blockiert die Bereitstellung, wenn ein Pull Request oder zusammengeführter Code, der in das Repository eingestellt wird, die Workflow-Tests nicht besteht.
Schritt 2: Erstellen einer Workflow-Datei
Definiere deinen Workflow, indem du eine YAML-Datei im Verzeichnis .github/workflows deines Repositorys erstellst. Dieses Verzeichnis sollte sich auf der Stammebene deines Projektarchivs befinden. Die Namenskonvention für Workflow-Dateien ist Name-des-Workflows.yml.
- Erstelle in deinem Repository ein .github-Verzeichnis.
- Erstelle innerhalb des .github-Verzeichnisses ein neues Verzeichnis namens workflows.
- Im Verzeichnis workflows erstellst du eine neue Datei mit dem Namen build-test-deploy.yml.
Schritt 3: Schreibe den CI/CD-Workflow
Nachdem du deine Workflow-Datei erstellt hast, definierst du einen Workflow mit den notwendigen Schritten, um die Syntax mit ESLint zu prüfen, Tests durchzuführen und die Anwendung einzusetzen.
CI-Ereignis erstellen
Wenn du eine CI-Pipeline erstellst, musst du dem Workflow zunächst einen Namen geben und dann das Ereignis festlegen, das den Workflow auslösen soll. In diesem Beispiel sind die beiden Ereignisse eine Pull-Anforderung und ein Push auf den Hauptzweig.
name: Build, Test, and Deploy
on:
push:
branches: "main"
pull_request:
branches: "main"
Wenn du regelmäßige Jobs (CRON-Jobs) für bestimmte Aufgaben planen möchtest, kannst du sie dem Workflow hinzufügen. Du könntest zum Beispiel bestimmte Aufgaben wie Datenbanksicherungen, Datenbereinigung oder andere regelmäßige Wartungsaufgaben ausführen.
Hier ist ein Beispiel dafür, wie du einen CRON-Job zum Workflow hinzufügen kannst:
on:
# Existing event triggers for push and pull_request
# Add a schedule for CRON jobs
schedule:
- cron: "0 0 * * *"
Im obigen Beispiel wird der Workflow jeden Tag um Mitternacht (UTC-Zeit) ausgelöst, da der Cron-Zeitplan auf 0 0 * * *
eingestellt ist. Du kannst den Cron-Zeitplan an deine Bedürfnisse anpassen.
Ein weiteres Beispiel: Angenommen, du möchtest den CI/CD-Workflow so planen, dass er jeden Montag um 8 Uhr morgens ausgeführt wird. Wir können einen CRON-Job mit dem Ereignis schedule
einrichten:
name: Build, Test, and Deploy
on:
push:
branches: "main"
pull_request:
branches: "main"
# Schedule the workflow to run every Monday at 8 a.m. (UTC time)
schedule:
- cron: "0 8 * * 1"
jobs:
# Add jobs
Die Zeitplansyntax, die im schedule
Event für GitHub Actions Workflows verwendet wird, basiert auf der UNIX cron Syntax. Sie ermöglicht es dir, bestimmte Zeiten oder Intervalle festzulegen, in denen dein Workflow automatisch ausgeführt werden soll. Die Syntax besteht aus fünf Feldern, die verschiedene Aspekte des Zeitplans darstellen. Jedes Feld wird durch ein Leerzeichen getrennt. Das allgemeine Format der Zeitplansyntax ist wie folgt:
* * * * *
┬ ┬ ┬ ┬ ┬
│ │ │ │ │
│ │ │ │ └─ Day of the week (0 - 7) (Sunday to Saturday, where both 0 and 7 represent Sunday)
│ │ │ └─── Month (1 - 12)
│ │ └───── Day of the month (1 - 31)
│ └─────── Hour (0 - 23)
└───────── Minute (0 - 59)
Schauen wir uns die einzelnen Felder an:
- Minute (0 – 59): Die Minute, in der der Cron-Job ausgelöst wird. Zum Beispiel bedeutet
15
, dass der Workflow in der 15. Minute der Stunde ausgelöst wird. - Stunde (0 – 23): Die Stunde, zu der der Cron-Job ausgelöst wird. Zum Beispiel bedeutet
8
, dass der Arbeitsablauf um 8 Uhr morgens ausgelöst wird. - Tag des Monats (1 – 31): Der Tag des Monats, an dem der Cron-Job ausgelöst wird. Beispiel:
1
bedeutet, dass der Arbeitsablauf am ersten Tag des Monats ausgelöst wird. - Monat (1 – 12): Der Monat, in dem der Cron-Job ausgelöst wird. Zum Beispiel:
6
bedeutet, dass der Arbeitsablauf im Juni ausgelöst wird. - Wochentag (0 – 7): Der Wochentag, an dem der Cron-Job ausgelöst wird. Hier stehen
0
und7
für den Sonntag,1
für den Montag und so weiter.4
bedeutet zum Beispiel, dass der Arbeitsablauf am Donnerstag ausgelöst wird.
Sonderzeichen:
*
(Sternchen): Passt zu jedem Wert in diesem Feld. Zum Beispiel bedeutet*
im Feld Minute, dass der Workflow jede Minute ausgelöst wird.*/n
(Schrägstrich): Gibt ein Intervall an. Zum Beispiel bedeutet*/5
im Minutenfeld, dass der Workflow alle 5 Minuten ausgelöst wird.,
(Komma): Gibt mehrere spezifische Werte an. Zum Beispiel bedeutet1,15,30
im Minutenfeld, dass der Arbeitsablauf zur ersten, 15. und 30. Minute ausgelöst wird.-
(Bindestrich): Gibt eine Reihe von Werten an. Zum Beispiel bedeutet1-5
im Feld Wochentag, dass der Arbeitsablauf von Montag bis Freitag (1 bis 5) ausgelöst wird.?
(Fragezeichen): Wird verwendet, um keinen bestimmten Wert anzugeben. Es wird häufig im Feld für den Wochentag verwendet, wenn der Tag des Monats angegeben ist. Zum Beispiel bedeutet?
im Feld für den Wochentag und15
im Feld für den Monat, dass der Arbeitsablauf am 15. Tag des Monats, unabhängig vom Wochentag.
Erstellen eines CI-Jobs zur Überprüfung der Syntax mit ESLint
Um den CI-Prozess einzurichten, erstellen wir die notwendigen Jobs oder Aufgaben. Jeder Job sollte einen klaren und verständlichen Namen haben. Nennen wir den ersten Job eslint
, da er die Überprüfung der Codesyntax mit ESLint beinhaltet.
Außerdem können wir eine menschenlesbare Beschreibung angeben, obwohl dieser Teil optional ist. Als Nächstes legen wir fest, dass der Job in einer Ubuntu-Umgebung laufen soll und verwenden eine Matrixstrategie, um den Code gegen zwei Node.js-Versionen zu testen: 18.x
und 20.x
.
jobs:
eslint:
name: Check Syntax with ESLint
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x]
Als Nächstes definieren wir die Schritte, die der Job „ESLint“ ausführen soll. Diese Schritte umfassen das Auschecken des Codes, das Einrichten der angegebenen Node.js-Version für die Ausführung von ESLint, das Zwischenspeichern von npm-Paketen, das Installieren von Projektabhängigkeiten und schließlich das Ausführen von ESLint zur Überprüfung der Codesyntax.
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }} to Check Lint
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install Dependencies
run: npm ci
- name: Run ESLint
run: npm run lint
Im obigen Arbeitsablauf ist jeder Schritt mit einer Beschreibung und einem Namen versehen, damit die Fehlerquelle bei der Überprüfung des Arbeitsablaufs über GitHub Actions leicht zu identifizieren ist. Im dritten Schritt verwenden wir den Befehl npm ci
, um die Abhängigkeiten zu installieren. Dieser ist npm install
vorzuziehen, da er eine saubere Installation durchführt. Außerdem setzt der letzte Schritt, die Ausführung von ESLint mit npm run lint
, voraus, dass du diesen Befehl in deiner package.json-Datei konfiguriert hast.
Nachfolgend findest du die vollständige Aufgabe zur Überprüfung der Codesyntax mit ESLint:
jobs:
eslint:
name: Check Syntax with ESLint
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x]
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }} to Check Lint
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install Dependencies
run: npm ci
- name: Run ESLint
run: npm run lint
CI-Job zum Ausführen von Tests erstellen
Um den CI-Job zur Ausführung von Tests hinzuzufügen, definierst du zunächst den Job und gibst ihm einen beschreibenden Namen, z. B. tests
. Außerdem geben wir an, dass dieser Job vom Job eslint
abhängt, d.h. der Job eslint
wird zuerst ausgeführt, bevor der Job tests
ausgeführt wird. Durch diese Abhängigkeit wird sichergestellt, dass der Code auf Syntaxfehler geprüft wird, bevor die Tests ausgeführt werden.
tests:
name: Run Tests
needs: eslint
runs-on: ubuntu-latest
Als Nächstes definierst du die Schritte für den Job tests
. Ähnlich wie beim vorigen Job checken wir den Code aus, richten die Node.js-Version 18.x
ein, um die Tests auszuführen, installieren die Projektabhängigkeiten mit npm ci
und führen die Tests dann mit dem Befehl npm run test
aus.
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Use Node.js 18.x to run Test
uses: actions/setup-node@v3
with:
node-version: 18.x
cache: 'npm'
- name: Install Dependencies
run: npm ci
- name: Run Tests
run: npm run test
CI-Job für die Bereitstellung Deployment mit der Kinsta-API erstellen
Um den CI-Job für die Bereitstellung über die Kinsta-API zu erstellen, definieren wir den Job und nennen ihn deploy
. Dieser Job ist von den Jobs eslint
und tests
abhängig und stellt sicher, dass die Bereitstellung erst ausgeführt wird, nachdem der Code auf Syntaxfehler geprüft wurde und die Tests bestanden hat. Wir richten den Job so ein, dass er in einer Ubuntu-Umgebung mit der neuesten verfügbaren Version läuft.
deploy:
name: Re-Deploy Application
needs: [eslint, tests]
runs-on: ubuntu-latest
Als Nächstes legst du die Schritte fest. In diesem Fall führst du einen cURL-Befehl aus, um programmatisch mit der Kinsta-API zu interagieren und eine Neuverteilung auszulösen. Zunächst wollen wir die Kinsta-API verstehen, die verschiedenen Informationen, die für die Interaktion mit der API benötigt werden, und wie du wichtige Informationen, die mit der API verbunden sind – wie den API-Schlüssel – sicher auf GitHub abrufst und speicherst.
Die Kinsta-API verstehen
Die Kinsta-API ist ein leistungsstarkes Werkzeug, mit dem du programmgesteuert mit den Diensten von Kinsta interagieren kannst. Um die API zu nutzen, musst du ein Konto mit mindestens einer WordPress-Site, einer Anwendung oder einer Datenbank in MyKinsta haben. Außerdem musst du einen API-Schlüssel erstellen, um dich zu authentifizieren und über die API auf dein Konto zuzugreifen. So generierst du einen API-Schlüssel:
- Gehe zu deinem MyKinsta-Dashboard.
- Navigiere zur Seite mit den API-Schlüsseln (Dein Name > Unternehmenseinstellungen > API-Schlüssel).
- Klicke auf API-Schlüssel erstellen.
- Wähle ein Ablaufdatum oder lege ein benutzerdefiniertes Startdatum und die Anzahl der Stunden fest, nach denen der Schlüssel abläuft.
- Gib dem Schlüssel einen eindeutigen Namen.
- Klicke auf Generieren.
Nachdem du einen API-Schlüssel erstellt hast, kopiere ihn und bewahre ihn an einem sicheren Ort auf (wir empfehlen die Verwendung eines Passwortmanagers), da er nur dann in MyKinsta angezeigt wird.
So löst du die Bereitstellung mit der Kinsta-API aus
Um eine Anwendung über die API auf Kinsta bereitzustellen, brauchst du zwei Parameter: die Anwendungs-ID und die Branche. Du kannst die ID deiner Anwendung programmatisch abrufen, indem du zuerst die Liste deiner Anwendungen abrufst, die Details zu jeder Anwendung, einschließlich ihrer ID, enthält. Nachdem du die notwendigen Informationen erhalten hast, kannst du eine POST-Anfrage an den API-Endpunkt /applications/deployments stellen. Für die CI-Pipeline verwenden wir cURL, ein Kommandozeilentool für die Interaktion mit URLs.
curl -i -X POST
https://api.kinsta.com/v2/applications/deployments
-H 'Authorization: Bearer <YOUR_TOKEN_HERE>'
-H 'Content-Type: application/json'
-d '{
"app_id": "<YOUR_APP_ID>",
"branch": "main"
}'
Auslösen der Bereitstellung mit cURL in der CI/CD-Pipeline
Um die Bereitstellung mit der Kinsta-API auszulösen, fügst du den cURL-Befehl zum Befehl run
für deine CI-Pipeline hinzu. Es ist jedoch wichtig, dass du deinen API-Schlüssel und deine Anwendungs-ID sicher speicherst. Um Geheimnisse auf GitHub zu speichern und sie in GitHub Actions zu verwenden, befolge diese Schritte:
- Navigiere zu dem Repository, in dem du das Geheimnis einrichten möchtest.
- Klicke auf die Registerkarte Einstellungen im Menü des Repositorys.
- Wähle in der linken Seitenleiste unter der Kategorie Optionen die Option Geheimnisse.
- Klicke auf Neues Repository-Geheimnis.
- Gib einen Namen für dein Geheimnis ein (z. B.
KINSTA_API_KEY
) und gib deinen Kinsta-API-Schlüssel in das Feld Wert ein. - Nachdem du den Namen und den Wert eingegeben hast, klicke auf die Schaltfläche Geheimnis hinzufügen, um es zu speichern.
- Wiederhole den Vorgang für andere Geheimnisse.
Sobald du die Geheimnisse hinzugefügt hast, kannst du sie in deinem GitHub Actions Workflow mit der ${{ secrets.SECRET_NAME }}
Syntax referenzieren.
Jetzt können wir den deploy
Job für deine GitHub Actions CI/CD-Pipeline fertigstellen. Definiere die Schritte wie zuvor, mit einem einzigen Schritt für die Bereitstellung auf Kinsta. Definiere zunächst die Secrets im Befehl env
und füge dann den cURL-Befehl hinzu, um die Bereitstellung auszuführen.
steps:
- name: Deploy to Kinsta
env:
KINSTA_API_KEY: ${{ secrets.KINSTA_API_KEY }}
APP_ID: ${{ secrets.APP_ID }}
run: |
curl -i -X POST
https://api.kinsta.com/v2/applications/deployments
-H "Authorization: Bearer $KINSTA_API_KEY"
-H "Content-Type: application/json"
-d '{
"app_id": "'"$APP_ID"'",
"branch": "main"
}'
Im cURL-Befehl siehst du, dass die Umgebungsvariablen innerhalb des Befehls hinzugefügt werden, damit während des Bereitstellungs-Prozesses sicher auf die Secrets zugegriffen werden kann.
So sieht dein endgültiger CI/CD-Workflow aus:
name: Build, Test, and Deploy
on:
push:
branches: "main"
pull_request:
branches: "main"
jobs:
eslint:
name: Check Syntax with ESLint
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x]
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }} to Check Lint
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install Dependencies
run: npm ci
- name: Run ESLint
run: npm run lint
tests:
name: Run Tests
needs: eslint
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Use Node.js 18.x to run Test
uses: actions/setup-node@v3
with:
node-version: 18.x
cache: 'npm'
- name: Install Dependencies
run: npm ci
- name: Run Tests
run: npm run test
deploy:
name: Re-Deploy Application
needs: [eslint, tests]
runs-on: ubuntu-latest
steps:
- name: Deploy to Kinsta
env:
KINSTA_API_KEY: ${{ secrets.KINSTA_API_KEY }}
APP_ID: ${{ secrets.APP_ID }}
run: |
curl -i -X POST
https://api.kinsta.com/v2/applications/deployments
-H "Authorization: Bearer $KINSTA_API_KEY"
-H "Content-Type: application/json"
-d '{
"app_id": "'"$APP_ID"'",
"branch": "main"
}'
Kopiere den angegebenen Workflow und füge ihn in deine build-test-deploy.yml-Datei ein. Als Nächstes stellst du einen Pull Request, um diese Datei dem Hauptzweig deines Repositorys hinzuzufügen. Denke daran, dass dieser Pull Request den Workflow automatisch auslöst.
So kannst du die Änderungen an deinem Repository überprüfen und sicherstellen, dass jede neue Änderung in der Pull-Anforderung den festgelegten Prüfungen entspricht, bevor du entscheidest, ob sie in deine Codebasis aufgenommen werden soll.
Wenn du den Pull Request zusammenführst. Gehe zur Registerkarte Aktionen deines GitHub-Repositorys und du wirst sehen, dass der CI/CD-Workflow läuft.
Du kannst auf jeden Job klicken, um mehr Details über den Job zu erfahren (deshalb musst du jedem Schritt deines Jobs eine aussagekräftige Beschreibung geben).
Pull Request Workflow auf GitHub erzwingen
Um eine effektive Codeverwaltung und Zusammenarbeit in GitHub-Repositories zu gewährleisten, ist es sinnvoll, einen Pull-Request-Workflow zu erzwingen und direkte Commits an den Hauptzweig zu blockieren. Auf diese Weise wird ein kontrollierter und organisierter Entwicklungsprozess geschaffen, bei dem alle Änderungen Pull-Requests und Überprüfungen durchlaufen müssen, bevor sie im Hauptzweig zusammengeführt werden.
Auf diese Weise können Entwicklungsteams die Codequalität verbessern, das Risiko von Fehlern minimieren und eine transparente Änderungshistorie aufrechterhalten.
Hier erfährst du, wie du den Pull-Request-Workflow einrichtest:
- Klicke auf die Registerkarte Einstellungen in deinem GitHub-Repository.
- Wähle unter Code und Automatisierung in der Seitenleiste die Option Zweige.
- Wenn keine Regeln vorhanden sind, klicke auf Zweigschutzregel hinzufügen.
- Gib einen Namen für die Regel ein und aktiviere dann das Kontrollkästchen Pull Request vor dem Zusammenführen anfordern. Daraufhin werden weitere Optionen zur Konfiguration angezeigt.
- Aktiviere auch das Kontrollkästchen Statusprüfung vor dem Zusammenführen erforderlich.
- Passe weitere Optionen nach deinen Wünschen und Anforderungen an.
- Klicke auf die Schaltfläche Erstellen, um die Regel zu speichern.
Wenn du diese Schritte befolgt hast, hast du erfolgreich eine Regel zur Durchsetzung des Pull-Request-Workflows in deinem GitHub-Repository eingerichtet. Dadurch wird sichergestellt, dass alle Änderungen überprüft und automatisch kontrolliert werden, bevor sie in den Hauptzweig eingefügt werden, was eine zuverlässigere und kollaborative Entwicklungsumgebung fördert.
Zusammenfassung
Durch die Kombination von GitHub Actions und der Kinsta-API kannst du deinen Entwicklungs-Workflow rationalisieren und eine kollaborative und effiziente Umgebung für dein Entwicklungsteam schaffen.
Die Entwickler können vertrauensvoll Code beisteuern, weil sie wissen, dass er gründlich getestet wird, bevor er in die Produktion gelangt. Die Beteiligten können sich darauf verlassen, dass der Bereitstellungsprozess gut kontrolliert und fehlerresistent ist.
Wie nutzt du die Kinsta-API? Welche Endpunkte würdest du gerne zur API hinzugefügt sehen? Welches Kinsta-API-Tutorial würdest du gerne als nächstes lesen?
Schreibe einen Kommentar