# Kvalitetsport

## Hva det er

En kvalitetsport er et sett krav en endring må oppfylle før den får gå videre
til neste trinn, med ett resultat til slutt: bestått eller feilet. Trinnet kan
være en commit, en merge inn i hovedgrenen, en utgivelse eller en
produksjonssetting. Kravene kan være alt en maskin kan avgjøre: en ren kjøring
av en linter, tester som består, et tall for testdekning, et bygg som fullføres.

Forskjellen ligger i hvem som bestemmer. En sjekk rapporterer; en
kvalitetsport bestemmer, og beslutningen håndheves av det som styrer neste
steg. En linter som skriver førti advarsler inn i en logg, er en sjekk. Den
samme linteren, koblet slik at merge-knappen er deaktivert til utdataene er
rene, er en del av en kvalitetsport.

SonarQube Cloud, der begrepet er en egen funksjon med navn, beskriver en
kvalitetsport (quality gate) som et sett betingelser koden måles mot under
analysen. Hver betingelse kobler en metrikk med en sammenligningsoperator og en
feilverdi, så «reliability issues greater than 0» er en betingelse, og hvis en
hvilken som helst betingelse slår til, feiler kvalitetsporten. En betingelse
gjelder enten ny kode eller koden som helhet.

Vertsplattformene bygger den samme tanken inn i flyten for merge:

- **GitHub** har en innstilling for beskyttede grener, «Require status checks
  before merging». En påkrevd sjekk må rapportere status som vellykket, hoppet
  over eller nøytral før endringer når den beskyttede grenen.
- **GitLab** har en merge-sjekk som heter «Pipelines must succeed». Når den er
  valgt, kan en merge request ikke slås sammen mens pipelinen feiler, og heller
  ikke når det ikke finnes noen pipeline i det hele tatt.
- **På utviklerens egen maskin** installerer pre-commit sjekker som Git-hooks
  som kjører ved commit eller ved push, og en oppgavekjører som Task gir hele
  settet ett navn.

## Hvorfor det er viktig

En standard som bare lever i et dokument, blir forhandlet bort ett unntak om
gangen. En standard som får bygget til å feile, følges av alle, også av den som
skrev den, den ettermiddagen vedkommende helst ville hoppet over den. En
kvalitetsport gjør avtalte regler kjørbare, slik at det å holde dem slutter å
avhenge av hukommelse og humør.

Den endrer også hva menneskelig gjennomgang er til for. Når en maskin allerede
har fanget formatering, ubrukte importer, døde lenker og hemmeligheter som har
lekket, kan den som gjennomgår, bruke oppmerksomheten på hensikt og design, som
en maskin ikke kan vurdere. Lista er dessuten den samme for hver forfatter.

Tanken om ny kode gjør det mulig å ta i bruk en kvalitetsport på en gammel
kodebase. Sonar way, den innebygde kvalitetsporten i SonarQube Cloud, retter
oppmerksomheten mot ny kode i stedet for å rydde opp i gammel, med betingelser
som minst 80,0 % testdekning og høyst 3,0 % duplisering i ny kode. På en pull
request brukes bare betingelsene om ny kode. Et prosjekt med tusenvis av
eksisterende funn kan slå kvalitetsporten på og holde hver ny endring til lista
uten først å betale ned etterslepet.

Hastighet avgjør om folk lar kvalitetsporten stå på. Task kjører
avhengighetene til en oppgave samtidig, så en kvalitetsport bygd av mange
uavhengige sjekker kan kjøre dem side om side i stedet for den ene etter den
andre. Det hjelper også å dele sjekkene
etter kostnad: den billige halvdelen ved hver commit, hele settet før koden
forlater maskinen.

Dette nettstedet har sin egen kvalitetsport: `task check`, som kjøres på
maskinen nettstedet skrives på, og ikke på en byggetjeneste hos noen andre. Hver
diagnostikk teller som en feil, nettstedsgeneratoren kjører med
`--panicOnWarning`, slik at selv en melding om utfasing stopper bygget, og
undertrykking i koden, som `# noqa` eller `stylelint-disable`, er forbudt, så en
feil rettes der den oppstår, i stedet for å bli dysset ned. En pre-commit-hook
kjører sjekkene på filnivå ved hver commit, og en pre-push-hook kjører hele
`task check`, som tar omtrent seks minutter. En grønn kjøring fra et rent tre
stempler det treet, og skriptet for produksjonssetting nekter et tre uten
stempel. Den eneste overstyringen er en miljøvariabel som skriver ut en tydelig
advarsel, så den som hopper over kvalitetsporten i en nødsituasjon, må si det
høyt.

## Slik ser det ut i praksis

Nettstedets [kolofon](/nb/credits/) lister opp verktøyene inne i den: vnu for
HTML, Stylelint, axe-core på én side per mal, Lighthouse, lychee for lenker,
Prettier, markdownlint, ESLint, Ruff, basedpyright, yamllint, ShellCheck,
codespell og gitleaks. Task kjører alt under ett navn per jobb, og pre-commit
kjører halvdelen på filnivå på nytt ved hver commit. Siden nevner også
tidsbudsjettet: nettlesersjekkene som Puppeteer styrer, tar omtrent tjue
minutter, så kvalitetsporten holder den raske halvdelen, og et menneske kjører
den langsomme ved behov.

Et enpersonsprosjekt har ingen som gjennomgår endringene i det hele tatt, og det
er situasjonen én side i porteføljen starter fra. Standardene måtte være
kjørbare, slik at et brudd på én av dem får en commit til å feile i stedet for å
vente på at noen legger merke til det. Svaret ble
[en kvalitetsport ved commit med 22 lintere og uten advarselsnivå](/nb/portfolio/built-a-22-linter-commit-gate-127/),
pluss prosjektspesifikke sjekker som intet hyllevareverktøy har noen mening om,
f.eks. at treet som settes i produksjon, ikke inneholder noen private stier
eller vertsnavn, og at teksten følger tonereglene på tre språk. En feil som
fantes fra før, rettes i samme omgang i stedet for å bli godtatt. Siden sier
rett ut hva det koster: det går tregt å committe, og kvalitetsporten er den
eneste som gjennomgår endringer i prosjektet.

En kvalitetsport kan også være uenig med seg selv. En annen side beskriver et
prosjekt med en commit-hook og en full kvalitetsport, et dokument som sa at
hooken kjørte hele kvalitetsporten, og to lister over sjekker som ble holdt ved
like for hånd i to filer.
[En test som løser opp begge listene til sjekkene de faktisk kaller](/nb/portfolio/found-the-commit-hooks-and-the-gate-disagreeing-150/),
viste fem sjekker som bare kjørte i den fulle kvalitetsporten, og det var
akkurat de som leser teksten. Hver sjekk som beskyttet kode, kjørte automatisk;
hver sjekk som beskyttet teksten, kjørte bare når noen husket det. De fem ble
flyttet inn i hooken, to av dem ble gjort raske nok til å holde seg innenfor
tidsbudsjettet, og sammenligningstesten ble beholdt, så listene ikke kan gli fra
hverandre igjen.

## Hvor det går galt

De fleste kvalitetsporter svikter ved å slippe ting gjennom mens de fortsatt
viser grønt.

**Advarsler hoper seg opp bak en godkjenning.** En kvalitetsport som bare feiler
på feil, lar advarsler samle seg til loggen er for lang til å lese, og så kommer
et ekte problem som advarsel nummer to hundre og én. Fjern advarselsnivået:
enten er en regel verdt å feile på, eller så er den slått av.

**En sjekk som hoppes over, teller som bestått.** På GitHub oppfyller en status
som er hoppet over, en påkrevd sjekk, og en jobb som hoppes over på grunn av
`if:`-betingelsen sin, rapporterer suksess. En jobb som avhenger av en jobb som
feilet, hoppes også over, og blokkerer kanskje ikke merge. GitHubs råd er å
bruke `always()` sammen med `needs`: krev én jobb som alltid kjører, og som
bare består når alt den trenger, har lyktes.

```yaml
# .github/workflows/check.yml (excerpt)
jobs:
    gate:
        if: always()
        needs: [lint, test, build]
        runs-on: ubuntu-latest
        steps:
            - run: |
                  for result in ${{ join(needs.*.result, ' ') }}; do
                      test "$result" = success || exit 1
                  done
```

Den motsatte fellen finnes også: en påkrevd arbeidsflyt som hoppes over på grunn
av et filter for sti eller gren, lar sjekkene sine stå som ventende og blokkerer
hver merge. Unngå å kreve arbeidsflyter som kan hoppes over.

**To lister glir fra hverandre.** Hooken kjører ett sett, serveren et annet, og
et dokument hevder at de stemmer overens. Gi sjekkene én definisjon og kall den
fra hvert sted, eller behold en test som sammenligner listene og feiler når de
er forskjellige.

**Kvalitetsporten blir treg, og folk går rundt den.** Et flagg som hopper over
hooks, er lett å ta i bruk, og på GitHub gjelder beskyttelse av grener som
standard ikke for administratorer av repoet; «Do not allow bypassing the above
settings» endrer det. Mål hvor tiden går, kjør uavhengige sjekker samtidig, hold
halvdelen som kjører ved commit, liten, og flytt en sjekk som virkelig er treg,
til et dokumentert manuelt steg.

**En sjekk består fordi den sjekket ingenting.** En linter som peker på et
mønster som treffer null filer, eller et ignoremønster som sluker en hel mappe,
avslutter rent, og en ren avslutning ser lik ut som ren kode. Test selve
kvalitetsporten: gi hver sjekk en fil du vet er feil, og forvent at den feiler.

## Les mer

- [Introduction to quality gates](https://docs.sonarsource.com/sonarqube-cloud/standards/managing-quality-gates/introduction-to-quality-gates)
  — betingelser, den innebygde kvalitetsporten Sonar way og vekten på ny kode.
- [About protected branches](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches)
  — påkrevde statussjekker, og hvem som kan gå forbi dem.
- [Troubleshooting required status checks](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/troubleshooting-required-status-checks)
  — hvilke statuser som teller som bestått, og hvordan arbeidsflyter og jobber
  som hoppes over, oppfører seg.
- [Auto-merge](https://docs.gitlab.com/user/project/merge_requests/auto_merge/)
  — sjekken «Pipelines must succeed» i GitLab, og hvordan den behandler
  pipeliner som hoppes over.
- [pre-commit](https://pre-commit.com/) — sjekker som deklareres én gang og
  installeres som Git-hooks for trinnene commit og push.
- [Task dependencies](https://taskfile.dev/docs/guide/dependencies) — hvordan
  Task kjører avhengighetene til en oppgave samtidig og kommandoene i rekkefølge.

<https://engineer.company/nb/glossary/quality-gate/>
