Docs de Autonomy
Integraciones

Jenkins

Dispara Autonomy desde Jenkins con una URL de previsualización o un artefacto móvil y haz fallar el build si la ejecución no pasa.

Añade Autonomy a un Pipeline de Jenkins después del despliegue de previsualización o del build móvil. Esta página incluye el script de API y tres Jenkinsfiles completos; elige el de tu plataforma.

Ubicación del flujo

Usa un job Pipeline con Pipeline script from SCM, apuntando a una rama de confianza y al Jenkinsfile del repositorio. Incluye .ci/autonomy.sh en el mismo commit, crea las credenciales indicadas abajo y selecciona Build Now. No necesitas un webhook ni un Multibranch Pipeline.

En un pipeline existente, coloca la etapa de Autonomy después de que termine el despliegue y la previsualización sea accesible. El ejemplo web independiente usa una URL ya desplegada. Para una previsualización dinámica, elimina el binding autonomy-preview-url y asigna a env.PREVIEW_URL la salida de la etapa de despliegue antes de esta etapa.

Instala los plugins Pipeline, Pipeline: Declarative, Git y Credentials Binding, con sus dependencias. Prepara un agente Linux con la etiqueta linux, Bash, Git, jq y curl 7.76 o posterior. Las etiquetas seleccionan tus agentes; Jenkins no proporciona runners alojados de Linux ni macOS.

Secretos requeridos

En Manage Jenkins → Credentials, añade credenciales de tipo Secret text accesibles para este job. Conserva exactamente los ID indicados. Crea solo las entradas necesarias para la plataforma elegida.

ID de credencialValor
autonomy-api-keyUna clave de API de organización aut_ del despliegue al que llamarás.
autonomy-api-urlURL base de la API, como https://YOUR_DEPLOYMENT.convex.site, sin /api.
autonomy-web-plan-idID de tu plan web en Test Plans.
autonomy-preview-urlURL HTTPS final y accesible de la previsualización, para el ejemplo web.
autonomy-ios-plan-idID de tu plan iOS en Test Plans.
autonomy-ios-bundle-idBundle ID de la app construida, por ejemplo com.example.app.
autonomy-android-plan-idID de tu plan Android en Test Plans.
autonomy-android-package-nameApplication ID del APK construido, por ejemplo com.example.app.
  • Crea un plan de pruebas con al menos un caso de prueba para la plataforma elegida. Usa su ID de Test Plans, no el ID de un caso individual de Test Cases.
  • Ten disponible un runner de Autonomy compatible. Las ejecuciones en cola no hacen pasar el pipeline hasta que terminan.
  • Usa casos que no necesiten valores de ejecución de un Environment guardado. Estos ejemplos envían objetivos explícitos y no seleccionan un Environment.
  • Haz que la previsualización sea accesible desde el runner de Autonomy. Una URL accesible solo desde Jenkins no funcionará.

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

URL de previsualización web

Guarda este script compartido como .ci/autonomy.sh. Los tres Jenkinsfiles siguientes usan exactamente este archivo. Envía el objetivo, imprime todos los ID de las ejecuciones en cola y espera todos los veredictos.

.ci/autonomy.sh
#!/usr/bin/env bash
set -euo pipefail
set +x
: "${AUTONOMY_API_URL:?Set the API base URL, without /api}"
: "${AUTONOMY_API_KEY:?Set an aut_ organization API key}"
: "${AUTONOMY_TEST_PLAN_ID:?Set the Test Plans ID for this platform}"
: "${AUTONOMY_REQUEST_ID:?Set a unique CI job/attempt ID}"
platform="${1:-web}"
api="${AUTONOMY_API_URL%/}"
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT

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

case "$platform" in
  web)
    : "${PREVIEW_URL:?Set the reachable preview URL after deployment}"
    targets=$(jq -n --arg url "$PREVIEW_URL" '{web:{baseUrl:$url}}')
    ;;
  ios|android)
    : "${ARTIFACT_PATH:?Set the path to the app archive or APK}"
    : "${APP_IDENTIFIER:?Set the bundle ID or Android package name}"
    test -s "$ARTIFACT_PATH"
    file=$(basename "$ARTIFACT_PATH")
    bytes=$(wc -c < "$ARTIFACT_PATH" | tr -d '[:space:]')
    content_type=application/zip
    if [ "$platform" = android ]; then
      content_type=application/vnd.android.package-archive
    fi
    upload=$(post /api/artifacts/upload-url "$(jq -n \
      --arg platform "$platform" --arg fileName "$file" \
      --arg contentType "$content_type" --argjson declaredBytes "$bytes" \
      '{platform:$platform,fileName:$fileName,contentType:$contentType,declaredBytes:$declaredBytes}')")
    url=$(jq -er '.uploadUrl | strings | select(length > 0)' <<< "$upload")
    storage=$(jq -er '.storageId | strings | select(length > 0)' <<< "$upload")
    intent=$(jq -er '.intentId | strings | select(length > 0)' <<< "$upload")
    jq -e '.method == "PUT"' <<< "$upload" > /dev/null
    curl --fail-with-body --silent --show-error \
      --connect-timeout 15 --max-time 600 -X PUT "$url" \
      -H "Content-Type: $content_type" --data-binary "@$ARTIFACT_PATH"
    scan=$(post /api/artifacts/scan "$(jq -n \
      --arg storageId "$storage" --arg intentId "$intent" \
      '{storageId:$storageId,intentId:$intentId}')")
    if ! jq -e '.status == "clean"' <<< "$scan" > /dev/null; then
      echo 'Artifact scan did not pass; refusing to trigger.' >&2
      exit 1
    fi
    targets=$(jq -n --arg platform "$platform" --arg storageId "$storage" \
      --arg fileName "$file" --arg identifier "$APP_IDENTIFIER" \
      '{($platform):({storageId:$storageId,sourceMode:"upload",fileName:$fileName} +
        (if $platform == "ios" then {bundleId:$identifier} else {packageName:$identifier} end))}')
    ;;
  *) echo 'Usage: bash .ci/autonomy.sh web|ios|android' >&2; exit 2 ;;
esac

body=$(jq -n --arg testPlanId "$AUTONOMY_TEST_PLAN_ID" \
  --arg platform "$platform" --argjson targets "$targets" \
  --arg branch "${AUTONOMY_BRANCH:-manual}" \
  --arg commitSha "${AUTONOMY_COMMIT_SHA:-}" \
  --arg key "$AUTONOMY_REQUEST_ID:$platform" \
  '{testPlanId:$testPlanId,platforms:[$platform],targets:$targets,
    branch:$branch,deployment:{branch:$branch,commitSha:$commitSha},idempotencyKey:$key}')
post /api/v1/run.trigger "$body" > "$work/trigger.json"
jq -e '.runIds | type == "array" and length > 0' "$work/trigger.json" > /dev/null
jq -er '.runIds[] | strings | select(length > 0)' "$work/trigger.json" > "$work/run-ids"
printf 'Queued Autonomy runs:\n'
cat "$work/run-ids"

deadline=$((SECONDS + ${AUTONOMY_WAIT_SECONDS:-1200}))
while IFS= read -r run_id; do
  while :; do
    if (( SECONDS >= deadline )); then
      echo "Timed out waiting for $run_id; failing the CI job." >&2
      exit 1
    fi
    result=$(post /api/v1/run.get "$(jq -n --arg runId "$run_id" '{runId:$runId}')")
    status=$(jq -er '.status' <<< "$result")
    case "$status" in
      queued|running|retrying) sleep 10 ;;
      passing|failed|unverified|invalid|canceled)
        printf '%s: %s\n' "$run_id" "$status"
        if ! jq -e '.verdict == "passed" and .validity == "valid"' <<< "$result" > /dev/null; then
          echo "Autonomy gate failed for $run_id." >&2
          exit 1
        fi
        break ;;
      *) echo "Unexpected run status: $status" >&2; exit 1 ;;
    esac
  done
done < "$work/run-ids"
echo 'Every Autonomy run passed.'

Guarda esto como Jenkinsfile para un job web. Las cuatro credenciales web anteriores permiten ejecutar el primer build manual 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-api-url', variable: 'AUTONOMY_API_URL'),
          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
'''
        }
      }
    }
  }
}

Transferencia de artefactos móviles

Usa un plan de pruebas separado para cada plataforma. Conserva .ci/autonomy.sh de la sección web y sustituye Jenkinsfile por el ejemplo correspondiente. El script solicita una URL de carga, sube el archivo, exige un análisis limpio del artefacto y luego dispara el plan.

iOS

Prepara un agente macOS real con la etiqueta macos, Xcode, su SDK de simulador, Git y zip. Jenkins no tiene un nivel gratuito de macOS alojado. Usa tu Mac existente; contratar capacidad de pago es una decisión aparte. Resuelve las dependencias del proyecto antes de xcodebuild y sustituye MyApp, la ruta del workspace y el nombre de salida por los de tu proyecto.

Construye un .app de simulador, no un .ipa de dispositivo. zip -y conserva los enlaces simbólicos y el archivo mantiene los permisos de ejecución. stash transfiere ese archivo a la etapa Linux; Linux lo sube sin descomprimirlo. Para apps grandes, configura un gestor remoto de artefactos de Jenkins para que el almacenamiento no sobrecargue 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-api-url', variable: 'AUTONOMY_API_URL'),
          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

Prepara un agente con la etiqueta linux-android, 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 necesite tu proyecto. Configura JAVA_HOME y ANDROID_HOME en el agente y acepta las licencias del SDK. El ejemplo supone un wrapper de Gradle ejecutable en android/gradlew; ajusta el directorio y la ruta del APK para tu proyecto. El build y la carga usan el mismo workspace.

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-api-url', variable: 'AUTONOMY_API_URL'),
          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 veredicto

El script llama a POST /api/v1/run.trigger y después consulta POST /api/v1/run.get para cada entrada devuelta en runIds. Una respuesta en cola solo indica que se aceptó la solicitud. Para tener éxito, todos los resultados finales deben contener verdict: "passed" y validity: "valid".

Un error de API, un análisis rechazado, un estado inesperado, un resultado fallido o no verificado, una cancelación o un tiempo de espera agotado produce una salida distinta de cero y hace fallar la etapa de Jenkins. El tiempo de consulta predeterminado es de 1.200 segundos para todas las ejecuciones devueltas; aumenta AUTONOMY_WAIT_SECONDS para planes más largos y ajusta también el timeout de Jenkins. El tiempo de espera del pipeline no cancela una ejecución de Autonomy ya en cola.

AUTONOMY_REQUEST_ID combina el nombre del job de Jenkins y el número de build; el script añade la plataforma. Reintentar el disparo en el mismo build reutiliza la clave de idempotencia; un nuevo build de Jenkins recibe una clave nueva.

Estrategia de ramas y eventos

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

Los scripts del checkout tienen acceso a las credenciales. No ejecutes el Jenkinsfile ni el script de una pull request no confiable con estas credenciales, aunque su rama coincida con un filtro. Usa un job de confianza para probar una URL de previsualización aprobada. Separa los agentes que contienen estas credenciales de los que ejecutan jobs no confiables.

Referencias del proveedor

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

On this page