# API de GitHub

> Vincula issues de GitHub a casos de prueba, ejecuciones y defectos, crea issues desde defectos y lanza workflows de GitHub Actions a través de la API para integradores.

Estos endpoints trabajan con la [integración con GitHub](https://docs.probara.net/es/guides/integrations/github/) de la
organización: las cuentas de GitHub donde está instalada la GitHub App de Probara. Instalar la
aplicación, desconectar una cuenta, cambiar los repositorios de un proyecto y su ajuste de
comentarios en pull requests requieren una sesión iniciada; con un token de API responden
`403 forbidden`. Todo lo demás funciona con un [token de API](https://docs.probara.net/es/developers/authentication/) y con
las [herramientas MCP](https://docs.probara.net/es/developers/mcp-tools/).

Todos los endpoints necesitan el plan Pro o Enterprise; en otros planes responden `403 forbidden`. Un
`{projectId}` es el **código** del proyecto, por ejemplo `SHOP`. Los repositorios se escriben
`propietario/nombre`.

## Repositorios que puede usar un proyecto

Un proyecto ofrece los repositorios de su **vinculación**. Si la vinculación está vacía, ofrece todos
los repositorios que ven las instalaciones de la organización. Cada endpoint de esta página que
recibe un `repository` lo comprueba con esa regla.

| Método y ruta                                                                | Qué hace                                                                                                                                                  |
| ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET /api/v1/projects/{projectId}/integrations/github/repositories`          | La vinculación del proyecto: `items` con `connectionUlid`, `repositoryId`, `repositoryFullName` e `isDefault`. Vacía si no hay vinculación.               |
| `GET /api/v1/projects/{projectId}/integrations/github/linkable-repositories` | Los repositorios que ofrece el proyecto ahora, haya vinculación o no: `connectionUlid`, `repositoryId`, `fullName` e `isDefault`.                         |
| `PUT /api/v1/projects/{projectId}/integrations/github/repositories`          | Sustituye la vinculación por `{ "repositories": [{ "connectionUlid", "repositoryId", "isDefault" }] }`. Solo con sesión, `integrations.manage`.           |
| `GET /api/v1/projects/{projectId}/integrations/github/settings`              | `{ "pullRequestComments": "on_failure" }`: cuándo las ejecuciones de GitHub Actions cerradas comentan en su pull request: `always`, `on_failure` u `off`. |
| `PUT /api/v1/projects/{projectId}/integrations/github/settings`              | Cambia `pullRequestComments` a `always`, `on_failure` u `off`. Solo con sesión, `integrations.manage`.                                                    |

## Vincular issues

Los mismos cuatro endpoints existen para casos de prueba, ejecuciones y defectos. `{entity}` es
`test-cases/{caseUlid}`, `runs/{runUlid}` o `defects/{defectUlid}`.

| Método y ruta                                            | Qué hace                                                            |
| -------------------------------------------------------- | ------------------------------------------------------------------- |
| `GET /api/v1/{entity}/github-issues`                     | Lista los vínculos de la entidad.                                   |
| `POST /api/v1/{entity}/github-issues/link`               | Vincula un issue. Responde `201` con el vínculo.                    |
| `POST /api/v1/{entity}/github-issues/{linkUlid}/refresh` | Vuelve a leer el issue en GitHub y responde el vínculo actualizado. |
| `DELETE /api/v1/{entity}/github-issues/{linkUlid}`       | Quita el vínculo. El issue sigue en GitHub. Responde `204`.         |

```json
{
  "connectionUlid": "01J9Z3K4M5N6P7Q8R9S0T1V2W3",
  "repository": "acme/shop",
  "issueNumber": 42,
  "linkKind": "requirement"
}
```

`linkKind` es `requirement` o `defect`, y por defecto vale `requirement` en un caso de prueba y
`defect` en una ejecución o un defecto. Solo los vínculos `defect` de un defecto sincronizan el
estado del defecto con el issue; los vínculos `requirement` de los casos de prueba son lo que los
informes de trazabilidad de GitHub cuentan como cobertura.

Un vínculo incluye `repositoryFullName`, `issueNumber`, `linkKind` y los datos del issue tal como
GitHub los devolvió por última vez: `title`, `state` (`open` o `closed`), `stateReason`, `labels`,
`authorLogin`, `htmlUrl` e `issueUpdatedAt`, junto con `syncedAt`, el momento en que Probara los
leyó. Estos datos guardados se devuelven aunque GitHub no responda, y se actualizan con cada cambio
del que informa GitHub.

Para leer vínculos hace falta acceso a la entidad. Para cambiarlos hace falta `test-cases.write` en
un caso de prueba, `runs.execute` en una ejecución y `defects.write` en un defecto.

### Buscar issues de un repositorio

`GET /api/v1/projects/{projectId}/integrations/github/issues/search?connectionUlid=…&repository=acme/shop&q=checkout`
busca issues del repositorio por palabras del título, o lee uno por número con `q=42` o `q=#42`. `q`
admite hasta 200 caracteres y se busca como texto literal: no puede añadir calificadores de búsqueda
de GitHub. Devuelve hasta 20 issues, nunca pull requests.

## Crear un issue desde un defecto

| Método y ruta                                                 | Qué hace                                                                                                                            |
| ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `GET /api/v1/defects/{defectUlid}/github-issues/draft`        | El `title` y el `body` precargados. Añade `?resultUlid=` (un resultado vinculado al defecto) para precargarlos desde ese resultado. |
| `GET /api/v1/projects/{projectId}/integrations/github/labels` | Las etiquetas de un repositorio (`connectionUlid`, `repository`), con `name`, `color` y `description`.                              |
| `POST /api/v1/defects/{defectUlid}/github-issues`             | Crea el issue y lo vincula al defecto como `defect`.                                                                                |

```json
{
  "connectionUlid": "01J9Z3K4M5N6P7Q8R9S0T1V2W3",
  "repository": "acme/shop",
  "resultUlid": "01J9Z3M7Q2R4S6T8V0W2X4Y6Z8",
  "labels": ["bug"]
}
```

`title` (hasta 256 caracteres) y `body` (Markdown de GitHub, hasta 65 536 caracteres) son
opcionales: si no se envían, se usa el borrador, cuyo cuerpo es la descripción del defecto en Markdown, con cada imagen en su URL de recurso almacenada. `labels` admite hasta 20 nombres del repositorio.
Necesita `defects.write`.

La creación se envía a GitHub una sola vez. Responde `201` con el vínculo nuevo, o `200` cuando un
reintento encuentra el issue que abrió una petición anterior. Si se pierde la respuesta de GitHub,
la petición responde `409 github_issue_creation_unconfirmed` con `Retry-After`: reintenta pasado ese
tiempo y Probara buscará el issue antes de abrir otro.

## Lanzar un workflow

| Método y ruta                                                    | Qué hace                                                                                       |
| ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `GET /api/v1/projects/{projectId}/integrations/github/workflows` | Los workflows activos del repositorio (`connectionUlid`, `repository`): `id`, `name` y `path`. |
| `GET /api/v1/projects/{projectId}/integrations/github/branches`  | Las ramas del repositorio, hasta 500.                                                          |
| `POST /api/v1/projects/{projectId}/integrations/github/launches` | Crea una ejecución automatizada e inicia el workflow en una rama.                              |

```json
{
  "connectionUlid": "01J9Z3K4M5N6P7Q8R9S0T1V2W3",
  "repository": "acme/shop",
  "ref": "main",
  "workflowId": "probara-e2e.yml",
  "runName": "Nightly E2E"
}
```

`workflowId` es un id de la lista de workflows o el nombre del archivo del workflow. `runName` es,
por defecto, el nombre del workflow y la rama. `inputs` añade hasta 24 inputs de texto más de
`workflow_dispatch`; Probara siempre envía el ULID de la ejecución como `probara_run_id`. El contrato
del workflow está en
[Ejecutar pruebas con GitHub Actions](https://docs.probara.net/es/guides/integrations/github-actions/#2-prepara-el-workflow).
Necesita `runs.execute` y acepta un `Idempotency-Key`.

- `201`: GitHub aceptó el inicio. El cuerpo es la ejecución, con `githubBuild.launchStatus`
  `dispatched`.
- `202`: se perdió la respuesta de GitHub. La ejecución se conserva con `launchStatus`
  `unconfirmed`; su build se completa si el workflow llegó a iniciarse.
- Un rechazo (`github_workflow_*`, `github_ref_not_found`) no deja ninguna ejecución.

`GET /api/v1/runs/{runUlid}` devuelve `githubBuild` en una ejecución lanzada (`null` en cualquier
otra): `launchStatus`, `repository`, `ref`, `workflowName`, `htmlUrl`, `status`, `conclusion`,
`runAttempt`, `startedAt` y `completedAt`. Los campos del build valen `null` hasta que GitHub
informa de la ejecución del workflow. Probara nunca cierra la ejecución a partir del build: la cierra
el job final del workflow.

## Fuentes de trazabilidad

Las conexiones de GitHub aparecen en `GET /api/v1/projects/{projectId}/traceability/sources` con
`provider: "github"`. Los repositorios, las etiquetas y los hitos que puede usar un informe están en
`/sources/{connectionUlid}/repositories`, `/labels?repository=` y `/milestones?repository=`.
Consulta la [API de informes de trazabilidad](https://docs.probara.net/es/developers/api/traceability-reports/#informes-de-github).

## Errores

Todos los errores usan el [formato de error estándar](https://docs.probara.net/es/developers/api-reference/#errores).

| Estado | Código                              | Cuándo                                                                                                          |
| ------ | ----------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `403`  | `forbidden`                         | El plan no incluye integraciones, falta el permiso, o una ruta solo con sesión recibió un token.                |
| `403`  | `github_repository_not_mapped`      | La vinculación del proyecto deja fuera el repositorio.                                                          |
| `404`  | `github_issue_not_found`            | El repositorio no tiene ningún issue con ese número.                                                            |
| `404`  | `github_workflow_not_found`         | El repositorio no tiene ese workflow.                                                                           |
| `409`  | `github_issue_already_linked`       | La entidad ya vincula ese issue.                                                                                |
| `409`  | `github_connection_stale`           | La instalación se suspendió o se desinstaló en GitHub.                                                          |
| `409`  | `github_issue_creation_in_progress` | Otra petición está creando el issue de este defecto en ese repositorio.                                         |
| `409`  | `github_issue_creation_unconfirmed` | Se perdió la respuesta de GitHub a una creación anterior. Reintenta pasado `Retry-After`.                       |
| `422`  | `github_repository_not_visible`     | La instalación no tiene acceso al repositorio.                                                                  |
| `422`  | `github_issue_is_pull_request`      | El número corresponde a un pull request.                                                                        |
| `422`  | `github_issues_disabled`            | Los issues están desactivados en el repositorio.                                                                |
| `422`  | `github_issue_rejected`             | GitHub rechazó el issue nuevo, por ejemplo por una etiqueta desconocida.                                        |
| `422`  | `github_workflow_not_dispatchable`  | El workflow está desactivado o no tiene el disparador `workflow_dispatch`.                                      |
| `422`  | `github_ref_not_found`              | El repositorio no tiene esa rama.                                                                               |
| `422`  | `github_workflow_missing_run_input` | El workflow no declara el input `probara_run_id` en `workflow_dispatch`.                                        |
| `422`  | `github_workflow_dispatch_failed`   | GitHub rechazó el inicio, por ejemplo por un input no declarado. `details.message` incluye el motivo de GitHub. |
| `422`  | `validation_failed`                 | El cuerpo o un parámetro de consulta no es válido.                                                              |
| `429`  | `too_many_requests`                 | Se alcanzó el límite de peticiones a GitHub de la organización. `Retry-After` indica cuándo reintentar.         |
| `503`  | `github_app_not_configured`         | La GitHub App no está configurada en este entorno.                                                              |
| `503`  | `github_unavailable`                | GitHub no responde en este momento.                                                                             |
