In de snelle wereld van webdevelopment zijn continuous integration en continuous deployment (CI/CD) onmisbare praktijken geworden om efficiënt software van hoge kwaliteit te leveren. Met CI/CD kunnen developers het proces van bouwen, testen en deployen van codewijzigingen automatiseren, het risico op menselijke fouten verkleinen en snellere iteraties mogelijk maken.

Dit artikel legt het belang uit van CI/CD, hoe je een CI pipeline maakt, en hoe je continuous deployment in je CI pipeline met de Kinsta API programmatisch kunt instellen – allemaal met GitHub Actions in je GitHub repository.

Waarom CI/CD gebruiken?

Kinsta’s Applicatie Hosting platform heeft altijd een optie geboden voor automatische deployment, die wordt geactiveerd zodra er een wijziging is op een specifieke branch op je gehoste Git repository. Dit is echter niet altijd ideaal voor grote projecten met meerdere teamleden. Veel developers hebben de neiging om automatische deployment om verschillende redenen te vermijden.

Eén reden is dat, in een samenwerkingsomgeving waar meerdere developers aan hetzelfde project werken, automatische deployments die worden geactiveerd door een wijziging van één developer aan het repository, kunnen leiden tot instabiliteit en onvoorziene problemen. Zonder goed testen en valideren kan zelfs een kleine codewijziging de live site verstoren, wat kan leiden tot downtime en negatieve gebruikerservaringen.

Dit is waar een CI/CD pipeline van pas kan komen. Door een zorgvuldig georkestreerde CI/CD workflow te creëren, kunnen developers ervoor zorgen dat codewijzigingen worden getest en gevalideerd voordat ze worden ingezet op de live site. Er zijn veel tools beschikbaar voor het implementeren van CI/CD in softwareontwikkeling, we zullen GitHub Actions gebruiken voor deze tutorial.

Wat is GitHub Actions?

GitHub Actions is een krachtige automatiseringstool van GitHub. Het biedt developers de mogelijkheid om verschillende jobs, processen en workflows binnen hun softwareontwikkelingsprojecten te automatiseren. Het integreert met GitHub repositories, waardoor het gemakkelijk te gebruiken is.

Met GitHub Actions en de Kinsta API kun je aangepaste workflows definiëren die passen bij de eisen van je project. Je kunt een CI pipeline opzetten die je applicatie test en de deployment op Kinsta activeert.

Aan de slag met GitHub Actions

GitHub Actions werkt op het concept van workflows, dat zijn sets van geautomatiseerde jobs die worden geactiveerd door specifieke gebeurtenissen of gepland op regelmatige tijdstippen. Deze gebeurtenissen kunnen code pushes, pull requests, issue creatie en meer zijn. Als een van deze gebeurtenissen plaatsvindt, voert GitHub Actions automatisch een bijbehorende workflow uit, waarbij een reeks vooraf gedefinieerde stappen wordt uitgevoerd.

Elke stap in de workflow vertegenwoordigt een bepaalde actie, zoals het bouwen van de code, het uitvoeren van tests, het implementeren of het versturen van meldingen. Laten we een workflow met drie jobs maken:

  1. Syntax controleren met ESLint
  2. Testen uitvoeren
  3. Je applicatie opnieuw deployen

Stap 1: Je GitHub repository instellen

Om aan de slag te gaan met GitHub Actions, heb je een GitHub repository nodig.

Hier gebruiken we deze GitHub repository, ontwikkeld voor de tutorial Zo bouw en deploy je een ChatGPT kloonapplicatie met React en OpenAI API.

Voel je vrij om de repository zelf te gebruiken door ernaar te navigeren op GitHub en te kiezen voor: Use this template > Create a new repository.

In deze React applicatie zijn unit tests gemaakt om elk onderdeel te testen. ESLint wordt ook gebruikt om een perfecte syntaxis en codeopmaak af te dwingen. De CI pipeline blokkeert een deployment als een pull request of samengevoegde code die naar de repository is gepusht niet voldoet aan de workflowtests.

Stap 2: Een workflowbestand maken

Definieer je workflow door een YAML bestand te maken in de .github/workflows map van je repository. Deze map moet op het hoofdniveau van je archief staan. De naamgevingsconventie voor workflowbestanden is naam-van-de-workflow.yml.

  1. Maak in je archief een .github map aan.
  2. Maak in de .github map een nieuwe map genaamd workflows.
  3. Maak in de map workflows een nieuw bestand aan met een naam als build-test-deploy.yml.

Stap 3: De CI/CD workflow schrijven

Nu je je workflowbestand hebt gemaakt, definieer je een workflow met de benodigde stappen om de syntax te controleren met ESLint, tests uit te voeren en de applicatie te deployen.

Creëer CI event

Bij het maken van een CI pipeline is de eerste stap het geven van een naam aan de workflow en vervolgens het instellen van het event die de workflow activeert. In dit voorbeeld zijn de twee events een pull request en een push naar de main branch.

name: Build, Test, and Deploy

on:
  push:
    branches: "main"
  pull_request:
    branches: "main"

Als je periodieke jobs (CRON jobs) wilt plannen voor specifieke jobs, dan kun je die toevoegen aan de workflow. Je zou bijvoorbeeld bepaalde jobs willen uitvoeren zoals database backups, gegevens opschonen of andere periodieke onderhoudsjobs.

Hier is een voorbeeld van hoe je een CRON job aan de workflow kunt toevoegen:

on:
  # Existing event triggers for push and pull_request

  # Add a schedule for CRON jobs
  schedule:
    - cron: "0 0 * * *"

Het bovenstaande voorbeeld zal de workflow elke dag om middernacht (UTC-tijd) triggeren, omdat het cron-schema is ingesteld op 0 0 * * *. Je kunt het cron-schema aanpassen aan je specifieke behoeften.

Een ander voorbeeld: stel dat je de CI/CD workflow wilt plannen om elke maandag om 8 uur ’s ochtends te starten. We kunnen een CRON job instellen met behulp van het schedule event:

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

De planning syntaxis die gebruikt wordt in het schedule event voor GitHub Actions workflows is gebaseerd op de UNIX cron syntaxis. Hiermee kun je specifieke tijden of intervallen definiëren waarop je workflow automatisch moet worden uitgevoerd. De syntaxis bestaat uit vijf velden die verschillende aspecten van de planning vertegenwoordigen. Elk veld wordt gescheiden door een spatie. Het algemene formaat van de schema syntaxis is als volgt:

* * * * *
┬ ┬ ┬ ┬ ┬
│ │ │ │ │
│ │ │ │ └─ 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)

Laten we nu elk veld even langsgaan:

  • Minute (0 – 59): De minuut waarop de cron job zal triggeren. 15 betekent bijvoorbeeld dat de workflow op de 15e minuut van het uur wordt geactiveerd.
  • Hour (0 – 23): Het uur waarop de cron job zal triggeren. Bijvoorbeeld, 8 betekent dat de workflow om 8 uur ’s ochtends wordt geactiveerd.
  • Day of the month (1 – 31): De dag van de maand waarop de cron job zal triggeren. Bijvoorbeeld, 1 betekent dat de workflow op de 1e dag van de maand wordt geactiveerd.
  • Month (1 – 12): De maand waarop de cron job zal triggeren. Bijvoorbeeld, 6 betekent dat de workflow in juni wordt geactiveerd.
  • Day of the week (0 – 7): De dag van de week waarop de cron job zal triggeren. Hier staan 0 en 7 allebei voor zondag, terwijl 1 staat voor maandag, enzovoort. 4 betekent bijvoorbeeld dat de workflow op donderdag wordt geactiveerd.

Speciale tekens:

  • * (sterretje): Komt overeen met elke waarde voor dat veld. Bijvoorbeeld: * in het veld minuut betekent dat de workflow elke minuut wordt geactiveerd.
  • */n (schuine streep): Specificeert een interval. Bijvoorbeeld, */5 in het minuutveld betekent dat de workflow elke 5 minuten wordt geactiveerd.
  • , (komma): Specificeert meerdere specifieke waarden. Bijvoorbeeld, 1,15,30 in het minutenveld betekent dat de workflow op de 1e, 15e en 30e minuut van het uur wordt geactiveerd.
  • - (koppelteken): Specificeert een reeks waarden. Bijvoorbeeld, 1-5 in het dag van de week veld betekent dat de workflow wordt geactiveerd van maandag tot vrijdag (1 tot 5).
  • ? (vraagteken): Wordt gebruikt om geen specifieke waarde op te geven. Het wordt vaak gebruikt in het dag van de week veld wanneer de dag van de maand is opgegeven. Bijvoorbeeld, ? in het dag van de week veld en 15 in het dag van de maand veld betekent dat de workflow zal starten op de 15e dag van de maand, ongeacht de dag van de week.

Maak een CI job om de syntax te controleren met ESLint

Om het CI proces op te zetten, maken we de nodige jobs of jobs aan. Elke job moet een duidelijke en begrijpelijke naam hebben. Laten we de eerste job eslint noemen, omdat deze betrekking heeft op het controleren van de code syntax met ESLint.

Daarnaast kunnen we een menselijk leesbare beschrijving geven, hoewel dit deel optioneel is. Vervolgens geven we aan dat de job moet draaien op een Ubuntu omgeving en gebruiken we een matrix strategie om de code te testen tegen twee Node.js versies: 18.x en 20.x.

jobs:
  eslint:
    name: Check Syntax with ESLint
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [18.x, 20.x]

Vervolgens definiëren we de stappen die de “ESLint” job zal uitvoeren. Deze stappen zijn het uitchecken van de code, het instellen van de opgegeven Node.js versie om ESLint uit te voeren, het cachen van npm pakketten, het installeren van dependencies van het project en tenslotte het uitvoeren van ESLint om de syntax van de code te controleren.

    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

In de workflow hierboven krijgt elke stap een beschrijving met een naam om het makkelijk te maken om de bron van fouten of bugs te identificeren bij het inspecteren van de workflow vanuit GitHub Actions. Met name in de derde stap gebruiken we het npm ci commando om dependencies te installeren, wat de voorkeur heeft boven npm install omdat het een schone installatie uitvoert. Bovendien gaat de laatste stap, het uitvoeren van ESLint met npm run lint, ervan uit dat je dit commando hebt geconfigureerd in je package.json bestand.

Hieronder staat de volledige opdracht voor het controleren van de syntaxis van code met 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

Maak een CI job aan om tests uit te voeren

Om de CI job toe te voegen om testen uit te voeren, begin je met het definiëren van de job en het geven van een beschrijvende naam, zoals tests. We geven ook aan dat deze job afhankelijk is van de job eslint, wat betekent dat de job eslint eerst wordt uitgevoerd voordat de job tests wordt uitgevoerd. Deze afhankelijkheid zorgt ervoor dat de code wordt gecontroleerd op syntaxfouten voordat de tests worden uitgevoerd.

  tests:
    name: Run Tests
    needs: eslint
    runs-on: ubuntu-latest

Definieer vervolgens de stappen voor de job tests. Net als bij de vorige opdracht controleren we de code, stellen we Node.js versie 18.x in om de tests uit te voeren, installeren we project-dependencies met npm ci en voeren we de tests uit met het commando npm run test.

    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

Maak CI job aan om te deployen met Kinsta API

Om de CI job te maken om uit te rollen naar Kinsta met behulp van de Kinsta API, definiëren we de job en geven hem de naam deploy. Deze job zal afhankelijk zijn van de jobs eslint en tests om ervoor te zorgen dat de deployment pas wordt uitgevoerd nadat de code is gecontroleerd op syntaxfouten en door de tests is gekomen. We stellen de job in om te draaien op een Ubuntu omgeving met de laatste beschikbare versie.

  deploy:
    name: Re-Deploy Application
    needs: [eslint, tests]
    runs-on: ubuntu-latest

Definieer vervolgens de stappen. In dit geval zul je een cURL commando uitvoeren om programmatisch te communiceren met de Kinsta API en een re-deployment te starten. Laten we eerst de Kinsta API begrijpen, de verschillende informatie die nodig is voor interactie met de API, en hoe je belangrijke informatie die geassocieerd is met de API – zoals de API sleutel – veilig op GitHub kunt krijgen/opslaan.

De Kinsta API begrijpen

De Kinsta API is een krachtige tool waarmee je programmatisch kunt communiceren met de diensten van Kinsta. Om de API te gebruiken, moet je een account hebben met ten minste één WordPress site, toepassing of database in MyKinsta. Je moet ook een API-sleutel genereren om je te authenticeren en toegang te krijgen tot je account via de API.

Om een API-sleutel te genereren:

  1. Ga naar je MyKinsta dashboard.
  2. Navigeer naar de pagina API sleutels (Je naam > Bedrijfsinstellingen > API sleutels).
  3. Klik op API sleutel aanmaken.
  4. Kies een vervaldatum of stel een aangepaste begindatum in en het aantal uren dat de sleutel moet verlopen.
  5. Geef de sleutel een unieke naam.
  6. Klik op Genereren.
API sleutel aanmaken op MyKinsta.
API sleutel aanmaken op MyKinsta.

Nadat je een API-sleutel hebt gemaakt, moet je deze kopiëren en ergens veilig opslaan (we raden aan een wachtwoordmanager te gebruiken), omdat dit de enige keer is dat de sleutel wordt getoond binnen Kinsta.

Zo trigger je deployments met Kinsta API

Om een applicatie te deployen naar Kinsta met behulp van de API, heb je twee vereiste parameters nodig: de applicatie-ID en de branch. Je kunt de applicatie ID programmatisch ophalen door eerst de lijst met applicaties op te halen, die details geeft over elke applicatie, inclusief de applicatie ID.

Nadat je de benodigde informatie hebt verkregen, kun je een POST verzoek doen naar het API /applications/deployments endpoint. Voor de CI pipeline gebruiken we cURL, een opdrachtregelprogramma voor interactie met URL’s.

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"
  }'

Deployment triggeren met cURL in CI/CD pipeline

Om de deployment te triggeren met de Kinsta API, voeg je het cURL commando toe aan het run commando voor je CI pipeline. Het is echter belangrijk om je API sleutel en applicatie ID veilig op te slaan.

Om secrets op GitHub op te slaan en ze in GitHub Actions te gebruiken, volg je deze stappen:

  1. Navigeer naar de repository waar je het secret wilt instellen.
  2. Klik op de Settings tab in het menu van de repository.
  3. Selecteer in de linker zijbalk Secrets in de categorie Options.
  4. Klik op New repository secret.
  5. Geef een naam op voor je secret (zoals KINSTA_API_KEY) en voer je Kinsta API sleutel in het veld Value in.
  6. Klik na het invoeren van de naam en waarde op de knop Add secret om het op te slaan.
  7. Herhaal het proces voor andere secrets.
Sla secrets op in GitHub.
Sla secrets op in GitHub.

Zodra je de secrets hebt toegevoegd, kun je ze in je GitHub Actions workflow gebruiken met de ${{ secrets.SECRET_NAME }} syntax.

Laten we nu de deploy job voor je GitHub Actions CI/CD pipeline afmaken. Definieer de stappen net als eerder, met een enkele stap om te deployen naar Kinsta. Definieer eerst de secrets in het env commando en voeg dan het cURL commando toe om de deployment uit te voeren.

    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"
            }'

In het cURL commando zul je zien dat de omgevingsvariabelen zijn toegevoegd in het commando, waardoor de secrets veilig toegankelijk zijn tijdens het deployment proces.

Zo ziet je uiteindelijke CI/CD workflow eruit:

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"
            }'

Kopieer de gegeven workflow en plak deze in je build-test-deploy.yml bestand. Initieer vervolgens een pull request om dit bestand toe te voegen aan de main branch van je repository. Onthoud dat dit pull request automatisch de workflow zal triggeren.

Dit stelt je in staat om wijzigingen in je repository te bekijken en ervoor te zorgen dat elke nieuwe wijziging in het pull request voldoet aan de gespecificeerde controles voordat je beslist of je het merget in je codebase.

Secrets opslaan in GitHub.
Secrets opslaan in GitHub.

Als je het pull request samenvoegt. Navigeer naar het tabblad Actions van je GitHub repository en dan zie je de CI/CD workflow lopen.

Overzicht GitHub Actions.
Overzicht GitHub Actions.

Je kunt op elke job klikken om meer details over de job te zien (dit is waarom je elke stap van je job een betekenisvolle beschrijving moet geven).

Details CI stappen.
Details CI stappen.

Pull request workflow afdwingen op GitHub

Om effectief codebeheer en samenwerking in GitHub repositories te garanderen, is het handig om een pull request workflow af te dwingen en directe commits naar de main branch te blokkeren. Deze aanpak zorgt voor een gecontroleerd en georganiseerd ontwikkelproces, waarbij alle wijzigingen pull requests en reviews moeten ondergaan voordat ze worden gemerged in de main branch.

Door deze werkwijze toe te passen kunnen devteams de kwaliteit van de code verbeteren, het risico op het introduceren van bugs minimaliseren en een transparante geschiedenis van wijzigingen bijhouden.

Hier lees je hoe je afdwingingen van de pull request workflow instelt:

  1. Klik op de Settings tab in je GitHub repository.
  2. Onder Code and automation, selecteer Branches uit de zijbalk opties.
  3. Als er geen regels bestaan, klik dan op Add rules for branch security.
  4. Geef een naam op voor de regel, vink dan het vakje aan Require a pull request before merging. Dit toont meer opties voor configuratie.
  5. Vink ook het vakje aan voor Require status checks to pass before merging.
  6. Pas extra opties aan op basis van je voorkeuren en vereisten.
  7. Klik op de knop Create om de regel op te slaan.
Afdwingen van de pull request workflow op GitHub.
Afdwingen van de pull request workflow op GitHub.

Door deze stappen te volgen, heb je met succes een regel ingesteld om de pull request workflow in je GitHub repository af te dwingen. Dit zorgt ervoor dat alle wijzigingen worden onderworpen aan beoordeling en geautomatiseerde controles voordat ze worden gemerged in de main branch, wat een meer betrouwbare en samenwerkende ontwikkelomgeving bevordert.

Samenvatting

Door de kracht van GitHub Actions en de Kinsta API te combineren, kun je je ontwikkelworkflow stroomlijnen en een collaboratieve en efficiënte omgeving voor je ontwikkelteam bevorderen.

Developers kunnen vol vertrouwen code bijdragen, wetende dat het grondig getest zal worden voordat het de productie bereikt, en belanghebbenden kunnen met een gerust hart weten dat het deploymentproces goed gecontroleerd en foutbestendig is.

Hoe gebruik jij de Kinsta API? Welke endpoints zou je graag toegevoegd zien aan de API? Welke Kinsta API gerelateerde tutorial zou je als volgende willen lezen?

Joel Olawanle Kinsta

Joel is a Frontend developer working at Kinsta as a Technical Editor. He is a passionate teacher with love for open source and has written over 200 technical articles majorly around JavaScript and it's frameworks.