Lille CI, stor forskel

Lille CI, stor forskel

∙ Du laver commit, pusher, og GitHub lyser rødt uden du aner hvorfor
∙ Din Node-app bygger fint lokalt, men Actions siger “command not found”
∙ Tests kører hurtigt hos dig, men hænger i CI
∙ Du har allerede googlet “github actions node fails locally works” mindst én gang

Hvis du nikkede til mindst et punkt, så er det her din “minimum CI” opsætning med GitHub Actions, uden magi og uden 400 linjer YAML.

Minimum CI vs “enterprise CI”

Der er to verdener:

  • Lokalt: din maskine, din Node-version, dine globale pakker, din tilfældige rækkefølge af kommandoer.
  • CI: ren maskine hver gang, alt skal beskrives, ingen gætterier.

Minimum CI er ikke alt det fancy med deploy, docker, matrix builds og 7 miljøer. Minimum er:

  • Installer afhængigheder
  • Lint koden
  • Kør tests
  • Byg projektet (hvis der er build-step)

Det er det. Ingen deploy, ingen releases, ingen ekstra filer. Bare en robot der kører de samme kommandoer som du bør køre lokalt, hver gang du pusher.

Hvad du ikke behøver endnu

Hvis du er i gang med små Node-projekter, måske en Vite-frontend eller en simpel backend fra vores kategori om backend til web, så spring trygt over:

  • Deploy til server eller cloud fra Actions
  • Docker-builds i CI
  • Kompleks caching med flere nøgler
  • 10 forskellige workflows til hver gren

Det kan komme senere. Først vil du have noget der giver dig et ærligt “grønt eller rødt” svar på: kan det her projekt køres af en maskine der ikke kender dig.

Før vs nu: dit lokale workflow og CI

Lad os sammenligne en typisk junior-workflow med den lille, sunde CI-version. Det her er den røde tråd i resten af artiklen.

Lokalt nu CI med minimum workflow
Du åbner projektet, kører “det plejer at virke”-kommandoer i vilkårlig rækkefølge. Actions kører altid: npm installnpm run lintnpm testnpm run build.
Du bruger en Node-version du engang installerede. Workflowet sætter eksplicit Node-version via setup-node.
Du har måske globale værktøjer installeret (jest, vite, eslint). Alt skal findes i package.json scripts, ellers fejler det.
Fejl kan gemme sig i cache, gammel build, mærkelig state. Ren maskine hver gang. Hvis noget kun virker “med held”, bliver det afsløret.

Målet er at de to kolonner bliver så ens som muligt. Samme Node-version, samme scripts, samme forventninger.

Et simpelt GitHub Actions workflow til Node

Her er en helt minimal YAML der giver dig checkout → setup node → install → lint → test → build.

name: CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build-and-test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout kode
        uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version-file: '.nvmrc'
          cache: 'npm'

      - name: Installer afhængigheder
        run: npm ci

      - name: Lint
        run: npm run lint

      - name: Test
        run: npm test

      - name: Build
        run: npm run build

Forudsætninger i dit projekt

For at det her ikke eksploderer på første push, så tjek:

  • Du har en .nvmrc med en Node-version, fx 18.19.0
  • Du bruger npm og har en package-lock.json
  • Du har scripts i package.json:
{
  "scripts": {
    "lint": "eslint .",
    "test": "vitest run",
    "build": "vite build"
  }
}

Hvis du ikke har lint eller tests endnu, så kan du starte med kun install + build, og tilføje resten senere. Men strukturen er den samme.

Node-version og lockfile: kilden til “virker lokalt”

Langt de fleste “GitHub Actions fejler men lokalt er alt fint”-problemer kommer fra to ting:

  • Forskellig Node-version
  • Rod i dine lockfiler

Sådan gør du Node-versionen ens

Lav en .nvmrc i roden af projektet:

18.19.0

Brug så nvm lokalt:

nvm use
node -v   # tjek at den matcher

I workflowet bruger vi node-version-file: '.nvmrc'. Det betyder at Actions læser den samme fil. Ikke noget med “vi har cirka samme version”. Præcis den samme.

Lockfile disciplin

Hvis du bruger npm, så:

  • Commit package-lock.json
  • Brug npm ci i CI, ikke npm install

npm ci er streng: hvis din package.json og package-lock.json ikke passer sammen, fejler den. Det er præcis det du vil have. Lokalt opdager du måske aldrig mismatch, fordi du bare kører npm install og npm “retter” dine afhængigheder.

Typisk fejlscenario

Symptom: Actions fejler på “Cannot find module X”, lokalt virker det.

Årsag: Du har installeret et ekstra modul uden at commite lockfilen, eller du har rodet i node_modules manuelt.

Fix:

  • Slet node_modules lokalt
  • Kør npm ci lokalt
  • Ret eventuelle versioner, commit package-lock.json
  • Push igen

Caching: hjælp vs. hovedpine

Vi brugte lige:

with:
  node-version-file: '.nvmrc'
  cache: 'npm'

Det er en simpel cache af npm-dependencies baseret på din lockfil. Det kan spare 10-30 sekunder, nogle gange mere, men vigtigere: det ejer du ikke selv. GitHub håndterer det for dig.

Når cache er en god idé

Brug den indbyggede cache når:

  • Du har én package manager (npm)
  • Du ikke selv opfinder nøgler og paths
  • Du er ok med at slette cachen ved at ændre lockfilen

Når cache skaber fejl

Fejl opstår typisk når man begynder at lave “kreativ” caching:

  • Du cacher hele node_modules manuelt med en for bred nøgle
  • Du ændrer Node-version uden at ændre cachen
  • Du har skiftet fra npm til pnpm eller yarn, men cachen ligger der stadig

Hvis du ser mærkelige fejl der forsvinder når du ændrer en tilfældig ting, så prøv at slå cache fra midlertidigt. CI skal være deterministisk. Ingen “det virkede da jeg ændrede et mellemrum i YAML”.

Secrets og miljøvariabler i CI

På et tidspunkt skal du bruge miljøvariabler i dine tests eller build: API-nøgler, base URLs, osv. I Actions bruger du Secrets og env.

Sådan sætter du en secret

  1. Gå til dit repo på GitHub
  2. Settings → Secrets and variables → Actions → New repository secret
  3. Kald den fx API_URL, værdi fx https://api.example.com

I workflowet kan du så gøre:

jobs:
  build-and-test:
    runs-on: ubuntu-latest
    env:
      API_URL: ${{ secrets.API_URL }}

Nu ser din Node-app bare en helt normal process.env.API_URL i CI.

Hvis du vil nørde mere i miljøvariabler generelt, så ligger der meget god baggrund i vores tag om miljøvariabler.

Typiske fejl med secrets i Actions

  • Du skriver secret-navnet forkert i YAML
  • Du prøver at echo hele secreten ud i loggen (det bliver maskeret, heldigvis)
  • Du har et .env lokalt, men har aldrig sat secrets i GitHub

Mini-tjek: alt hvad du har i din lokale .env som projektet faktisk behøver for at køre tests/build, skal også findes som secret eller env i CI.

Flaky tests: hvorfor de dør i CI først

Flaky tests er tests der fejler nogle gange. De er irriterende lokalt. I CI er de direkte skadelige, for så lærer du at ignorere røde builds.

Fem klassiske flaky-årsager

  1. Testen afhænger af tid
    Du bruger setTimeout eller forventer noget inden for X millisekunder. På en travl CI-maskine kan det være for langsomt.
  2. Global state genbruges
    Tests deler globale variabler, database state eller filer.
  3. Netværkskald uden mock
    Tests ringer ud i verden. Nogen gange er nettet langsomt, API nede, osv.
  4. Order dependency
    Test B virker kun hvis test A kørte først.
  5. Randomness uden kontrol
    Du bruger Math.random() uden at styre outputtet i testen.

Hurtige fixes

  • Brug fake timers eller mock tid hvor det giver mening
  • Ryd op i global state i beforeEach / afterEach
  • Mock netværkskald i stedet for at ramme rigtige endpoints
  • Sørg for at hver test kan køre alene
  • Kontroller randomness ved at mocke eller isolere den del

Hvis du vil have lidt flere vinkler på test-setup i frontend-land, så har jeg skrevet om “test uden tårer i Vite projekter” før. Det spiller ret godt sammen med det her Actions-setup.

PR checks og branch protection: solo vs team

Der er lidt forskel på om du sidder alene på et projekt, eller om du er flere.

Solo-projekt

Mit forslag:

  • Lad workflowet køre på både push og pull_request
  • Du kan merge selv om CI er rødt, men gør det bevidst (og fiks det hurtigt)
  • Brug Actions mest som ekstra sikkerhed og vane-træner

Lille team eller studiegruppe

Her begynder CI for alvor at gøre en forskel, sammen med de ting vi også er inde på under samarbejde og workflows.

Et simpelt setup:

  • Sæt branch protection på main:
  1. Repo → Settings → Branches → Add rule
  2. Vælg branch: main
  3. Slå “Require status checks to pass before merging” til
  4. Vælg dit CI workflow som krævet check

Nu kan ingen merge til main uden grønt CI. Det virker måske lidt strengt, men det sparer jer for “hov, jeg glemte lige at køre tests” 3 dage før deadline.

Skabelon: minimum Node-workflow linje for linje

Her er en lille “copy-adapt” version med kommentarer. Kopier den, tilpas navnene og fjern kommentarerne.

name: Node CI

on:
  push:
    branches: [ main ]   # kør når der pushes til main
  pull_request:
    branches: [ main ]   # og når der laves PRs mod main

jobs:
  build-and-test:
    runs-on: ubuntu-latest  # GitHub hostet Linux-maskine

    steps:
      - name: Checkout kode
        uses: actions/checkout@v4  # henter dit repo ned på runneren

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version-file: '.nvmrc'  # brug samme version som lokalt
          cache: 'npm'                  # enkel cache baseret på lockfile

      - name: Installer afhængigheder
        run: npm ci            # brug lockfile præcist

      - name: Lint
        run: npm run lint      # kør din lint-kommando

      - name: Test
        run: npm test          # eller npm run test, alt efter script

      - name: Build
        run: npm run build     # byg projektet (drop step hvis du ingen build har)

Mini-øvelse: gør lokalt og CI ens

Prøv det her i et eksisterende projekt:

  1. Lav .nvmrc hvis du ikke har den
  2. Sørg for at npm ci kan køre lokalt uden fejl
  3. Tjek at npm run lint, npm test og npm run build alle virker lokalt
  4. Tilføj workflow-filen ovenfor som .github/workflows/ci.yml
  5. Commit og push
  6. Åbn Actions-tabben og læs loggen roligt igennem, især første gang den fejler

Hvis du allerede har øvet dig på at læse logs i andre sammenhænge, fx via vores kategori om fejlfinding og debugging, så føles Actions hurtigt som “endnu et sted hvor maskinen fortæller dig hvad der sker” og ikke som en fjende.

Min egen lille holdning til CI for begyndere

Jeg synes faktisk minimum CI er en af de bedste måder at blive tvunget til at tage din egen kode seriøst, længe før du får dit første udviklerjob. En grøn Actions-historik på et lille projekt siger mere om dine vaner end endnu en to-do app uden tests nogensinde kommer til.

Sara Vestergaard er selvlært kode-nørd, der stille og roligt er gået fra at rode med en enkelt HTML-side til at bygge små værktøjer, scripts og hjemmesider til sig selv og vennerne. Hun startede med at lave en simpel band-hjemmeside som teenager og opdagede, hvor tilfredsstillende det er, når noget, du har skrevet, pludselig lever på skærmen.

For Sara handler kodning ikke om store ord eller imponerende titler, men om meget konkrete problemer: den kedelige opgave, der tager for lang tid, den ven der mangler en lille porteføljeside, eller den liste, der burde sortere sig selv. Hun elsker at pille ting fra hinanden – også kode – for at se, hvad der egentlig foregår, og hun har brugt utallige aftener på at google fejlbeskeder, teste små eksempler og langsomt bygge sin forståelse op.

På Coding Class deler hun den tilgang videre. Hun skriver til dig, der gerne vil lære at kode ved at gøre det i praksis: små projekter, korte kodebidder og forklaringer, der hænger sammen med det, du faktisk sidder med på skærmen. Hun skærer ind til benet, viser typiske fejl og deres løsninger og giver altid et forslag til, hvordan du kan bygge en tand videre, når grundideen først virker.

Når hun ikke skriver til Coding Class eller nørkler med nye små projekter, hænger Sara på klatrevæggen, vander sine altanplanter eller spiller gamle Nintendo-spil. Men hun ender næsten altid tilbage ved tasterne – for der er altid endnu en lille ting, der kunne være smartere, hurtigere eller bare lidt sjovere at bruge.

Send kommentar

You May Have Missed