Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save johlju/aa25a256a8c3c14eb99491bdd35bafed to your computer and use it in GitHub Desktop.

Select an option

Save johlju/aa25a256a8c3c14eb99491bdd35bafed to your computer and use it in GitHub Desktop.
Produktionsbygg och containerbaserad testmiljö - Grill With Docs exempel

Prompt

Vi behöver bygga en workflow som bygger färdiga containers i ett GitHub Actions workflow för detta projekt, sen startar upp de i podman compose och kör Playwright tester mot dem genom att använda en session cookie (som befintliga integrationstester gör).

Vi ska ha 4 separata containrar i en intern podman network, en för nginx, en för appen, en för sql server och en för keycloak. Vi ska terminera TLS på nginx reverse proxy och använda certifikat som genereras i workflow så att Playwright kan kommunicera säkert med appen. Nginx, Application, Keycloak och sql server ska ha konfiguration externt så att de inte behöver byggas om varje gång.

Alla containers ska byggas i workflow och pushas till GitHub Container Registry, och sedan användas i podman compose. Vi ska använda versioner i container taggarna så att vi kan ha kontroll över vilka versioner som används i testerna och som vi gör release på. Utvärdera om npm changeset är rätt verktyg för versionshantering och release notes eller det finns bättre verktyg för detta ändamål. Versionen vore också bra om den kan användas i Playwright testerna så att vi kan logga vilken version som testas, samt om vi kan få in den i appen så att den kan visas om man hovrar över titeln i header. Det är också viktigt att använda minimum image, minimum konfiguration och low privilege när container byggs för att minimera attackytan. Vi ska också bara ha med produktionskod i containern och inte lint-verktyg, utvecklingsverktyg, dokumentation eller testkod och all konfiguration runt det.

Alla containrar ska ha konfiguration externt så att de inte behöver byggas om varje gång, och vi ska använda en intern podman network för att kommunicera mellan dem. Vi ska också terminera TLS på nginx reverse proxy och använda certifikat som genereras i workflow så att Playwright kan kommunicera säkert med appen. Konfigurationen ska vara minimal för det som behövs för respektive container en containers konfiguration ska inte innehålla konfiguration för andra containrar, så att de kan byggas om och startas om oberoende av varandra.

Databasen som används ska vara persistent och inte försvinna när containern startas om, så vi ska använda en extern databas som sql server som körs i en separat container.

Idag finns test seed data som används vid utveckling tillsamman med data som krävs för att köra appen (det måste finnas motsvarande även imorgon som stöd vid utveckling där utvecklare kan skapa test data för ny funktionalitet). Men vi behöver separera nödvändig databas data som kan laddas in separat från övrig test data, övrig test data som ska vara frivillig, t.ex. behövs kanske övrig test data för att köra integration testerna för produktionsflödet eller om en användare vill sätta upp en PoC av appen.

Vi ska också ha en separat container för keycloak som körs i samma interna nätverk så att vi kan testa autentisering och auktorisering i en miljö som liknar produktion, genom att använda en session cookie (som befintliga integrationstester gör).

Produktionsbygg och containerbaserad testmiljö

Detta dokument beskriver den målbild vi arbetar fram för ett produktionsbygg- och testflöde för Kravhantering. Dokumentet uppdateras löpande när designbeslut blir tydliga.

Målbild

Vi ska bygga ett GitHub Actions-flöde som producerar färdiga containerartefakter, startar en produktionslik miljö med Podman Compose och kör Playwright-tester mot miljön med samma session-cookie-mönster som befintliga integrationstester använder.

Körningsmiljön ska bestå av fyra separata containrar i ett internt Podman-nätverk:

  • nginx som TLS-terminerande omvänd proxy
  • Next.js-applikationen
  • Microsoft SQL Server
  • Keycloak

Endast nginx ska exponeras mot Playwright via HTTPS. Certifikat ska genereras i arbetsflödet och Playwright ska lita på den certifikatkedjan så att testerna kör mot en säker URL utan att behöva sänka säkerhetskraven i applikationen. Certifikat och privata nycklar ska bara vara kortlivade körningsfiler och får inte sparas som artefakter.

Alla containrar ska ha sin konfiguration externt så att konfigurations- ändringar inte kräver ombyggnad av avbildningar. Varje containers konfiguration ska vara minimal och bara innehålla den konfiguration som den containern behöver.

Beslut hittills

Releaseenhet

Releaseenheten är en versionerad applikations- och stackrelease, till exempel v0.2.0.

Appavbildningen är den primära egenbyggda releaseartefakten. nginx- konfiguration, Podman Compose-manifest, seed-/migreringskommandon och testkonfiguration hör till samma release och ska versioneras tillsammans med applikationen.

Microsoft SQL Server och Keycloak behandlas som externa leverantörsavbildningar. De ska låsas till uttryckliga versionstaggar och digest i den testade och släppta Compose-konfigurationen. Digest är det som Compose ska använda för att starta containern, medan tagg och ursprungligt avbildningsnamn dokumenteras som metadata. Om releaseflödet måste kunna dra allt från GitHub Container Registry kan leverantörsavbildningar speglas dit, men de ska inte byggas om med egna containerrecept bara för att få en projektägd tagg.

nginx ska också behandlas som en extern leverantörsavbildning. Den ska låsas till tagg och digest, och Compose ska starta nginx med en avbildning som är låst till digest. Projektet äger nginx-konfigurationen och TLS-materialet, inte en egen nginx-avbildning. nginx.conf, site-konfiguration och certifikat ska monteras in skrivskyddat. En egen nginx-avbildning ska bara byggas om vi senare behöver egna moduler eller annan körningskod i nginx.

nginx-konfigurationen ska ligga statiskt i källträdet under containers/nginx/ och granskas i pull requests. Arbetsflödet ska inte generera hela nginx-konfigurationen dynamiskt i första versionen. Det ska bara generera kortlivade certifikat och den slutliga Compose-fil som pekar ut vilka filer som monteras in.

GitHub-projektet och applikationen är publika/open source. Det innebär att:

  • avbildningar, konfigurationsexempel och releaseartefakter inte får innehålla hemligheter
  • GHCR-paket bör vara publikt läsbara där det är möjligt
  • OCI-etiketter, versionsanteckningar, avbildningsdigest och versionsmetadata ska vara lätta att granska utifrån
  • releaseflödet ska vara robust mot pull requests från forks där GitHub-token normalt inte ska ha paketpubliceringsrättigheter

Versionsexponering

Samma releaseversion ska kunna användas av flera ytor:

  • containeravbildningstaggar
  • Playwright-loggning och testartefakter
  • applikationens körningsmetadata
  • tooltip när användaren hovrar över titeln i headern

Första metadata-kontraktet ska vara en liten offentlig public/build.json som skapas i arbetsflödet före next build och byggs in i appavbildningen. Den ska inte innehålla hemligheter. Minsta innehåll är version, commit-SHA, byggtid och avbildningstagg. Avbildningens digest kan fyllas i när den är känd efter bygg och publicering eller sparas i separat byggmetadata.

Appen ska kunna använda samma metadata för tooltipen i headern, och Playwright ska kunna spara metadatafilen eller motsvarande innehåll som testartefakt. Filen kan utökas med mer metadata senare utan att byta grundmodell.

Öppna designfrågor

Teststrategi

Den befintliga fulla produktionslika sviten ska fortsätta köras som CI-krav i befintligt flöde. Det nya containerflödet ska i stället köra en separat röktestsvit för release mot den Podman Compose-startade miljön.

Playwright ska alltid köras på GitHub Actions-runnern, inte som en Compose-tjänst. Runnern ska nå miljön via nginx och https://kravhantering.test. Playwright-rapporter, skärmdumpar, spårningsfiler och byggmetadata ska sparas som GitHub Actions-artefakter.

Röktestsviten för release ska ladda upp skärmdumpar som bevis, men den får inte enbart besöka sidor. Den ska ha kontroller som verifierar det som är unikt eller särskilt riskfyllt i containerflödet:

  • HTTPS via kravhantering.test
  • OIDC-redirect via nginx /auth
  • lyckad Keycloak-inloggning och återanvänd sessionskaka
  • autentiserad /api/auth/me
  • minst en sida som läser från SQL Server
  • minst en muterande åtgärd eller API-kontroll så CSRF, session och databasskrivning fungerar
  • statiska resurser laddas från containeravbildningen
  • appversion eller byggmetadata kan visas eller hämtas

Syftet är att containerflödet ska verifiera TLS, nginx, Keycloak via publik utfärdar-URL, sessionskakor, avbildningsinnehåll, miljökonfiguration och SQL Server-koppling utan att duplicera hela den befintliga produktionslika sviten.

Röktestsviten för release ska använda fasta röktestdata från db:seed:demo för sidor och skärmdumpsbevis. Själva testet får dessutom göra en liten muterande åtgärd med ett unikt prefix för att verifiera skrivvägen, men ska inte behöva skapa hela sin datagrund dynamiskt. Den muterande åtgärden ska i första hand köras med release-smoke-user om behörighetsmodellen tillåter det, så röktestet verifierar session, CSRF och databasskrivning med en vanlig användare. release-smoke-admin ska bara användas för sidor eller kontroller som faktiskt kräver förhöjd behörighet.

Första versionens muterande kontroll ska vara ett minimalt kravskapande via API: GET /api/requirement-areas, POST /api/requirements med en unik beskrivning som börjar med release-smoke-<run-id> och därefter GET /api/requirements/:id för att verifiera att posten sparades. Det återanvänder ett redan etablerat integrationsmönster och ger ett litet men tydligt bevis på autentiserad skrivning mot SQL Server. Röktestet ska inte städa bort den skapade kravposten i första versionen. Posten ska ha unikt körningsprefix och får lämnas kvar som felsökningsspår i den körningsspecifika testdatabasen. Testets resultat ska inte vara beroende av att en efterföljande städning lyckas.

Bygg och publicering

Pull request-flöden ska inte publicera avbildningar till GitHub Container Registry. De ska bygga appavbildningen lokalt i arbetsflödet, starta Podman Compose med den lokala avbildningen och köra Playwright mot den produktionslika miljön.

PR-flödet ska alltid spara följande GitHub Actions-artefakter:

  • Playwright-rapport
  • serverloggar
  • genererad Compose-fil
  • testad avbildningstagg och digest
  • byggmetadata
  • stacklåsningsfil med testade avbildningsnamn, taggar, digest-värden och källor
  • hashes.sha256 med SHA256-kontrollsummor för sparade artefakter

Serverloggar och containerstatus ska samlas in i två steg. Om röktestet misslyckas ska arbetsflödet först samla loggar medan Compose-stacken fortfarande kör, så tillståndet inte tappas om felet beror på hängande tjänster, startproblem eller sena containerfel. Därefter ska arbetsflödet försöka stänga ned stacken och spara en slutlig logg- och statusdump efter nedstängningsförsöket. Loggar och statusdump får inte innehålla hemligheter.

Statusdumpen ska ha en liten fast uppsättning uppgifter: resultat från podman compose ps, avslutskoder för engångsjobben, senaste loggar per tjänst, använda avbildningsreferenser med digest, monterade konfigurationsfiler som sökvägar utan filinnehåll, nätverksnamn och volymnamn. Första versionen ska inte spara fullständig rådata från podman inspect, eftersom den lätt blir stor och kan innehålla miljövärden.

Statusdumpen ska sparas som artefakt i PR-, förhandsversions- och releaseflöden, till exempel som läsbar container-status.txt och som liten strukturerad container-status.json med samma uttryckligt tillåtna kärndata. Arbetsflödet ska om möjligt ha ett eget steg för att samla och visa containerstatus. Steget kan skriva en kort sammanfattning till GitHub Actions jobbsammanfattning, men fullständiga loggar och statusfiler ska ligga i artefakter.

PR-flödet ska också spara de containers som projektet bygger själv som artefakter, så länge de inte innehåller hemligheter. Dessa artefakter ska vara kortlivade och får inte ersätta GHCR-publicering i betrodda main- och releaseflöden.

PR-flödet ska spara separata komprimerade OCI-arkiv per egenbyggd avbildning, inte ett samlat arkiv för hela stacken. Första versionen ska minst skapa ett arkiv för app-runtime och ett arkiv för db-job. Arkivnamn, digest-värden och SHA256-kontrollsummor ska framgå i hashes.sha256 och container-stack.lock.json, så det är tydligt vilken avbildning som användes för appstart respektive migrering och seedning.

PR-röktestet ska starta från de lokala avbildningar som just byggts i arbetsflödet, inte genom att packa upp de sparade OCI-arkiven i normalfallet. Det håller testkörningen snabb och undviker onödiga extra laddningssteg. Efter export ska arbetsflödet ändå göra en snabb kontroll av varje OCI-arkiv, till exempel genom att läsa metadata eller ladda arkivet i en kontroll, och verifiera att digest-värdet stämmer med container-stack.lock.json. Om arkivet inte går att läsa eller digest-värdet avviker ska PR-flödet misslyckas, eftersom artefakten då inte är användbar för felsökning.

Lokala PR-taggar ska vara tydliga körningstaggar som inte ska publiceras. De ska inte använda latest eller semantiska versionstaggar. Exempel:

localhost/kravhantering/app-runtime:pr-<pr>-<run-id>-<sha>
localhost/kravhantering/db-job:pr-<pr>-<run-id>-<sha>

Taggen ska bara göra den lokala Compose-körningen läsbar. Digest-värdet och container-stack.lock.json är spårbarhetskällan.

Artefakter ska ha olika lagringstid beroende på syfte och storlek. Självbyggda OCI-arkiv i PR-flöden ska sparas kort, 1-3 dagar. Playwright-rapport, serverloggar, statusfiler, genererad Compose-fil, stacklåsningsfil, byggmetadata och hashes.sha256 från PR-flöden ska sparas längre, till exempel 7 dagar, så att felsökning hinner göras utan att tunga arkiv blir kvar i onödan. För förhandsversioner och stabila releases ska de publicerade releaseartefakterna, kontrollsummorna och länkarna i GitHub Release/GitHub-förhandsutgåva vara det permanenta revisionsspåret.

Avbildningsbyggen ska i första versionen göras med Docker Buildx i GitHub Actions. Buildx ska användas för bygg, taggning, publicering till GHCR och för att skapa den metadata som behövs för SBOM/provenance och digest. Podman Compose ska däremot vara körmiljön för röktestet, både i PR och i publicerade releaseflöden. Det ger starkt stöd för GitHub Actions-attesteringar utan att flytta själva testmiljön från Podman. Första målplattformen är linux/amd64. Fler arkitekturer ska inte vara ett krav i första versionen, utan kan läggas till senare när appavbildning och stack är stabila och reproducerbara.

PR-, förhandsversions- och releaseflöden ska använda samma Dockerfile, Buildx-konfiguration och byggargument för appavbildningen. Skillnaden ska vara exporter och behörigheter: PR-flöden exporterar till lokalt OCI-arkiv och laddar avbildningen till testmiljön utan GHCR-publicering, medan betrodda main- och releaseflöden publicerar till GHCR och skapar signaturer, attesteringar och versionsanteckningar.

Produktionscontainrar ska ha en egen tydlig plats under containers/. Appens Dockerfile ska ligga i containers/app/Dockerfile, inte i repo-roten. Syftet är att inte blanda ihop produktionsavbildningen med Dev Containers, lokala utvecklingsverktyg eller andra Dockerfile-varianter som kan finnas för utvecklingsmiljöer.

Byggkontexten för appavbildningen ska vara repo-roten, eftersom Next.js- bygget behöver filer från flera rotkataloger, till exempel package.json, package-lock.json, next.config.ts, tsconfig.json, app/, lib/, components/ och public/. För att ändå hålla produktionsbygget smalt ska containers/app/Dockerfile.dockerignore användas som Dockerfile-specifik .dockerignore-fil. Den ska exkludera dokumentation, tester, lokala utvecklingsfiler, tidigare byggresultat och annat som inte behövs för produktion. Rotens .dockerignore får finnas för andra lokala byggflöden, men appens produktionsbygge ska styras av den Dockerfile-specifika .dockerignore-filen.

Alla självbyggda containerarkiv ska sparas på varje PR-körning. För den första versionen innebär det ett komprimerat OCI-arkiv för appavbildningen med kort retention, till exempel 1-3 dagar.

Eftersom projektet är publikt får artefakter och dokumentation aldrig innehålla riktiga hemligheter. Däremot får de innehålla samma publika dev-only exempelvärden som används för lokal utveckling, till exempel Keycloak-klienthemligheter, testlösenord och session-cookie-nycklar som redan är uttryckligen markerade som icke-produktionsvärden.

PoC-dokumentationen ska beskriva hur en användare startar miljön med dessa exempelvärden och hur de byts mot egna värden när miljön används utanför lokal test eller demo.

PoC-dokumentationen ska ha demo som standardprofil: miljön ska kunna startas med publika dev-only exempelvärden utan hemliga förberedelser. Dokumentationen ska samtidigt vara tydlig med att demo-värdena inte är säkra för en exponerad miljö. Den ska förklara exakt vilka värden som måste ändras för att göra miljön säker, minst session-cookie-nyckel, Keycloak-klienthemligheter, Keycloak-adminlösenord, SQL Server-lösenord, TLS-certifikat och eventuella MCP-klienthemligheter.

Merge till main får bygga och publicera ögonblicksavbildningar till GHCR, till exempel main-<sha> och sha-<sha>.

Taggade releases, till exempel v0.2.0, ska bygga, publicera och testa exakta releaseavbildningar för både app-runtime och db-job. I förhandsversions- och releaseflöden ska den testade Compose-konfigurationen alltid referera till appavbildningen med digest, till exempel ghcr.io/<owner>/<image>@sha256:<digest>, så att röktestet för release bevisar exakt artefakt. Versionstaggen är metadata och söknyckel, men ska inte vara det som avgör vilken avbildning Compose startar.

Compose-konfigurationen ska ha en källstyrd mall i källträdet. Arbetsflödet ska generera en slutlig Compose-fil från mallen och den aktuella stacklåsningsfilen. Den slutliga filen ska innehålla exakt de avbildningsreferenser, digest-värden, monterade konfigurationsfiler och testspecifika sökvägar som röktestet använder.

I PR-flöden ska den genererade Compose-filen referera till lokala localhost/kravhantering/...:pr-<pr>-<run-id>-<sha>-taggar, eftersom avbildningarna bara finns i runnerns lokala bildlager. PR-flödets container-stack.lock.json, container-status.json och hashes.sha256 ska däremot alltid innehålla digest-värden för de testade och exporterade avbildningarna.

Regeln om lokala PR-taggar gäller bara projektets egenbyggda avbildningar, först app-runtime och db-job. Leverantörsavbildningar för nginx, SQL Server och Keycloak ska vara digest-låsta även i PR-Compose, enligt stacklåsningsfilen. På så sätt är bara de avbildningar som byggs i PR lokala, medan den omgivande stacken fortfarande är reproducerbar och skyddad mot flyttade leverantörstaggar.

Den genererade Compose-filen ska sparas som artefakt i PR-, förhandsversions- och releaseflöden. Release- och förhandsflöden ska också kunna hänvisa till samma genererade fil från versionsanteckningarna. Det gör basen granskningsbar i källträdet samtidigt som varje körning bevarar bevis för exakt vilken stack som testades.

GitHub Actions-arbetsflödet ska styra startordningen uttryckligen i stället för att bara förlita sig på Compose-beroenden. Det ska starta SQL Server och Keycloak först, vänta in SQL Server, köra ett bootstrap-jobb som skapar databasen och de SQL-principaler som stacken använder, köra db-migrate, köra db-seed-required, vid behov köra db-seed-demo, starta app-runtime och nginx, vänta på nginx, Keycloak discovery och appen via nginx och därefter köra Playwright.

Compose-mallen får fortfarande ha hälsokontroller och beroenden för tydlighet, lokal felsökning och bättre loggar. Release-röktestet ska däremot använda arbetsflödets explicita steg som källa till ordningen. Det gör fel lättare att förstå och undviker att migrering, seedning och appstart glider ihop till en implicit Compose-effekt.

Första väntelogiken ska använda konkreta kontroller per tjänst:

  • SQL Server ska väntas in med TCP- och inloggningskontroll, motsvarande dagens db:wait.
  • Keycloak ska väntas in via sin publika HTTPS-baserade utfärdar-URL genom nginx, till exempel OIDC-konfigurationen för valt Keycloak-realm under .well-known/openid-configuration.
  • nginx ska väntas in med TLS-verifierad HTTPS mot kravhantering.test.
  • appen ska först väntas in via nginx och befintliga GET /api/health, som ska returnera HTTP 200 med { "status": "ok" }.
  • appens beroenden ska därefter väntas in via nginx och ett nytt GET /api/ready, som ska returnera HTTP 200 när appen kan använda de beroenden som krävs för att ta emot trafik.

Alla väntesteg i arbetsflödet ska ha flera försök med tydlig total tidsgräns innan de misslyckas. Första versionen bör räkna med att SQL Server, Keycloak, nginx eller appen kan behöva minst 30 sekunder innan de svarar. Ett rimligt startvärde är 90-120 sekunders total väntan per kritisk tjänst, med korta intervall mellan försöken och sista felsvaret utskrivet i loggen.

Containerstackens hjälpkommandon ska ligga i scripts/containers/ och anropas från GitHub Actions-arbetsflödet. Det omfattar till exempel generering av slutlig Compose-fil, kontroller av container-stack.lock.json och väntan på SQL Server, Keycloak, nginx, /api/health, /api/ready och OIDC-discovery. YAML-filen ska hålla ihop stegens ordning, behörigheter och artefakter, men inte innehålla större inbäddade kommandoblock för väntan, tolkning av svar eller felrapportering. Samma skript ska kunna användas lokalt för PoC/demo så att CI och lokal felsökning väntar på samma sätt. scripts/release/ ska reserveras för hjälpkommandon som bara hör till GitHub-utgåvor, till exempel versionsanteckningar, checksummor och sammanställning av releaseartefakter.

Vänteskripten ska i första hand vara Node-baserade .mjs-skript. De ska hantera HTTP-anrop, JSON-tolkning, tidsgränser, flera försök och tydliga felmeddelanden. Det passar projektets befintliga skriptmiljö och gör kontrollerna enklare att testa än om all logik ligger i YAML. Kommandoskal ska bara användas för små systemnära steg där det behövs, till exempel anrop till podman, openssl eller uppdatering av /etc/hosts.

/api/health är en liveness-kontroll för appens HTTP-process och ska inte behöva verifiera databas, Keycloak eller användarsession. /api/ready ska vara en readiness-kontroll för appens trafikberoenden. Den ska ha kort tidsgräns, inte kräva autentisering, inte mutera databasen och inte läcka hemligheter eller detaljerade interna fel i svaret.

Första versionen av /api/ready ska minst verifiera att appens körningskonfiguration är tillräcklig och att SQL Server kan nås med en enkel SQL-fråga som bara läser data. Den ska också verifiera att den konfigurerade OIDC-utfärdaren kan nås via sin discovery-URL, med kort tidsgräns per anrop. /api/ready ska inte själv göra lång väntan; det är arbetsflödets väntesteg som gör flera försök. Röktestsviten ska fortfarande vara den verifiering som bevisar Keycloak-inloggning, session, autentiserad API-kontroll, databasläsning och en liten muterande åtgärd.

Det publika svaret från /api/ready ska vara snålt. Vid lyckad kontroll ska svaret vara HTTP 200 med till exempel { "status": "ready" }. Vid misslyckad kontroll ska svaret vara HTTP 503 med till exempel { "status": "not_ready" }. Svaret ska ha Cache-Control: no-store och ska inte innehålla beroendenamn, anslutningssträngar, interna felmeddelanden eller annan topologi. Detaljer om vilken kontroll som föll ska i stället skrivas till serverloggen, som sparas som CI-artefakt.

Säkerhet i leveranskedjan

Betrodda GHCR-avbildningar från main, förhandsversioner och stabila releaseflöden ska signeras och förses med verifierbar provenance och SBOM. Detta gäller alla avbildningar projektet bygger själv, i första versionen app-runtime och db-job.

Containeravbildningen ska signeras med Cosign och nyckelfri signering via Sigstore och GitHub Actions OIDC, inte med långlivade privata signeringsnycklar i GitHub Secrets. Signeringen ska göras mot avbildningens digest, inte mot en flyttbar tagg. Arbetsflödena behöver minst id-token: write och packages: write när signering och attestering görs mot GHCR.

Provenance och SBOM ska skapas som GitHub Artifact Attestations och kopplas till samma avbildningsdigest som signeras. För containeravbildningar ska attestering använda fullständigt avbildningsnamn och digest, utan att blanda in flyttbara taggar som identitet. Användare ska kunna verifiera både Cosign-signaturen och GitHub-attesteringarna från versionsanteckningarna.

PR-flöden ska inte publicera till GHCR och ska därför inte skapa publika Cosign-signaturer eller permanenta attesteringar. PR-flöden ska i stället bygga lokalt, skanna den lokala avbildningen och spara kortlivade OCI-arkiv och rapporter som artefakter.

Röktestflödet för release ska verifiera publicerade avbildningar innan Podman Compose startas. I main, förhandsversioner och stabila releaseflöden ska verifieringen omfatta förväntad avbildningsdigest, Cosign-signatur och GitHub Artifact Attestation för provenance för både app-runtime och db-job. SBOM-attestering ska också verifieras när den finns. PR-flöden verifierar inte publika signaturer eller permanenta attesteringar eftersom de inte publicerar till GHCR. Efter verifieringen ska Podman Compose starta samma avbildningar som verifierats och som är låsta till digest, inte taggar som kan flyttas.

Även leverantörsavbildningar för nginx, SQL Server och Keycloak ska vara låsta till digest i röktest och Compose-konfiguration för release. Projektet signerar inte leverantörsavbildningar, men digest-låsning gör testmiljön reproducerbar och förhindrar att en flyttad tagg i källregistret ändrar vad röktestet för release faktiskt testar.

Låsta leverantörsavbildningar ska ha sin källa i respektive containerkatalog, inte i en gemensam fil. Första versionen ska använda en liten incheckad image.lock.json per leverantörscontainer, till exempel:

containers/nginx/image.lock.json
containers/sqlserver/image.lock.json
containers/keycloak/image.lock.json

Varje image.lock.json ska använda samma fältnamn som en tjänstepost i container-stack.lock.json: name, role, image, tag, digest och source. Uppdateringar av leverantörsavbildningar ska därför göras som vanliga PR-ändringar där tagg och digest granskas tillsammans i berörd containerkatalog. Den genererade container-stack.lock.json ska kunna kopiera leverantörsposterna utan fältöversättning och komplettera med aktuella digest-värden för projektets egenbyggda avbildningar.

image.lock.json ska vara ren JSON utan kommentarer, så scripts/containers/ kan läsa filerna utan specialparser. Instruktioner för hur tagg och digest uppdateras ska i stället ligga i respektive containers/<komponent>/README.md.

Varje leverantörscontainers README ska beskriva ett kort manuellt uppdateringsflöde: uppdatera tagg och digest tillsammans i image.lock.json, kör scripts/containers/generate-stack-lock.mjs för kontroll och kör röktestet. Första versionen ska inte skapa automatiska uppdaterings-PR:er för leverantörsavbildningar.

Första versionen ska inte kräva särskild text i PR-beskrivningen när image.lock.json ändras. Spårbarheten ska komma från PR-diffen, container-stack.lock.json, hashes.sha256 och röktestartefakterna.

scripts/containers/generate-stack-lock.mjs ska kunna köras både för att skapa container-stack.lock.json och för att kontrollera en befintlig stacklåsningsfil. När skriptet körs för kontroll ska det verifiera att varje leverantörspost i container-stack.lock.json exakt motsvarar respektive containers/<komponent>/image.lock.json. Kontrollen ska misslyckas om en post saknas, om tagg eller digest avviker, eller om leverantörsposter har redigerats direkt i den genererade stacklåsningsfilen.

Första versionen ska inte lägga till extra semantisk validering av image.lock.json, till exempel särskild kontroll av digest-format, tom tagg, avbildningsnamnets form eller en tillåten lista med roller. Sådana kontroller kan läggas till senare om de ger tydligt värde. Första skyddet är att filerna är ren JSON, att de kopieras konsekvent till stacklåsningsfilen och att den faktiska körningen använder de låsta avbildningarna.

Varje PR-, förhandsversions- och releasekörning ska skapa en stacklåsningsfil som artefakt. Den ska lista exakt avbildningsnamn, versionstagg, digest, källa och roll i stacken för app-runtime, db-job, nginx, SQL Server och Keycloak. För release- och förhandsflöden ska samma information också användas för att skapa den digest-låsta Compose-fil som testas. Eftersom appens slutliga digest inte är känd förrän efter bygg och publicering är den fullständiga stacklåsningsfilen en genererad artefakt, inte bara en statisk fil i källträdet.

Stacklåsningsfilen ska heta container-stack.lock.json och ha ett fast JSON-format som kontrolleras i CI utan separat schema. Första formatet ska minst innehålla schemaVersion, releaseVersion, commitSha, generatedAt, generatedBy, services[] och ett objekt för varje tjänst med name, role, image, tag, digest och source. Filen behöver inte ha ett verified-fält, eftersom CI ska kräva att verifieringen passerar innan stacklåsningsfilen används som release- eller PR-artefakt. Formatet ska vara enkelt att utöka med mer metadata utan att bryta befintliga verktyg. Ett separat JSON Schema kan införas senare om formatet börjar användas av flera oberoende verktyg, eller viktigt för CI, men behövs inte i första versionen.

Versions- och releaseverktyg

GitVersion ska vara versionskälla för CI. Skälet är att GitVersion kan räkna ut SemVer från git-historik, taggar och konfigurerade commit-meddelanderegler, vilket passar flödet där betrodda merges till main får förhandsversioner medan en stabil release skapas genom att publicera en full versionstagg.

Huvudspåret är:

  • main får förhandsversioner med strikt SemVer-format, till exempel 1.0.0-preview.1 i stället för 1.0.0-preview1
  • förhandsversionstaggar skapas bara för betrodda main-merges som ändrar applikationskod, körningskonfiguration eller beroendemanifest som kan påverka applikationens säkerhet eller funktionalitet
  • förhandsversionen används för avbildningstaggar, public/build.json, genererad Compose-fil, Playwright-loggning och artefakter
  • förhandsversionstaggar ska också skapa GitHub-förhandsutgåvor för enhetlighet, men de ska markeras som pre-release och inte som latest release
  • när en stabil tagg som v1.0.1 publiceras ska taggbygget producera exakt version 1.0.1 och motsvarande releaseavbildning
  • git- och GitHub-taggar ska ha v-prefix, till exempel v1.0.1 och v1.0.2-preview.1
  • versionsvärdet i appen, public/build.json, Compose, Playwright- loggning och avbildningstaggar ska sakna v-prefix, till exempel 1.0.1 och 1.0.2-preview.1
  • GitHub Actions måste checka ut full git-historik och taggar, till exempel med fetch-depth: 0, så att GitVersion kan räkna rätt
  • Conventional Commits-regler ska konfigureras så att feat höjer minor, fix och perf höjer patch och breaking changes höjer major

Första versionen av filtret för förhandsversionstaggar ska utgå från ändrade filer. Ändringar i till exempel app/**, components/**, i18n/**, messages/**, public/**, middleware.ts, next.config.ts, typeorm/migrations/**, relevanta seedfiler, container-/Compose-/ nginx-konfiguration samt package.json och package-lock.json ska kunna skapa förhandsversion. Dokumentation, tester, lintkonfiguration och andra utvecklingsytor ska inte skapa semantiska förhandsversionstaggar på egen hand. Alla main-merges får däremot fortfarande kunna bygga ögonblicksavbildningar med main-<sha> och sha-<sha> om kostnaden för arbetsflödet är rimlig.

Första versionen ska inte använda en incheckad CHANGELOG.md. Versionsanteckningar ska i stället vara taggbaserade och skapas i GitHub Release. Förhandsversioner ska skapa GitHub-förhandsutgåvor och stabila versionstaggar ska skapa vanliga GitHub Releases. Båda ska kompletteras med den metadata som behövs för att kunna spåra den testade artefakten, till exempel avbildningstagg, avbildningsdigest, commit-SHA och länk till röktestartefakter för release.

Versionsanteckningar ska också innehålla SHA256-kontrollsummor för publicerade releaseartefakter. Minst ska en hashes.sha256-fil skapas som artefakt eller bifogad fil i GitHub Release, och GitHub Release-texten ska återge eller länka till kontrollsummorna. Det ska omfatta till exempel stacklåsningsfil, genererad Compose-fil, byggmetadata, container-status.txt, container-status.json, SBOM, testad avbildningsdigest och eventuella nedladdningsbara arkiv.

GitHub-förhandsutgåvor ska behållas permanent i GitHub. De är en del av revisionsspåret för testade artefakter och ska inte tas bort när motsvarande stabil release publiceras. Det är viktigare att kunna följa vilken förhandsversion som testades än att hålla GitHub Releases-listan helt kort. Bara stabila releases ska markeras som latest.

release-please ska inte ingå i första implementationen. Det kan utvärderas senare om projektet behöver release-PR:er, kuraterad ändringslogg eller mer kontrollerad releasekommunikation. Changesets är fortsatt ett svagt alternativ för detta repo eftersom det främst löser paketversionering.

Minimal appavbildning

Appavbildningen ska byggas med produktionsmål och bara innehålla körningskod. Den ska inte innehålla lintverktyg, testkod, dokumentation, utvecklingsverktyg, devDependencies eller konfigurationsfiler som bara hör till utveckling, tester eller demo.

Next.js ska konfigureras med output: "standalone" för produktionsbygget. Körningssteget i Dockerfile ska utgå från den genererade standalone- utmatningen och bara kopiera in det som krävs för att starta appen: .next/standalone, .next/static, public och nödvändiga produktionsfiler. Det ska inte kopiera hela källträdet.

För första versionen ska appavbildningen bygga på Debian slim för Node 24, till exempel node:24-bookworm-slim, både i byggsteg och körningssteg. Alpine väljs inte som första spår eftersom Next.js, SQL Server-drivrutiner och native/transitiva beroenden typiskt ger mindre friktion på en glibc-baserad avbildning.

Basavbildningar som projektet faktiskt bygger vidare på ska låsas direkt i Dockerfile med tagg och digest, till exempel FROM node:24-bookworm-slim@sha256:<digest>. Digest i Dockerfile ska inte användas för nginx, SQL Server eller Keycloak, eftersom de är leverantörsavbildningar som körs direkt i Compose utan projektägda wrapper-avbildningar.

Körningssteget ska köra som icke-root användare. Miljöspecifik konfiguration ska komma via miljövariabler eller monterade filer vid start, inte genom att .env-filer, Compose-filer, certifikat, Playwright-konfiguration eller annan icke-produktionskonfiguration byggs in i avbildningen.

containers/app/Dockerfile ska ha minst två produktionsmål:

  • app-runtime är den minsta avbildningen för den körande appen. Den ska bara innehålla standalone-utmatningen och filer som krävs vid appstart.
  • db-job är en separat avbildning för databasmigrering och db:seed:required. Den får innehålla migreringsfiler och produktionsrelevant seedningskod, men inte demo- eller testdata.

Båda målen ska byggas från samma Dockerfile och samma källrevision, men de ska taggas och beskrivas separat i stacklåsningsfilen. I första versionen är app-runtime den avbildning som nginx skickar trafik till, medan db-job bara används av engångsjobb före appstart. db-job är inte en femte långkörande container i runtime-stacken.

Eftersom db-job måste köras innan någon appcontainer startas ska den följa samma release som app-runtime. Den ska publiceras till GHCR, låsas till digest, signeras, attesteras, få SBOM/provenance och listas i stacklåsningsfilen och versionsanteckningarna. Release-Compose ska köra db-job som lyckade engångsjobb före app-runtime, och app-runtime får inte starta förrän migrering och db:seed:required har slutförts. Första Compose-modellen ska ha separata engångstjänster som använder samma db-job-avbildning:

  • db-bootstrap skapar databasen och de SQL-principaler som app och databasjobb använder.
  • db-migrate kör databasmigrering.
  • db-seed-required kör db:seed:required efter lyckad migrering.

Uppdelningen ger tydligare loggar, enklare felsökning och säkrare omkörning än ett enda sammanslaget jobb.

CI-, röktest- och PoC-demo-profiler ska dessutom kunna lägga till db-seed-demo som tredje engångstjänst med samma db-job-avbildning. Den ska köras efter db-seed-required men före app-runtime. Produktionsprofilen ska inte köra db-seed-demo.

Compose-konfigurationen ska använda profiler för att hålla standardflödet rent:

  • standardprofilen startar SQL Server, Keycloak, nginx, db-bootstrap, db-migrate, db-seed-required och app-runtime
  • demo lägger till db-seed-demo och eventuell demo-specifik extern konfiguration
  • release-smoke lägger till db-seed-demo, röktest-specifik extern konfiguration och runner-baserade Playwright-bevis

Standardprofilen ska motsvara en tom produktionslik miljö med nödvändig referensdata, utan demo- eller testdata.

Extern konfiguration

nginx, applikationen, Keycloak och SQL Server ska kunna startas med extern konfiguration. Det ska gå att ändra URL:er, certifikat, OIDC-inställningar, databasanslutning och test-specifika värden utan att bygga om respektive avbildning.

Varje container ska ha en egen katalog under containers/. Katalogen ska innehålla all projektägd konfiguration, alla exempel på miljövariabler och ett dokumenterat miljövariabelkontrakt i README.md för just den containern. Det gäller även när själva avbildningen är en leverantörsavbildning, till exempel SQL Server eller Keycloak. db-job får en egen katalog för sitt miljökontrakt och sina engångskommandon även om avbildningen byggs från samma Dockerfile som appen.

Det ska inte finnas gemensamma miljö- eller konfigurationsfiler som täcker två eller flera containrar. Om samma värde behövs av flera containrar ska det föras in i respektive containers egen konfiguration vid generering eller start, i stället för att ägas av en gemensam .env-fil. Compose-mallen och stacklåsningsfilen får beskriva sammansättning, monteringar, beroenden och digest-låsta avbildningar, men ska inte vara källan för containrarnas egna miljövärden.

Dokumentationen i varje containerkatalog ska beskriva vilka värden som krävs, vilka som bara är för demo eller röktest, vilka som är känsliga och vilka exempelvärden som är säkra att publicera i ett öppet källträd. Demo- och testprofiler får ha publika exempelfiler med utvecklingsvärden i respektive containerkatalog så att PoC-miljön kan startas utan hemliga förberedelser. Dessa filer ska vara tydligt märkta som osäkra för exponerad drift. GitHub Actions-arbetsflödena ska däremot mata in hemligheter och genererade värden vid körning, till exempel certifikat, sessionsnycklar och lösenord. Arbetsflödena ska inte vara beroende av underförstådda lokala .env-filer.

Publika exempelvärden ska namnges per container, till exempel containers/app/.env.app.example och containers/keycloak/.env.keycloak.demo.example. Arbetsflödet får generera tillfälliga lokala filer med samma ägarskap, till exempel containers/app/.env.app.local eller containers/keycloak/.env.keycloak.local, när Compose behöver läsa miljövariabler från fil. .local-filerna ska vara ignorerade av Git, får innehålla hemligheter och ska aldrig sparas som artefakter. Artefakter ska i stället innehålla publicerbara exempelvärden, redigerad Compose-fil utan hemligheter, stacklåsningsfil, byggmetadata och checksummor.

nginx-konfigurationen ska vara en minimal, källstyrd konfiguration som bara beskriver nginx ansvar: TLS-terminering, omvänd proxy till applikationen och vidarebefordran till Keycloak under /auth. Certifikat och privat nyckel ska monteras in som externa filer. Om fler värdnamn eller sökvägar behövs senare kan en liten mall införas, men första versionen ska vara statisk och enkel att granska.

nginx ska inte använda miljövariabler i första versionen. Dess containerkatalog ska innehålla filbaserad konfiguration, till exempel containers/nginx/nginx.conf och eventuell containers/nginx/conf.d/. Certifikat, privat nyckel och tillfälliga CA-filer ska genereras av arbetsflödet eller av lokal PoC-start och monteras in som filer. Om nginx senare behöver miljöberoende värden ska det införas som ett separat beslut, inte som en underförstådd .env-fil.

Databas och seed

SQL Server ska köras som separat container med beständig volym. Databasen får inte försvinna vid containeromstart.

PR-, förhandsversions- och release-röktest ska däremot alltid starta med en körningsspecifik SQL Server-volym. Arbetsflödet ska skapa volymen, köra migrering, db:seed:required och db:seed:demo, spara loggar och artefakter och därefter ta bort volymen. Röktestet ska därför vara reproducerbart från avbildningar, låsfil och seeddata, inte från kvarvarande databasläge.

Lokal PoC/demo får använda en stabil namngiven SQL Server-volym för att data ska finnas kvar mellan containeromstarter. Dokumentationen ska samtidigt ha ett tydligt nollställningskommando som tar bort PoC/demo-volymen när användaren vill börja om från migrering och seed.

SQL Server-containern ska inte ansvara för applikationens migreringar eller seedning. Den ska vara en leverantörsavbildning för databasmotorn, inte en specialbyggd avbildning med Node.js, TypeORM, applikationskod eller seedningslogik. Det håller databascontainern minimal och gör att Microsofts avbildning kan låsas till tagg och digest utan egen ombyggnad.

Databasmigrering och seedning ska köras som separata engångsjobb före appstart, inte som en del av appcontainerns normala start. I CI/demo kan det vara Compose-tjänster eller podman compose run --rm-kommandon som använder db-job-avbildningen för att köra migrering och seedning. Appcontainern ska därefter starta utan att mutera databasen.

Skälet är att appen på sikt kan köras med flera appcontainrar bakom lastbalanserare. Om varje appinstans försöker migrera eller köra seedning vid start finns risk för samtidiga schema- och dataändringar. Engångsjobb ger tydligare ordning, enklare felsökning och bättre stöd för horisontell skalning.

Seeddata behöver delas upp i minst två nivåer:

  • db:seed:required med nödvändig referensdata och systemdata som krävs för att appen ska fungera
  • db:seed:demo med frivillig test-, demo-, PoC- och Playwright-data som laddas när utvecklare, integrationstester eller PoC-miljöer behöver realistiskt innehåll

Produktionsavbildningen får innehålla den kod och de filer som krävs för migrering och db:seed:required, eftersom det är produktionsrelevant driftsättning. db:seed:demo ska däremot inte bygga in demo- eller testdata i produktionsavbildningen. Demo- och röktestdata ska tillföras externt i CI/demo, till exempel som monterad fil, separat artefakt eller separat Compose-konfiguration.

db:seed:demo ska köras som ett separat engångsjobb i PR-röktest, release-röktest och PoC-demo. Jobbet ska använda samma db-job- avbildning men läsa demo- och testdata från extern konfiguration eller monterade filer. Det får inte vara en del av produktionsprofilen och får inte krävas för en tom produktionslik miljö.

Utvecklare ska även fortsättningsvis kunna skapa och underhålla ny testdata för ny funktionalitet.

Det befintliga db:seed-kommandot ska tas bort i stället för att leva kvar som kompatibilitetsalias. Alla flöden ska välja seedprofil uttryckligen. CI för integrationstester kör migrering, db:seed:required och db:seed:demo. En tom produktionslik miljö kör migrering och db:seed:required, men inte db:seed:demo.

Autentisering i produktionslik testmiljö

Keycloak ska köras i samma interna Podman-nätverk som övriga tjänster. Playwright ska skapa sessioner genom det riktiga OIDC-flödet och sedan återanvända storageState och sessionskaka på samma sätt som befintliga integrationstester gör.

Keycloak ska exponeras via nginx under samma publika HTTPS-värd som applikationen, till exempel under /auth. Applikationens AUTH_OIDC_ISSUER_URL, Keycloaks annonserade värdnamn och Playwrights inloggningsflöde ska därför använda samma HTTPS-baserade utfärdar-URL. Det undviker specialfall där webbläsaren och appens serverkod ser olika OIDC-utfärdare och gör demo-/testmiljön mer lik en riktig driftsättning.

CI- och demo-miljön ska använda kravhantering.test som fast testvärdnamn. .test är en reserverad toppdomän för test och ska inte användas via publik DNS. I GitHub Actions ska värdnamnet pekas mot 127.0.0.1, till exempel via /etc/hosts, och certifikatet som genereras i arbetsflödet ska ha kravhantering.test i SAN. Playwright ska köra mot https://kravhantering.test, och OIDC-utfärdare ska vara https://kravhantering.test/auth/realms/kravhantering-test.

GitHub Actions-runnern ska generera en kortlivad lokal CA och ett servercertifikat för kravhantering.test. Certifikat och privat nyckel ska monteras skrivskyddat i nginx. Runnern ska lita på samma CA under Playwright-körningen så att testerna kan använda vanlig HTTPS-verifiering. Privata nycklar och CA-material ska inte laddas upp som artefakter.

Den produktionslika containerstacken ska ha en egen realm-fil under containers/keycloak/, till exempel containers/keycloak/realm-kravhantering-test.json. Den ska inte återanvända dev/keycloak/realm-kravhantering-dev.json som källa vid release-röktest eller PoC-demo. Utvecklingsrealmen kan fortsätta höra till befintliga lokala utvecklingsflöden, medan containerstackens realm-fil ska följa containerstackens publika HTTPS-adresser och konfigurationsstruktur.

Realm-filen i containers/keycloak/ ska innehålla de klienter, roller, claim-mappningar och demo-/röktestanvändare som behövs för containerbaserat röktest och PoC-demo. Den ska matcha https://kravhantering.test/auth/realms/kravhantering-test, AUTH_OIDC_CLIENT_ID, AUTH_OIDC_REDIRECT_URI, AUTH_OIDC_POST_LOGOUT_REDIRECT_URI och eventuellt AUTH_OIDC_API_AUDIENCE. Klienthemligheter och testlösenord i den incheckade realm-filen får bara vara publika utvecklingsvärden. För exponerad drift ska dokumentationen beskriva hur dessa värden byts ut.

Release-röktestet ska använda egna minimala testidentiteter i realm-kravhantering-test.json, skilda från de konton och den data som dagens integrationstester använder. Första versionen bör ha en administratör och en vanlig användare med stabila tekniska användarnamn, till exempel release-smoke-admin och release-smoke-user. Kontona ska bara ha de roller som krävs för röktestets förutbestämda sidor och enkla skrivflöde. PoC-demo får lägga till fler demoanvändare, men release-röktestets minsta konton ska hållas små och stabila.

Appcontainern ska också använda samma publika utfärdar-URL via nginx, inte en separat intern Keycloak-URL. OIDC-flödet skickar användarens webbläsare till Keycloaks annonserade URL:er, så webbläsaren, appens serverkod och Playwright måste se samma publika HTTPS-baserade utfärdare. Den interna Podman-konfigurationen ska därför se till att appcontainern kan nå nginx på kravhantering.test.

Implementationsfaser för containerflödet

Detta dokument delar upp designen i prompt.md i mindre implementationsfaser. Faserna ska göras i ordning, med fas 1 först. Syftet är att en AI-agent ska kunna genomföra en fas i taget utan att behöva bära hela lösningen i ett enda arbetspass.

Dokumentet ska inte ändra designen. Om en fas upptäcker att designen behöver ändras ska prompt.md uppdateras först, och arbetet med fasen ska fortsätta efter att beslutet är tydligt.

Gemensamma regler

  • En fas ska lämna källträdet i ett verifierbart läge.
  • Riktiga hemligheter får inte läggas i källkod, avbildningar eller artefakter.
  • Gemensamma miljöfiler som styr flera containrar ska inte införas.
  • Varje container ska äga sin egen konfiguration i sin egen katalog.
  • PR-flöden får inte publicera till GHCR.
  • Leverantörsavbildningar ska inte byggas om som wrapper-avbildningar.
  • Relevanta tester och dokumentkontroller ska köras innan fasen avslutas.

Minsta dokumentkontroll efter ändringar i dessa dokument:

npx cspell docs/prompt.md docs/prompt-faser.md --no-progress
npx markdownlint-cli2 docs/prompt.md docs/prompt-faser.md

Fas 1: Grundstruktur och containerkontrakt

Mål: skapa den filstruktur och de kontrakt som senare faser bygger vidare på, utan att ännu bygga hela stacken.

Gör:

  • Skapa containerkataloger för app, db-job, nginx, sqlserver och keycloak.
  • Lägg till README.md i varje containerkatalog med respektive containers miljövariabelkontrakt, känsliga värden, publika exempelvärden och uppdateringsregler.
  • Lägg till image.lock.json för nginx, sqlserver och keycloak.
  • Säkerställ att varje image.lock.json är ren JSON med fälten name, role, image, tag, digest och source.
  • Dokumentera i varje leverantörscontainers README hur tagg och digest uppdateras manuellt.
  • Lägg till publika .env.<containernamn>.example-filer där de behövs.
  • Säkerställ att .env.<containernamn>.local-mönstret är ignorerat av Git.

Klart när:

  • Varje container har en tydlig egen katalog och inget nytt delat kontrakt för miljöfiler har införts.
  • Leverantörsavbildningarnas låsning går att granska per container.
  • Dokumentkontrollerna är gröna.

Inte i fasen:

  • Ingen Compose-start.
  • Inga egna produktionsavbildningar.
  • Inga GitHub Actions-arbetsflöden.

Fas 2: Appens metadata och readiness

Mål: ge appen de körningskontroller och den versionsmetadata som containerflödet behöver.

Gör:

  • Skapa eller anpassa generering av public/build.json före produktionsbygge.
  • Säkerställ att public/build.json innehåller minst version, commit-SHA, byggtid och avbildningstagg.
  • Visa versionen i appen enligt designen, till exempel i tooltip på titeln i headern.
  • Lägg till GET /api/ready med minimal publik respons.
  • Låt /api/ready kontrollera körningskonfiguration, SQL Server med en enkel läsfråga och OIDC-discovery med kort tidsgräns.
  • Säkerställ att /api/ready inte kräver autentisering, inte muterar data och inte läcker interna fel.

Klart när:

  • /api/health fortsätter vara enkel liveness-kontroll.
  • /api/ready kan användas av kommande vänteskript.
  • Appens metadata kan läsas av Playwright eller sparas som artefakt.
  • Relevanta enhets- eller integrationstester är uppdaterade.

Inte i fasen:

  • Ingen containerbyggnation.
  • Ingen Podman Compose-start.

Fas 3: Migrering och seedprofiler

Mål: dela databasseedning i produktionsrelevant data och frivillig demo- eller röktestdata.

Gör:

  • Inför db:seed:required för nödvändig referensdata och systemdata.
  • Inför db:seed:demo för demo-, PoC- och röktestdata.
  • Ta bort befintligt db:seed utan kompatibilitetsalias.
  • Säkerställ att demo- och röktestdata kan läsas externt i senare containerflöden.
  • Dokumentera hur utvecklare skapar och underhåller ny testdata för ny funktionalitet.

Klart när:

  • Befintliga integrationstester kan köra migrering, db:seed:required och db:seed:demo.
  • En tom produktionslik miljö kan beskrivas som migrering plus db:seed:required.
  • Demo- och röktestdata är tydligt frivilliga.

Inte i fasen:

  • Ingen db-job-avbildning ännu.
  • Ingen release-röktestsvit.

Fas 4: App- och db-job-avbildningar

Mål: skapa projektets egenbyggda produktionsavbildningar med minimal körningsyta.

Gör:

  • Säkerställ att utvecklingscontainern installerar Docker CLI, Docker Compose-plugin och Buildx via Docker outside-of-Docker eller motsvarande åtkomst till värdens Docker-socket.
  • Verifiera före avbildningsbyggen att docker --version, docker buildx version, docker info och docker buildx ls fungerar, eller dokumentera manuella installationskommandon om miljön blockerar installation.
  • Konfigurera Next.js med output: "standalone" om det inte redan är gjort.
  • Skapa containers/app/Dockerfile med målen app-runtime och db-job.
  • Lås basavbildningar i Dockerfile med tagg och digest.
  • Kör runtime-steget som icke-root användare.
  • Kopiera bara .next/standalone, .next/static, public och nödvändiga produktionsfiler till app-runtime.
  • Låt db-job innehålla migrationsfiler och produktionsrelevant seedningskod, men inte demo- eller testdata.
  • Skapa containers/app/Dockerfile.dockerignore.
  • Lägg till lokala byggkommandon för app-runtime och db-job.

Klart när:

  • Båda avbildningsmålen kan byggas lokalt.
  • Avbildningarna saknar testkod, dokumentation, lintverktyg och utvecklingskonfiguration.
  • Basavbildningen är låst i Dockerfile.

Inte i fasen:

  • Ingen GHCR-publicering.
  • Ingen Cosign-signering.
  • Ingen full Compose-stack.

Fas 5: nginx, Keycloak och SQL Server-konfiguration

Mål: göra leverantörscontainrarna körbara i den produktionslika stacken utan att bygga om dem.

Gör:

  • Lägg till statisk nginx-konfiguration under containers/nginx/.
  • Se till att nginx bara ansvarar för TLS-terminering, proxy till appen och vidarebefordran till Keycloak under /auth.
  • Lägg till containers/keycloak/realm-kravhantering-test.json.
  • Säkerställ att realmen matchar https://kravhantering.test/auth/realms/kravhantering-test.
  • Lägg till minimala röktestidentiteter, till exempel release-smoke-admin och release-smoke-user.
  • Lägg till SQL Server-konfiguration och exempelvärden i containers/sqlserver/.

Klart när:

  • nginx kan konfigureras helt med filer och monterade certifikat.
  • Keycloak-realmen är skild från dev/keycloak/.
  • Publika demo- och testvärden är tydligt markerade som osäkra för exponerad drift.

Inte i fasen:

  • Ingen full Compose-generering.
  • Ingen Playwright-körning.

Fas 6: Stacklås, Compose-generering och vänteskript

Mål: skapa de återanvändbara skript som håller ihop containerstacken i lokal felsökning och CI.

Gör:

  • Implementera scripts/containers/generate-stack-lock.mjs.
  • Låt skriptet skapa container-stack.lock.json från per-container image.lock.json och aktuella digest-värden för app-runtime och db-job.
  • Låt skriptet kontrollera att leverantörsposterna i en befintlig stacklåsningsfil exakt matchar respektive image.lock.json.
  • Implementera generering av slutlig Compose-fil från källstyrd mall och stacklåsningsfil.
  • Implementera Node-baserade vänteskript för SQL Server, Keycloak via nginx, nginx, /api/health och /api/ready.
  • Lägg till hantering för kortlivade .env.<containernamn>.local-filer där Compose behöver läsa miljövariabler från fil.

Klart när:

  • En stacklåsningsfil kan genereras och kontrolleras.
  • En slutlig Compose-fil kan genereras för PR-läge och release-läge.
  • Vänteskripten kan köras lokalt utan GitHub Actions.

Inte i fasen:

  • Ingen full GitHub Actions-implementation.
  • Ingen Cosign- eller GHCR-integration.

Fas 7: Lokal containerstack utan Playwright

Mål: bevisa att Podman Compose-stacken kan starta och bli redo innan Playwright kopplas på.

Gör:

  • Säkerställ att utvecklingscontainern kan köra Podman Compose eller dokumentera att lokal verifiering behöver köras från en värd med Podman.
  • Bygg app-runtime och db-job lokalt.
  • Starta SQL Server och Keycloak i Podman Compose.
  • Kör ett bootstrap-jobb som skapar databasen och SQL-principalerna för app och databasjobb.
  • Kör db-migrate, db-seed-required och vid behov db-seed-demo som engångsjobb.
  • Starta app-runtime och nginx först efter lyckad migrering och required seed.
  • Vänta på nginx, Keycloak discovery och appen via nginx med /api/health och /api/ready.
  • Skapa container-status.txt och container-status.json.
  • Skapa hashes.sha256 för relevanta körningsfiler.

Klart när:

  • Lokal stack kan startas från noll med körningsspecifik testvolym.
  • Lokal PoC/demo kan använda stabil namngiven SQL Server-volym.
  • Loggar och statusfiler saknar hemligheter.
  • Podman-kapacitet kan verifieras med podman --version, podman compose version och podman info.

Inte i fasen:

  • Ingen Playwright-röktestsvit.
  • Ingen PR-artefaktuppladdning.

Fas 8: Release-röktest med Playwright

Mål: lägga till den smala Playwright-svit som bevisar containerflödets viktigaste risker.

Gör:

  • Skapa separat release-röktestsvit.
  • Kör Playwright från runnern, inte som Compose-tjänst.
  • Använd https://kravhantering.test.
  • Logga in via Keycloak genom nginx och återanvänd storageState.
  • Verifiera /api/auth/me.
  • Besök förutbestämda sidor och spara skärmdumpar.
  • Kör minimalt kravskapande via API med prefix release-smoke-<run-id>.
  • Läs tillbaka skapad kravpost utan att städa bort den.
  • Spara Playwright-rapport, skärmdumpar, spårningsfiler och byggmetadata.

Klart när:

  • Röktestet bevisar TLS, nginx, Keycloak, session, CSRF, databasläsning, databasskrivning, statiska resurser och appversion.
  • Röktestet är mycket mindre än den befintliga fulla produktionslika sviten.

Inte i fasen:

  • Ingen GHCR-publicering.
  • Ingen releasepublicering.

Fas 9: PR-arbetsflöde

Mål: köra containerbygg, Podman Compose och release-röktest i pull requests utan att publicera till GHCR.

Gör:

  • Skapa GitHub Actions-arbetsflöde för PR.
  • Bygg app-runtime och db-job med Docker Buildx.
  • Använd lokala PR-taggar för egenbyggda avbildningar.
  • Exportera separata komprimerade OCI-arkiv för app-runtime och db-job.
  • Kontrollera att OCI-arkiven kan läsas och matchar stacklåsningsfilen.
  • Generera PR-Compose med lokala app- och db-job-taggar.
  • Håll nginx, SQL Server och Keycloak digest-låsta även i PR-Compose.
  • Kör Podman Compose och release-röktest.
  • Spara artefakter med rätt lagringstid.
  • Samla loggar och status både före och efter nedstängningsförsök vid fel.

Klart när:

  • PR från normalt repo kör hela röktestflödet utan GHCR-publicering.
  • PR-artefakter innehåller Playwright-rapport, loggar, statusfiler, genererad Compose-fil, stacklåsningsfil, byggmetadata, OCI-arkiv och hashes.sha256.
  • Fork-säkerheten är bevarad.

Inte i fasen:

  • Ingen publicering av förhandsversioner.
  • Ingen Cosign-signering.

Fas 10: Betrodd main- och releasepublicering

Mål: publicera, verifiera och dokumentera betrodda avbildningar från main och stabila versionstaggar.

Gör:

  • Inför GitVersion som versionskälla.
  • Skapa förhandsversionstaggar för relevanta betrodda merges till main.
  • Skapa GitHub-förhandsutgåvor för förhandsversioner.
  • Publicera app-runtime och db-job till GHCR i betrodda flöden.
  • Starta release-Compose med GHCR-referenser låsta till digest.
  • Signera egenbyggda avbildningar med Cosign keyless signing.
  • Skapa och verifiera GitHub Artifact Attestations för provenance och SBOM.
  • Skapa versionsanteckningar med digest, checksummor och länkar till röktestartefakter.
  • Behåll GitHub-förhandsutgåvor permanent och markera bara stabila releases som latest.

Klart när:

  • Main kan skapa förhandsversioner enligt GitVersion-reglerna.
  • Stabil tagg kan skapa normal GitHub Release.
  • Releaseflödet verifierar signaturer, attesteringar och digest innan Podman Compose startas.

Inte i fasen:

  • Ingen release-please-implementation.
  • Ingen automatisk uppdatering av leverantörsavbildningar.

Fas 11: PoC- och demodokumentation

Mål: göra stacken användbar för demo och PoC utan att ge intryck av att publika utvecklingsvärden är säkra i exponerad drift.

Gör:

  • Dokumentera hur demo-profilen startas med publika exempelvärden.
  • Dokumentera vilka värden som måste bytas för en säker exponerad miljö: sessionsnyckel, Keycloak-klienthemligheter, Keycloak-adminlösenord, SQL Server-lösenord, TLS-certifikat och eventuella MCP-klienthemligheter.
  • Dokumentera nollställningskommandot för den lokala PoC/demo-volymen.
  • Dokumentera hur image.lock.json uppdateras per leverantörscontainer.
  • Dokumentera hur en användare hittar testad version, digest och röktestbevis.

Klart när:

  • En användare kan starta demo utan hemliga förberedelser.
  • Samma dokumentation varnar tydligt för vad som måste ändras före exponerad drift.
  • PoC/demodokumentationen pekar på de artefakter och låsfiler som faktiskt skapas av arbetsflödena.

Inte i fasen:

  • Ingen ny produktionsdriftsättning utanför den containerbaserade PoC/demo-modellen.

$grill-with-docs jag vill att vi jobbar och få fram den bästa lösningen som översiktligt beskrivs i docs/prompt.md, jag vill att vi ändrar i docs/prompt.md allt eftersom vi kommer fram till hur vi bäst gör ett produktions bygg/deploy flöde för detta project.

Jag tror vi behöver dela upp designen i implementationsfaser utan att förändra designen då det kan vara mycket att göra i ett svep för en AI. Kan du skapa en prompt-faser.md med lämpliga faser i ordning där fas 1 görs först

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment