Docs de Autonomy
Integraciones

Jenkins

Inicia ejecuciones de Autonomy desde Jenkins para una URL de previsualización o un build móvil, y haz fallar el build salvo que todas las ejecuciones se superen.

Añade Autonomy a un Pipeline de Jenkins después del despliegue de tu previsualización o de tu build móvil. Esta página tiene el script compartido y tres Jenkinsfiles completos; elige el de tu plataforma. El build solo se supera cuando todas las ejecuciones son Passed.

Dónde va en el pipeline

Usa un job de tipo Pipeline con Pipeline script from SCM, apuntando a una rama de confianza y al Jenkinsfile de tu repositorio. Haz commit de .ci/autonomy.sh a su lado, crea las credenciales de abajo y pulsa Build Now. No hace falta ningún webhook ni Multibranch Pipeline.

En un pipeline existente, pon el stage de Autonomy cuando el despliegue haya terminado y la previsualización responda. El ejemplo web usa una URL ya desplegada. Para una previsualización dinámica, quita el binding autonomy-preview-url y define env.PREVIEW_URL desde tu stage de despliegue antes de este stage.

Instala los plugins Pipeline, Pipeline: Declarative, Git y Credentials Binding con sus dependencias. Proporciona un agente Linux con la etiqueta linux que tenga Bash, Git, jq y curl 7.76 o posterior. Las etiquetas seleccionan tus agentes; Jenkins no proporciona máquinas Linux ni macOS alojadas.

Secretos necesarios

En Manage Jenkins → Credentials, añade credenciales de tipo Secret text que este job pueda ver. Mantén los IDs exactamente como se muestran. Crea solo las entradas que necesita tu plataforma.

ID de la credencialValor
autonomy-api-keyUna clave de API de la organización que empieza por aut_. Los propietarios y administradores crean claves en Organization → API keys.
autonomy-web-plan-idEl ID de tu plan de pruebas web: la última parte de /p/<project>/plans/<id> en Test plans.
autonomy-preview-urlLa URL HTTPS de previsualización definitiva, para el ejemplo web.
autonomy-ios-plan-idEl ID de tu plan de pruebas de iOS.
autonomy-ios-bundle-idEl bundle ID de la app compilada, por ejemplo com.example.app.
autonomy-android-plan-idEl ID de tu plan de pruebas de Android.
autonomy-android-package-nameEl application ID del APK compilado, por ejemplo com.example.app.
  • Crea un plan de pruebas con al menos un caso de prueba para la plataforma. Para ejecutar un solo caso de prueba, vincula su ID (de /p/<project>/tests/<id>) a AUTONOMY_TEST_CASE_ID en lugar de AUTONOMY_TEST_PLAN_ID. Define exactamente uno de los dos.
  • Haz que la previsualización sea accesible desde internet. Una URL a la que solo llega Jenkins no funcionará.
  • Estos ejemplos envían la URL o el build directamente, sin entorno, así que las ejecuciones no tienen valores del entorno: un paso que usa {{env.NAME}} termina como Couldn't start. Para usar en su lugar un entorno guardado del mismo proyecto, con sus objetivos y valores, define AUTONOMY_ENVIRONMENT_SLUG con su slug; en ese caso el script omite la subida.

withCredentials inyecta los valores solo alrededor de la llamada a la API. Los bloques de shell de Groovy entre comillas simples dejan la expansión a Bash; mantén set +x y nunca imprimas la clave.

URL de previsualización web

Guarda este script compartido como .ci/autonomy.sh. Los tres Jenkinsfiles de abajo usan este mismo archivo. Inicia las ejecuciones, imprime sus IDs y espera todos los resultados.

.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.'

Guarda esto como Jenkinsfile para un job web. Con las tres credenciales web de arriba, el primer build manual funciona sin parámetros del 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
'''
        }
      }
    }
  }
}

Entrega de builds móviles

Usa un plan de pruebas distinto para cada plataforma. Conserva .ci/autonomy.sh de la sección web y sustituye Jenkinsfile por el ejemplo que necesites. Para iOS y Android, el script pide una URL de subida, sube el build, espera a que el control de seguridad salga limpio y después inicia las ejecuciones.

iOS

Las pruebas en iOS llegarán pronto. Guarda esto para cuando estén disponibles. Hasta entonces, Autonomy no inicia ejecuciones en iOS.

Proporciona un agente macOS con la etiqueta macos que tenga Xcode, su SDK de Simulator, Git y zip. Jenkins no tiene máquinas macOS alojadas, así que usa un Mac propio. Instala las dependencias de tu proyecto antes de xcodebuild y sustituye MyApp, la ruta de su workspace y el nombre de salida por los tuyos.

Compila un .app de simulador, no un .ipa de dispositivo. zip -y conserva los enlaces simbólicos, y el zip conserva los permisos de ejecución. stash pasa el zip al stage de Linux, que lo sube tal cual. Para apps grandes, configura un gestor de artifacts remoto de Jenkins para que el stash no cargue el controlador.

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

Proporciona un agente con la etiqueta linux-android que tenga Bash, Git, jq, curl 7.76 o posterior, un JDK compatible con tu versión de Gradle y los paquetes del SDK de Android que necesita tu proyecto. Define JAVA_HOME y ANDROID_HOME en el agente y acepta las licencias del SDK. El ejemplo da por hecho un wrapper de Gradle ejecutable en android/gradlew; ajusta la carpeta y la ruta del APK a tu proyecto. El build y la subida comparten el workspace. Un .aab también funciona.

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

Condicionar el pipeline al resultado

El script inicia las ejecuciones con POST /api/v1/run.trigger e imprime sus IDs. Un plan de pruebas inicia una ejecución por cada caso de prueba. Después sigue cada ejecución con POST /api/v1/run.get, devolviendo el último cursor con waitMs: 25000, de modo que cada llamada espera hasta que la ejecución cambia. Una respuesta con timedOut: true solo significa que todavía no ha cambiado nada. Consulta Ejecuciones y evidencia.

Resultado de la ejecuciónEl jobQué dice el log
PassedSe supera, si todas las demás ejecuciones también se superaron. Una ejecución superada con reintento (flaky) cuenta como superada.Passed
FailedFalla.Abre la ejecución para ver qué paso falló y por qué, incluido un Runner problem.
Couldn't startFalla.No se pudo probar el entorno, por ejemplo porque el build no se instaló o faltaba un valor. Esto no dice nada de tu app; corrige la causa y vuelve a ejecutar.
CancelledFalla.Cancelled

Los errores de la API, un build que no supera el control de seguridad y agotar el tiempo de espera también hacen fallar el job. AUTONOMY_WAIT_SECONDS es el tiempo de espera total para todas las ejecuciones, 1.200 segundos por defecto. Mantén el límite de tiempo del job de CI por encima de ese valor más el tiempo de build y de subida. Detener el job de CI no cancela las ejecuciones que ya empezaron.

AUTONOMY_REQUEST_ID, con la plataforma añadida, se convierte en el idempotencyKey de run.trigger: si el mismo intento del job vuelve a enviar la petición, recibe las mismas ejecuciones en lugar de otras nuevas. Da a cada intento del job su propio ID. La parte anterior a los primeros dos puntos se envía como proveedor del despliegue, y la rama y el commit acompañan a las ejecuciones. Los formatos completos de petición y respuesta están en Operaciones.

Una salida distinta de cero hace fallar el stage de Jenkins. Sube el timeout de Jenkins junto con AUTONOMY_WAIT_SECONDS para planes más largos.

Estrategia de ramas y eventos

Empieza con builds manuales desde una rama predeterminada de confianza. Cuando la entrega desde el despliegue funcione, añade al job tu disparador de SCM o tu programación existentes. Usa planes de smoke cortos para los builds de cambios de confianza y planes más amplios para los builds de release o programados.

Los scripts del código descargado pueden leer estas credenciales. No ejecutes con ellas el Jenkinsfile ni el script de una pull request que no sea de confianza, aunque el nombre de su rama coincida con un filtro. Usa un job de confianza para probar una URL de previsualización aprobada. Mantén los agentes que tienen estas credenciales separados de los agentes que ejecutan jobs que no son de confianza.

Referencias del proveedor

Los ejemplos usan la sintaxis de Declarative Pipeline documentada, los bindings de Secret text, los pasos stash y unstash y los agentes de Jenkins. Consulta Using a Jenkinsfile para configurar un Pipeline desde SCM.

¿No lo encontraste?

On this page