Gå til hovedinnhold

Utgivelsestog

Et utgivelsestog leverer flere repositoryer sammen. Når toget kuttes, låser fremforge commiten til hvert repository i toget (komponentene) i et uforanderlig, hashet manifest. Deretter tar det utgivelsen gjennom stegene sine, for eksempel tst og så prd, ved å starte hvert repositorys egne utrullings-workflower med de låste commitene.

  • Én utgivelse på tvers av repositoryer. En plattform, infrastrukturen og en innlesingstjeneste leveres som ett tog med ett manifest, ikke som tre utrullinger som tilfeldigvis kjører tett på hverandre.
  • Kuttet etter en regel. fremforge kutter toget når den eldste mergen som ennå ikke er med i et tog, er gammel nok, for eksempel 60 minutter. Du kan også kutte for hånd.
  • Ingen merger sniker seg inn. Ved kuttet stopper et merge-stopp merging inn i komponentene til steget du angir, er passert. Merge-knappen, API-et og merge-køen venter alle.
  • Utrullingssperrene gjelder uendret. Et tog starter bare workflow-kjøringer. Hver kjøring møter sperrene i miljøet sitt (godkjennere, tillatte brancher, tidsvinduer, påkrevde sjekker, samtidighet, pauseflagget) nøyaktig som en kjøring du starter selv.

Utgivelsestog er avslått til en eier av organisasjonen slår dem på. Når de slås på, starter de i shadow only: de registrerer hva de ville rullet ut, og ruller ikke ut noe før en eier slår det av.

Definere et tog

Et tog er én fil, trains/<name>.yaml, i et repository i organisasjonen som du velger (et lite release-repository fungerer godt). Filnavnet er togets navn: 1 til 40 små bokstaver, sifre og bindestreker. En endring av et tog er en pull request som alle andre, så den blir gjennomgått, og repositoryets historikk er togets historikk.

version: 1
owner: hk                       # the person the train acts for
description: Platform, infrastructure and ingestion
components:
  platform: { repo: platform }
  infra:    { repo: infrastructure-azure, branch: main }
  ingest:   { repo: ingestion }
cut:
  after_oldest_unreleased_minutes: 60
hold:
  release_after: tst            # hold merges until stage tst has passed
stages:
  - name: tst
    timeout_minutes: 90
    steps:
      - component: platform
        workflow: code-deploy.yaml
        inputs: { app: migrate, confirm: migrate, sha: "{{ sha }}" }
        environment: tst
      - component: platform
        workflow: code-deploy.yaml
        inputs: { app: web, confirm: web, sha: "{{ sha }}" }
        environment: tst
  - name: prd
    steps:
      - component: platform
        workflow: code-deploy-prd.yaml
        inputs: { app: migrate, confirm: migrate, sha: "{{ sha }}", infra_pin: "{{ sha.infra }}" }
        environment: prd
FeltPåkrevdMerknader
versionja1.
ownerjaForgejo-innloggingen til personen toget handler for. Personen må være medlem av organisasjonen. Togets merge-stopp er personens.
descriptionneiÉn linje, opptil 200 tegn. Sett den i anførselstegn hvis den inneholder #, som YAML leser som starten på en kommentar.
componentsja1 til 20, hver et navn du velger, koblet til repo og branch (standard main). Et repository og en branch kan være med i et tog én gang.
cutja{ after_oldest_unreleased_minutes: N } (0 til 10080), eller manual for bare å kutte for hånd.
holdneirelease_after: et stegnavn, cut (stopp bare mens manifestet skrives) eller end (til toget er ferdig). Standard er det første steget. enabled: false setter ikke noe stopp.
dispatchneiworkflows (standard) starter workflow-kjøringene i stegene. none er shadow-modus: toget kuttes, stopper merging og skriver manifestet sitt, men starter ingenting.
plan_filesneiEt glob-mønster for utgivelsesplanfiler, standard .release/*.md. Planfiler som er endret siden forrige tog, oppføres i manifestet med sha256. null oppfører ingen.
stagesja1 til 5, i rekkefølge. Hvert har et name, en valgfri timeout_minutes (5 til 2880, standard 120) og 1 til 20 steps.

Hvert trinn starter én workflow-kjøring:

FeltPåkrevdMerknader
componentjaHvilken komponents repository som kjører workflowen, på den komponentens branch.
workflowjaWorkflow-filen, for eksempel code-deploy.yaml. Den må ha en workflow_dispatch-utløser.
inputsneiworkflow_dispatch-inputene, som strenger.
environmentneiMiljøet kjøringen må gå gjennom. Trinnet er bestått bare når kjøringens utrulling til dette miljøet passerte sperrene. Angi det alltid når workflowen ruller ut, slik at en kjøring som aldri nådde sperrene, ikke kan telle som en utrulling.

Inputverdiene kan bruke disse variablene:

VariabelVerdi
{{ sha }}Den låste commiten til trinnets komponent.
{{ sha.<component> }}Den låste commiten til en annen komponent.
{{ train.name }}, {{ train.number }}, {{ train.id }}Togets navn, nummer (1, 2, 3, …) og ID.

En workflow som får commiten som input, bør bygge eller rulle ut nøyaktig den commiten, ikke toppen av branchen. Branchen kan gå videre etter at togets stopp er opphevet.

Sjekk en definisjon før den merges

curl -X POST https://frem.sh/_app/api/v1/orgs/<org>/trains/validate \
  -H "Authorization: Bearer $FREMFORGE_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --arg s "$(cat trains/platform.yaml)" '{path: "trains/platform.yaml", source: $s}')"

Svaret har valid, errors og warnings. I tillegg til selve filen sjekker fremforge den mot organisasjonen din: at eieren er medlem, at hvert repository og hver branch finnes, at hver workflow finnes på branchen sin, godtar workflow_dispatch og inputene trinnet sender (og får hver input den krever), og at hvert miljø er konfigurert. En advarsel sier fra når en workflow aldri nevner trinnets miljø, eller når stoppet dekker en branch der fremforge/hold ikke er en påkrevd sjekk. Kjør den fra pull requesten som endrer toget. Tokenet trenger deployments:read.

Slå på utgivelsestog

curl -X PUT https://frem.sh/_app/api/v1/orgs/<org>/trains/settings \
  -H "Authorization: Bearer $FREMFORGE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"enabled": true, "definitions_repo": "release", "definitions_branch": "main"}'

Tokenet trenger policy:write. Så lenge utgivelsestog er slått på, leser fremforge trains/*.yaml hvert annet minutt, alle fra samme commit i branchen.

Svaret, og GET .../trains/settings, har også shadow_only. Det er true til en eier slår det av i konsollen; se Shadow only.

For at stoppet skal stoppe merge-knappen, slår du på håndheving av merge-stopp for komponentenes brancher. Uten det blir stoppet registrert og lest av verktøy, men merge-knappen venter ikke.

Hva et kutt gjør

  1. fremforge sjekker definisjonen mot organisasjonen, slik validate gjør. Et tog som ikke kan kjøre, kuttes ikke.
  2. Det setter merge-stoppet: ett per branch som komponentene bruker, eid av togets eier, med begrunnelsen Release train <name> #<n>: merges held until stage <stage> has passed.
  3. Det leser toppcommiten til hver komponent og sammenligner den med commiten forrige tog låste.
  4. Det skriver manifestet og toget samtidig.

Hvis noe etter stoppet feiler, opphever fremforge stoppet igjen. Et mislykket kutt etterlater aldri en branch stoppet.

En togdefinisjon har ett pågående tog om gangen. Neste kutt venter til det gjeldende toget er fullført, har feilet eller er avbrutt.

Regelen. For hver komponent er de uutgitte commitene de som har kommet siden commiten til forrige tog. Når den eldste av dem, i en hvilken som helst komponent, er after_oldest_unreleased_minutes gammel, kuttes toget. Alderen er commitens committer-dato, som er merge-tidspunktet ved en squash-merge. Det første toget i en definisjon regner fra toppcommiten i hver branch.

For hånd. En eier av organisasjonen, eller et token som handler for en person, kan kutte nå:

curl -X POST https://frem.sh/_app/api/v1/orgs/<org>/trains/<name>/cut \
  -H "Authorization: Bearer $FREMFORGE_TOKEN"

Tokenet trenger deployments:write og må handle for en person: et kutt for hånd registreres som personens.

Manifestet

Manifestet er det toget leverte, og det endres aldri: databasen avviser enhver oppdatering av det. Det inneholder:

  • hver komponents repository, branch og låste commit, commiten forrige tog låste, og hvor mange commits som er nye;
  • planfilene som er endret siden forrige tog, hver med sha256 av innholdet i den låste commiten;
  • togdefinisjonen slik den var ved kuttet, og commiten i definisjonsrepositoryet den ble lest fra, slik at en senere endring av filen aldri endrer et tog som allerede kjører.

manifest_sha256 er sha256 av manifestet skrevet som kanonisk JSON: objektnøkler sortert på alle nivåer, uten mellomrom. Du kan beregne den på nytt fra det lagrede manifestet:

curl -s https://frem.sh/_app/api/v1/orgs/<org>/trains/runs/<id> \
  -H "Authorization: Bearer $FREMFORGE_TOKEN" \
  | jq -cS '.manifest' | tr -d '\n' | sha256sum

Steg

Et kuttet tog starter det første steget ved neste kjøring av jobben, innen to minutter. Hvert steg kjører trinnene sine etter hverandre:

  1. fremforge starter trinnets workflow med workflow_dispatch på komponentens branch, med inputene fylt ut.
  2. Kjøringen møter sperrene i miljøet sitt. Mens den venter på en godkjenning, sier toget fra om det (waiting for approval in environment tst). Godkjennerne godkjenner utrullingen som vanlig, under Bygg og utrulling → Utrullinger.
  3. Når kjøringen lykkes, og (hvis trinnet angir et environment) utrullingen til det miljøet passerte sperrene, er trinnet bestått, og det neste starter.

Når alle trinnene i et steg er bestått, går toget videre til neste steg. Stoppet oppheves etter steget som er angitt i hold.release_after, og senest når toget avsluttes.

fremforge starter kjøringene med plattformens Forgejo-konto, slik en tidsplan ville gjort. Derfor kan togets eier godkjenne togets egne utrullinger: en godkjenning fra den som startet en kjøring, avvises.

Et tog stopper (failed, med årsaken) når:

  • en kjøring feiler eller avbrytes, eller sperrene i et miljø avviser den (årsaken navngir sperren);
  • en kjøring lykkes uten å gå gjennom trinnets miljø;
  • et steg kjører lenger enn timeout_minutes;
  • en dispatch feiler eller utfallet er ukjent. fremforge starter aldri et trinn to ganger: hvis det ikke kan avgjøre om en kjøring startet, stopper det toget og ber deg se på Actions i repositoryet.

Når et tog stopper, oppheves stoppene. Commits som allerede er merget, forblir merget; neste kutt tar med rettelsen.

Avbryt et tog som ikke er ferdig:

curl -X POST https://frem.sh/_app/api/v1/orgs/<org>/trains/runs/<id>/cancel \
  -H "Authorization: Bearer $FREMFORGE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"reason": "Re-cut after the hotfix"}'

Stoppene oppheves. Workflow-kjøringer som toget allerede har startet, fortsetter å kjøre.

I konsollen ser eiere av organisasjonen det samme under Bygg og utrulling → Utgivelsestog (https://frem.sh/<org>/_admin/release-trains): hvert tog, det som pågår med hva det venter på, nylige tog med de låste commitene og knapper for å kutte nå eller avbryte.

Shadow-modus

Sett dispatch: none for å prøve et tog før det ruller ut noe, for eksempel ved siden av utgivelsesprosessen du bruker i dag:

version: 1
owner: hk
dispatch: none
# components, cut, hold and stages as usual

Et tog i shadow-modus:

  • kuttes etter regelen sin, setter merge-stoppet og skriver manifestet, nøyaktig som et tog som ruller ut;
  • registrerer ved kuttet, for hvert trinn, hva det ville startet: repository, branch, workflow og inputer med de låste commitene (would_dispatch på hvert trinn, med statusen shadow);
  • går deretter gjennom stegene uten å starte en eneste workflow-kjøring, opphever stoppet etter steget som er angitt i hold.release_after, og fullføres;
  • er merket shadow: true i API-et, med et Skygge-merke i konsollen og i hver hendelse og revisjonspost.

Sammenlign det toget ville startet, med det du faktisk leverte. Når de stemmer, fjerner du dispatch: none. Neste kutt ruller ut så snart organisasjonen ikke lenger er shadow only.

Shadow only

dispatch: none er en linje i en fil, og alle som kan merge inn i definisjonsrepositoryet, kan fjerne den. Bare skygge er organisasjonens egen innstilling, og fremforge håndhever den uansett hva en definisjon sier. Så lenge den er på:

  • er hvert tog et shadow-tog: hvert trinn registrerer hva det ville startet, og ingen workflow-kjøring startes;
  • settes ingen merge-stopp, selv når en definisjon ber om det;
  • sier et kutt der definisjonen ba om å rulle ut eller stoppe merging, fra om det i manifestet (definition.org_shadow_only) og i revisjonsloggen (release_train.shadow_only_enforced);
  • stopper et tog som ble kuttet mens shadow only var av, før neste dispatch hvis shadow only slås på igjen. Stoppene oppheves.

Shadow only er på for alle organisasjoner til en eier slår det av. Slå det av i konsollen, under Bygg og utrulling → Utgivelsestog: åpne La togene rulle ut, skriv organisasjonens navn nøyaktig og bekreft.

  • Det krever en eier av organisasjonen som er logget inn i konsollen.
  • Hvis organisasjonen din har konfigurert sin egen ekstra innloggingskontroll, blir du bedt om å logge inn på nytt først, med mindre du gjorde det de siste 10 minuttene.
  • Hver eier av organisasjonen får en e-post om hvem som slo det av, og en train.shadow_only_cleared-hendelse legges i hendelsesfeeden.
  • API-et kan slå shadow only på ("shadow_only": true i PUT .../trains/settings), men aldri av. "shadow_only": false avvises for alle tokener.

Å slå shadow only på igjen er én knapp på den samme siden. Revisjonsloggen registrerer release_train.shadow_only_cleared og release_train.shadow_only_set, med hvem som endret det.

Lese tog

KallSvar
GET /orgs/<org>/trainsHver definisjon, toget som pågår, og det siste toget, og filene som ikke kan tolkes, med feilene deres. MCP: list_release_trains.
GET /orgs/<org>/trains/runs?train=<name>Kuttede tog, nyeste først, med status, steg, trinn og stopp. MCP: list_release_train_runs.
GET /orgs/<org>/trains/runs/<id>Ett tog med manifestet og manifest_sha256. MCP: get_release_train.
GET /orgs/<org>/trains/settingsOm utgivelsestog er slått på, og hvor de er definert.

Alle krever deployments:read. Et togs status er cut, running, completed, failed eller cancelled, og shadow er true for et tog i shadow-modus.

Hendelser og revisjon

Hendelsesfeeden har én hendelse per komponentrepository for hver av disse, med sha som commiten toget låste der:

TypeNår
train.cutEt tog ble kuttet. data har train, number, train_id, owner, component og manifest_sha256.
train.stage_promotedEt steg ble passert. data.stage, og data.next_stage (null etter det siste steget).
train.completedAlle steg er passert.
train.failedToget stoppet. data.stage og data.reason; data.outcome er cancelled når noen avbrøt det.

Hendelser fra et tog i shadow-modus har data.shadow: true.

Revisjonsloggen registrerer release_train.settings_updated, release_train.shadow_only_set, release_train.shadow_only_cleared, release_train.shadow_only_enforced, release_train.cut, release_train.step_dispatched, release_train.stage_promoted, release_train.completed, release_train.failed og release_train.cancelled, sammen med stoppets egne merge_hold.created og merge_hold.released. Et kutt etter regelen og hvert trinn registreres som system:release-trains, som handler for togets eier; et kutt eller en avbrytelse for hånd som personen som gjorde det.

Hva et tog ikke gjør

  • Det godkjenner ingenting. Godkjenninger gis av godkjennerne på utrullingene, som uten et tog.
  • Det endrer ikke sperrene. Et miljø med omgåelse av sperrer, et pauseflagg eller påkrevde godkjennere oppfører seg nøyaktig som for enhver kjøring.
  • Det ruller ikke tilbake. Et steg som feiler, stopper toget. Commits som er merget, forblir merget; lever rettelsen i neste tog.
  • Det avbryter ikke kjøringer. Å avbryte et tog lar kjøringene det har startet, være i fred.