Andere CI-Systeme

TestDetta funktioniert auf jeder CI, die .NET und Git ausführt. Außerhalb von GitHub Actions genügen zwei Befehle und ein Ordner.

<maps> ist ein gewöhnlicher Ordner. Legen Sie ihn dort ab, wo Ihre CI Dateien zwischen Pipelines aufbewahren kann:

TestDetta selbst nutzt für Maps nie das Netzwerk.

Was jede Einrichtung benötigt

Installation

dotnet tool install --global <package>

Der Paketname wird zum Launch festgelegt. Bis dahin bauen Sie TestDetta aus dem Quellcode mit dotnet build src/TestDetta.Cli -c Release -o testdetta-bin und rufen überall dort, wo unten td steht, dotnet testdetta-bin/TestDetta.Cli.dll auf.

GitLab CI

variables:
  GIT_DEPTH: "0"                    # full history
  TESTDETTA_MAPS: /srv/testdetta-maps/$CI_PROJECT_PATH   # a disk the runners share; or sync with object storage

record-coverage:
  stage: test
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  script:
    - td record --maps-dir "$TESTDETTA_MAPS"

affected-tests:
  stage: test
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  script:
    - git fetch --no-tags origin "+refs/heads/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME:refs/remotes/origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME"
    - td test --base "origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" --maps-dir "$TESTDETTA_MAPS" --summary-file testdetta-summary.md
  artifacts:
    when: always
    paths: [testdetta-summary.md]

Setzen Sie TESTDETTA_LICENSE unter Settings → CI/CD → Variables, je nach Bedarf maskiert und geschützt. TestDetta erkennt öffentliche Projekte über CI_PROJECT_VISIBILITY.

Jenkins

Eine deklarative Multibranch-Pipeline:

pipeline {
  agent any
  environment {
    TESTDETTA_LICENSE = credentials('testdetta-license')          // a "secret text" credential
    TESTDETTA_MAPS = "/srv/testdetta-maps/${env.JOB_NAME.split('/')[0]}"
  }
  options { skipDefaultCheckout() }
  stages {
    stage('Checkout') {
      steps {
        checkout scmGit(branches: scm.branches, userRemoteConfigs: scm.userRemoteConfigs,
                        extensions: [cloneOption(shallow: false, depth: 0, noTags: true, reference: '')])
      }
    }
    stage('Record coverage') {
      when { branch 'main' }
      steps { sh 'td record --maps-dir "$TESTDETTA_MAPS"' }
    }
    stage('Affected tests') {
      when { changeRequest() }
      steps {
        sh 'git fetch --no-tags origin "+refs/heads/$CHANGE_TARGET:refs/remotes/origin/$CHANGE_TARGET"'
        sh 'td test --base "origin/$CHANGE_TARGET" --maps-dir "$TESTDETTA_MAPS"'
      }
    }
  }
}

Jenkins klont oft mit Anmeldedaten in der Remote-URL. TestDetta entfernt sie, bevor es den Lizenz-Scope abgleicht, und loggt sie nie. Wenn der Remote nicht das Repository benennt, das die Lizenz abdeckt (zum Beispiel bei einem lokalen Mirror), setzen Sie TESTDETTA_LICENSE_SCOPE, etwa auf git.acme.corp/scm/pay/app.

Azure DevOps Pipelines

trigger: [main]
pr: [main]

steps:
  - checkout: self
    fetchDepth: 0
  - script: td record --maps-dir "$(TESTDETTA_MAPS)"
    condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
    env:
      TESTDETTA_LICENSE: $(TestDettaLicense)          # a secret pipeline variable
  - script: |
      target="${SYSTEM_PULLREQUEST_TARGETBRANCH#refs/heads/}"
      git fetch --no-tags origin "+refs/heads/$target:refs/remotes/origin/$target"
      td test --base "origin/$target" --maps-dir "$(TESTDETTA_MAPS)" --summary-file "$(Agent.TempDirectory)/testdetta.md"
      echo "##vso[task.uploadsummary]$(Agent.TempDirectory)/testdetta.md"
    condition: and(succeeded(), eq(variables['Build.Reason'], 'PullRequest'))
    env:
      TESTDETTA_LICENSE: $(TestDettaLicense)

TESTDETTA_MAPS verweist auf einen Netzwerkpfad auf selbst gehosteten Agents. Auf von Microsoft gehosteten Agents synchronisieren Sie den Ordner mit Azure Blob Storage (siehe Objektspeicher).

Bitbucket Pipelines

clone:
  depth: full

pipelines:
  branches:
    main:
      - step:
          script:
            - td record --maps-dir maps
            - ./sync-maps.sh upload maps        # see "Object storage"
  pull-requests:
    '**':
      - step:
          script:
            - git fetch --no-tags origin "+refs/heads/$BITBUCKET_PR_DESTINATION_BRANCH:refs/remotes/origin/$BITBUCKET_PR_DESTINATION_BRANCH"
            - ./sync-maps.sh download maps "origin/$BITBUCKET_PR_DESTINATION_BRANCH"
            - td test --base "origin/$BITBUCKET_PR_DESTINATION_BRANCH" --maps-dir maps

Legen Sie TESTDETTA_LICENSE als geschützte Repository-Variable an.

Objektspeicher statt gemeinsamem Laufwerk

Auf gehosteten Runnern gibt es kein gemeinsames Laufwerk. Laden Sie den neuen Ordner nach der Aufzeichnung hoch. Laden Sie vor dem Testen nur die Ordner herunter, die verwendet werden können: die Merge-Base und bis zu 50 First-Parent-Commits davor. Laden Sie sie vom neuesten an herunter und hören Sie beim ersten vorhandenen auf. Ein Beispiel für S3; jedes Werkzeug mit cp und ls funktioniert genauso:

#!/usr/bin/env bash
# sync-maps.sh upload <maps>          | after td record on the default branch
# sync-maps.sh download <maps> <base> | before td test on a pull request
set -euo pipefail
remote="s3://my-bucket/testdetta-maps/my-repository"
case "$1" in
  upload)
    aws s3 cp --recursive "$2/$(git rev-parse HEAD)" "$remote/$(git rev-parse HEAD)" ;;
  download)
    for sha in $(git rev-list --first-parent -n 51 "$(git merge-base "$3" HEAD)"); do
      if aws s3 ls "$remote/$sha/" > /dev/null 2>&1; then
        aws s3 cp --recursive "$remote/$sha" "$2/$sha"
        break
      fi
    done ;;
esac

Wenn es keine Map gibt

Die ersten Pull Requests nach der Einrichtung oder solche, die weit zurückliegen, finden keine Map in Reichweite. TestDetta wählt dann nur anhand von Namen aus, was sicher ist, aber mehr auswählt, und vermerkt das in seinem Log. Die Auswahl wird präzise, sobald td record auf dem Standard-Branch gelaufen ist.