# GitHub Actions

La Action compuesta, sus tres modos y las dos cosas que un runner alojado no conserva.

Source: https://jmrplens.github.io/ghchronicle/es/install/actions/

El repositorio incluye una Action compuesta, así que un workflow no necesita
cadena de herramientas de Go: descarga un binario de release y lo llama.

```yaml
- uses: jmrplens/ghchronicle@v1
  with:
    token: ${{ secrets.GHCHRONICLE_TOKEN }}
    mode: once
    config: .github/ghchronicle.yaml
```

## Entradas

| Entrada           | Por omisión              | Qué hace                                                                                                                                              |
| ----------------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `token`           | obligatoria              | Un token personal. El `GITHUB_TOKEN` automático no basta                                                                                              |
| `config`          | `""`                     | Ruta a un fichero de configuración. Omítela para usar valores por defecto a partir de `user`                                                          |
| `user`            | el dueño del repositorio | La cuenta que recoger cuando no se da fichero de configuración                                                                                        |
| `mode`            | `once`                   | `once`, `backfill` o `card`                                                                                                                           |
| `backfill-since`  | `""`                     | Límite para `backfill`: una fecha, `90d`, `2y` o una duración de Go. Vacío es sin límite                                                              |
| `card`            | `""`                     | Ruta del SVG que escribir. Vacío significa sin tarjeta                                                                                                |
| `card-layout`     | `summary`                | Uno de los trece diseños registrados                                                                                                                   |
| `card-theme`      | `auto`                   | `dark`, `light`, `auto`, o `both` para una tarjeta clara y su gemela `_dark`                                                                          |
| `card-fields`     | `""`                     | Campos separados por comas. Vacío es el conjunto por defecto del diseño                                                                               |
| `card-motion`     | `once`                   | `once`, `loop` u `off`; `loop` solo cambia `terminal` y `ticker`                                                                                                     |
| `card-width`      | `""`                     | Ancho de la tarjeta en píxeles. Vacío dibuja el diseño con su propio ancho; cada uno dibuja entre dos extremos propios, que dice su [sección](/ghchronicle/es/card/layouts/). Solo `activity-heatmap` se gasta el sitio en datos, un año entero del calendario en su extremo lejano |
| `card-speed`      | `""`                     | A qué velocidad se reproduce un diseño animado, como decimal de 0 a 1. Vacío significa 0.5, el ritmo con el que siempre se han dibujado las tarjetas; por debajo la tarjeta va más lenta y por encima más rápida, y todos los diseños animados se escalan juntos. 0 es la animación más lenta y no una tarjeta quieta, eso es `card-motion: off` |
| `include-private` | `false`                  | `true` cuenta los repositorios privados cuando no se da fichero de configuración (el comportamiento por defecto hasta v1.0.0). Ver el aviso más abajo |
| `version`         | `latest`                 | La release que instalar                                                                                                                               |

## Los tres modos

- **once**

  Una pasada contra un fichero de configuración que tú das, que es como un
  workflow alimenta una base de datos.

  ```yaml
  name: Collect
  on:
    schedule:
      - cron: "*/30 * * * *"
    workflow_dispatch:

  jobs:
    collect:
      runs-on: ubuntu-latest
      steps:
        - uses: actions/checkout@v7
        - uses: jmrplens/ghchronicle@v1
          with:
            token: ${{ secrets.GHCHRONICLE_TOKEN }}
            mode: once
            config: .github/ghchronicle.yaml
          env:
            INFLUX_TOKEN: ${{ secrets.INFLUX_TOKEN }}
  ```

  El fichero de configuración referencia las credenciales de la base de datos
  como `${VAR}`, así que entran como secretos y nunca al repositorio.

- **card**

  Una pasada y un SVG, y nada escrito en ninguna base de datos. Este es el
  único modo que no necesita almacén alguno configurado.

  ```yaml
  name: Profile card
  on:
    schedule:
      - cron: "17 6 * * *"
    workflow_dispatch:

  permissions:
    contents: write

  jobs:
    card:
      runs-on: ubuntu-latest
      steps:
        - uses: actions/checkout@v7
        - uses: jmrplens/ghchronicle@v1
          with:
            token: ${{ secrets.GHCHRONICLE_TOKEN }}
            mode: card
            card: generated/github-stats.svg
            card-layout: github-stats
            card-theme: both
        - name: Commit if it changed
          run: |
            git config user.name "github-actions[bot]"
            git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
            git add generated/
            git diff --staged --quiet || git commit -m "Update the profile card"
            git push
  ```

  El SVG es idéntico byte a byte para la misma entrada, así que un día sin
  cambios no produce ningún commit.

- **backfill**

  Llega tan atrás como GitHub permita y espera al límite de peticiones en vez
  de detenerse. Ejecútalo una vez, a mano.

  ```yaml
  name: Backfill
  on:
    workflow_dispatch:
      inputs:
        since:
          description: "A date, 90d, 2y, or empty for no bound"
          default: "2y"

  jobs:
    backfill:
      runs-on: ubuntu-latest
      timeout-minutes: 360
      steps:
        - uses: actions/checkout@v7
        - uses: jmrplens/ghchronicle@v1
          with:
            token: ${{ secrets.GHCHRONICLE_TOKEN }}
            mode: backfill
            backfill-since: ${{ inputs.since }}
            config: .github/ghchronicle.yaml
  ```

  Dale un `timeout-minutes` generoso: un relleno sin límite aparca en cada
  reinicio de la ventana, y un job que muere a medias ha gastado la cuota y se
  ha quedado con parte del beneficio.

## Una tarjeta en el README de tu perfil

GitHub muestra en lo alto de tu perfil el README del repositorio que se llama
como tu cuenta, `<tú>/<tú>`. La tarjeta vive en ese repositorio como un fichero,
el README apunta a ella una vez y un workflow programado sustituye el fichero.
El README no se reescribe nunca.

1. Crea un token personal (ver [el token](/ghchronicle/es/start/token/)) y
   guárdalo en `<tú>/<tú>` como el secreto `GHCHRONICLE_TOKEN`. El
   `GITHUB_TOKEN` automático no sirve, ni siquiera para números públicos: no es
   un usuario, así que la Action no puede listar tus repositorios con él y la
   tarjeta sale vacía.
2. Añade `.github/workflows/card.yml`:

   ```yaml
   name: Profile card
   on:
     schedule:
       - cron: "17 6 * * *"
     workflow_dispatch:

   permissions:
     contents: write

   # One run at a time: two that overlap would race each other to push.
   concurrency:
     group: profile-card
     cancel-in-progress: false

   jobs:
     card:
       runs-on: ubuntu-latest
       steps:
         - uses: actions/checkout@v7
         - uses: jmrplens/ghchronicle@v1
           with:
             token: ${{ secrets.GHCHRONICLE_TOKEN }}
             mode: card
             card: generated/card.svg
             card-layout: animated-counters
             card-theme: both
             card-motion: once
         - name: Commit if it changed
           run: |
             git config user.name "github-actions[bot]"
             git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
             git add generated/
             git diff --staged --quiet || git commit -m "Update the profile card"
             git push
   ```

3. Pega esta línea en `README.md` donde deba aparecer la tarjeta, una sola vez:

   ```html
   <picture><source media="(prefers-color-scheme: dark)" srcset="generated/card_dark.svg"><img src="generated/card.svg" alt="Mis estadísticas de GitHub"></picture>
   ```

4. Ejecuta el workflow a mano desde la pestaña Actions la primera vez, para que
   los ficheros existan antes de la primera ejecución programada.

**Más de una tarjeta.** Repite el paso `uses: jmrplens/ghchronicle@v1` con otra
ruta en `card` y otro diseño, y pega un `<picture>` por tarjeta. Cada paso es una
pasada propia, y cada una vuelve a pedir todos los números: una pasada que
escribe una tarjeta recoge todas las familias diga lo que diga su cadencia, y
una en modo `card` no escribe nada en el fichero de estado. Así que varios pasos
de tarjeta pueden compartir un `config`, y por tanto un `state_file`, y cada
tarjeta es la cuenta entera. Eso es también lo que cuesta cada una: una tarjeta
es una pasada en frío de todas las familias, así que N tarjetas son N pasadas
[al precio por familia](/ghchronicle/es/api/cost/).

**Un README que no está en la raíz.** Esto es para otros repositorios, porque
GitHub muestra el README de un perfil solo desde la raíz de `<tú>/<tú>`. Las
rutas del `<picture>` son relativas al README, así que un `docs/README.md`
apunta a `../generated/card.svg`.

> **Repositorios privados**
>
> Sin fichero de configuración, las estrellas, los forks, los lenguajes, el
> tráfico y los nombres de repositorio salen solo de los repositorios públicos,
> mientras que las cifras de contribuciones, commits y pull requests son totales
> de la cuenta, tal como GitHub los muestra en tu perfil. Hasta v1.0.0, la
> configuración por defecto de la Action contaba los repositorios privados. Con
> `include-private: true` la tarjeta suma las estrellas, forks, lenguajes y
> tráfico de tus repositorios privados, y los diseños que listan repositorios
> **publican sus nombres**: `repo-list` y `summary` (el diseño por defecto de la
> Action) los listan de entrada, y también cualquier diseño al que se le pida
> `top_repos` en `card-fields`. Para contarlos sin nombrarlos, elige tú los campos y
> deja fuera `top_repos`, por ejemplo
> `card-fields: stars,forks,followers,contributions`.

## Dos cosas que un runner alojado no conserva

> **El token tiene que ser un token personal**
>
> El tráfico necesita acceso de escritura a *cada* repositorio que se recoja, y
> el `GITHUB_TOKEN` automático solo lo tiene para el repositorio en el que corre
> el workflow. Tampoco tiene `security_events` ni `read:packages`, y no es un
> usuario, así que nada de ámbito de cuenta funciona con él. Ver [el
> token](/ghchronicle/es/start/token/).

**El fichero de estado no sobrevive entre ejecuciones.** Cada ejecución vuelve
por tanto a recorrer entera la lista de estrellas. En una cuenta pequeña son un
puñado de llamadas; en una cuenta con muchas estrellas, guárdalo en caché:

```yaml
- uses: actions/cache@v4
  with:
    path: ~/.ghchronicle
    key: ghchronicle-state-${{ github.run_id }}
    restore-keys: ghchronicle-state-
```

y apunta `state_file` a `~/.ghchronicle/state.json` en la configuración. Una
pasada en modo `card` lee el estado restaurado, que es lo que le permite
saltarse el recorrido, y nunca lo vuelve a escribir: entrega sus puntos a la
tarjeta y a ningún almacén, así que nada de lo que aprendió puede decirle a la
siguiente recogida que una familia ya está hecha. Quien llena la caché es un
paso `once` o `backfill`.

## Sin la Action

Lo mismo a mano, si prefieres no depender de ella:

```yaml
- uses: actions/setup-go@v7
  with:
    go-version: stable
- run: go install github.com/jmrplens/ghchronicle/cmd/ghchronicle@latest
- run: ghchronicle -config .github/ghchronicle.yaml -card profile.svg -card-only
  env:
    GITHUB_TOKEN: ${{ secrets.GHCHRONICLE_TOKEN }}
```
