# El reporter de Cypress en tu CI

> Pipelines completos para @probara/cypress-reporter en GitHub Actions, GitLab CI, Jenkins, CircleCI, Azure Pipelines, Bitbucket Pipelines y Buildkite, con secretos, specs repartidas, detección del CI y seguridad frente a forks.

El reporter se ejecuta dentro de `npx cypress run`: pasa el token al paso que ejecuta tus pruebas, y
cada resultado se envía cuando termina la ejecución. No hay un paso de envío aparte. Todos los
ejemplos suponen que el reporter, su plugin y su archivo de soporte están registrados como en
[Primeros pasos](https://docs.probara.net/es/developers/ci-integrations/cypress/#3-registra-el-reporter-y-su-plugin).

## En cualquier CI

- **El token es un secreto, el ID del Proyecto no.** Guarda el token de aplicación (de la tarjeta
  **Cypress** en **Workspace › Integraciones**) como un secreto llamado `PROBARA_API_TOKEN`. Pon el
  ID del Proyecto en `cypress.config.js` (`projectId`) o en el pipeline (`PROBARA_PROJECT`).
- **Node.js 22.12 o posterior** para el Node.js que arranca `cypress`, que es donde se ejecuta el
  plugin, y los navegadores y las bibliotecas del sistema que Cypress necesita en el agente.
- **Las pruebas fallidas hacen fallar el paso, los problemas al reportar no**: el código de salida de
  `cypress run` sigue siendo el número de sus pruebas fallidas.
- **`cypress-io/github-action` y `npm test` también funcionan**: el reporter sale de la
  configuración, no del comando. Pasa a su paso el mismo entorno.
- **Fija la versión** en `package.json` (o en el lockfile), para que una versión nueva nunca cambie un
  pipeline por sorpresa. La CLI no es una dependencia del reporter: los pasos de abajo que la usan
  fijan su versión en el propio comando (`npx --yes @probara/cli@0.3`). Con `@probara/cli` en tus
  `devDependencies`, `npx @probara/cli` después de `npm ci` ejecuta la versión del lockfile.
- **Los builds de forks no reportan nada**: sin el secreto, el reporter no envía nada y las pruebas
  se ejecutan como siempre. La CLI termina con código 2 sin token, así que las recetas desactivan el
  envío (`PROBARA_ENABLED=false`) donde la ejecutaría el build de un fork: entonces termina con
  código 0. Cada sección de abajo explica cómo mantener el secreto fuera de los builds de forks en
  ese CI.
- **Los pipelines repartidos** crean una ejecución por máquina salvo que compartan una. En GitHub Actions
  los jobs de un mismo workflow comparten una ejecución automáticamente
  ([jobs en paralelo](#jobs-en-paralelo-una-ejecución-cerrada-por-el-job-final)). Fuera de GitHub
  Actions, créala primero, pasa su ULID a cada máquina, y ciérrala al final
  ([sharding](https://docs.probara.net/es/developers/ci-integrations/cypress/sharding/)).

### Lo que rellena la detección del CI

Salvo que una opción o una variable `PROBARA_*` los defina, la ejecución toma su nombre y su origen
del CI:

| CI                  | Se detecta por           | Nombre de la ejecución                            |
| ------------------- | ------------------------ | ------------------------------------------------- |
| GitHub Actions      | `GITHUB_ACTIONS=true`    | `GITHUB_WORKFLOW #GITHUB_RUN_NUMBER`              |
| GitLab CI           | `GITLAB_CI=true`         | `CI_PROJECT_NAME #CI_PIPELINE_IID`                |
| Jenkins             | `JENKINS_URL`            | `JOB_NAME #BUILD_NUMBER`                          |
| CircleCI            | `CIRCLECI=true`          | `CIRCLE_PROJECT_REPONAME #CIRCLE_BUILD_NUM`       |
| Azure Pipelines     | `TF_BUILD=True`          | `BUILD_DEFINITIONNAME #BUILD_BUILDNUMBER`         |
| Bitbucket Pipelines | `BITBUCKET_BUILD_NUMBER` | `BITBUCKET_REPO_SLUG #BITBUCKET_BUILD_NUMBER`     |
| Buildkite           | `BUILDKITE=true`         | `BUILDKITE_PIPELINE_SLUG #BUILDKITE_BUILD_NUMBER` |

La rama, el commit y la URL del build de cada uno figuran en su sección, y en la
[detección del CI de `@probara/core`](https://github.com/cynch-me/probara-sdk/blob/main/packages/core/README.md#ci-detection).
Sustitúyelos con `PROBARA_BRANCH`, `PROBARA_COMMIT` y `PROBARA_BUILD_URL`.

## GitHub Actions

### 1. Añade el secreto

En el repositorio (o en la organización): **Settings → Secrets and variables → Actions → New
repository secret**, con el nombre `PROBARA_API_TOKEN` y el token de aplicación como valor.

### 2. Pasa el token al paso de pruebas

```yaml
name: Cypress

on:
  push:
    branches: [main]
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-node@v5
        with:
          node-version: 22
      - run: npm ci
      - name: Run Cypress tests
        run: npx cypress run
        env:
          PROBARA_API_TOKEN: ${{ secrets.PROBARA_API_TOKEN }}
          PROBARA_PROJECT: SHOP
```

### Jobs en paralelo: una ejecución, cerrada por el job final

En GitHub Actions los jobs de un mismo workflow envían a una misma ejecución: el origen de la
ejecución identifica la ejecución del workflow por su repositorio, su id y su intento, y el primer job
que envía crea la ejecución. Por defecto, cada job cierra su ejecución al terminar, así que los jobs
en paralelo definen `PROBARA_CLOSE_RUN: false`. Un job final cierra la ejecución con
`probara run close`, que no necesita aquí el ULID: `needs` hace que espere a todos los jobs de
pruebas, y `if: always()` lo ejecuta aunque fallen. Cypress no tiene un flag `--shard`: cada job
ejecuta sus propias specs, aquí con `--spec`.

```yaml
jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        spec: ['cypress/e2e/cart/**', 'cypress/e2e/login/**']
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-node@v5
        with:
          node-version: 22
      - run: npm ci
      - run: npx cypress run --spec '${{ matrix.spec }}'
        env:
          PROBARA_API_TOKEN: ${{ secrets.PROBARA_API_TOKEN }}
          PROBARA_PROJECT: SHOP
          # A job leaves the run open for the others.
          PROBARA_CLOSE_RUN: false

  probara:
    needs: test
    if: always()
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-node@v5
        with:
          node-version: 22
      - run: npx --yes @probara/cli@0.6 run close
        env:
          PROBARA_API_TOKEN: ${{ secrets.PROBARA_API_TOKEN }}
          PROBARA_PROJECT: SHOP
```

- **Un job tras el cierre** recibe `409 ci_run_closed`: el reporter registra el motivo, no envía nada
  más, y las pruebas conservan su código de salida
  ([solución de problemas](https://docs.probara.net/es/developers/ci-integrations/cypress/troubleshooting/#un-job-envía-después-de-cerrarse-su-ejecución)).
  Mantén cada job que envía en `needs`.
- **Una nueva ejecución del workflow** (re-run) es otro intento: envía a una ejecución propia.
- **Un solo job** no necesita nada de esto: su ejecución se cierra al terminar, como en cualquier otro caso.

### Detección del CI

| Campo            | De dónde sale                                                                                |
| ---------------- | -------------------------------------------------------------------------------------------- |
| Nombre           | `GITHUB_WORKFLOW #GITHUB_RUN_NUMBER`, por ejemplo `Cypress #42`                              |
| Rama             | `GITHUB_HEAD_REF` (la rama de la pull request), si no `GITHUB_REF_NAME`; ninguna para un tag |
| Commit           | `GITHUB_SHA`                                                                                 |
| URL del build    | `GITHUB_SERVER_URL/GITHUB_REPOSITORY/actions/runs/GITHUB_RUN_ID`                             |
| Repositorio      | `GITHUB_REPOSITORY`                                                                          |
| Autor del commit | `GITHUB_ACTOR`                                                                               |
| Pull request     | `<n>` de `GITHUB_REF` `refs/pull/<n>/merge`                                                  |
| Workflow         | `GITHUB_WORKFLOW`                                                                            |
| Ejecución de CI  | `GITHUB_RUN_ID`, intento `GITHUB_RUN_ATTEMPT`                                                |
| Job de CI        | `GITHUB_JOB`                                                                                 |

En los eventos `pull_request`, `GITHUB_SHA` es el commit de merge que creó GitHub, no el último commit
de la rama. Para registrar este último, añade
`PROBARA_COMMIT: ${{ github.event.pull_request.head.sha }}`. En GitHub Actions el commit debe ser un
SHA hexadecimal de 7 a 40 caracteres: otro valor se descarta con una advertencia.

### Pull requests desde forks

Los workflows que lanza una pull request desde un fork reciben los secretos vacíos. Sin token, el
reporter queda desactivado, así que los builds de forks ejecutan sus pruebas y no reportan nada, sin
ningún ajuste adicional. No uses `pull_request_target` para llevar el secreto a un build de un fork:
ejecuta el código del fork con tus secretos.

## GitLab CI

### 1. Añade la variable

**Settings → CI/CD → Variables → Add variable**: clave `PROBARA_API_TOKEN`, el token como valor, con
**Mask variable** activado. **Protect variable** la limita a ramas y tags protegidos: actívalo solo si
reportas únicamente desde ellos.

### 2. El job de pruebas

```yaml
stages:
  - test

cypress:
  stage: test
  image: cypress/browsers:latest
  variables:
    PROBARA_PROJECT: SHOP
  script:
    - npm ci
    - npx cypress run
```

- `cypress/browsers` incluye Node.js, los navegadores y las bibliotecas del sistema que Cypress
  necesita; fija una etiqueta cuyo Node.js sea 22.12 o posterior.
- Un job sin la variable (el pipeline de un fork, una rama no protegida con una variable protegida)
  ejecuta sus pruebas y no reporta nada.
- Para conservar lo que no se pudo enviar, define `PROBARA_RESULTS_FILE` y guarda el archivo y su
  carpeta de adjuntos como artefactos
  ([archivo de resultados](https://docs.probara.net/es/developers/ci-integrations/cypress/results-and-retries/#archivo-de-resultados)).
- Los jobs `parallel:` que comparten una ejecución, con un job `.pre` que la crea y un job `.post`
  que la cierra, están en [sharding](https://docs.probara.net/es/developers/ci-integrations/cypress/sharding/#gitlab-ci).

### Detección del CI

| Campo         | De dónde sale                                                                                                                    |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Nombre        | `CI_PROJECT_NAME #CI_PIPELINE_IID`, por ejemplo `shop #42`                                                                       |
| Rama          | `CI_MERGE_REQUEST_SOURCE_BRANCH_NAME` en un pipeline de merge request, si no `CI_COMMIT_REF_NAME`; ninguna en un pipeline de tag |
| Commit        | `CI_COMMIT_SHA`                                                                                                                  |
| URL del build | `CI_PIPELINE_URL`                                                                                                                |

### Merge requests desde forks

Una merge request desde un fork ejecuta su pipeline en el proyecto del fork, que no tiene tus
variables de CI/CD: el reporter queda desactivado ahí. Si los maintainers ejecutan pipelines de forks
en el proyecto principal, esos pipelines sí reciben la variable, así que revisa el cambio antes de
ejecutarlo.

## Jenkins

### 1. Añade la credencial

**Manage Jenkins → Credentials → (un dominio) → Add Credentials**: tipo **Secret text**, ID
`probara-api-token`, el token como secreto.

### 2. El pipeline

Un `Jenkinsfile` declarativo, con Node.js 22 y las bibliotecas del sistema que Cypress necesita en el
agente, enlaza el token en la etapa de pruebas con `withCredentials`:

```groovy
pipeline {
  agent any
  environment {
    PROBARA_PROJECT = 'SHOP'
  }
  stages {
    stage('Test') {
      steps {
        sh 'npm ci'
        withCredentials([string(credentialsId: 'probara-api-token', variable: 'PROBARA_API_TOKEN')]) {
          sh 'npx cypress run'
        }
      }
    }
  }
}
```

Jenkins enmascara el secreto enlazado en el log del build.

### Etapas en paralelo: una ejecución

Las etapas en paralelo que ejecutan cada una una parte de las specs crean una ejecución cada una
salvo que compartan una: créala en una primera etapa, pásala a cada una y ciérrala en un bloque
`post { always { ... } }`:

```groovy
pipeline {
  agent any
  environment {
    PROBARA_PROJECT = 'SHOP'
  }
  stages {
    stage('Create the Probara run') {
      steps {
        sh 'npm ci'
        withCredentials([string(credentialsId: 'probara-api-token', variable: 'PROBARA_API_TOKEN')]) {
          sh 'npx --yes @probara/cli@0.3 run create > probara-run-ulid'
        }
        script {
          env.PROBARA_RUN_ULID = readFile('probara-run-ulid').trim()
        }
      }
    }
    stage('Test') {
      parallel {
        stage('Cart') {
          steps {
            withCredentials([string(credentialsId: 'probara-api-token', variable: 'PROBARA_API_TOKEN')]) {
              sh "npx cypress run --spec 'cypress/e2e/cart/**' --config screenshotsFolder=cypress/screenshots-cart"
            }
          }
        }
        stage('Login') {
          steps {
            withCredentials([string(credentialsId: 'probara-api-token', variable: 'PROBARA_API_TOKEN')]) {
              sh "npx cypress run --spec 'cypress/e2e/login/**' --config screenshotsFolder=cypress/screenshots-login"
            }
          }
        }
      }
    }
  }
  post {
    always {
      withCredentials([string(credentialsId: 'probara-api-token', variable: 'PROBARA_API_TOKEN')]) {
        sh 'npx --yes @probara/cli@0.3 run close'
      }
    }
  }
}
```

Aquí ambas etapas comparten un workspace, instalado una sola vez antes de que empiecen (dos `npm ci`
a la vez en una misma carpeta se estorbarían). Cada `cypress run` vacía su carpeta de capturas al
arrancar, así que en un mismo agente cada etapa tiene su propio `screenshotsFolder`. En agentes
separados, cada una hace su propio checkout e instalación.

### Pull requests desde forks

Jenkins no oculta por sí solo las credenciales a los builds de pull requests: un job multibranch que
construye forks puede entregar el token a su código. Configura **Discover pull requests from forks →
Trust** para que una pull request de cualquier otra persona se construya con el `Jenkinsfile` de su
rama destino: en el branch source de GitHub, **From users with Admin or Write permission** (o
**Nobody**); en el branch source de Bitbucket, **Nobody**, o **Forks in the same account** a partir
de la versión 871 del plugin (la corrección de CVE-2024-28152). **Everyone** construye un fork con su
propio `Jenkinsfile`, que puede enlazar la credencial por sí mismo, y deja inútil la protección con
`env.CHANGE_FORK` de abajo.

En el `Jenkinsfile` de la rama destino, enlaza la credencial solo cuando `env.CHANGE_FORK` no está
definida (Jenkins la define para una pull request desde un fork), y desactiva el envío en un build de
un fork:

```groovy
pipeline {
  agent any
  environment {
    PROBARA_PROJECT = 'SHOP'
  }
  stages {
    stage('Test') {
      steps {
        sh 'npm ci'
        script {
          if (env.CHANGE_FORK) {
            withEnv(['PROBARA_ENABLED=false']) {
              sh 'npx cypress run'
            }
          } else {
            withCredentials([string(credentialsId: 'probara-api-token', variable: 'PROBARA_API_TOKEN')]) {
              sh 'npx cypress run'
            }
          }
        }
      }
    }
  }
}
```

En el pipeline en paralelo, envuelve cada `withCredentials` de la misma forma: con el envío
desactivado, `run create` no escribe ningún ULID y `run close` termina con 0 sin token.

### Detección del CI

Con `JENKINS_URL` definida, la ejecución se llama `JOB_NAME #BUILD_NUMBER`, y su rama, su commit y la
URL del build salen de `BRANCH_NAME` (o `GIT_BRANCH`), `GIT_COMMIT` y `BUILD_URL`.

## CircleCI

### 1. Añade el token

**Organization Settings → Contexts → Create Context** (por ejemplo `probara-reporting`), y después
añade la variable de entorno `PROBARA_API_TOKEN`. Una variable de entorno de proyecto (**Project
Settings → Environment Variables**) también sirve; en ese caso quita `context:` del ejemplo.

### 2. El job

Un `.circleci/config.yml` cuyas specs reparte `circleci tests split` entre las máquinas de
`parallelism`, en una ejecución creada al principio. Sin `parallelism`, deja solo el job `test` y
ejecuta `npx cypress run`:

```yaml
version: 2.1

jobs:
  create-run:
    docker:
      - image: cimg/node:22.12
    environment:
      PROBARA_PROJECT: SHOP
    steps:
      - run: |
          # Without the token (a fork's build), turn reporting off: run create prints nothing.
          [ -n "$PROBARA_API_TOKEN" ] || export PROBARA_ENABLED=false
          PROBARA_RUN_ULID=$(npx --yes @probara/cli@0.3 run create)
          echo "PROBARA_RUN_ULID=$PROBARA_RUN_ULID" > probara.env
      - persist_to_workspace:
          root: .
          paths: [probara.env]
  test:
    docker:
      - image: cimg/node:22.12-browsers
    parallelism: 4
    environment:
      PROBARA_PROJECT: SHOP
    steps:
      - checkout
      - attach_workspace:
          at: .
      - run: npm ci
      - run: |
          export $(cat probara.env)
          SPECS=$(circleci tests glob 'cypress/e2e/**/*.cy.js' | circleci tests split | paste -sd, -)
          npx cypress run --spec "$SPECS"
  close-run:
    docker:
      - image: cimg/node:22.12
    environment:
      PROBARA_PROJECT: SHOP
    steps:
      - attach_workspace:
          at: .
      - run:
          when: always
          command: |
            export $(cat probara.env)
            [ -n "$PROBARA_API_TOKEN" ] || export PROBARA_ENABLED=false
            npx --yes @probara/cli@0.3 run close

workflows:
  cypress:
    jobs:
      - create-run:
          context: probara-reporting
      - test:
          context: probara-reporting
          requires: [create-run]
      - close-run:
          context: probara-reporting
          requires:
            - test: [success, failed]
```

`circleci tests split` da a cada máquina su parte de los archivos de spec, y `--spec` los recibe como
una lista separada por comas. La imagen `-browsers` incluye los navegadores y las bibliotecas del
sistema que Cypress necesita.

### Forks y detección del CI

CircleCI no pasa contextos ni variables de proyecto a los builds de pull requests desde forks, salvo
que **Pass secrets to builds from forked pull requests** esté activado: déjalo desactivado. Los jobs
de un fork que usan un contexto no llegan a ejecutarse (CircleCI muestra un error de autorización).
Con variables del proyecto se ejecutan sin el token: el reporter no envía nada, y la línea
`PROBARA_ENABLED=false` de `create-run` y `close-run` hace que `run create` no imprima nada y que
ambos terminen con código 0.

Con `CIRCLECI=true`, la ejecución se llama `CIRCLE_PROJECT_REPONAME #CIRCLE_BUILD_NUM`, con la rama
`CIRCLE_BRANCH`, el commit `CIRCLE_SHA1` y la URL del build `CIRCLE_BUILD_URL`.

## Azure Pipelines

### 1. Añade la variable

**Pipelines → tu pipeline → Edit → Variables → New variable**: nombre `PROBARA_API_TOKEN`, el token
como valor, con **Keep this value secret** activado. Un grupo de variables de la biblioteca también
sirve. Las variables secretas nunca llegan solas a los scripts: asigna la variable en el `env` del
paso, como abajo.

### 2. El job

```yaml
variables:
  # Off in a fork's build, which gets no secrets (see forks below).
  PROBARA_ENABLED: $[ ne(variables['System.PullRequest.IsFork'], 'True') ]

pool:
  vmImage: ubuntu-latest

steps:
  - task: NodeTool@0
    inputs:
      versionSpec: '22.x'
  - script: npm ci
  - script: npx cypress run
    env:
      PROBARA_API_TOKEN: $(PROBARA_API_TOKEN)
      PROBARA_PROJECT: SHOP
```

`ubuntu-latest` incluye los navegadores y las bibliotecas del sistema que Cypress necesita.

### Jobs en paralelo: una ejecución

Una `matrix` de jobs que ejecutan cada uno una parte de las specs, en una ejecución que crea un
primer job y cierra un último job que se ejecuta incluso después de pruebas fallidas:

```yaml
variables:
  PROBARA_ENABLED: $[ ne(variables['System.PullRequest.IsFork'], 'True') ]

jobs:
  - job: create_run
    pool:
      vmImage: ubuntu-latest
    steps:
      - task: NodeTool@0
        inputs:
          versionSpec: '22.x'
      - script: |
          set -e
          ulid=$(npx --yes @probara/cli@0.3 run create)
          echo "##vso[task.setvariable variable=ulid;isOutput=true]$ulid"
        name: probara
        env:
          PROBARA_API_TOKEN: $(PROBARA_API_TOKEN)
          PROBARA_PROJECT: SHOP

  - job: test
    dependsOn: create_run
    strategy:
      matrix:
        cart:
          SPEC: 'cypress/e2e/cart/**'
        login:
          SPEC: 'cypress/e2e/login/**'
    variables:
      PROBARA_RUN_ULID: $[ dependencies.create_run.outputs['probara.ulid'] ]
    pool:
      vmImage: ubuntu-latest
    steps:
      - task: NodeTool@0
        inputs:
          versionSpec: '22.x'
      - script: npm ci
      - script: npx cypress run --spec '$(SPEC)'
        env:
          PROBARA_API_TOKEN: $(PROBARA_API_TOKEN)
          PROBARA_PROJECT: SHOP
          PROBARA_RUN_ULID: $(PROBARA_RUN_ULID)

  - job: close_run
    dependsOn: [create_run, test]
    condition: always()
    variables:
      PROBARA_RUN_ULID: $[ dependencies.create_run.outputs['probara.ulid'] ]
    pool:
      vmImage: ubuntu-latest
    steps:
      - task: NodeTool@0
        inputs:
          versionSpec: '22.x'
      - script: npx --yes @probara/cli@0.3 run close
        env:
          PROBARA_API_TOKEN: $(PROBARA_API_TOKEN)
          PROBARA_PROJECT: SHOP
          PROBARA_RUN_ULID: $(PROBARA_RUN_ULID)
```

### Forks y detección del CI

Los builds de pull requests desde forks no reciben variables secretas salvo que **Make secrets
available to builds of forks** esté activado: déjalo desactivado. Azure deja tal cual una macro que
no puede resolver, así que en el build de un fork `$(PROBARA_API_TOKEN)` llega al paso como ese
texto literal, un token que Probara rechaza con 401. La variable `PROBARA_ENABLED` del pipeline vale
`False` cuando `System.PullRequest.IsFork` es `True`, así que el build de un fork ejecuta sus pruebas
con el envío desactivado, y `run create` y `run close` terminan con código 0.

Con `TF_BUILD=True`, la ejecución se llama `BUILD_DEFINITIONNAME #BUILD_BUILDNUMBER`, y su rama, su
commit y la URL del build salen de las variables del pipeline.

## Bitbucket Pipelines

### 1. Añade la variable

**Repository settings → Pipelines → Repository variables**: nombre `PROBARA_API_TOKEN`, el token como
valor, con **Secured** activado. Bitbucket pasa las variables del repositorio a cada paso, así que el
reporter la encuentra en `npx cypress run`.

### 2. El pipeline

```yaml
image: cypress/browsers:latest

pipelines:
  default:
    - step:
        name: Cypress
        script:
          - npm ci
          - export PROBARA_PROJECT=SHOP
          - npx cypress run
```

### Pasos en paralelo: una ejecución

Los pasos en paralelo que ejecutan cada uno una parte de las specs reportan en una ejecución que crea
un primer paso y cierra un último paso:

```yaml
image: cypress/browsers:latest

pipelines:
  default:
    - step:
        name: Create the Probara run
        script:
          - export PROBARA_PROJECT=SHOP
          # Without the token (a fork's build), turn reporting off: run create prints nothing.
          - test -n "$PROBARA_API_TOKEN" || export PROBARA_ENABLED=false
          - PROBARA_RUN_ULID=$(npx --yes @probara/cli@0.3 run create)
          - echo "PROBARA_RUN_ULID=$PROBARA_RUN_ULID" > probara.env
        artifacts:
          - probara.env
    - parallel:
        - step:
            name: Cart
            script:
              - npm ci
              - export PROBARA_PROJECT=SHOP $(cat probara.env)
              - npx cypress run --spec 'cypress/e2e/cart/**' || touch cart.failed
            artifacts:
              - '*.failed'
        - step:
            name: Login
            script:
              - npm ci
              - export PROBARA_PROJECT=SHOP $(cat probara.env)
              - npx cypress run --spec 'cypress/e2e/login/**' || touch login.failed
            artifacts:
              - '*.failed'
    - step:
        name: Close the Probara run
        script:
          - export PROBARA_PROJECT=SHOP $(cat probara.env)
          - test -n "$PROBARA_API_TOKEN" || export PROBARA_ENABLED=false
          - npx --yes @probara/cli@0.3 run close
          - if ls *.failed >/dev/null 2>&1; then echo "Tests failed in a parallel step" >&2; exit 1; fi
```

Bitbucket no ejecuta ningún paso después de uno fallido, así que un paso en paralelo que fallara
dejaría la ejecución abierta. En su lugar, cada paso deja constancia de las pruebas fallidas en un
artefacto `<nombre>.failed`, y el último paso cierra la ejecución y luego hace fallar el pipeline si
algún paso dejó uno: los pasos en paralelo aparecen como correctos, y el fallo lo muestra el último
paso. Si aun así el cierre nunca se ejecutó, cierra la ejecución en Probara, o
[a mano](https://docs.probara.net/es/developers/ci-integrations/cypress/sharding/#si-el-cierre-nunca-se-ejecutó).

### Forks y detección del CI

Las pull requests desde forks no reciben variables protegidas (**Secured**): el reporter no envía
nada ahí. Con `BITBUCKET_BUILD_NUMBER` definida, la ejecución se llama
`BITBUCKET_REPO_SLUG #BITBUCKET_BUILD_NUMBER`, con la rama `BITBUCKET_BRANCH`, el commit
`BITBUCKET_COMMIT` y un enlace al pipeline.

## Buildkite

### 1. Añade el token

Expón `PROBARA_API_TOKEN` a los pasos desde un almacén de secretos: un hook `environment` del agente
que lo lea de tu gestor de secretos, o
[Buildkite secrets](https://buildkite.com/docs/pipelines/security/secrets/buildkite-secrets).

### 2. El pipeline

Un `pipeline.yml` cuyos jobs en paralelo reportan en una ejecución que crea un primer paso y que se
cierra después de ellos, aunque hayan fallado. Sin `parallelism`, basta con un paso que ejecute
`npx cypress run`:

```yaml
env:
  PROBARA_PROJECT: SHOP

steps:
  - label: Create the Probara run
    key: create-run
    command: |
      PROBARA_RUN_ULID="$(npx --yes @probara/cli@0.3 run create)"
      if [ -n "$$PROBARA_RUN_ULID" ]; then buildkite-agent meta-data set probara-run "$$PROBARA_RUN_ULID"; fi

  - label: Cypress
    depends_on: create-run
    parallelism: 4
    command: |
      npm ci
      export PROBARA_RUN_ULID="$(buildkite-agent meta-data get probara-run --default '')"
      SPECS=$(ls cypress/e2e/*.cy.js | awk -v i="$$BUILDKITE_PARALLEL_JOB" -v n="$$BUILDKITE_PARALLEL_JOB_COUNT" 'NR % n == i' | paste -sd, -)
      npx cypress run --spec "$$SPECS"

  - wait: ~
    continue_on_failure: true

  - label: Close the Probara run
    command: PROBARA_RUN_ULID="$(buildkite-agent meta-data get probara-run --default '')" npx --yes @probara/cli@0.3 run close
```

- `buildkite-agent pipeline upload` sustituye `$NAME` y `${NAME}` al subir el pipeline, y una
  variable que en ese momento no conoce, como `BUILDKITE_PARALLEL_JOB_COUNT`, que solo reciben los
  jobs en paralelo, queda vacía; un `$$` se deja como un `$` para la shell del job, mientras que
  `$(...)` y `$((...))` se dejan tal cual.
- La ejecución se crea en una línea propia, así que un `run create` fallido hace fallar el primer
  paso en lugar de guardar un ULID vacío. Con el envío desactivado (un build de un fork en un agente
  que define `PROBARA_ENABLED=false`, como se explica abajo), `run create` no imprime ningún ULID y
  termina con 0: no se guarda nada, `meta-data get --default ''` da a los demás pasos un ULID vacío
  en lugar de un error, y también se ejecutan con el envío desactivado.
- Cada job en paralelo toma uno de cada `n` archivos de spec (`BUILDKITE_PARALLEL_JOB` empieza en 0),
  y `--spec` los recibe como una lista separada por comas. Los agentes necesitan los navegadores y
  las bibliotecas del sistema que Cypress necesita.

Con `BUILDKITE=true`, la ejecución se llama `BUILDKITE_PIPELINE_SLUG #BUILDKITE_BUILD_NUMBER`, con la
rama `BUILDKITE_BRANCH`, el commit `BUILDKITE_COMMIT` y la URL del build `BUILDKITE_BUILD_URL`.

### Pull requests desde forks

Buildkite construye pull requests desde forks solo si el pipeline lo permite (desactivado por
defecto). Esos builds se ejecutan en tus agentes, y `pipeline upload` lee el propio `pipeline.yml`
del fork, que también elige la cola de los agentes. El código del fork se ejecuta en la shell del job
como el usuario del agente, así que en un agente que puede leer el token de tu almacén de secretos,
el código del fork también puede leerlo: ningún hook ni paso lo impide.

Mantén desactivados los builds de forks, o mantenlos lejos de esos agentes: su hook `environment`
rechaza un build de un fork (`BUILDKITE_PULL_REQUEST_REPO` lo identifica), y los forks solo se
construyen en una cola o un cluster aparte cuyos agentes no pueden leer el almacén de secretos:

```bash
# The environment hook of the agents that can read the token: no build of a fork runs there.
if [ -n "$BUILDKITE_PULL_REQUEST_REPO" ]; then
  case "${BUILDKITE_PULL_REQUEST_REPO#*://}" in
    github.com/acme/shop.git) ;;
    *)
      echo "Refusing a build of a fork: $BUILDKITE_PULL_REQUEST_REPO" >&2
      exit 1
      ;;
  esac
fi
export PROBARA_API_TOKEN="$(your-secrets-manager get probara-api-token)"
```

`BUILDKITE_PULL_REQUEST_REPO` está vacía fuera de las pull requests, y es una URL `https://` desde
2022 (antes, una `git://`), así que el hook la compara sin el esquema: pon tu repositorio en lugar de
`github.com/acme/shop.git`. Si no coincide, tus propias pull requests también fallan en estos agentes,
lo que la primera de ellas pondrá de manifiesto. En los agentes que construyen forks, define
`PROBARA_ENABLED=false` en su hook `environment`: las pruebas del fork se ejecutan y
`npx cypress run` no reporta nada, y `run create` y `run close` terminan con 0 sin token.

Relacionado: [Sharding y proyectos](https://docs.probara.net/es/developers/ci-integrations/cypress/sharding/) ·
[Archivo de resultados](https://docs.probara.net/es/developers/ci-integrations/cypress/results-and-retries/#archivo-de-resultados) ·
[Solución de problemas](https://docs.probara.net/es/developers/ci-integrations/cypress/troubleshooting/) · Fuente:
[docs/ci](https://github.com/cynch-me/probara-sdk/tree/main/packages/cypress-reporter/docs/ci)
