Ir al contenido

GitHub 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.

- uses: jmrplens/ghchronicle@v2
with:
token: ${{ secrets.GHCHRONICLE_TOKEN }}
mode: once
config: .github/ghchronicle.yaml
EntradaPor omisiónQué hace
tokenobligatoriaUn 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, cuyo estado dura una ejecución y no se puede guardar en caché
userel dueño del repositorioLa cuenta que recoger cuando no se da fichero de configuración
modeonceonce, backfill, card o migrate
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-layoutsummaryUno de los trece diseños registrados
card-themeautodark, 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-motiononceonce, 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. 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-privatefalsetrue cuenta los repositorios privados cuando no se da fichero de configuración. Ver el aviso más abajo
versionlatestLa release que instalar: latest para la más nueva, o una release con o sin su v, así que 2.6.5 y v2.6.5 son la misma. La etiqueta mayor v2 es lo que toma uses:, no una release, y se rechaza

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

name: Collect
on:
schedule:
- cron: "*/30 * * * *"
workflow_dispatch:
jobs:
collect:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: jmrplens/ghchronicle@v2
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.

En once y backfill, un arranque comprueba los almacenes igual y avisa de lo pendiente. Sobre un fichero de estado nuevo, que es cada ejecución sin uno restaurado, no aplica nada por su cuenta ni siquiera con migrate: auto: el fichero de estado se va con el runner, y con él el registro de que aún se debe una relectura, así que una relectura que falló o un job cancelado a medias dejaría un almacén despejado y nada que lo dijera. Con un fichero de estado restaurado aplica lo que es seguro aplicar sin supervisión, como una máquina. La Action convierte cada línea que dice que un cambio está pendiente, que se aplicó uno al arrancar, que uno falló o que se debe una relectura en una anotación de la ejecución en cuanto se escribe la línea, así que se ve en la página de resumen del workflow y no solo en su registro, también en un job cancelado a medias.

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) 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:

    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@v2
    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:

    <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@v2 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.

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.

El fichero de estado no sobrevive entre ejecuciones. Sin él cada ejecución es una primera ejecución. Corren todas las familias, diga lo que diga su cadencia, porque nada recuerda cuándo corrieron por última vez. Se vuelve a recorrer entera la lista de estrellas, y se lee entero el historial diario de estrellas de cada repositorio, una página por cada treinta semanas de su vida. achievements recorre enteras las pull requests fusionadas de la cuenta para su recuento de coautorías, que fueron 35 consultas y 23,7 MB en una cuenta con 2.315. Y sin fichero de caché junto al estado, nada se pregunta con un ETag, así que ninguna respuesta es un 304 gratis. En una cuenta pequeña son un puñado de llamadas; en una cuenta con muchas estrellas, repositorios antiguos o una historia larga, guárdalo en caché.

Guardarlo en caché necesita un fichero config:. Sin él la Action escribe una configuración propia y pone el estado en un directorio temporal nuevo dentro de RUNNER_TEMP en cada ejecución, que ningún paso de caché puede nombrar. Con él, apunta state_file al directorio personal:

state_file: ~/.ghchronicle/state.json

y restaura y guarda ese directorio con un paso anterior al de ghchronicle:

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

Una ~ en state_file es el directorio personal a partir de la 2.6.1. Una versión anterior la lee tal como está escrita, un directorio llamado ~ dentro del checkout que el paso de caché nunca ve, así que con un version anterior a la 2.6.1 escribe la ruta entera: /home/runner/.ghchronicle/state.json en un runner Ubuntu alojado por GitHub.

El directorio guarda entonces también el fichero de caché, que un paso once escribe y un paso backfill solo lee, así que una ejecución restaurada de él pregunta además a GitHub con los validadores que guardó la anterior, y solo paga lo que cambió. Una pasada en modo card lee el estado restaurado, que es lo que le permite saltarse los dos recorridos, y nunca lo vuelve a escribir, ni tampoco el fichero de caché que hay a su lado: 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 entrada de actions/cache es un paso once, con los dos ficheros, o un paso backfill, solo con el fichero de estado. Ningún paso deja ahí un registro de escrituras: una ejecución que termina con su pasada no abre ninguno.

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

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