# Sharding de Cypress y varios proyectos

> Una sola ejecución de Probara para todas las máquinas de un pipeline de Cypress repartido, sea cual sea la herramienta que reparte las specs, y cómo reportar una batería de Cypress en varios proyectos de Probara.

Fuera de GitHub Actions, cada `cypress run` crea su propia ejecución por defecto, así que cuatro
máquinas que ejecutan cada una una parte de las specs crean cuatro ejecuciones. En GitHub Actions los
jobs de un mismo workflow comparten una ejecución automáticamente
([jobs en paralelo](https://docs.probara.net/es/developers/ci-integrations/cypress/ci-providers/#jobs-en-paralelo-una-ejecución-cerrada-por-el-job-final)).
En otros entornos, esta página muestra cómo tener **una** ejecución
para todo un pipeline, y cómo una batería de Cypress reporta en varios proyectos de Probara.

## Una ejecución para todas las máquinas

| Patrón                                                                                                               | Elígelo cuando                                                                |
| -------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| [Crea la ejecución, reporta desde cada máquina y ciérrala](#crea-la-ejecución-reporta-desde-cada-máquina-y-ciérrala) | La forma habitual: los resultados de cada máquina aparecen en cuanto termina  |
| [Archivos de resultados importados por un job](#alternativa-archivos-de-resultados-importados-por-un-job)            | Las máquinas no pueden tener el token, o un solo job debe hacer todo el envío |

Cada job que reporta necesita `PROBARA_API_TOKEN`, un token de aplicación de la tarjeta **Cypress**
en **Workspace › Integraciones**, configurado como en la
[sección de tu proveedor de CI](https://docs.probara.net/es/developers/ci-integrations/cypress/ci-providers/).

### Cómo se reparten las specs

Cypress no tiene opción `--shard`: cada máquina ejecuta su propio `cypress run`, y algo decide qué
specs ejecuta. El reporter no depende de qué:

| Herramienta                                                                                                       | Qué ejecuta cada máquina                                                                                                                                                            |
| ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--spec`                                                                                                          | Las specs que indicas, como `npx cypress run --spec 'cypress/e2e/cart/**'`                                                                                                          |
| [Cypress Cloud](https://docs.cypress.io/cloud/features/smart-orchestration/parallelization) `--record --parallel` | Las specs que le entrega Cypress Cloud                                                                                                                                              |
| Un plugin como [cypress-split](https://github.com/bahmutov/cypress-split)                                         | Su parte de las specs; regístralo [con cypress-on-fix](https://docs.probara.net/es/developers/ci-integrations/cypress/configuration/#con-otros-plugins), porque también registra `after:spec` y `after:run` |

La ejecución propia de Cypress Cloud (`--ci-build-id`, `--group`) es un concepto de Cypress Cloud:
el reporter ni la lee ni la necesita. Lo que hace que las máquinas reporten en una sola ejecución de
Probara es `PROBARA_RUN_ULID`, el mismo en todas las máquinas.

### Crea la ejecución, reporta desde cada máquina y ciérrala

1. Un primer job ejecuta `probara run create`, que crea una ejecución automatizada vacía e imprime su
   ULID en stdout.
2. Cada máquina ejecuta `npx cypress run` (con su parte de las specs) con ese ULID en
   `PROBARA_RUN_ULID`. Un `cypress run` nunca cierra una ejecución que no creó, así que todas
   reportan en la ejecución abierta. Probara concilia las máquinas que reportan a la vez: ningún caso
   se añade dos veces a la ejecución.
3. Un último job ejecuta `probara run close`, después de todas las máquinas, **incluso si fallaron
   pruebas**.

En una shell, el flujo completo es:

```bash
PROBARA_RUN_ULID=$(npx @probara/cli run create)
export PROBARA_RUN_ULID
npx cypress run --spec 'cypress/e2e/cart/**'
npx cypress run --spec 'cypress/e2e/login/**'
npx @probara/cli run close
```

Asigna el ULID en una línea propia: `export PROBARA_RUN_ULID=$(...)` ocultaría un fallo al crearla.
En CI, los jobs de abajo fijan la versión de la CLI (`npx --yes @probara/cli@0.3`), que no es una
dependencia del reporter; con `@probara/cli` en tus `devDependencies`, `npx @probara/cli` después de
`npm ci` ejecuta la versión del lockfile. `probara` es la [CLI de Probara](https://docs.probara.net/es/developers/cli/); usa
el mismo token y el mismo proyecto. Las opciones de una ejecución nueva van a `run create`
(`--run-name`, `--environment`, `--milestone`, `--plan`, `--tag`...), porque las máquinas reutilizan
la ejecución e ignoran sus propias opciones de ejecución.

Cada máquina envía sus resultados cuando termina su propio `cypress run`, así que la ejecución se
completa máquina a máquina, no prueba a prueba.

### GitHub Actions

```yaml
jobs:
  probara-run:
    runs-on: ubuntu-latest
    outputs:
      ulid: ${{ steps.create.outputs.ulid }}
    env:
      PROBARA_API_TOKEN: ${{ secrets.PROBARA_API_TOKEN }}
      PROBARA_PROJECT: SHOP
    steps:
      - uses: actions/setup-node@v5
        with:
          node-version: 22
      - id: create
        run: |
          ulid=$(npx --yes @probara/cli@0.3 run create)
          echo "ulid=$ulid" >> "$GITHUB_OUTPUT"

  test:
    needs: probara-run
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        spec: ['cypress/e2e/cart/**', 'cypress/e2e/login/**', 'cypress/e2e/search/**']
    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
          PROBARA_RUN_ULID: ${{ needs.probara-run.outputs.ulid }}

  close-probara-run:
    needs: [probara-run, test]
    if: always() && needs.probara-run.outputs.ulid != ''
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-node@v5
        with:
          node-version: 22
      - run: npx --yes @probara/cli@0.3 run close
        env:
          PROBARA_API_TOKEN: ${{ secrets.PROBARA_API_TOKEN }}
          PROBARA_PROJECT: SHOP
          PROBARA_RUN_ULID: ${{ needs.probara-run.outputs.ulid }}
```

Sin el token (un fork), `run create` no imprime nada y sale con 0, las máquinas no reportan nada, y
el job de cierre se omite. Con Cypress Cloud, sustituye la matriz de specs por
`containers: [1, 2, 3]` y ejecuta `npx cypress run --record --parallel` en cada una: la parte de
`PROBARA_RUN_ULID` no cambia.

### GitLab CI

Un artefacto dotenv entrega el ULID a cada job posterior:

```yaml
stages:
  - test

create-probara-run:
  stage: .pre
  image: node:22
  variables:
    PROBARA_PROJECT: SHOP
  rules:
    - if: $PROBARA_API_TOKEN
  script:
    - PROBARA_RUN_ULID=$(npx --yes @probara/cli@0.3 run create)
    - echo "PROBARA_RUN_ULID=$PROBARA_RUN_ULID" > probara.env
  artifacts:
    reports:
      dotenv: probara.env

cypress:
  stage: test
  image: cypress/browsers:latest
  parallel:
    matrix:
      - SPEC: ['cypress/e2e/cart/**', 'cypress/e2e/login/**', 'cypress/e2e/search/**']
  variables:
    PROBARA_PROJECT: SHOP
  script:
    - npm ci
    - npx cypress run --spec "$SPEC"

close-probara-run:
  stage: .post
  image: node:22
  variables:
    PROBARA_PROJECT: SHOP
  rules:
    - if: $PROBARA_API_TOKEN
      when: always
  script:
    - npx --yes @probara/cli@0.3 run close
```

`.pre` y `.post` son las etapas inicial y final integradas de GitLab; `when: always` cierra la
ejecución incluso después de pruebas fallidas.

### Otros proveedores de CI

Jenkins, CircleCI, Azure Pipelines, Bitbucket Pipelines y Buildkite siguen los mismos tres pasos; sus
pipelines completos están en [Proveedores de CI](https://docs.probara.net/es/developers/ci-integrations/cypress/ci-providers/).

### Alternativa: archivos de resultados importados por un job

Cada máquina se ejecuta con el envío desactivado y escribe cada resultado en un
[archivo de resultados](https://docs.probara.net/es/developers/ci-integrations/cypress/results-and-retries/#archivo-de-resultados),
que se guarda como artefacto con su carpeta de adjuntos. Un último job, que tiene el token, los
descarga todos y los envía.

Un archivo de resultados escrito con el envío desactivado no nombra ninguna ejecución: importado por
separado, cada archivo crea su propia ejecución. Para tener una sola, créala primero, importa en ella
cada archivo y ciérrala:

```yaml
jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        shard: [cart, login, search]
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-node@v5
        with:
          node-version: 22
      - run: npm ci
      - run: npx cypress run --spec 'cypress/e2e/${{ matrix.shard }}/**'
        env:
          PROBARA_ENABLED: 'false'
          PROBARA_PROJECT: SHOP
          PROBARA_RESULTS_FILE: probara-results-${{ matrix.shard }}.json
      - uses: actions/upload-artifact@v4
        if: ${{ !cancelled() }}
        with:
          name: probara-results-${{ matrix.shard }}
          path: probara-results-*
          retention-days: 1

  report:
    needs: test
    # Forks get no token: there is nothing to send with.
    if: ${{ !cancelled() && github.event.pull_request.head.repo.fork != true }}
    runs-on: ubuntu-latest
    env:
      PROBARA_API_TOKEN: ${{ secrets.PROBARA_API_TOKEN }}
      PROBARA_PROJECT: SHOP
    steps:
      - uses: actions/setup-node@v5
        with:
          node-version: 22
      - uses: actions/download-artifact@v4
        with:
          pattern: probara-results-*
          merge-multiple: true
      - run: |
          PROBARA_RUN_ULID=$(npx --yes @probara/cli@0.3 run create)
          export PROBARA_RUN_ULID
          npx --yes @probara/cli@0.3 import results 'probara-results*.json'
          npx --yes @probara/cli@0.3 run close
```

- Da a cada máquina su propio nombre de archivo (`probara-results-<shard>.json`), para que las
  descargas nunca se sobrescriban entre sí.
- `probara import results` envía cada archivo a `PROBARA_RUN_ULID`, deja esa ejecución abierta
  (porque la reutilizó) y elimina cada archivo una vez enviado. `run close` la cierra después.
- Las capturas y los vídeos se nombran en el archivo por su ruta en la máquina de pruebas, sin
  copiarse: el job de envío, que nunca los tuvo, no puede enviarlos. Usa la ejecución compartida de
  arriba cuando te importen.
- No llega nada a Probara hasta el último job: una máquina que se cae no deja archivo de resultados,
  y sus resultados faltan.
- Volver a ejecutar el job de envío ejecuta `run create` de nuevo: los archivos, descargados otra vez
  tal como estaban, van a una segunda ejecución con los mismos resultados. Cierra o elimina la que no
  quieras conservar.

### Si el cierre nunca se ejecutó

Una ejecución queda abierta hasta que se cierra. Ciérrala a mano, con el ULID del registro del job
que la creó:

```bash
npx @probara/cli run close --run-ulid 01J9Z3K4M5N6P7Q8R9S0T1V2W3
```

### Qué enfoque pierde menos

| Algo sale mal                                     | Ejecución compartida (crear, máquinas, cerrar)                                        | Archivos de resultados, una importación                                  |
| ------------------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| Una máquina se cae antes de terminar su ejecución | Sus resultados se pierden: se envían cuando termina su `cypress run`                  | Falta su archivo: no se envía ninguno de sus resultados                  |
| Probara no está disponible                        | El archivo de resultados de cada máquina guarda lo que no pudo enviar (si se definió) | La importación sale con 1 y guarda en cada archivo lo que no pudo enviar |
| El job de cierre no se ejecuta                    | La ejecución queda abierta: [ciérrala a mano](#si-el-cierre-nunca-se-ejecutó)         | Igual                                                                    |

## Varios proyectos de Probara

Una batería de Cypress puede contener las pruebas de varios proyectos de Probara. Indica los demás
proyectos en `projects`, y cada resultado va al proyecto de su caso, en una ejecución de ese
proyecto.

```js
reporterOptions: { projectId: 'SHOP', projects: ['WEB'] },
```

`PROBARA_PROJECTS=WEB` los indica desde el entorno.

### Qué va a dónde

- **Por el proyecto del caso.** Un resultado vinculado a `WEB-3` va a una ejecución de `WEB`. Los IDs
  de cada proyecto indicado también vinculan desde los títulos y salen de la clave, como los del
  proyecto configurado.
- **Una prueba vinculada a casos de varios proyectos** se envía una vez por caso, cada una a su
  proyecto, con sus archivos subidos a cada resultado.
- **Una prueba sin caso** va al proyecto configurado (`projectId`), donde se asocia por su clave o se
  crea: solo el proyecto configurado crea casos, bajo `suiteUlid`.
- **Un ID de un proyecto que no está indicado**, en un título, no es un ID: se queda en el título y en
  la clave, y la prueba va al proyecto configurado. Pasado a `probara.id()`, es un caso que el
  reporter no puede enviar a ninguna parte: se omite, y la línea `Sending` lo cuenta.

```text
[probara] Sending 3 results of 3 tests (3 passed, 0 failed, 0 skipped, 0 blocked); 1 linked only to cases of unlisted projects
```

Un proyecto de Cypress (el bloque `e2e` o `component` de `defineConfig`) no dice nada sobre a dónde
va un resultado: dos configuraciones que reportan en el mismo proyecto de Probara producen las mismas
claves, y por tanto los mismos casos.

### Una ejecución por proyecto

Cada proyecto indicado tiene su propia ejecución, creada con su primer resultado (un proyecto sin
resultados no tiene ejecución), con el mismo nombre, descripción, entorno (por nombre), etiquetas y
origen de CI que la del proyecto configurado, y cerrada al final como cualquier ejecución que crea el
reporter. La línea `Recorded` nombra el proyecto cuando hay más de uno: `in R-1 of WEB`.

El hito, el plan y las configuraciones son entidades de planificación de un proyecto: un nombre que
existe en `SHOP` puede no existir en `WEB`, donde Probara rechazaría todo el informe. Por eso, como
las formas ULID del entorno, el hito y las configuraciones, solo acompañan a la ejecución del
proyecto configurado, y una advertencia los nombra. Para dar los suyos a la ejecución de otro
proyecto, créala primero y reutilízala:

```bash
WEB_RUN=$(npx @probara/cli run create --project WEB --milestone "Web sprint 4")
PROBARA_RUN_ULIDS="WEB=$WEB_RUN" PROBARA_PROJECTS=WEB npx cypress run
```

`run.ulids` (`PROBARA_RUN_ULIDS=SHOP=<ulid>,WEB=<ulid>`) nombra la ejecución que se reutiliza en cada
proyecto; la entrada del proyecto configurado cuenta como `run.ulid`. Una ejecución reutilizada queda
abierta salvo que `closeRun` sea `true`: ciérrala con
`probara run close --project WEB --run-ulid <ulid>`.

### Pipelines repartidos con varios proyectos

Crea una ejecución por proyecto antes de las máquinas, pásalas todas en `PROBARA_RUN_ULIDS` y
ciérralas después de la última máquina:

```bash
SHOP_RUN=$(npx @probara/cli run create)
WEB_RUN=$(npx @probara/cli run create --project WEB)
export PROBARA_PROJECTS=WEB PROBARA_RUN_ULIDS="SHOP=$SHOP_RUN,WEB=$WEB_RUN"
npx cypress run --spec 'cypress/e2e/cart/**'
npx @probara/cli run close --run-ulid "$SHOP_RUN"
npx @probara/cli run close --project WEB --run-ulid "$WEB_RUN"
```

Cuando se reutiliza la ejecución del proyecto configurado pero un proyecto indicado no tiene
ejecución en `run.ulids`, cada máquina crea su propia ejecución en ese proyecto, y una advertencia lo
dice. Un informe fallido solo detiene su proyecto: los demás siguen.

Relacionado: [Proveedores de CI](https://docs.probara.net/es/developers/ci-integrations/cypress/ci-providers/) ·
[Resultados y reintentos](https://docs.probara.net/es/developers/ci-integrations/cypress/results-and-retries/) ·
[CLI](https://docs.probara.net/es/developers/cli/) · Fuente:
[ci/sharding.md](https://github.com/cynch-me/probara-sdk/blob/main/packages/cypress-reporter/docs/ci/sharding.md),
[projects.md](https://github.com/cynch-me/probara-sdk/blob/main/packages/cypress-reporter/docs/projects.md)
