Gå til hovedindhold

Release-tog

Et release-tog udgiver flere repositories samlet. Når toget skæres, fastlåser fremforge committen i hvert repository i toget (dets komponenter) i et uforanderligt manifest med hash. Derefter fører fremforge releasen gennem togets stadier, for eksempel tst og så prd, ved at starte hvert repositorys egne deploy-workflows med de fastlåste commits.

  • Én release på tværs af repositories. En platform, dens infrastruktur og en ingestion-tjeneste udgives som ét tog med ét manifest, ikke som tre udrulninger, der tilfældigvis kører tæt på hinanden.
  • Skåret efter en regel. fremforge skærer toget, når den ældste merge, der endnu ikke er med i et tog, er gammel nok, for eksempel 60 minutter. Du kan også skære i hånden.
  • Ingen merges smutter med. Ved skæringen stopper et merge-hold merges til komponenterne, indtil det stadie, du angiver, er bestået. Merge-knappen, API’et og merge-køen venter alle.
  • Deploy-gates gælder uændret. Et tog starter kun workflow-kørsler. Hver kørsel møder sit miljøs gates (godkendere, tilladte branches, tidsvinduer, påkrævede tjek, samtidighed, pauseflaget) præcis som en kørsel, du selv starter.

Release-tog er slået fra, indtil en ejer af organisationen slår dem til. Når de slås til, starter de i kun skygge: de registrerer, hvad de ville udrulle, og udruller intet, før en ejer slår det fra.

Definér et tog

Et tog er én fil, trains/<name>.yaml, i et repository i din organisation, som du vælger (et lille release-repository fungerer godt). Filnavnet er togets navn: 1 til 40 små bogstaver, cifre og bindestreger. En ændring af et tog er en pull request som alle andre, så den bliver reviewet, og repositoryets historik er togets historik.

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åkrævetBemærkninger
versionja1.
ownerjaForgejo-login for den person, toget handler på vegne af. Personen skal være medlem af organisationen. Togets merge-holds er personens.
descriptionnejÉn linje, op til 200 tegn. Sæt den i anførselstegn, hvis den indeholder #, som YAML læser som starten på en kommentar.
componentsja1 til 20, hver et navn, du vælger, knyttet til repo og branch (standard main). Et repository og en branch kan kun være med i et tog én gang.
cutja{ after_oldest_unreleased_minutes: N } (0 til 10080), eller manual for kun at skære i hånden.
holdnejrelease_after: et stadienavn, cut (hold kun, mens manifestet skrives) eller end (indtil toget er færdigt). Standard er det første stadie. enabled: false sætter intet hold.
dispatchnejworkflows (standard) starter stadiernes workflow-kørsler. none er skyggetilstand: toget skærer, holder og skriver sit manifest, men starter intet.
plan_filesnejEt glob for release-planfiler, standard .release/*.md. Planfiler, der er ændret siden det forrige tog, står i manifestet med deres sha256. null medtager ingen.
stagesja1 til 5, i rækkefølge. Hvert har et name, et valgfrit timeout_minutes (5 til 2880, standard 120) og 1 til 20 steps.

Hvert trin starter én workflow-kørsel:

FeltPåkrævetBemærkninger
componentjaHvilken komponents repository der kører workflowet, på komponentens branch.
workflowjaWorkflow-filen, for eksempel code-deploy.yaml. Den skal have en workflow_dispatch-trigger.
inputsnejworkflow_dispatch-inputs som strenge.
environmentnejDet miljø, kørslen skal igennem. Trinnet er kun bestået, når kørslens udrulning til dette miljø har passeret sine gates. Angiv det, hver gang workflowet udruller, så en kørsel, der aldrig nåede frem til gates, ikke kan tælle som en udrulning.

Input-værdier kan bruge disse pladsholdere:

PladsholderVærdi
{{ sha }}Den fastlåste commit for trinnets komponent.
{{ sha.<component> }}Den fastlåste commit for en anden komponent.
{{ train.name }}, {{ train.number }}, {{ train.id }}Togets navn, dets nummer (1, 2, 3, …) og dets id.

Et workflow, der får committen som input, skal bygge eller udrulle præcis den commit, ikke branchens hoved. Branchen kan rykke videre, når togets hold er frigivet.

Tjek en definition, 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. Ud over selve filen tjekker fremforge den mod din organisation: at ejeren er medlem, at hvert repository og hver branch findes, at hvert workflow findes på sin branch, accepterer workflow_dispatch og de inputs, trinnet sender (og får alle de inputs, det kræver), og at hvert miljø er konfigureret. En advarsel fortæller dig, når et workflow aldrig nævner trinnets miljø, eller når holdet dækker en branch, hvor fremforge/hold ikke er et påkrævet tjek. Kør det fra den pull request, der ændrer toget. Tokenet skal have deployments:read.

Slå tog til

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 skal have policy:write. Mens tog er slået til, læser fremforge trains/*.yaml hvert andet minut, alle fra samme commit på branchen.

Svaret, og GET .../trains/settings, har også shadow_only. Det er true, indtil en ejer slår det fra i konsollen; se Kun skygge.

For at holdet kan stoppe merge-knappen, skal du slå håndhævelse af merge-holds til for komponenternes branches. Uden det registreres holdet og kan læses af værktøjer, men merge-knappen venter ikke.

Hvad en skæring gør

  1. fremforge tjekker definitionen mod organisationen, som validate gør. Et tog, der ikke kan køre, bliver ikke skåret.
  2. Det sætter merge-holdet: ét pr. branch, komponenterne bruger, ejet af togets ejer, med begrundelsen Release train <name> #<n>: merges held until stage <stage> has passed.
  3. Det læser hver komponents hoved-commit og sammenligner den med den commit, det forrige tog fastlåste.
  4. Det skriver manifestet og toget samlet.

Hvis noget efter holdet fejler, frigiver fremforge holdet igen. En mislykket skæring efterlader aldrig en branch i hold.

En togdefinition har ét tog i gang ad gangen. Den næste skæring venter, indtil det nuværende tog er gennemført, fejlet eller annulleret.

Reglen. For hver komponent er de ikke-udgivne commits dem, der er kommet siden det forrige togs commit. Når den ældste af dem, på en hvilken som helst komponent, er after_oldest_unreleased_minutes gammel, skæres toget. Alderen er committens committer-dato, som er tidspunktet for merge ved en squash-merge. Det første tog i en definition regner fra hver branchs hoved-commit.

I hånden. En ejer af organisationen, eller et token, der handler på vegne af en person, kan skære med det samme:

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

Tokenet skal have deployments:write og skal handle på vegne af en person: en skæring i hånden registreres som personens.

Manifestet

Manifestet er det, toget udgav, og det ændres aldrig: databasen afviser enhver opdatering af det. Det indeholder:

  • hver komponents repository, branch og fastlåste commit, den commit, det forrige tog fastlåste, og hvor mange commits der er nye;
  • de planfiler, der er ændret siden det forrige tog, hver med sha256 af indholdet ved den fastlåste commit;
  • togdefinitionen, som den var ved skæringen, og committen i definitionsrepositoryet, den blev læst fra, så en senere ændring af filen aldrig ændrer et tog, der allerede kører.

manifest_sha256 er sha256 af manifestet skrevet som kanonisk JSON: objektnøgler sorteret på alle niveauer, intet whitespace. Du kan genberegne den ud fra det gemte manifest:

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

Stadier

Et skåret tog starter sit første stadie ved jobbets næste kørsel, inden for to minutter. Hvert stadie kører sine trin et ad gangen:

  1. fremforge starter trinnets workflow med workflow_dispatch på komponentens branch, med inputs udfyldt.
  2. Kørslen møder sit miljøs gates. Mens den venter på en godkendelse, siger toget det (waiting for approval in environment tst). Godkendere godkender udrulningen som sædvanlig under Byg og udrul → Udrulninger.
  3. Når kørslen lykkes, og (hvis trinnet angiver et environment) dens udrulning til det miljø har passeret sine gates, er trinnet bestået, og det næste starter.

Når alle trin i et stadie er bestået, går toget videre til næste stadie. Holdet frigives efter det stadie, der er angivet i hold.release_after, og senest når toget slutter.

fremforge starter kørslerne med platformens Forgejo-konto, som en tidsplan ville gøre. Derfor kan togets ejer godkende togets egne udrulninger: en godkendelse fra den person, der startede en kørsel, afvises.

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

  • en kørsel fejler eller annulleres, eller et miljøs gates afviser den (begrundelsen nævner gaten);
  • en kørsel lykkes uden at gå gennem trinnets miljø;
  • et stadie kører ud over sit timeout_minutes;
  • en dispatch fejler, eller udfaldet er ukendt. fremforge starter aldrig et trin to gange: hvis det ikke kan afgøre, om en kørsel startede, stopper det toget og beder dig se på repositoryets Actions.

Når et tog stopper, frigives dets holds. Commits, der allerede er merget, forbliver merget; den næste skæring tager rettelsen med.

Annullér et tog, der ikke er færdigt:

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"}'

Holdene frigives. Workflow-kørsler, toget allerede har startet, kører videre.

I konsollen ser organisationens ejere det samme under Byg og udrul → Release-tog (https://frem.sh/<org>/_admin/release-trains): alle tog, det igangværende med det, det venter på, de seneste tog med deres fastlåste commits, og knapper til at skære nu eller annullere.

Skyggetilstand

Sæt dispatch: none for at afprøve et tog, før det udruller noget, for eksempel ved siden af den release-proces, du bruger i dag:

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

Et tog i skyggetilstand:

  • skæres efter sin regel, sætter sit merge-hold og skriver sit manifest, præcis som et tog, der udruller;
  • registrerer ved skæringen for hvert trin, hvad det ville starte: repository, branch, workflow og inputs med de fastlåste commits (would_dispatch på hvert trin, med status shadow);
  • går derefter gennem sine stadier uden at starte en eneste workflow-kørsel, frigiver sit hold efter det stadie, der er angivet i hold.release_after, og bliver gennemført;
  • er markeret shadow: true i API’et, med mærket Skygge i konsollen og i alle hændelser og revisionsposter.

Sammenlign det, toget ville have startet, med det, du udgav. Når de stemmer overens, så fjern dispatch: none. Den næste skæring udruller, når organisationen ikke længere er i kun skygge.

Kun skygge

dispatch: none er en linje i en fil, og alle, der kan merge til definitionsrepositoryet, kan fjerne den. Kun skygge er organisationens egen indstilling, og fremforge håndhæver den, uanset hvad en definition siger. Så længe den er slået til:

  • er alle tog skyggetog: hvert trin registrerer, hvad det ville starte, og der startes ingen workflow-kørsel;
  • sættes der intet merge-hold, heller ikke når en definition beder om et;
  • angiver en skæring, hvis definition bad om at udrulle eller holde, det i manifestet (definition.org_shadow_only) og i revisionsloggen (release_train.shadow_only_enforced);
  • stopper et tog, der blev skåret, mens kun skygge var slået fra, før sin næste dispatch, hvis kun skygge slås til igen. Dets holds frigives.

Kun skygge er slået til for alle organisationer, indtil en ejer slår det fra. Slå det fra i konsollen under Byg og udrul → Release-tog: åbn Lad togene udrulle, skriv organisationens navn præcist, og bekræft.

  • Det kræver en ejer af organisationen, der er logget ind i konsollen.
  • Hvis din organisation har sit eget ekstra login-trin konfigureret, bliver du bedt om at logge ind igen først, medmindre du har gjort det inden for de seneste 10 minutter.
  • Alle ejere af organisationen får en e-mail om, hvem der slog det fra, og en train.shadow_only_cleared-hændelse kommer i hændelsesstrømmen.
  • API’et kan slå kun skygge til ("shadow_only": true i PUT .../trains/settings), men aldrig fra. "shadow_only": false afvises for alle tokens.

At slå kun skygge til igen er én knap på samme side. Revisionsloggen registrerer release_train.shadow_only_cleared og release_train.shadow_only_set med, hvem der ændrede det.

Læs tog

KaldSvar
GET /orgs/<org>/trainsAlle definitioner, deres igangværende og seneste tog, og de filer, der ikke kan parses, med deres fejl. MCP: list_release_trains.
GET /orgs/<org>/trains/runs?train=<name>Skårne tog, nyeste først, med status, stadie, trin og holds. MCP: list_release_train_runs.
GET /orgs/<org>/trains/runs/<id>Ét tog med dets manifest og manifest_sha256. MCP: get_release_train.
GET /orgs/<org>/trains/settingsOm tog er slået til, og hvor de er defineret.

Alle kræver deployments:read. Et togs status er cut, running, completed, failed eller cancelled, og shadow er true for et tog i skyggetilstand.

Hændelser og revision

Hændelsesstrømmen har én hændelse pr. komponent-repository for hver af disse, med sha som den commit, toget fastlåste der:

TypeHvornår
train.cutEt tog blev skåret. data har train, number, train_id, owner, component og manifest_sha256.
train.stage_promotedEt stadie blev bestået. data.stage og data.next_stage (null efter det sidste stadie).
train.completedAlle stadier blev bestået.
train.failedToget stoppede. data.stage og data.reason; data.outcome er cancelled, når nogen annullerede det.

Hændelser fra et tog i skyggetilstand har data.shadow: true.

Revisionsloggen 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 holdets egne merge_hold.created og merge_hold.released. En skæring efter reglen og alle trin registreres som system:release-trains, der handler på vegne af togets ejer; en skæring eller annullering i hånden som den person, der foretog den.

Hvad et tog ikke gør

  • Det godkender ikke noget. Godkendelser gives af godkendere på udrulningerne, som uden et tog.
  • Det ændrer ikke gates. Et miljø med en undtagelse fra gates, et pauseflag eller påkrævede reviewere opfører sig præcis som for enhver anden kørsel.
  • Det ruller ikke tilbage. Et mislykket stadie stopper toget. Mergede commits forbliver merget; udgiv rettelsen i det næste tog.
  • Det annullerer ikke kørsler. Når du annullerer et tog, lader det de kørsler, det har startet, være i fred.