GitLab CI
Ejecuta Autonomy desde GitLab CI con una URL de previsualización, una app de iOS Simulator o un APK de Android y condiciona el pipeline al veredicto.
Dispara Autonomy después de un despliegue o un build móvil y mantén abierto el job de GitLab hasta que cada caso de prueba seleccionado tenga un veredicto válido y aprobado. Copia el script compartido y la configuración de la plataforma que distribuyes.
Ubicación en el flujo de trabajo
Coloca Autonomy después del job de despliegue que obtiene la URL de previsualización definitiva, o después del job que construye el artefacto móvil. El ejemplo web siguiente recibe una URL ya desplegada y comprueba que responde. No despliega tu aplicación.
Cada ejemplo YAML es una alternativa completa de .gitlab-ci.yml. Al añadirlo a un pipeline existente, conserva los jobs de build y despliegue, combina las etapas y dirige needs al job que produce la URL o el artefacto. Ejecuta únicamente código de confianza en una rama predeterminada protegida.
Secretos necesarios
En Settings → CI/CD → Variables, añade estos valores de tipo Variable, no File, con ámbito de entorno *. Protege las variables y desactiva la expansión de referencias a variables. Marca la clave API como Masked and hidden. Protege la rama predeterminada antes de ejecutar los ejemplos.
-
AUTONOMY_API_KEY: una clave API de organización que empiece poraut_, creada en Settings → API Keys de Autonomy. Debe pertenecer a la organización que contiene el plan. Actualmente, las claves de organización incluyen todos los ámbitos de la API. -
AUTONOMY_API_URL: copiaCONVEX_SITE_URLoNEXT_PUBLIC_CONVEX_SITE_URLdel despliegue correspondiente a tu clave API. Usa su URL HTTP de Convex exacta, incluida cualquier región en el nombre del host, sin/apini una ruta final. No uses la URL del panel ni.convex.cloud. Una clave de desarrollo necesita la URL de su despliegue de desarrollo. -
AUTONOMY_TEST_PLAN_ID: el ID de un plan real de Test Plans, con al menos un caso de prueba web. Copia el ID de la URL del plan. Un ID de Test Cases no es intercambiable con un ID de plan. -
PREVIEW_URL: la URL HTTPS de previsualización lista para el ejemplo web. Debe ser accesible desde el runner de Autonomy, nolocalhostni un servicio disponible solo dentro del job de CI. - Para iOS, añade
AUTONOMY_IOS_TEST_PLAN_IDyAUTONOMY_IOS_BUNDLE_ID. Para Android, añadeAUTONOMY_ANDROID_TEST_PLAN_IDyAUTONOMY_ANDROID_PACKAGE_NAME. Cada plan debe contener casos de esa plataforma; los identificadores deben coincidir con la app construida. - Ten un runner de Autonomy disponible para la plataforma solicitada y suficientes créditos de ejecución. Para estos objetivos independientes explícitos, usa casos que no hagan referencia a valores de ejecución de un Environment guardado.
Los ejemplos establecen AUTONOMY_REQUEST_ID a partir de CI_JOB_ID, de modo que un job reintentado de GitLab recibe una solicitud nueva. Los metadatos de rama y commit proceden de las variables predefinidas de GitLab. Consulta Test Plans para organizar los casos seleccionados.
URL de previsualización web
Crea .ci/ en la raíz del repositorio, guarda el siguiente script como .ci/autonomy.sh y haz commit junto con el archivo del pipeline. Todas las recetas de esta página usan exactamente este script. Necesita Bash, jq y curl 7.76 o posterior; los jobs con contenedor los instalan.
#!/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 lo siguiente como .gitlab-ci.yml. Habilita los runners Linux alojados de GitLab para tu proyecto; los jobs web y el job de subida de iOS seleccionan explícitamente saas-linux-small-amd64. En GitLab Self-Managed, sustituye esa etiqueta en cada job con contenedor Linux por la etiqueta de tu propio runner Linux con ejecutor Docker. Empieza en Build → Pipelines → New pipeline sobre la rama predeterminada protegida. El registro muestra Queued Autonomy runs: y los ID de ejecución cuando la API acepta la solicitud.
stages: [prepare, qa]
workflow:
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_COMMIT_REF_PROTECTED == "true"'
- when: never
default:
tags: [saas-linux-small-amd64]
image: debian:stable-slim
before_script:
- apt-get update && apt-get install -y --no-install-recommends bash curl jq ca-certificates
preview_ready:
stage: prepare
script:
- |
: "${PREVIEW_URL:?Set the deployed preview URL in CI/CD Variables}"
curl --fail --silent --show-error --location \
--connect-timeout 15 --max-time 60 "$PREVIEW_URL" > /dev/null
printf 'PREVIEW_URL=%s\n' "$PREVIEW_URL" > preview.env
artifacts:
reports:
dotenv: preview.env
expire_in: 1 day
autonomy_web:
stage: qa
timeout: 30m
needs:
- job: preview_ready
artifacts: true
script:
- |
export AUTONOMY_REQUEST_ID="gitlab:$CI_PROJECT_ID:$CI_JOB_ID"
export AUTONOMY_BRANCH="$CI_COMMIT_REF_NAME"
export AUTONOMY_COMMIT_SHA="$CI_COMMIT_SHA"
bash .ci/autonomy.sh webSi la URL se crea dentro de tu job de despliegue existente, mueve las entradas printf y artifacts:reports:dotenv a ese job y cambia needs por su nombre. Elimina la variable de proyecto fija PREVIEW_URL para que no sobrescriba el valor generado de dotenv. No incluyas credenciales en preview.env; GitLab lo almacena como artefacto descargable.
Transferencia de artefactos móviles
Usa el mismo .ci/autonomy.sh. El script solicita una URL de subida firmada, sube el archivo, exige un análisis limpio y entrega el ID de almacenamiento resultante a la ejecución. Selecciona solo la plataforma cuyo build existe en tu repositorio.
iOS
Usa un runner macOS propio, dedicado y protegido, con la etiqueta macos, el ejecutor de shell Bash, Xcode y el SDK de iOS Simulator instalados. Desactiva Run untagged jobs para este runner y no le asignes la etiqueta del runner Linux. Los runners macOS alojados de GitLab requieren Premium o Ultimate, salvo los programas de código abierto que cumplan sus requisitos. Un proyecto Free normal necesita su propio Mac. Linux no puede realizar este build de Xcode.
Este ejemplo nativo supone ios/MyApp.xcodeproj, un esquema compartido MyApp y un producto MyApp.app. Sustituye los tres por los nombres de tu proyecto e instala las dependencias específicas antes de xcodebuild. Para un proyecto con workspace, usa -workspace ios/MyApp.xcworkspace en lugar de -project. Genera un .app de Simulator, no un .ipa para dispositivo.
El job de macOS archiva la app conservando permisos de ejecución y enlaces simbólicos antes de que GitLab transporte el ZIP. needs:artifacts descarga ese ZIP en el job de Linux, que se encarga de subirlo y esperar.
stages: [build, qa]
workflow:
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_COMMIT_REF_PROTECTED == "true"'
- when: never
build_ios:
stage: build
tags: [macos]
script:
- |
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 .build
(cd ios/build/Build/Products/Release-iphonesimulator && \
zip -qry -y "$CI_PROJECT_DIR/.build/MyApp.app.zip" MyApp.app)
artifacts:
paths:
- .build/MyApp.app.zip
expire_in: 1 day
autonomy_ios:
stage: qa
tags: [saas-linux-small-amd64]
image: debian:stable-slim
timeout: 30m
needs:
- job: build_ios
artifacts: true
before_script:
- apt-get update && apt-get install -y --no-install-recommends bash curl jq ca-certificates
script:
- |
export AUTONOMY_TEST_PLAN_ID="$AUTONOMY_IOS_TEST_PLAN_ID"
export APP_IDENTIFIER="$AUTONOMY_IOS_BUNDLE_ID"
export ARTIFACT_PATH=".build/MyApp.app.zip"
export AUTONOMY_REQUEST_ID="gitlab:$CI_PROJECT_ID:$CI_JOB_ID"
export AUTONOMY_BRANCH="$CI_COMMIT_REF_NAME"
export AUTONOMY_COMMIT_SHA="$CI_COMMIT_SHA"
bash .ci/autonomy.sh iosAndroid
Registra un runner Linux propio, dedicado y protegido, con la etiqueta android-linux y el ejecutor de shell Bash. Desactiva Run untagged jobs para este runner y no le asignes la etiqueta del runner con contenedor Linux. Instala en esa máquina el JDK requerido por el proyecto, las herramientas de línea de comandos de Android, las versiones de compile SDK y build-tools, Bash, jq, curl 7.76 o posterior y certificados CA de confianza. Configura ANDROID_HOME, acepta las licencias del SDK y añade al repositorio un android/gradlew ejecutable con sus archivos wrapper. El ejecutor de shell usa las herramientas del host; no instala un SDK de Android por ti.
Este ejemplo construye el APK de depuración del módulo app y lo sube en el mismo job de Linux. Ajusta la tarea de Gradle y ARTIFACT_PATH conjuntamente si tu proyecto usa variantes u otro módulo.
stages: [qa]
workflow:
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_COMMIT_REF_PROTECTED == "true"'
- when: never
autonomy_android:
stage: qa
tags: [android-linux]
timeout: 60m
script:
- |
set -euo pipefail
(cd android && ./gradlew --no-daemon assembleDebug)
export AUTONOMY_TEST_PLAN_ID="$AUTONOMY_ANDROID_TEST_PLAN_ID"
export APP_IDENTIFIER="$AUTONOMY_ANDROID_PACKAGE_NAME"
export ARTIFACT_PATH="android/app/build/outputs/apk/debug/app-debug.apk"
export AUTONOMY_REQUEST_ID="gitlab:$CI_PROJECT_ID:$CI_JOB_ID"
export AUTONOMY_BRANCH="$CI_COMMIT_REF_NAME"
export AUTONOMY_COMMIT_SHA="$CI_COMMIT_SHA"
bash .ci/autonomy.sh androidCondicionar el pipeline al veredicto
El script consulta cada ID devuelto por run.trigger mediante run.get. Sigue esperando mientras la ejecución esté en queued, running o retrying, y solo tiene éxito cuando todos los resultados finales tienen verdict: "passed" y validity: "valid". Una ejecución en cola no aprueba el pipeline. Los resultados fallidos, inválidos, no verificados, cancelados, inesperados o agotados por tiempo hacen fallar el job.
El presupuesto de espera predeterminado es de 1.200 segundos para todas las ejecuciones devueltas. Configura AUTONOMY_WAIT_SECONDS para cambiarlo y mantén los límites de tiempo del job y del runner de GitLab por encima de ese presupuesto más el tiempo de build y subida. Mantén allow_failure desactivado. Cancelar el job de GitLab o agotar su tiempo detiene el sondeo; no cancela una ejecución de Autonomy ya en cola.
Estrategia de ramas y eventos
Estas configuraciones permiten pushes, ejecuciones programadas y pipelines iniciados manualmente solo en la rama predeterminada protegida. Los pipelines de merge requests y el código de forks no reciben la clave. Usa planes de smoke cortos en despliegues normales; utiliza una programación o un pipeline manual con un plan más amplio para comprobar versiones.
Para comprobar la previsualización de una merge request, ejecuta manualmente la configuración de confianza de la rama predeterminada con la URL de previsualización revisada. No habilites acceso a variables protegidas para código no confiable de merge requests. Si amplías las reglas de ramas, protege esas ramas y revisa primero sus cambios de pipeline.
Referencias del proveedor
- Referencia YAML de GitLab CI/CD: etapas, reglas del flujo, etiquetas de runners, tiempos de espera y
needs. - Variables de CI/CD: valores de variables, enmascaramiento y ramas protegidas.
- Artefactos de jobs e informes dotenv: transferencia de un ZIP o una URL entre jobs.
- Variables predefinidas: ID de jobs, nombres de ramas y SHA de commits.
- Runners Linux alojados: la etiqueta
saas-linux-small-amd64y la ejecución en contenedores. - Runners macOS alojados y ejecutor de shell: disponibilidad de runners y requisitos del host.