Jeg blev træt af at klikke “Deploy” – så lod jeg GitHub gøre det
Jeg ville have en lille webapp til at deploye sig selv, hver gang jeg pushede til main. Ingen flere “hov, jeg glemte lige at bygge først”. I stedet endte jeg med et simpelt GitHub Actions-setup, der tester, bygger og deployer for mig på under et minut.
I den her tekst viser jeg dig præcis den pipeline jeg selv ville sætte op i dag, hvis jeg startede fra nul med GitHub Actions og CI/CD.
Hvad CI/CD faktisk gør for et lille projekt
Jeg starter lige med det, jeg selv skulle have forstået meget tidligere: CI/CD er ikke kun til kæmpe virksomheder.
Du får tre meget konkrete ting, også i dit lille hobbyprojekt:
- Mindre rod: Samme kommandoer hver gang, på en ren maskine.
- Mindre “virker kun hos mig”: Din kode skal virke på GitHub, før den kommer ud.
- Mindre manuel klikfest: Du skubber kode, en robot gør resten.
CI betyder “Continuous Integration” – hver gang du pusher, bliver koden bygget og testet. CD kan være to ting: “Continuous Delivery” (klar til deploy) eller “Continuous Deployment” (faktisk deploy automatisk). Vi snupper en lille blanding: test + build hver gang, og automatisk deploy fra main.
Hvad du skal have klar før du overhovedet åbner GitHub Actions
Jeg antager, at du har en lille webapp. Det kan være en Vite/React, Next.js, Svelte eller bare noget bundet sammen med npm scripts.
Før du begynder på GitHub Actions, så tjek tre ting lokalt:
1. Et repo på GitHub
Din kode skal ligge i et GitHub repository. Ikke kun lokalt. Push alt op:
git init
git add .
git commit -m "første commit"
git branch -M main
git remote add origin git@github.com:dit-navn/dit-repo.git
git push -u origin main
Hvis du er i tvivl om git-delen, så er det bedre at få den på plads først. GitHub Actions lever og dør med dit repo.
2. En sikker build kommando
I din package.json skal du have noget i den her stil:
{
"scripts": {
"build": "vite build",
"test": "vitest run"
}
}
Kør lokalt:
npm install
npm run build
Hvis det fejler lokalt, skal du ikke forvente mirakler i CI. Fix det her først.
3. En test-kommando (også selvom den er lille)
Du kan sagtens starte uden tests, men du lærer mere af at have bare én test, der kan fejle.
Eksempel med vitest:
// sum.test.js
import { describe, it, expect } from 'vitest'
import { sum } from './sum'
describe('sum', () => {
it('lægger to tal sammen', () => {
expect(sum(2, 3)).toBe(5)
})
})
Og i terminalen:
npm run test
Når både build og test virker lokalt, er du klar til at skubbe det over i GitHub Actions.
Din første GitHub Actions workflow der rent faktisk gør noget
Nu kommer det stykke, jeg selv brugte alt for meget tid på første gang: YAML-filen.
Vi laver en fil, der gør tre ting, hver gang du pusher til main:
- Tjekker koden ud
- Installerer dependencies (med cache)
- Bygger projektet
Opret workflow-filen
Lav mappen og filen:
.github/
workflows/
ci.yml
Indhold:
name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Tjek kode ud
uses: actions/checkout@v4
- name: Sæt Node.js version
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Installer dependencies
run: npm ci
- name: Byg projekt
run: npm run build
Hvad sker der faktisk her?
Lad os tage den blok for blok.
name er bare navnet i GitHub Actions UI. Ren kosmetik.
on styrer, hvornår workflowet kører:
push > branches: [ main ]betyder: kør når der pushes til main.pull_request > branches: [ main ]betyder: kør for PRs mod main.
jobs.build.runs-on er typen af virtuel maskine. ubuntu-latest er standarden og fin til stort set alt web.
steps er de enkelte kommandoer.
actions/checkouthenter din kode ned på maskinen.actions/setup-nodevælger node-version og sætter npm-cache op.npm ciinstallerer dependencies hurtigt og deterministisk.npm run buildbygger din app.
Commit filen, push til main og åbn fanen “Actions” på GitHub. Du skulle gerne se workflowet køre.
Typisk fejl i første forsøg
En af de klassiske:
- Fejl:
npm cifejler fordi der ikke er enpackage-lock.json. - Løsning: kør
npm installlokalt, commitpackage-lock.json, og push igen.
Hvis du bruger pnpm eller yarn, skal du skifte kommandoer og cache-type. GitHub har eksempler i deres dokumentation, men jeg vil anbefale at starte med npm, hvis du er helt ny.
Tilføj tests: lad workflowet fejle før du deployer
Når build kører stabilt, er næste trin bare at tilføje tests. Her bliver CI-delen rigtig nyttig.
Vi indfører en test-step før build:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Tjek kode ud
uses: actions/checkout@v4
- name: Sæt Node.js version
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Installer dependencies
run: npm ci
- name: Kør tests
run: npm test
- name: Byg projekt
run: npm run build
Nu sker der en simpel men vigtig ting: hvis npm test fejler, bliver de næste steps aldrig kørt. Dit workflow stopper der.
Det betyder, at du kan lave en regel med dig selv: “Hvis Actions er rød, deployer jeg ikke”. I næste afsnit lader vi GitHub gøre deployet, så du heller ikke selv kan snige dig udenom.
Secrets i GitHub Actions uden at lække nøgler
Inden vi deployer til en ekstern host, bliver vi nødt til at tale om secrets. Det er ikke specielt spændende, men det er det sted, hvor mange kommer til at lægge nøgler direkte i YAML-filen.
Du må aldrig committe dine API-nøgler, deploy-tokens eller andre hemmeligheder. Brug i stedet GitHub Secrets.
Hvor ligger secrets i GitHub?
Gå til dit repo på GitHub:
- Settings
- Secrets and variables
- Actions
- “New repository secret”
Her kan du kalde en secret fx NETLIFY_AUTH_TOKEN og indsætte værdien.
I workflowet får du adgang via ${{ secrets.NETLIFY_AUTH_TOKEN }}.
Eksempel: brug af secret i en step
- name: Deploy til et eller andet
run: some-cli deploy --token ${{ secrets.NETLIFY_AUTH_TOKEN }}
Den værdi bliver sat som en environment variabel, når step’et kører. GitHub forsøger også at maskere den i logs, hvis den skulle blive skrevet ud ved et uheld.
Hvis du på et tidspunkt vil arbejde videre med environment variables til selve din app (for eksempel API_URL), så har jeg også en artikel om env-vars og deploy, som du kan starte med: Hvis det kun virker på din maskine, virker det ikke.
Deploy-mønstre: vælg en host du ikke skal slås med
Nu har vi en CI-del. Den bygger og tester. Næste skridt er CD: faktisk at sende din buildede app ud.
Her er tre typiske mål for en simpel webapp:
- Netlify: God til statiske sites og SPA.
- Vercel: God til Next.js og moderne frontend.
- GitHub Pages: Simpelt, gratis, men lidt mere manuelt.
Både Netlify og Vercel kan egentlig selv lave CI/CD, bare ved at koble dit GitHub repo direkte på. I mange små projekter er det rigeligt.
Men hvis du vil øve “rigtig” CI/CD, hvor GitHub Actions styrer hele forløbet, så kan du lade Actions stå for byg + deploy, og bruge Netlify/Vercel som ren hosting.
Deploy til Netlify med GitHub Actions
Det mest lige-ud-af-landevejen setup er ofte:
- Opret site i Netlify (via webinterfacet).
- Hent en “personal access token”.
- Angiv “site ID”.
- Brug
netlify-clii dit workflow.
Først installerer du netlify-cli som dev dependency:
npm install -D netlify-cli
Tilføj secrets i GitHub:
NETLIFY_AUTH_TOKENNETLIFY_SITE_ID
Så udvider vi workflowet:
name: CI/CD
on:
push:
branches: [ main ]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Tjek kode ud
uses: actions/checkout@v4
- name: Sæt Node.js version
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Installer dependencies
run: npm ci
- name: Kør tests
run: npm test
- name: Byg projekt
run: npm run build
- name: Deploy til Netlify
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
run: npx netlify deploy --dir=dist --prod
Her antager jeg, at dit build-output ligger i mappen dist. Hvis du bruger noget andet (fx build), så skift --dir=dist ud.
Netlify-dokumentationen har lidt mere om det her mønster, men i virkeligheden er det bare: bygg app, kør CLI med token.
Deploy til Vercel med GitHub Actions
Med Vercel er den normale måde at lade Vercel selv stå for hele CI/CD-processen, når du forbinder repoet. Men du kan godt styre det via GitHub Actions, hvis du vil have ét samlet flow.
Grundideen er den samme:
- Få en Vercel token.
- Sæt den som secret i GitHub.
- Brug
vercelCLI i en deploy-step.
Eksempel på en simpel step:
- name: Deploy til Vercel
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
run: npx vercel --prod --token=$VERCEL_TOKEN
Her lader du typisk Vercel selv håndtere build. Hvis du vil bruge dit eget build-output, skal du ind og lege lidt mere med config. Hvis målet er at lære GitHub Actions, ville jeg personligt tage Netlify-varianten til at starte med.
Deploy til GitHub Pages
GitHub Pages er fin til simple statiske sites. Her kan du bruge en eksisterende Action i stedet for at skrive hele deploy-delen selv.
Hvis du allerede har en build-step, der laver en dist-mappe, kan du tilføje noget i stil med:
- name: Deploy til GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./dist
Husk at slå GitHub Pages til under repoets Settings, og peg den på den branch, actionen bruger (typisk gh-pages).
Fejlsøgning: når din pipeline går i rødt midt om aftenen
Første gang din workflow bliver rød, er det fristende bare at skubbe endnu et commit og håbe. Jeg har prøvet. Det hjælper sjældent.
Jeg gør typisk det her, når noget fejler i Actions:
1. Læs hele loggen for det step der fejler
Klik ind på det job der er rødt. Fold step’et ud, der fejler. Scrol helt ned. Næsten alle fejl ender med en ret brugbar linje.
Eksempler jeg ofte ser:
Command 'npm ci' not found– du kører måske i et andet environment end forventet.Cannot find module 'react'– manglende dependency eller fejl i lockfile.ReferenceError: process is not defined– bundling-problem eller forkert environment.
2. Genskab fejlen lokalt
Hvis fejlen handler om build eller tests, så prøv at gøre det samme lokalt i et friskt miljø.
Jeg plejer at gøre noget i den her stil:
rm -rf node_modules
rm -rf dist
npm ci
npm test
npm run build
Hvis det fejler lokalt på samme måde, har du problemet tættere på fingrene.
3. Brug “Re-run jobs” når du retter workflow-filen
Du kan genkøre et job uden at pushe ny kode. På Actions-siden er der en “Re-run jobs” knap. Det er rart, når du kun har ændret på YAML-filen og vil se, om det nu kører igennem.
Pas dog på ikke at bruge det som “jeg håber det virker nu”-knap. Hvis du ikke har ændret noget relevant, vil den jo fejle på samme måde.
Nogle meget almindelige begynderslag i hovedet
- Forkert node-version: Du bygger på Node 20 lokalt, men Actions kører Node 16. Løsning: sæt samme version i
actions/setup-nodesom du bruger lokalt. - Manglende env vars i CI: Din app bruger
process.env.API_URL, men du har ikke sat den i GitHub. Løsning: brug repository variables/secrets. - Sti til build-output: Deploy-step peger på
dist, men din app bygger tilbuildeller noget helt tredje. Løsning: tjekpackage.jsoneller bundler-config.
Hvis du rammer deploy-issues, der kun opstår i produktion, er det værd at kigge på den artikel om deploy-fejl og miljøforskelle: Jeg stoler mere på mine logs end på min hukommelse.
Et lille “best practice” workflow du kan kopiere fra projekt til projekt
Når jeg starter et nyt lille webprojekt, ender jeg ret hurtigt med noget i den her stil. Det er ikke perfekt, men det er simpelt nok til, at jeg gider vedligeholde det.
name: CI/CD
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test-and-build:
runs-on: ubuntu-latest
steps:
- name: Tjek kode ud
uses: actions/checkout@v4
- name: Sæt Node.js version
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Installer dependencies
run: npm ci
- name: Kør tests
run: npm test
- name: Byg projekt
run: npm run build
deploy:
needs: test-and-build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- name: Tjek kode ud
uses: actions/checkout@v4
- name: Sæt Node.js version
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Installer dependencies
run: npm ci
- name: Byg projekt
run: npm run build
- name: Deploy til Netlify
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
run: npx netlify deploy --dir=dist --prod
Hvad gør det her anderledes?
- To jobs: Ét til test/build, ét til deploy.
needs: test-and-build: Deploy kører kun, hvis tests og build lykkes.if:-betingelse: Deploy job’et kører kun på direkte pushes til main, ikke på pull requests.
Det betyder, at du får:
- CI på både pushes og pull requests (uden deploy).
- CD kun på main, og kun når alt er grønt.
Hvis du vil være lidt mere avanceret, kan du begynde at dele ting op med matrix-builds, cache af build-output osv., men så er du også et godt stykke forbi “begynder”.
Hvor du kan bygge videre herfra
Når du først har én pipeline, der kører stabilt, er det fristende at stoppe der. Det kan du sådan set også fint. Men hvis du vil øve dig i DevOps-tankegangen, er der nogle naturlige næste trin:
- Tilføj linting (fx
npm run lint) som en ekstra step før tests. - Del workflows op, så nogle kun kører på PRs (fx tests), og andre kun på tags (fx release-builds).
- Byg små tjeklister til dine projekter, som du altid kører igennem, inden du kalder noget “færdigt”.
Hvis du kommer dertil, hvor du har flere services, backend, database og det hele, kan du begynde at lege med flere jobs, artefacts og måske endda docker-containere. Så er vi ovre i noget, der minder om materiale til en helt anden artikel.
Personligt prøver jeg at holde mine små CI/CD setups mindst lige så simple som min surdej: få ingredienser, gentagelig proces, og så lader jeg være med at pille for meget, når det virker. I modsætning til surdejen har GitHub Actions dog aldrig eksploderet i mit køkken, så på den måde er det lidt tryggere at eksperimentere med.







Send kommentar
Du skal være logget ind for at skrive en kommentar.