Docs Autonomy
Intégrations

Jenkins

Lancez des exécutions Autonomy depuis Jenkins pour une URL de prévisualisation ou un build mobile, et faites échouer le build si une exécution ne réussit pas.

Ajoutez Autonomy à un Pipeline Jenkins après votre déploiement de prévisualisation ou votre build mobile. Cette page contient le script partagé et trois Jenkinsfiles complets ; choisissez celui de votre plateforme. Le build ne réussit que si toutes les exécutions sont Passed.

Placement du workflow

Utilisez un job Pipeline avec Pipeline script from SCM, pointant vers une branche de confiance et le Jenkinsfile de votre dépôt. Committez .ci/autonomy.sh à côté, créez les identifiants ci-dessous, puis cliquez sur Build Now. Aucun webhook ni Multibranch Pipeline n'est nécessaire.

Dans un pipeline existant, placez le stage Autonomy une fois le déploiement terminé et la prévisualisation joignable. L'exemple web utilise une URL déjà déployée. Pour une prévisualisation dynamique, supprimez la liaison autonomy-preview-url et définissez env.PREVIEW_URL depuis votre stage de déploiement, avant ce stage.

Installez les plugins Pipeline, Pipeline: Declarative, Git et Credentials Binding ainsi que leurs dépendances. Fournissez un agent Linux avec le label linux, doté de Bash, Git, jq et curl 7.76 ou ultérieur. Les labels sélectionnent vos agents ; Jenkins ne fournit pas de machines Linux ou macOS hébergées.

Secrets requis

Dans Manage Jenkins → Credentials, ajoutez des identifiants Secret text visibles par ce job. Conservez les ID exactement tels qu'ils sont indiqués. Ne créez que les entrées dont votre plateforme a besoin.

ID de l'identifiantValeur
autonomy-api-keyUne clé API d'organisation commençant par aut_. Les propriétaires et les administrateurs créent les clés dans Organization → API keys.
autonomy-web-plan-idL'identifiant de votre plan de test web : la dernière partie de /p/<project>/plans/<id> dans Test plans.
autonomy-preview-urlL'URL de prévisualisation HTTPS finale, pour l'exemple web.
autonomy-ios-plan-idL'identifiant de votre plan de test iOS.
autonomy-ios-bundle-idLe bundle ID de l'application construite, par exemple com.example.app.
autonomy-android-plan-idL'identifiant de votre plan de test Android.
autonomy-android-package-nameL'application ID de l'APK construit, par exemple com.example.app.
  • Créez un plan de test contenant au moins un cas de test pour la plateforme. Pour exécuter plutôt un seul cas de test, liez son identifiant (issu de /p/<project>/tests/<id>) à AUTONOMY_TEST_CASE_ID à la place de AUTONOMY_TEST_PLAN_ID. Définissez exactement l'un des deux.
  • Rendez la prévisualisation joignable depuis Internet. Une URL que seul Jenkins peut atteindre ne fonctionnera pas.
  • Ces exemples envoient directement l'URL ou le build, sans environnement : les exécutions n'ont donc pas de valeurs d'environnement, et une étape qui utilise {{env.NAME}} se termine en Couldn't start. Pour utiliser plutôt un environnement enregistré du même projet, avec ses cibles et ses valeurs, définissez AUTONOMY_ENVIRONMENT_SLUG sur son slug ; le script saute alors le téléversement.

withCredentials n'injecte les valeurs qu'autour de l'appel d'API. Les blocs shell Groovy entre apostrophes simples laissent l'expansion à Bash ; gardez set +x et n'affichez jamais la clé.

URL de prévisualisation web

Enregistrez ce script partagé sous .ci/autonomy.sh. Les trois Jenkinsfiles ci-dessous utilisent tous ce même fichier. Il lance les exécutions, affiche leurs identifiants et attend chaque résultat.

.ci/autonomy.sh
#!/usr/bin/env bash
# Usage: bash .ci/autonomy.sh web|ios|android
# Starts Autonomy runs for this CI job and exits 0 only if every run passed.
set -euo pipefail
set +x
: "${AUTONOMY_API_KEY:?Set an aut_ organization API key}"
: "${AUTONOMY_REQUEST_ID:?Set an ID that is unique for this CI job attempt}"
platform="${1:?Usage: bash .ci/autonomy.sh web|ios|android}"
api="${AUTONOMY_API_URL:-https://api.autonomyqa.com}"
api="${api%/}"

post() {
  curl --fail-with-body --silent --show-error \
    --connect-timeout 15 --max-time 90 \
    -X POST "$api$1" \
    -H "Authorization: Bearer $AUTONOMY_API_KEY" \
    -H 'Content-Type: application/json' --data-binary "$2"
}

# 1. What to run: a test plan or one test case.
if [ -n "${AUTONOMY_TEST_PLAN_ID:-}" ] && [ -z "${AUTONOMY_TEST_CASE_ID:-}" ]; then
  what=$(jq -n --arg id "$AUTONOMY_TEST_PLAN_ID" '{testPlanId:$id}')
elif [ -n "${AUTONOMY_TEST_CASE_ID:-}" ] && [ -z "${AUTONOMY_TEST_PLAN_ID:-}" ]; then
  what=$(jq -n --arg id "$AUTONOMY_TEST_CASE_ID" '{testCaseId:$id}')
else
  echo 'Set exactly one of AUTONOMY_TEST_PLAN_ID or AUTONOMY_TEST_CASE_ID.' >&2
  exit 2
fi

# 2. Where to run: a saved environment, or this job's preview URL or build.
if [ -n "${AUTONOMY_ENVIRONMENT_SLUG:-}" ]; then
  where=$(jq -n --arg slug "$AUTONOMY_ENVIRONMENT_SLUG" '{environmentSlug:$slug}')
else
  case "$platform" in
    web)
      : "${PREVIEW_URL:?Set PREVIEW_URL, or AUTONOMY_ENVIRONMENT_SLUG}"
      where=$(jq -n --arg url "$PREVIEW_URL" '{targets:{web:{baseUrl:$url}}}')
      ;;
    ios|android)
      : "${ARTIFACT_PATH:?Set the path to the zipped .app, .apk or .aab}"
      : "${APP_IDENTIFIER:?Set the iOS bundle ID or Android package name}"
      file=$(basename "$ARTIFACT_PATH")
      bytes=$(wc -c < "$ARTIFACT_PATH" | tr -d '[:space:]')
      if [ "$platform" = ios ]; then
        type=application/zip
      else
        type=application/vnd.android.package-archive
      fi
      upload=$(post /api/artifacts/upload-url "$(jq -n \
        --arg platform "$platform" --arg fileName "$file" \
        --arg contentType "$type" --argjson declaredBytes "$bytes" \
        '{platform:$platform,fileName:$fileName,contentType:$contentType,declaredBytes:$declaredBytes}')")
      storage=$(jq -er '.storageId' <<< "$upload")
      intent=$(jq -er '.intentId' <<< "$upload")
      curl --fail-with-body --silent --show-error \
        --connect-timeout 15 --max-time 600 \
        -X PUT "$(jq -er '.uploadUrl' <<< "$upload")" \
        -H "Content-Type: $type" --data-binary "@$ARTIFACT_PATH"
      scan=$(post /api/artifacts/scan "$(jq -n \
        --arg storageId "$storage" --arg intentId "$intent" \
        '{storageId:$storageId,intentId:$intentId}')")
      if [ "$(jq -r '.status' <<< "$scan")" != clean ]; then
        echo 'The build did not pass the safety check, so no run was started:' >&2
        jq -r '.findings[]?.message' <<< "$scan" >&2
        exit 1
      fi
      where=$(jq -n --arg p "$platform" --arg storageId "$storage" \
        --arg fileName "$file" --arg id "$APP_IDENTIFIER" \
        '{targets:{($p):({storageId:$storageId,sourceMode:"upload",fileName:$fileName}
          + (if $p == "ios" then {bundleId:$id} else {packageName:$id} end))}}')
      ;;
    *) echo 'Usage: bash .ci/autonomy.sh web|ios|android' >&2; exit 2 ;;
  esac
fi

# 3. Start the runs. A plan starts one run per test case.
#    The request ID's prefix (github, gitlab, ...) names the CI provider.
body=$(jq -n --argjson what "$what" --argjson where "$where" \
  --arg platform "$platform" \
  --arg branch "${AUTONOMY_BRANCH:-}" --arg pr "${AUTONOMY_PR:-}" \
  --arg commitSha "${AUTONOMY_COMMIT_SHA:-}" \
  --arg provider "${AUTONOMY_REQUEST_ID%%:*}" \
  --arg key "$AUTONOMY_REQUEST_ID:$platform" \
  '$what + $where + ({
     platforms: [$platform],
     branch: $branch,
     pr: $pr,
     deployment: ({provider: $provider, branch: $branch, commitSha: $commitSha}
       | with_entries(select(.value != ""))),
     idempotencyKey: $key
   } | with_entries(select(.value != "")))')
run_ids=$(post /api/v1/run.trigger "$body" | jq -er '.runIds[]')
printf 'Started Autonomy runs:\n%s\n' "$run_ids"

# 4. Follow each run until it ends. run.get waits up to 25 s for a change;
#    sending the last cursor back makes each call wait for something new.
deadline=$((SECONDS + ${AUTONOMY_WAIT_SECONDS:-1200}))
all_passed=true
for run_id in $run_ids; do
  cursor=null
  while :; do
    left=$((deadline - SECONDS))
    if [ "$left" -le 0 ]; then
      echo "Stopped waiting for run $run_id after AUTONOMY_WAIT_SECONDS. It keeps going in Autonomy." >&2
      exit 1
    fi
    wait_ms=$(( left < 25 ? left * 1000 : 25000 ))
    run=$(post /api/v1/run.get "$(jq -n --arg runId "$run_id" \
      --argjson cursor "$cursor" --argjson waitMs "$wait_ms" \
      '{runId:$runId,cursor:$cursor,waitMs:$waitMs}')")
    cursor=$(jq '.cursor' <<< "$run")
    status=$(jq -r '.status' <<< "$run")
    [ "$status" = queued ] || [ "$status" = running ] || break
  done

  name="$(jq -r '.testCaseName' <<< "$run") ($run_id)"
  case "$status" in
    passed)
      echo "Passed: $name" ;;
    failed)
      echo "Failed: $name. Open the run in Autonomy to see which step failed." >&2
      all_passed=false ;;
    couldnt_start)
      echo "Couldn't start: $name. The environment couldn't be tested, so this says nothing about your app. Open the run for the reason, fix it and run again." >&2
      all_passed=false ;;
    cancelled)
      echo "Cancelled: $name." >&2
      all_passed=false ;;
    *)
      echo "Unexpected status $status: $name." >&2
      all_passed=false ;;
  esac
done

if [ "$all_passed" != true ]; then
  echo 'Not every Autonomy run passed.' >&2
  exit 1
fi
echo 'Every Autonomy run passed.'

Enregistrez ceci sous Jenkinsfile pour un job web. Les trois identifiants web ci-dessus permettent au premier build manuel de fonctionner sans paramètres de job.

Jenkinsfile
pipeline {
  agent { label 'linux' }
  options {
    skipDefaultCheckout(true)
    timeout(time: 30, unit: 'MINUTES')
  }
  stages {
    stage('Checkout') {
      steps { checkout scm }
    }
    stage('Autonomy web') {
      steps {
        withCredentials([
          string(credentialsId: 'autonomy-api-key', variable: 'AUTONOMY_API_KEY'),
          string(credentialsId: 'autonomy-web-plan-id', variable: 'AUTONOMY_TEST_PLAN_ID'),
          string(credentialsId: 'autonomy-preview-url', variable: 'PREVIEW_URL')
        ]) {
          sh '''#!/usr/bin/env bash
set -euo pipefail
set +x
export AUTONOMY_REQUEST_ID="jenkins:${JOB_NAME}:${BUILD_NUMBER}"
export AUTONOMY_BRANCH="${BRANCH_NAME:-${GIT_BRANCH:-manual}}"
export AUTONOMY_COMMIT_SHA="$(git rev-parse HEAD)"
bash .ci/autonomy.sh web
'''
        }
      }
    }
  }
}

Transmission d'un build mobile

Utilisez un plan de test distinct pour chaque plateforme. Conservez .ci/autonomy.sh de la section web et remplacez Jenkinsfile par l'exemple dont vous avez besoin. Pour iOS et Android, le script demande une URL de téléversement, téléverse le build, attend que le contrôle de sécurité revienne sans anomalie, puis lance les exécutions.

iOS

Les tests iOS arrivent bientôt. Conservez cette section pour le moment où ils seront disponibles. D'ici là, Autonomy ne lance pas d'exécutions sur iOS.

Fournissez un agent macOS avec le label macos, doté de Xcode, de son SDK Simulator, de Git et de zip. Jenkins n'a pas de machines macOS hébergées : utilisez donc votre propre Mac. Installez les dépendances de votre projet avant xcodebuild, et remplacez MyApp, le chemin de son workspace et le nom du fichier produit par les vôtres.

Construisez un .app de simulateur, et non un .ipa pour appareil physique. zip -y conserve les liens symboliques, et le zip conserve les permissions d'exécution. stash transmet le zip au stage Linux, qui le téléverse tel quel. Pour les applications volumineuses, configurez un gestionnaire d'artefacts Jenkins distant pour que le stash ne surcharge pas le contrôleur.

Jenkinsfile
pipeline {
  agent none
  options { skipDefaultCheckout(true) }
  stages {
    stage('Build iOS simulator app') {
      agent { label 'macos' }
      options { timeout(time: 60, unit: 'MINUTES') }
      steps {
        checkout scm
        sh '''#!/usr/bin/env bash
set -euo pipefail
xcodebuild \
  -workspace ios/MyApp.xcworkspace \
  -scheme MyApp \
  -configuration Release \
  -sdk iphonesimulator \
  -destination 'generic/platform=iOS Simulator' \
  -derivedDataPath ios/build \
  CODE_SIGNING_ALLOWED=NO build
mkdir -p .build
rm -f .build/MyApp.app.zip
(cd ios/build/Build/Products/Release-iphonesimulator && \
  zip -qry "$WORKSPACE/.build/MyApp.app.zip" MyApp.app)
'''
        stash name: 'ios-simulator', includes: '.build/MyApp.app.zip'
      }
    }
    stage('Autonomy iOS') {
      agent { label 'linux' }
      options { timeout(time: 35, unit: 'MINUTES') }
      steps {
        checkout scm
        unstash 'ios-simulator'
        withCredentials([
          string(credentialsId: 'autonomy-api-key', variable: 'AUTONOMY_API_KEY'),
          string(credentialsId: 'autonomy-ios-plan-id', variable: 'AUTONOMY_TEST_PLAN_ID'),
          string(credentialsId: 'autonomy-ios-bundle-id', variable: 'APP_IDENTIFIER')
        ]) {
          sh '''#!/usr/bin/env bash
set -euo pipefail
set +x
export ARTIFACT_PATH='.build/MyApp.app.zip'
export AUTONOMY_REQUEST_ID="jenkins:${JOB_NAME}:${BUILD_NUMBER}"
export AUTONOMY_BRANCH="${BRANCH_NAME:-${GIT_BRANCH:-manual}}"
export AUTONOMY_COMMIT_SHA="$(git rev-parse HEAD)"
bash .ci/autonomy.sh ios
'''
        }
      }
    }
  }
}

Android

Fournissez un agent avec le label linux-android, doté de Bash, Git, jq, curl 7.76 ou ultérieur, d'un JDK compatible avec votre version de Gradle et des paquets du SDK Android dont votre projet a besoin. Définissez JAVA_HOME et ANDROID_HOME sur l'agent et acceptez les licences du SDK. L'exemple suppose un wrapper Gradle exécutable dans android/gradlew ; adaptez le dossier et le chemin de l'APK à votre projet. Le build et le téléversement partagent le workspace. Un .aab fonctionne aussi.

Jenkinsfile
pipeline {
  agent { label 'linux-android' }
  options {
    skipDefaultCheckout(true)
    timeout(time: 60, unit: 'MINUTES')
  }
  stages {
    stage('Build Android APK') {
      steps {
        checkout scm
        sh '''#!/usr/bin/env bash
set -euo pipefail
cd android
./gradlew --no-daemon assembleDebug
'''
      }
    }
    stage('Autonomy Android') {
      steps {
        withCredentials([
          string(credentialsId: 'autonomy-api-key', variable: 'AUTONOMY_API_KEY'),
          string(credentialsId: 'autonomy-android-plan-id', variable: 'AUTONOMY_TEST_PLAN_ID'),
          string(credentialsId: 'autonomy-android-package-name', variable: 'APP_IDENTIFIER')
        ]) {
          sh '''#!/usr/bin/env bash
set -euo pipefail
set +x
export ARTIFACT_PATH='android/app/build/outputs/apk/debug/app-debug.apk'
export AUTONOMY_REQUEST_ID="jenkins:${JOB_NAME}:${BUILD_NUMBER}"
export AUTONOMY_BRANCH="${BRANCH_NAME:-${GIT_BRANCH:-manual}}"
export AUTONOMY_COMMIT_SHA="$(git rev-parse HEAD)"
bash .ci/autonomy.sh android
'''
        }
      }
    }
  }
}

Conditionner le pipeline au résultat

Le script lance les exécutions avec POST /api/v1/run.trigger et affiche leurs identifiants. Un plan de test lance une exécution par cas de test. Le script suit ensuite chaque exécution avec POST /api/v1/run.get, en renvoyant le dernier cursor avec waitMs: 25000, si bien que chaque appel attend que l'exécution évolue. Une réponse avec timedOut: true signifie seulement que rien n'a encore changé. Voir Exécutions et preuves.

Résultat de l'exécutionLe jobCe qu'indique le log
PassedRéussit, si toutes les autres exécutions ont aussi réussi. Une réussite flaky compte comme une réussite.Passed
FailedÉchoue.Ouvrez l'exécution pour voir quelle étape a échoué et pourquoi, y compris un Runner problem.
Couldn't startÉchoue.L'environnement n'a pas pu être testé, par exemple parce que le build ne s'est pas installé ou qu'une valeur manquait. Cela ne dit rien de votre application ; corrigez la cause et relancez.
CancelledÉchoue.Cancelled

Les erreurs d'API, un build qui ne passe pas le contrôle de sécurité et l'épuisement du temps d'attente font aussi échouer le job. AUTONOMY_WAIT_SECONDS est le temps d'attente total pour toutes les exécutions, 1 200 secondes par défaut. Donnez au job de CI une limite de temps supérieure à cette valeur, augmentée du temps de build et de téléversement. Arrêter le job de CI n'annule pas les exécutions déjà lancées.

AUTONOMY_REQUEST_ID, complété par la plateforme, devient l'idempotencyKey de run.trigger : si la même tentative de job renvoie la requête, elle récupère les mêmes exécutions au lieu d'en créer de nouvelles. Donnez à chaque tentative de job son propre identifiant. La partie située avant le premier deux-points est envoyée comme fournisseur de déploiement, et la branche et le commit accompagnent les exécutions. Les formats complets de requête et de réponse figurent dans Opérations.

Un code de sortie non nul fait échouer le stage Jenkins. Pour les plans plus longs, augmentez le timeout Jenkins en même temps que AUTONOMY_WAIT_SECONDS.

Stratégie de branches et d'événements

Commencez par des builds manuels depuis une branche par défaut de confiance. Une fois la transmission depuis le déploiement fonctionnelle, ajoutez au job votre déclencheur SCM ou votre planification existants. Utilisez de courts plans de smoke test pour les builds de modifications de confiance, et des plans plus larges pour les builds de release ou planifiés.

Les scripts récupérés peuvent lire ces identifiants. N'exécutez pas avec eux le Jenkinsfile ou le script d'une pull request non fiable, même si son nom de branche correspond à un filtre. Utilisez un job de confiance pour tester une URL de prévisualisation approuvée. Séparez les agents qui détiennent ces identifiants de ceux qui exécutent des jobs non fiables.

Références de l'éditeur

Les exemples utilisent la syntaxe Declarative Pipeline, les liaisons Secret text, les étapes stash et unstash et les agents Jenkins, tels qu'ils sont documentés. Voir Using a Jenkinsfile pour configurer un Pipeline depuis le SCM.

Vous n’avez pas trouvé ?

On this page