Files
alva/.gitea/workflows/felsokning.yml
T
Claude 69a519be75 Observation: se var tiden går, utan ett enda nytt beroende
CloudWatch gav loggar och mätvärden men svarade inte på frågan man
faktiskt har när något är långsamt: var tog tiden vägen.

Tjänsterna har medvetet nästan inga beroenden — plattformen har
pg-drivrutinen, orkestern har Claude-klienten. Att dra in ett
OpenTelemetry-SDK med trettio paket för att mäta fyra saker vore fel
avvägning. I stället två standarder som båda bara är text på stdout:

W3C Trace Context. Klienten startar spåret och traceparent följer med
genom plattformen till orkestern, så en teknikers handling går att följa
hela vägen till modellsvaret i stället för att bli två orelaterade spår.

CloudWatch EMF. Strukturerad JSON som CloudWatch själv extraherar
mätvärden ur — ingen agent, ingen SDK, inget som kan sluta fungera tyst.

Varje anrop ger en loggrad med nedbrytning av tiden per del: databasen,
modellanropet, objektlagringen, kundens leverantör. Det svarar direkt på
om ett långsamt ärende beror på S3 eller på Opus-granskningen, i stället
för att någon ska korrelera fem loggrader.

Vägen normaliseras innan den blir dimension, och organisation, ärende-id
och spår-id blir aldrig dimensioner — varje unik kombination är en egen
tidsserie som kostar. De ligger som vanliga fält, sökbara i Logs
Insights. Ett test låser det, eftersom det är precis den sortens sak som
smyger in senare.

Tre larm på det teknikern märker: svarstid p95 över tre sekunder
(medelvärdet döljer att var tjugonde tekniker väntar orimligt länge),
serverfel med spår-id i loggraden, och att modellen avböjer — det senare
tyder på att underlaget innehåller något oväntat, inte på ett driftfel.

Modulen är delad mellan tjänsterna i stället för duplicerad.
Byggkontexten flyttas därför till felsokning/services, och en symlänk gör
att testerna och integrationstestet kör mot samma fil som bilderna.

Verifierat: 106 vitest-tester (10 nya för spårning, EMF-format och att
dimensionerna hålls få), typkontroll, eslint på klient och tjänster,
rotens CI, terraform fmt och referenskontroll på båda lagren, samt
integrationstest mot riktig Postgres där spårraderna syns live.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
2026-08-04 13:36:47 +00:00

130 lines
4.6 KiB
YAML

# Guidad Felsökning — CI på egna runners i klustret.
#
# Samma syntax som GitHub Actions men körs av Gitea Actions-runners på
# vår egen EKS. Inget externt beroende: koden, bygget och registret
# ligger i vår AWS-miljö.
#
# .github/workflows/ tillhör Semantika och rörs inte.
name: Guidad Felsökning
on:
push:
paths: ['felsokning/**', '.gitea/workflows/felsokning.yml']
pull_request:
paths: ['felsokning/**']
workflow_dispatch:
inputs:
bildtagg:
description: 'Bildtagg att driftsätta (commit-SHA från publiceringen)'
required: true
type: string
jobs:
test-och-bygg:
runs-on: felsokning
defaults:
run:
working-directory: felsokning/app
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22 }
- run: npm ci --no-audit --no-fund
- run: npx vitest run
- run: npm run typkontroll
- run: npx eslint src/felsokning src/pages/felsokning src/test --ext .ts,.tsx
- run: npm run build
tjanster:
runs-on: felsokning
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22 }
- run: npm install --no-audit --no-fund
working-directory: felsokning/services/plattform
- run: npx eslint . --ext .mjs
working-directory: felsokning/services
# Hela flödet mot riktig Postgres: organisationer, roller,
# append-only-triggern, isolering, delning, bilagor.
- run: sudo apt-get update && sudo apt-get install -y postgresql
- run: sudo bash integrationstest.sh
working-directory: felsokning/services/plattform
- run: npx swagger-cli validate felsokning/services/plattform/openapi.yaml
working-directory: .
terraform:
runs-on: felsokning
defaults:
run:
working-directory: felsokning/infra/terraform
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
with: { terraform_version: 1.9.8 }
- run: terraform fmt -check -recursive
- run: terraform init -backend=false -input=false
- run: terraform validate
# Bygger och publicerar till vårt eget ECR. Bara på main och bara när
# kontrollerna ovan gått igenom.
publicera:
needs: [test-och-bygg, tjanster, terraform]
if: gitea.ref == 'refs/heads/main'
runs-on: felsokning
permissions:
id-token: write
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ vars.BYGG_ROLL_ARN }}
aws-region: ${{ vars.AWS_REGION }}
- uses: aws-actions/amazon-ecr-login@v2
id: ecr
- name: Bygg och publicera
env:
REGISTER: ${{ steps.ecr.outputs.registry }}
TAGG: ${{ gitea.sha }}
run: |
# Klientens API-adress bakas in vid bygget (Vite), så bilden är
# miljöbunden.
docker build -t "$REGISTER/guidad-felsokning-web:$TAGG" \
--build-arg VITE_PLATTFORM_URL="https://${{ vars.DOMAN }}" \
--build-arg VITE_AI_ORKESTER_URL="https://${{ vars.DOMAN }}" \
felsokning/app
# Byggkontexten är felsokning/services så båda tjänsterna når
# den delade observationsmodulen utan att den dupliceras.
docker build -t "$REGISTER/guidad-felsokning-plattform:$TAGG" \
-f felsokning/services/plattform/Dockerfile felsokning/services
docker build -t "$REGISTER/guidad-felsokning-ai-orkester:$TAGG" \
-f felsokning/services/ai-orkester/Dockerfile felsokning/services
for bild in web plattform ai-orkester; do
docker push "$REGISTER/guidad-felsokning-$bild:$TAGG"
done
# Driftsättning är ett eget, manuellt steg: en bild i registret är inte
# samma sak som en bild som kör. Rollback = kör igen med tidigare tagg.
driftsatt:
if: gitea.event_name == 'workflow_dispatch'
runs-on: felsokning
environment: produktion
permissions:
id-token: write
defaults:
run:
working-directory: felsokning/infra/terraform
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ vars.DRIFT_ROLL_ARN }}
aws-region: ${{ vars.AWS_REGION }}
- run: aws eks update-kubeconfig --name ${{ vars.KLUSTER }}
- uses: hashicorp/setup-terraform@v3
with: { terraform_version: 1.9.8 }
- run: terraform init -input=false
- run: terraform apply -input=false -auto-approve -var bildtagg=${{ inputs.bildtagg }}
- run: terraform output karta