Die Nordlicht Reisen GmbH hat inzwischen sechs Repositories, und in allen steht fast dieselbe Pipeline: SDK installieren, wiederherstellen, bauen, testen, Ergebnis veröffentlichen. Jede Verbesserung muss sechsmal gemacht werden, und zwei Repositories sind schon abgewichen. Sie sollen einen zentralen Baustein entwerfen, den alle nutzen, der sich versionieren lässt und bei dem niemand die Grundregeln des Unternehmens auslassen kann. Das ist die Fähigkeit dieser Lektion: wiederverwendbare Pipeline-Elemente entwerfen, nämlich YAML-Vorlagen, Task Groups, Variablen und Variablengruppen, und ihre Gegenstücke bei GitHub.

Vorwissen: Eine einfache Vorlage mit typisierten Parametern für Schritte und die Variablengruppe mit Key Vault zeigt K3, Lektion 3.7.2. Hier beginnt die Lektion bei dem, was darüber hinausgeht: extends und Einfügen von Jobs und Stufen, Vorlagen aus anderen Repositories, die Vorrangregeln der Variablen, Task Groups und die wiederverwendbaren Workflows von GitHub. Die Sicherheitsseite von extends (erforderliche Vorlagen, Freigaben) vertieft Modul D5.

Nicht gegen Azure oder GitHub ausgeführt; aus der Referenz von Microsoft und GitHub: Alle YAML-Dateien und Befehle dieser Lektion stammen aus der Dokumentation oder sind aus ihr abgeleitet; sie sind in diesem Kurs nicht ausgeführt worden.

Vorlagen in Azure Pipelines

Microsoft unterscheidet zwei Arten:

  • Includes templates fügen Inhalt ein, wie eine Include-Anweisung in einer Programmiersprache. Der Inhalt der Vorlage steht danach in der Pipeline oder Vorlage, die ihn einbindet.
  • Extends templates legen Logik und Struktur fest, der eine Pipeline folgen muss. Das dient der Durchsetzung von Sicherheit, Konformität und Unternehmensstandards.

Einfügen: Schritte, Jobs, Stufen, Variablen

Dieselbe Vorlagen-Syntax - template: <pfad> steht an der Stelle, an der der Inhalt erscheinen soll. Eine Vorlage für Schritte beginnt mit steps:, für Jobs mit jobs:, für Stufen mit stages: und für Variablen mit variables:.

YAML
# File: templates/insert-npm-steps.yml
steps:
  - script: npm install
  - script: npm test
YAML
# File: azure-pipelines.yml
jobs:
  - job: Linux
    pool:
      vmImage: 'ubuntu-latest'
    steps:
      - template: templates/insert-npm-steps.yml
  - job: Windows
    pool:
      vmImage: 'windows-latest'
    steps:
      - script: echo This script runs before the template's steps, only on Windows.
      - template: templates/insert-npm-steps.yml
      - script: echo This step runs after the template's steps.

Mehr Schritte vor und nach der Vorlage sind erlaubt. Für Jobs gilt eine Regel aus Microsoft: Werden mehrere Jobs mit derselben Vorlage eingefügt, lassen Sie den Namen im Job der Vorlage weg (- job: ohne Namen) oder machen ihn zu einem Parameter, damit die Namen nicht kollidieren. Eine Vorlage für Variablen darf nur Variablen definieren; für Schritte und Logik nutzen Sie extends. Variablenvorlagen nehmen ebenfalls Parameter:

YAML
# File: templates/package-release-with-params.yml
parameters:
  - name: DIRECTORY
    type: string
    default: "."

variables:
  - name: RELEASE_COMMAND
    value: echo release from ${{ parameters.DIRECTORY }}

Microsoft rät, Parameter statt Variablen zu nehmen, wenn Typen und Sicherheit zählen (zum Beispiel für Argumente von Shell-Aufgaben).

Erweitern: eine feste Form erzwingen

Mit extends ruft die Pipeline eine Vorlage auf, die das Gerüst bestimmt, und die Pipeline füllt nur die vorgesehenen Lücken. Im Beispiel von Microsoft ist buildSteps ein Parameter vom Typ stepList; die Vorlage fügt eigene Schritte davor und danach ein und lehnt Schritte eines bestimmten Typs ab:

YAML
# File: start-extends-template.yml
parameters:
  - name: buildSteps
    type: stepList
    default: []
stages:
  - stage: secure_buildstage
    pool:
      vmImage: windows-latest
    jobs:
      - job: secure_buildjob
        steps:
          - script: echo This happens before code
            displayName: 'Base: Pre-build'
          - ${{ each step in parameters.buildSteps }}:
              - ${{ each pair in step }}:
                  ${{ if ne(pair.value, 'CmdLine@2') }}:
                    ${{ pair.key }}: ${{ pair.value }}
                  ${{ if eq(pair.value, 'CmdLine@2') }}:
                    '${{ pair.value }}': error
          - script: echo This happens after code
            displayName: 'Base: Signing'
YAML
# File: azure-pipelines.yml
trigger:
  - main

extends:
  template: start-extends-template.yml
  parameters:
    buildSteps:
      - bash: echo Test #Passes
        displayName: succeed
      - task: CmdLine@2
        inputs:
          script: echo "Script Test"

Der Schritt mit CmdLine@2 wird mit einem YAML-Syntaxfehler abgelehnt („Unexpected value 'CmdLine@2'“); der Schritt mit bash geht durch. Wer so die Form erzwingt, ergänzt eine erforderliche Vorlagen-Freigabe (required template approval), damit niemand die Pipeline ohne extends betreibt: Das vertieft Modul D5; Microsoft beschreibt es auf der Seite zur Sicherheit mit Vorlagen (security/templates).

Parameter und Datentypen

Parameter haben Name und Datentyp; Standardwerte dürfen nur Literale sein. Microsoft listet die Typen: string, stringList (nur außerhalb von Vorlagen), number, boolean, object, step, stepList, job, jobList, deployment, deploymentList, stage und stageList. Mit values: schränken Sie ein:

YAML
parameters:
  - name: myNumber
    type: number
    default: 2
    values:
      - 1
      - 2
      - 4
  - name: mySteplist
    type: stepList
    default:
      - script: echo step one

Eine Falle, die Microsoft ausdrücklich beschreibt: Ein Parameter ohne Typ gilt als Zeichenfolge, und nicht leere Zeichenfolgen sind in einem booleschen Zusammenhang wahr. eq(true, parameters['myparam']) ist daher auch dann wahr, wenn der Parameter das Wort false enthält. Geben Sie deshalb den Typ boolean an, oder vergleichen Sie Zeichenfolgen ausdrücklich.

Mit ${{ if … }} nehmen Sie Elemente bedingt auf, mit ${{ each … }} durchlaufen Sie Listen und Objekte. templateContext hängt an jobList, deploymentList und stageList zusätzliche Eigenschaften an, die die Vorlage beim Durchlaufen liest.

Vorlagen aus anderen Repositories

Zentrale Bausteine liegen in einem eigenen Repository. Die Pipeline nennt es unter resources und verweist mit @ auf den Namen:

YAML
resources:
  repositories:
    - repository: templates
      type: github
      name: Contoso/BuildTemplates
      ref: refs/tags/v1.0

jobs:
  - template: common.yml@templates
    parameters:
      vmImage: 'windows-latest'

Voraussetzung ist laut Microsoft bei GitHub eine GitHub-Dienstverbindung. Das Repository wird einmal beim Start aufgelöst; danach wird nur der Inhalt der Vorlagen verwendet, und die fertig erweiterte Pipeline läuft, als stünde sie ganz im Quell-Repository. Deshalb kann die Pipeline keine Skripte aus dem Vorlagen-Repository benutzen, nur dessen Vorlagen.

Festlegen auf eine Version: Ohne ref gilt refs/heads/main. Ein ref ist ein Branch (refs/heads/<name>) oder ein Tag (refs/tags/<name>); wer einen bestimmten Commit will, legt zuerst ein Tag auf ihn oder nennt die 40-stellige SHA. Mit @self verweist eine Vorlage auf das Repository der ursprünglichen Pipeline.

Grenzen: Microsoft setzt Grenzen, damit Vorlagen nicht unkontrolliert wachsen: höchstens 100 eingebundene YAML-Dateien (direkt oder indirekt), höchstens 100 Ebenen Verschachtelung und höchstens 20 Megabyte Arbeitsspeicher beim Lesen. Vorlagen müssen beim Start des Laufs im Dateisystem liegen, aus einem Artefakt lassen sich keine einbinden. Und, aus Lektion 4.1: Trigger gehören in die Haupt-Datei, nicht in eine Vorlage; ein Pipeline-Abschluss-Trigger ist in Vorlagen nicht unterstützt.