# Design Rationale — Gitea Actions CI -kirjasto > Miksi kirjasto on rakennettu näin. Arvot, periaatteet ja reunaehdot, joiden > varaan arkkitehtuuri nojaa. > > Tämä dokumentti on **normatiivinen** — arkkitehtuurin on noudatettava näitä > periaatteita. Jos ehdotettu muutos on ristiriidassa rationalen kanssa, > rationalen on muututtava ensin. --- ## Miksi tämä projekti on olemassa Mikropalveluarkkitehtuurissa jokainen palvelu tarvitsee CI-putken: testit, laatutarkistukset, buildin, kontituksen ja julkaisun. Ilman jaettua kirjastoa jokainen tiimi kopioi saman YAML-boilerplaten, tekee omat virheensä ja ylläpitää omaa versiotaan. Ajan myötä putket ajautuvat erilleen — toisessa on `shell: bash`, toisessa ei; toinen käyttää `set -o pipefail`, toinen kadottaa exit-koodin `tee`:hen. Tämä kirjasto on se mitä kopioidaan. Se tarjoaa valmiit, testatut, dokumentoidut rakennuspalikat joista jokainen tiimi kokoaa oman putkensa. Palikat ovat Gitea Actionsin `uses:`-direktiivillä kutsuttavia reusable workflow'ta — ei asennusta, ei runtime-riippuvuutta, ei versiopäivityksiä projekteihin. --- ## Suunnitteluperiaatteet ### 1. Palikka-arkkitehtuuri: pieniä, vaihdettavia, yhden vastuun workflow'ta Jokainen provider-workflow tekee yhden asian: | Workflow | Vastuu | |---|---| | `config-provider.yml` | Lataa ja validoi konfiguraatio | | `check-version.yml` | Tarkistaa onko commit buildattu, laskee version | | `docker-build-push.yml` | Buildaa, puskea ja tagittaa kontin | Mikään workflow ei kutsu toista provider-workflowta. Consumer — siis mikropalvelun oma pipeline-tiedosto — on ainoa paikka joka tietää mitä palikoita tarvitaan ja missä järjestyksessä. **Miksi:** Tämä on sama periaate kuin Unix-putkissa tai mikropalveluissa: pieniä, itsenäisiä komponentteja jotka tekevät yhden asian hyvin. Consumer voi vaihtaa yhden palikan toiseen — esimerkiksi Docker-buildin tilalle Maven-paketoinnin — ilman että muut palikat muuttuvat. Ratkaisu ei ole se että kaikki ajetaan, vaan se että jokainen tiimi valitsee mitä tarvitsee. Monoliittinen "kaikki yhdessä" -workflow pakottaisi jokaisen tiimin ajamaan tarpeettomia vaiheita. ### 2. Gitea ensin — hyödynnä alustaa, älä taistele sitä vastaan Gitea Actions tarjoaa kolme asiaa ilmaiseksi: 1. **Jobien visuaalinen status** — jokainen jobi näkyy automaattisesti commit-näkymässä checkmarkilla, spinnerillä tai ristillä. 2. **Cross-job riippuvuudet** — `needs` hoitaa virheiden propagointin: jos edeltävä jobi feilaa, riippuvat jobit skipataan. 3. **Reusable workflow -jakelu** — `uses: org/repo/.gitea/workflows/file.yml@v1` on natiivisti versioitu, skopattu ja välimuistitettu. Kirjasto käyttää näitä kaikkia. Ei omaa tilakonetta, ei custom action -runtimea, ei ulkoista orkestraattoria. **Esimerkki:** Tool-jobit eivät kutsu commit-status API:a lainkaan. Gitean oma job-status riittää — `success`/`failure`/`running` näkyy automaattisesti. API:a käytetään vain kun tarvitaan **custom-linkki** (testiraporttiin tai Docker registryyn), jota natiivistaatus ei tarjoa. Tämä linjaus on dokumentoitu ADR 0004 ja 0007:ssä. ### 3. Status näkyy siellä missä työ tehdään — Git-commitissa Kehittäjä työskentelee Gitissä. `git log`, `git blame`, PR-näkymä — nämä ovat päivittäiset työkalut. CI-statuksen kuuluu näkyä siellä, ei erillisessä dashboardissa. Gitea Actionsin natiivi job-status tekee tämän automaattisesti: jokainen commit näyttää välittömästi mitkä jobit on ajettu ja millä tuloksella. Testiraportteihin pääsee yhdellä klikkauksella commitin status-kuvakkeesta — koska `report-status.sh` asettaa `target_url`:n osoittamaan suoraan HTML-raporttiin git-pagesissa. Tämä ei ole kosmeettinen yksityiskohta. Se on devops-käytännön ydin: palautesilmukka on lyhin mahdollinen. Commit → build → status näkyy samassa näkymässä jossa kehittäjä jo on. ### 4. Exit-koodi on ainoa totuus CI-putken jokaisen `run`-stepin onnistuminen määräytyy **vain ja ainoastaan** exit-koodin perusteella. Ei tiedoston olemassaolon, ei stdout-tulosteen, ei arvauksen. `0` = ok, kaikki muu = ei ok. Tämä kuulostaa itsestään selvältä, mutta YAML-pipelineissa se rikkoutuu helposti. Pipe (`|`) `tee`:hen syö exit-koodin. Tiedoston olemassaolon tarkistus (`[ -f results.xml ]`) ei kerro testien läpimenosta. **Käytännössä:** Jokainen `run`-steppi ottaa exit-koodin talteen `$?`-muuttujaan ennen kuin mikään muu komento ehtii muuttaa sitä, ja stepin viimeinen rivi on `exit ${EXIT}`. Pipeä ei käytetä työvaiheen viimeisenä komentona. Ks. ADR 0008. ### 5. Pienin mahdollinen pinta-ala Jokainen ylimääräinen riippuvuus on ylimääräinen vikaantumispiste. Kirjaston ainoat riippuvuudet: - Gitea Actions (alusta) - `bash`, `curl`, `jq` (ubuntu-latest runnerissa valmiina) - Docker (runnerissa valmiina) - git-pages (raporttien hostaus, erillinen palvelu) Ei Pythonia, ei Node.js:ää ajonaikaisesti (testit omissa konteissaan). Ei tietokantaa. Ei ulkoista tilanhallintaa. Kirjasto on joukko YAML-tiedostoja ja shell-skriptejä — samat työkalut jotka jokainen devops-ihminen jo osaa. ### 6. Konfiguraatio repoon, salaisuudet Giteaan Projektikohtainen konfiguraatio (`.gitea/workflows/gitea-env.conf`) asuu mikropalvelun omassa repossa. Kehittäjä omistaa sen — hän tietää mikä on Docker-imagen nimi, mihin registryyn puskea, mikä on testiympäristön URL. Salaisuudet (tokenit, salasanat) elävät Gitean secrets-mekanismissa, eivät repon tiedostoissa. `secrets: inherit` välittää ne providerin workflow'hun ilman että consumerin tarvitsee tietää mitä salaisuuksia mikäkin provider tarvitsee. Poikkeus: infra-tason asetukset (`GIT_PAGES_URL`, `GITEA_API_URL`) ovat Gitean organization secrets/variables -mekanismissa. Ne eivät ole repokohtaisia. ### 7. Consumer omistaa orkestroinnin, provider tarjoaa palikat Tämä on kirjaston tärkein arkkitehtuurinen päätös (ADR 0005). Provider (`gitea-ci-library`) ei tiedä mitä testejä ajetaan, missä järjestyksessä, tai millä branchilla. Se tarjoaa kolme reusable workflow'ta ja joukon skriptejä. Consumer (mikropalvelun `example-feature.yml` / `example-main.yml`) päättää: - Mitkä palikat kutsutaan - Missä järjestyksessä (`needs`) - Millä branch-ehdoilla (`if`) - Mitkä testikontit käytetään (input-parametrit) Tämä on tarkoituksellinen vallanjako. Provider ei voi tietää jokaisen tiimin tarpeita — eikä sen pidäkään. Consumer ei voi muuttaa providerin sisäistä toteutusta — eikä sen pidäkään. Rajapinta on `workflow_call` ja se on molemmille osapuolille selvä. ### 8. Branch-kohtainen reititys, ei yhtä kaikille Eri brancheilla on eri tavoite: - **Feature-haara:** Onko koodi laadukasta? → testit, validointi - **Main-haara:** Onko tästä versiosta jo artifakti? Jos ei → testit + build + push + tag. Jos on → ei tehdä mitään (tai jatketaan klusteritesteihin). Tämä logiikka elää consumerin pipeline-tiedostossa, ei providerissa. Se on puhdasta `if`-ehtoa ja `needs`-ketjutusta — ei skriptausta, ei monimutkaisia ehtoja providerin sisällä. ### 9. Raportit erillisellä palvelulla, linkit commitissa Gitea Actionsin artifact-järjestelmä on binääriarkisto — ZIP-lataus, ei HTML-selailtavuutta. Testiraportit (Cucumber HTML, Bats-coverage) on voitava avata selaimessa yhdellä klikkauksella. Ratkaisu: git-pages Helm-chartti, joka tarjoaa staattista tiedostohostingia HTTP:llä. `publish-git-pages.sh` vie raportit sinne; `report-status.sh` linkittää commit-statuksen suoraan raporttiin. Retention hoitaa git-pagesin sidecar automaattisesti. Tulevaisuudessa `GITHUB_STEP_SUMMARY` (Gitea 1.27+) tarjoaa vaihtoehtoisen kanavan: jobin Summary-välilehdelle renderöityvä Markdown-taulukko kaikista raporttilinkeistä. ### 10. Vain Gitea — ei monialustatukea ilman tarvetta Yhden alustan tukeminen kunnolla on vaikeampaa kuin kolmen tukeminen huonosti. Gitea Actionsin `uses:`-mekanismi, `needs`-semantiikka, `secrets: inherit`, `gitea`-konteksti — nämä ovat alustakohtaisia ominaisuuksia joita abstraktiokerros vain haittaisi. Jos toinen alusta tulee ajankohtaiseksi, sille kirjoitetaan oma toteutus. Siihen asti yksi alusta riittää. Ennenaikainen yleistys on devopsissa yhtä haitallista kuin ohjelmistosuunnittelussa. --- ## Arkkitehtuuriset rajoitteet ### Mitä kirjasto EI tee - **Ei ulkoista orkestraattoria.** Pipeline-ohjaus on Gitea Actionsin `needs`-ketjuissa ja consumerin `if`-ehdoissa. - **Ei custom actioneita.** Reusable workflow on kevyempi, versioitu ja jaeltu Gitean oman mekanismin kautta. - **Ei asennusta projekteihin.** Consumer viittaa `uses:`-direktiivillä suoraan tämän repon workflow-tiedostoihin. Ei npm-pakettia, ei git-submodulea, ei kopioitavia tiedostoja. - **Ei runtime-riippuvuuksia.** Provider-skriptit käyttävät vain työkaluja jotka ovat Gitea Actionsin `ubuntu-latest` runnerissa valmiina: `bash`, `curl`, `jq`. - **Ei monorepo-konfiguraatiota.** Jokainen mikropalvelu omistaa oman pipeline-tiedostonsa ja konfiguraationsa. --- ## Mitä tietoisesti hylättiin | Hylätty | Syy | |---|---| | Monoliittinen "kaikki yhdessä" -workflow | Pakottaa kaikille samat vaiheet. Palikka-arkkitehtuuri antaa jokaiselle tiimille vain mitä se tarvitsee | | Oma orkestraattoripalvelin | Ylimääräinen ylläpidettävä. Gitean `needs` ja `if` riittävät | | Docker-pohjaiset custom actionit | Tuovat riippuvuuden Docker-rekisteriin. Reusable workflow on natiivimpi | | Commit-status API jokaiselle vaiheelle | Duplikointia — Gitea näyttää job-statuksen automaattisesti. API vain custom-linkeille | | `tee`-putki debug-näkyvyyteen | Syö exit-koodin. stdout ohjataan tiedostoon `>` ilman pipeä | | Multi-Git-platform-tuki | Ennenaikaista optimointia ilman tarvetta | | Gitea Packages raporttien hostingiin | Ei HTML-selailtavuutta — vain binäärilataus | | Gitea Pages + reports-branch | Race condition rinnakkaisten pushien kanssa | | `repository_dispatch` ketjutukseen | Lisää konfiguraatiota vastaanottaviin repoihin. Suora API-kutsu eksplisiittisempi |