CircleCI
Inicia ejecuciones de Autonomy desde CircleCI para una URL de previsualización web o un build móvil, y haz fallar el pipeline salvo que todas las ejecuciones se superen.
Usa CircleCI para iniciar ejecuciones de Autonomy después de un despliegue o de un build móvil. Esta página tiene el script compartido, configuraciones completas y la condición sobre el resultado. El job solo se supera cuando todas las ejecuciones son Passed.
Dónde va en el workflow
Ejecuta Autonomy cuando la previsualización responda o el build móvil exista. Cada configuración de abajo es un .circleci/config.yml alternativo completo; copia la de tu plataforma. En un workflow existente, conserva tu job de despliegue y haz que el job de Autonomy dependa de él con requires. Pasa la URL desplegada definitiva, no el localhost de un contenedor de CI.
Secretos necesarios
En CircleCI, abre Project Settings → Environment Variables y añade los valores de abajo. CircleCI los inyecta en la shell del job; no escribas la clave de API en el YAML. También puede guardar esos mismos valores un context restringido: conéctalo con context en la entrada del job dentro de workflows, no dentro de jobs.
-
AUTONOMY_API_KEY: una clave de API de la organización que empieza poraut_. Los propietarios y administradores crean claves en Organization → API keys. -
AUTONOMY_TEST_PLAN_ID: abre el plan en Test plans del proyecto y copia la última parte de la URL de la página,/p/<project>/plans/<id>. El plan necesita al menos un caso de prueba para la plataforma. Para ejecutar un solo caso de prueba, defineAUTONOMY_TEST_CASE_ID(de/p/<project>/tests/<id>) y deja sin definir el ID del plan. Define exactamente uno de los dos. - Web:
PREVIEW_URL, la URL HTTPS del despliegue, accesible. - Móvil:
APP_IDENTIFIER, el bundle ID de iOS o el package name de Android del build. - Opcional:
AUTONOMY_ENVIRONMENT_SLUGejecuta contra un entorno guardado del mismo proyecto, con sus objetivos y valores del entorno, en lugar de la URL o el build de este job. En ese caso el script omite la subida.AUTONOMY_WAIT_SECONDScambia el tiempo de espera total.
Sin un entorno, una ejecución no tiene valores del entorno: un paso que usa {{env.NAME}} termina como Couldn't start. Define AUTONOMY_ENVIRONMENT_SLUG o usa casos de prueba que no necesiten valores del entorno.
Usa un plan distinto para cada plataforma. Las configuraciones de abajo esperan que AUTONOMY_TEST_PLAN_ID y APP_IDENTIFIER correspondan a la receta. Para combinar plataformas, proporciona los valores con contexts separados o con variables específicas de cada plataforma. Consulta Planes de pruebas para agrupar casos de prueba.
URL de previsualización web
Crea .ci/ en tu repositorio, guarda este script como .ci/autonomy.sh y haz commit junto con .circleci/config.yml. Los jobs lo llaman con Bash, así que no necesita ser ejecutable. Necesita Bash, curl y jq, que incluyen las imágenes Linux de abajo.
#!/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 esta configuración como .circleci/config.yml. Para una primera ejecución, define PREVIEW_URL con una previsualización ya desplegada y haz push de los archivos a staging. La comprobación de disponibilidad debe superarse antes de que empiece Autonomy.
Para previsualizaciones por commit, exporta PREVIEW_URL y llama al script en el mismo step run. Un export simple no pasa a otro step. Para usarla en un step run posterior del mismo job, añade esta línea en el step de despliegue cuando ya se conozca la URL; CircleCI carga BASH_ENV en los steps posteriores:
printf 'export PREVIEW_URL=%q\n' "$PREVIEW_URL" >> "$BASH_ENV"Para jobs separados, escribe la URL en una sola línea en /tmp/autonomy-preview/preview-url.txt. Usa persist_to_workspace con root: /tmp/autonomy-preview y paths: [preview-url.txt] en el job de despliegue. En el job de Autonomy, usa attach_workspace con at: /tmp/autonomy-preview y ejecuta export PREVIEW_URL="$(cat /tmp/autonomy-preview/preview-url.txt)" antes de la comprobación de disponibilidad. BASH_ENV no pasa valores entre jobs.
version: 2.1
jobs:
autonomy-web:
docker:
- image: cimg/base:current
steps:
- checkout
- run:
name: Check preview and run Autonomy
no_output_timeout: 30m
command: |
set -euo pipefail
set +x
: "${PREVIEW_URL:?Set the deployed preview URL}"
curl --fail --silent --show-error --location \
--connect-timeout 15 --max-time 30 \
--retry 10 --retry-delay 3 --retry-all-errors \
--output /dev/null "$PREVIEW_URL"
export AUTONOMY_BRANCH="${CIRCLE_BRANCH:-local}"
export AUTONOMY_COMMIT_SHA="${CIRCLE_SHA1:-$(git rev-parse HEAD)}"
export AUTONOMY_REQUEST_ID="circleci:${CIRCLE_WORKFLOW_ID:-local}:${CIRCLE_BUILD_NUM:-$(date +%s)-$$}"
bash .ci/autonomy.sh web
workflows:
preview-qa:
jobs:
- autonomy-web:
filters:
branches:
only: stagingPara una comprobación local en macOS o Linux, arranca Docker y descarga la versión oficial v0.1.47860 de la CLI antigua. Elige el .tar.gz de tu sistema operativo (darwin para macOS, linux para Linux) y de tu CPU (arm64 para Apple silicon o ARM, amd64 para Intel o AMD). Extráelo y pon el ejecutable circleci-v0 en tu PATH. La CLI v1 actual eliminó la ejecución local, así que los comandos de abajo usan el binario antiguo.
Exporta los tres valores en tu shell local y ejecuta estos comandos desde el repositorio con los cambios ya confirmados. CircleCI no importa en local los secretos ni los contexts del proyecto, así que cada -e es necesario. La configuración de la versión 2.1 debe procesarse antes de ejecutarse.
circleci-v0 config validate .circleci/config.yml
circleci-v0 config process .circleci/config.yml > /tmp/autonomy-circleci.yml
circleci-v0 local execute -c /tmp/autonomy-circleci.yml \
-e "AUTONOMY_API_KEY=$AUTONOMY_API_KEY" \
-e "AUTONOMY_TEST_PLAN_ID=$AUTONOMY_TEST_PLAN_ID" \
-e "PREVIEW_URL=$PREVIEW_URL" \
-e "AUTONOMY_WAIT_SECONDS=${AUTONOMY_WAIT_SECONDS:-1200}" \
autonomy-webLa ejecución local solo ejecuta este job de Docker. No demuestra la planificación del workflow, las restricciones de los contexts, la ejecución en macOS ni la entrega del workspace de iOS.
Entrega de builds móviles
Para iOS y Android, el script pide una URL de subida con el tamaño y el tipo del archivo, sube con PUT y envía tanto storageId como intentId al control de seguridad. Solo inicia las ejecuciones tras un resultado clean. Conserva .ci/autonomy.sh de arriba y sustituye .circleci/config.yml por la configuración que necesites.
iOS
Las pruebas en iOS llegarán pronto. Guarda esto para cuando estén disponibles. Hasta entonces, Autonomy no inicia ejecuciones en iOS.
Compila el .app de simulador en macOS, comprímelo en zip para que se conserven los permisos y los enlaces simbólicos, y pasa el zip a un job de Linux con persist_to_workspace y attach_workspace. La subida y la espera consumen créditos de Linux.
Este ejemplo da por hecho un proyecto nativo de Xcode en ios/MyApp.xcodeproj, un scheme compartido MyApp y un producto MyApp.app. Sustituye esos nombres por los tuyos. Para un workspace de Xcode, sustituye -project por -workspace y su ruta; conserva tus steps de instalación de dependencias antes de xcodebuild. No subas un .ipa de dispositivo.
CircleCI documenta actualmente 27.0.0 en m4pro.medium. macOS está disponible en el plan Free dentro de sus créditos incluidos; comprueba tu saldo antes de lanzar builds. circleci-v0 local execute no puede ejecutar este job de macOS.
version: 2.1
jobs:
build-ios:
macos:
xcode: "27.0.0"
resource_class: m4pro.medium
steps:
- checkout
- run:
name: Build and zip the simulator app
command: |
set -euo pipefail
xcodebuild \
-project ios/MyApp.xcodeproj \
-scheme MyApp \
-configuration Release \
-sdk iphonesimulator \
-destination 'generic/platform=iOS Simulator' \
-derivedDataPath ios/build \
CODE_SIGNING_ALLOWED=NO build
mkdir -p /tmp/autonomy-ios
(cd ios/build/Build/Products/Release-iphonesimulator && \
zip -qry /tmp/autonomy-ios/MyApp.app.zip MyApp.app)
- persist_to_workspace:
root: /tmp/autonomy-ios
paths:
- MyApp.app.zip
autonomy-ios:
docker:
- image: cimg/base:current
steps:
- checkout
- attach_workspace:
at: /tmp/autonomy-ios
- run:
name: Upload and run Autonomy
no_output_timeout: 30m
command: |
set -euo pipefail
set +x
export ARTIFACT_PATH=/tmp/autonomy-ios/MyApp.app.zip
export AUTONOMY_BRANCH="$CIRCLE_BRANCH"
export AUTONOMY_COMMIT_SHA="$CIRCLE_SHA1"
export AUTONOMY_REQUEST_ID="circleci:$CIRCLE_WORKFLOW_ID:$CIRCLE_BUILD_NUM"
bash .ci/autonomy.sh ios
workflows:
ios-qa:
jobs:
- build-ios:
filters:
branches:
only: staging
- autonomy-ios:
requires:
- build-ios
filters:
branches:
only: stagingAndroid
La imagen Linux de Android incluye el SDK, Java, curl y jq. Este ejemplo da por hecho un proyecto nativo de Gradle en android/, su gradlew ejecutable incluido en el repositorio y el APK de debug del módulo app. Cambia la tarea de Gradle y la ruta del APK según tu módulo o tu product flavor. Conserva la preparación de dependencias si la app también necesita JavaScript u otras herramientas de build. Un .aab también funciona.
version: 2.1
jobs:
autonomy-android:
docker:
- image: cimg/android:2026.08
resource_class: medium
steps:
- checkout
- run:
name: Build the debug APK
command: |
set -euo pipefail
cd android
./gradlew --no-daemon :app:assembleDebug
- run:
name: Upload and run Autonomy
no_output_timeout: 30m
command: |
set -euo pipefail
set +x
export ARTIFACT_PATH=android/app/build/outputs/apk/debug/app-debug.apk
export AUTONOMY_BRANCH="$CIRCLE_BRANCH"
export AUTONOMY_COMMIT_SHA="$CIRCLE_SHA1"
export AUTONOMY_REQUEST_ID="circleci:$CIRCLE_WORKFLOW_ID:$CIRCLE_BUILD_NUM"
bash .ci/autonomy.sh android
workflows:
android-qa:
jobs:
- autonomy-android:
filters:
branches:
only: stagingCondicionar 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ón | El job | Qué dice el log |
|---|---|---|
| Passed | Se supera, si todas las demás ejecuciones también se superaron. Una ejecución superada con reintento (flaky) cuenta como superada. | Passed |
| Failed | Falla. | Abre la ejecución para ver qué paso falló y por qué, incluido un Runner problem. |
| Couldn't start | Falla. | 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. |
| Cancelled | Falla. | 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.
Sube no_output_timeout junto con AUTONOMY_WAIT_SECONDS: si no, el límite de silencio predeterminado de CircleCI puede cortar una espera normal. Pon los jobs de release después del job de Autonomy con requires, para que una ejecución que no se superó bloquee la promoción.
Estrategia de ramas y eventos
Estos ejemplos se ejecutan en staging. Amplía filters solo a ramas de confianza. Usa un plan de smoke pequeño para los cambios de previsualización revisados y planes más amplios para los pipelines de release o programados. Configura los disparadores por push, por pull request o programados en el proyecto de CircleCI de tu VCS conectado; los filtros de rama por sí solos no los crean.
Mantén desactivado Pass secrets to builds from forked pull requests donde exista ese ajuste. Revisa el código que no sea de confianza antes de ejecutarlo en un job con la clave de API de la organización, y limita cualquier context compartido a los proyectos previstos y a personas de confianza. El ID de petición combina el ID del workflow y el número del job, así que un nuevo intento del job inicia ejecuciones nuevas, mientras que una petición repetida dentro de un mismo intento recibe las mismas ejecuciones.
Referencias del proveedor
Comprobado con la documentación pública de CircleCI el 20 de septiembre de 2026:
¿No lo encontraste?
Bitrise
Inicia ejecuciones de Autonomy desde un workflow de Bitrise para una previsualización web o un APK de Android, y haz fallar el build salvo que todas las ejecuciones se superen.
Vercel
Ejecuta las pruebas de un proyecto en cada despliegue de Vercel e informa del resultado como un check de despliegue de Vercel.