Saltar al contenido

Dispara Native Builds mediante Webhooks

Capgo Build normalmente se inicia desde un portátil o un job de CI. Los equipos que despliegan desde un dashboard de administración, CMS o portal interno suelen querer un único webhook HTTP: pulsar un botón en su propia UI y que arranque un build nativo firmado. Esta guía muestra el patrón estándar: una llamada HTTP autenticada y ligera a tu host de code, que ejecuta el mismo flujo de trabajo de Capgo Build que ya usas.

No necesitas que Capgo exponga un “build webhook” público. Tu CI ya tiene los secrets de firma; el único trabajo del webhook es iniciar ese job de CI de forma segura.

  • Un workflow de Capgo Build que ya funciona desde la CI UI o al hacer push (Acciones de GitHub)
  • Permiso para crear un token GitHub granular, un token de trigger de GitLab o equivalente
  • Un secreto compartido que enviará tu dashboard de admin (header o body)
Sección titulada “Opción A — GitHub repository_dispatch (recomendado)”

GitHub acepta una llamada API autenticada que inicia un workflow a la escucha de repository_dispatch. Cualquier dashboard que pueda POST enviar JSON puede hacerlo.

Valida el payload antes en el checkout y antes de cualquier paso con secretos. Pasa los valores aceptados mediante job outputs / env vars; nunca los interpoles client_payload directamente en run: scripts (guía sobre inyección de scripts).

.github/workflows/capgo-build-webhook.yml
name: Capgo Build (Webhook)
on:
repository_dispatch:
types: [capgo-native-build]
jobs:
validate:
runs-on: ubuntu-latest
outputs:
platform: ${{ steps.check.outputs.platform }}
mode: ${{ steps.check.outputs.mode }}
ref: ${{ steps.check.outputs.ref }}
platforms_json: ${{ steps.check.outputs.platforms_json }}
steps:
- id: check
env:
RAW_PLATFORM: ${{ github.event.client_payload.platform }}
RAW_MODE: ${{ github.event.client_payload.mode }}
RAW_REF: ${{ github.event.client_payload.ref }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
run: |
PLATFORM="${RAW_PLATFORM:-android}"
MODE="${RAW_MODE:-release}"
REF="${RAW_REF:-$DEFAULT_BRANCH}"
case "$PLATFORM" in ios|android|both) ;; *)
echo "Invalid platform: $PLATFORM" >&2; exit 1;;
esac
case "$MODE" in debug|release) ;; *)
echo "Invalid mode: $MODE" >&2; exit 1;;
esac
# Allowlist branches / tags / full SHAs only
if [[ ! "$REF" =~ ^(main|master|production|release/[A-Za-z0-9._-]+|[0-9a-f]{40})$ ]]; then
echo "Ref not allowlisted: $REF" >&2
exit 1
fi
if [ "$PLATFORM" = "both" ]; then
PLATFORMS_JSON='["ios","android"]'
else
PLATFORMS_JSON=$(printf '["%s"]' "$PLATFORM")
fi
{
echo "platform=$PLATFORM"
echo "mode=$MODE"
echo "ref=$REF"
echo "platforms_json=$PLATFORMS_JSON"
} >> "$GITHUB_OUTPUT"
build:
needs: validate
runs-on: ubuntu-latest
environment: ${{ needs.validate.outputs.mode == 'release' && 'production' || 'build-debug' }}
strategy:
fail-fast: false
matrix:
platform: ${{ fromJSON(needs.validate.outputs.platforms_json) }}
steps:
- uses: actions/checkout@v4
with:
ref: ${{ needs.validate.outputs.ref }}
- uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'npm'
- run: npm ci
- run: npm run build
- name: Sync native project
env:
PLATFORM: ${{ matrix.platform }}
run: npx cap sync "$PLATFORM"
- name: Capgo Build
env:
CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }}
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
CAPGO_IOS_PROVISIONING_MAP: ${{ secrets.CAPGO_IOS_PROVISIONING_MAP }}
APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }}
APPLE_KEY_CONTENT: ${{ secrets.APPLE_KEY_CONTENT }}
APP_STORE_CONNECT_TEAM_ID: ${{ secrets.APP_STORE_CONNECT_TEAM_ID }}
ANDROID_KEYSTORE_FILE: ${{ secrets.ANDROID_KEYSTORE_FILE }}
KEYSTORE_KEY_ALIAS: ${{ secrets.KEYSTORE_KEY_ALIAS }}
KEYSTORE_KEY_PASSWORD: ${{ secrets.KEYSTORE_KEY_PASSWORD }}
KEYSTORE_STORE_PASSWORD: ${{ secrets.KEYSTORE_STORE_PASSWORD }}
PLAY_CONFIG_JSON: ${{ secrets.PLAY_CONFIG_JSON }}
PLATFORM: ${{ matrix.platform }}
MODE: ${{ needs.validate.outputs.mode }}
run: |
npx @capgo/cli@latest build request com.example.app \
--platform "$PLATFORM" \
--build-mode "$MODE"

Crea un token de acceso personal con permisos granulares (o un token de instalación de GitHub App) con Contenido: Read and write en el repositorio (requerido para repository_dispatch). Guárdalo solo en tu backend de administración; nunca en el navegador.

3. Llama al webhook desde tu dashboard de administración

Sección titulada “3. Llama al webhook desde tu dashboard de administración”

Tu backend (no el navegador del usuario final) debe enviar:

Ventana de terminal
curl -X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/OWNER/REPO/dispatches \
-d '{
"event_type": "capgo-native-build",
"client_payload": {
"platform": "both",
"mode": "release",
"ref": "main",
"requested_by": "admin@example.com"
}
}'
CampoPropósito
event_typeDebe coincidir types en el workflow (capgo-native-build)
client_payload.platformios, android, o both
client_payload.modedebug o release
client_payload.refRama o tag a build (opcional)

Conecta el mismo JSON POST a un botón en tu UI de admin (“Build native apps”). El dashboard solo necesita llegar al tus backend; el backend contiene el GITHUB_TOKEN.

Opción B — Proxy webhook ligero (cualquier host)

Sección titulada “Opción B — Proxy webhook ligero (cualquier host)”

Si la herramienta de administración solo puede hacer POST a una URL que controles (Zapier, Make, Cloudflare Worker, ruta de Express), coloca un proxy corto delante de GitHub:

// Example Cloudflare Worker / Node handler (sketch)
export default {
async fetch(request, env) {
if (request.method !== 'POST') {
return new Response('Method not allowed', { status: 405 })
}
if (request.headers.get('x-webhook-secret') !== env.WEBHOOK_SECRET) {
return new Response('Unauthorized', { status: 401 })
}
const body = await request.json().catch(() => ({}))
const platform = body.platform || 'both'
const mode = body.mode || 'release'
const ref = body.ref || 'main'
if (!['ios', 'android', 'both'].includes(platform)) {
return new Response('Invalid platform', { status: 400 })
}
if (!['debug', 'release'].includes(mode)) {
return new Response('Invalid mode', { status: 400 })
}
if (!/^(main|master|production|release\/[A-Za-z0-9._-]+|[0-9a-f]{40})$/.test(ref)) {
return new Response('Ref not allowlisted', { status: 400 })
}
const res = await fetch(
`https://api.github.com/repos/${env.GITHUB_OWNER}/${env.GITHUB_REPO}/dispatches`,
{
method: 'POST',
headers: {
Accept: 'application/vnd.github+json',
Authorization: `Bearer ${env.GITHUB_TOKEN}`,
'X-GitHub-Api-Version': '2022-11-28',
},
body: JSON.stringify({
event_type: 'capgo-native-build',
client_payload: { platform, mode, ref },
}),
},
)
return new Response(res.status === 204 ? 'Build queued' : await res.text(), {
status: res.status === 204 ? 200 : res.status,
})
},
}

Luego configura el producto de administración:

ConfiguraciónValor
URLhttps://your-worker.example.com/native-build
MétodoPOST
Headerx-webhook-secret: <shared secret>
Body{ "platform": "both", "mode": "release" }

Esta es la estructura habitual para conectar un webhook a un dashboard de administración: el dashboard guarda una URL y un secret; las credenciales de Capgo permanecen en GitHub Actions.

GitLab ofrece tokens de disparo de pipeline que funcionan como objetivos de webhook nativos.

# .gitlab-ci.yml fragment
capgo_native_webhook:
stage: build
script:
- npm ci && npm run build
- npx cap sync "${PLATFORM:-android}"
- npx @capgo/cli@latest build request com.example.app --platform "${PLATFORM:-android}" --build-mode "${BUILD_MODE:-release}"
rules:
- if: '$CI_PIPELINE_SOURCE == "trigger"'

Crea un token de disparo en Configuración → CI/CD → Tokens de activación de pipeline, y luego desde el backend de administración:

Ventana de terminal
curl -X POST \
-F token=$GITLAB_TRIGGER_TOKEN \
-F ref=main \
-F "variables[PLATFORM]=android" \
-F "variables[BUILD_MODE]=release" \
https://gitlab.com/api/v4/projects/PROJECT_ID/trigger/pipeline

Para ambas plataformas, lanza dos disparadores o expande el job en una matriz paralela, igual que en el ejemplo de GitHub.

PlataformaMecanismo de Webhook
BitbucketURL de trigger del pipeline o pipeline personalizado + app password POST
Azure DevOpsREST API de ejecución de pipeline con un PAT; usa el pipeline manual de Disparar desde la UI de CI

El patrón es idéntico: admin → tu secret check → host API → job de Capgo Build.

Al construir el formulario del dashboard, recolecta al menos:

  • Plataforma — ios / android / ambos
  • Modo — debug (QA) o release (store)
  • Ref de Git — branch o tag a buildear
  • Actor — email o user id para audit logs (pass through) client_payload)

Opcional: tras la ejecución, que el CI notifique al admin API con la URL de descarga de --output-record / build last-output.

  • Autentica cada webhook (x-webhook-secret, firma HMAC o mTLS).
  • Allowlist platform, mode, y ref tanto en el proxy como en el workflow validate job — trata client_payload como entrada no confiable.
  • Pasa los valores aceptados mediante env vars / job outputs; no interpoles campos del payload en run: scripts.
  • Mantén los tokens de GitHub/GitLab solo en el servidor.
  • Es preferible usar tokens con alcance a un solo repositorio.
  • Aplica rate-limit al proxy; los Native Build consumen minutos de build.
  • Para lanzamientos enviados a la tienda, mapea release a un entorno GitHub protegido (como en el ejemplo) o requiere un flag de confirmación extra en validate.