diff --git a/.github/workflows/action-test.yml b/.github/workflows/action-test.yml
index 2494d85..3a2d61c 100644
--- a/.github/workflows/action-test.yml
+++ b/.github/workflows/action-test.yml
@@ -53,6 +53,15 @@ jobs:
species: cactus
out: out/cactus.svg
+ - name: Action con la especie calabaza (sin red)
+ id: pumpkin
+ uses: ./
+ with:
+ user: ""
+ fixture: testdata/events.json
+ species: pumpkin
+ out: out/pumpkin.svg
+
- name: Action con la tarjeta compacta (sin red)
id: compact
uses: ./
@@ -145,6 +154,7 @@ jobs:
FIXTURE_PATH: ${{ steps.fixture.outputs.path }}
MUSHROOM_PATH: ${{ steps.mushroom.outputs.path }}
CACTUS_PATH: ${{ steps.cactus.outputs.path }}
+ PUMPKIN_PATH: ${{ steps.pumpkin.outputs.path }}
COMPACT_PATH: ${{ steps.compact.outputs.path }}
BADSIZE_OUTCOME: ${{ steps.badsize.outcome }}
REAL_PATH: ${{ steps.real.outputs.path }}
@@ -165,7 +175,7 @@ jobs:
test "$NOUSER_OUTCOME" = failure || { echo 'un usuario inexistente debería fallar'; exit 1; }
cmp out/nouser.svg "$FIXTURE_PATH" || { echo 'el SVG previo del usuario inexistente cambió'; exit 1; }
cmp out/keep.svg "$FIXTURE_PATH" || { echo 'el SVG previo cambió'; exit 1; }
- for f in "$FIXTURE_PATH" "$MUSHROOM_PATH" "$CACTUS_PATH" "$COMPACT_PATH" "$REAL_PATH"; do
+ for f in "$FIXTURE_PATH" "$MUSHROOM_PATH" "$CACTUS_PATH" "$PUMPKIN_PATH" "$COMPACT_PATH" "$REAL_PATH"; do
test -s "$f"
head -c 120 "$f"; echo
grep -q '' "$f"
@@ -178,6 +188,8 @@ jobs:
if grep -q 'Retoño' "$MUSHROOM_PATH"; then echo 'la especie hongo no se aplicó'; exit 1; fi
grep -q 'Cactus' "$CACTUS_PATH"
if grep -q 'Retoño' "$CACTUS_PATH"; then echo 'la especie cactus no se aplicó'; exit 1; fi
+ grep -q 'Calabacita' "$PUMPKIN_PATH"
+ if grep -q 'Retoño' "$PUMPKIN_PATH"; then echo 'la especie calabaza no se aplicó'; exit 1; fi
grep -q "@$OWNER" "$REAL_PATH"
grep -q '#2b2724' "$REAL_PATH"
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4b0cfa9..16f0268 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -109,7 +109,7 @@ jobs:
go build -o "$RUNNER_TEMP/commitling" ./cmd/commitling
mkdir -p out/wf
"$RUNNER_TEMP/commitling" workflow > out/wf/base.yml 2>/dev/null
- for sp in moss mushroom cactus; do for th in light dark both; do for sz in full compact; do
+ for sp in moss mushroom cactus pumpkin; do for th in light dark both; do for sz in full compact; do
"$RUNNER_TEMP/commitling" workflow --user octocat --species "$sp" --theme "$th" --size "$sz" > "out/wf/$sp-$th-$sz.yml" 2>/dev/null
done; done; done
if grep -L 'uses: BertMarti/commitling@v1$' out/wf/*.yml | grep .; then echo "sin @v1"; exit 1; fi
diff --git a/AGENTS.md b/AGENTS.md
index 68bc33f..78bc040 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -80,6 +80,10 @@ Una mascota pixel-art **original** que vive en el README de tu perfil de GitHub.
- Lo que solo es cierto tras publicar (quitar «el cactus aún no está en `@v1`», «desde la versión 1.0.0» de `go run …@latest`, ejemplos `@v1.0.0`) va en la rama `v1-tras-publicar`, que además borra `release/checksums.txt` (cambia la plantilla embebida).
- Semver a partir de aquí: la interfaz de `action.yml` y el formato del SVG solo cambian de forma incompatible en una `v2`; mejoras compatibles → 1.x (menor), arreglos → parche.
+## Versión 1.1.0 (31/10/2026)
+- **Cuarta especie `pumpkin`** (`internal/creature/pumpkin.go`, «Calabaza»). La OG coloca dos especies por fila (`stagePos` en `internal/og/og.go`).
+- Mismo ciclo que la 1.0.0: el último commit del PR lleva `release/checksums.txt`; lo que solo es cierto tras publicar (quitar «la calabaza llega con la 1.1.0», borrar `release/checksums.txt` de `main`) va en la rama `v1.1-tras-publicar`.
+
## Reglas de contenido
- El personaje es **diseño propio**. Prohibido imitar criaturas de videojuegos, anime o marcas.
- Las reglas de fase y ánimo están documentadas en el README y son deterministas (mismos datos, mismo SVG).
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f3fdb19..98662a2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,10 +6,21 @@ El formato sigue [Keep a Changelog](https://keepachangelog.com/es-ES/1.1.0/) y e
## [Sin publicar]
+## [1.1.0] - 2026-10-31
+
+Una especie nueva y nada más que cambie para quien ya usa commitling: **la interfaz de `action.yml` es la misma** (mismas entradas con los mismos valores por defecto, misma salida `path`, mismos archivos; solo cambia el texto de ayuda de `species` para nombrar la nueva) y las tarjetas del brote de musgo, el hongo y el cactus son byte a byte las de la v1.0.0. Con `@v1` no hay que hacer nada.
+
+### Añadido
+
+- **Cuarta especie: la calabaza** (`species: pumpkin`, también vale `calabaza`), de diseño propio: una calabaza de huerto con ojos que pasa por **Pepita** (una pepita con un brote), **Plántula** (dos hojas redondas con la cáscara de la pepita encima), **Calabacita** (verde y rayada), **Calabaza** (naranja, con gajos, hoja y zarcillo) y **Calabaza gigante**, con las mismas reglas, ánimos y accesorios. Está en la Action, en `--species` de la CLI (`render` y `workflow`), en la galería de la web (claro, oscuro y tarjeta compacta), en los dos generadores de la web y en la imagen de vista previa.
+
### Cambiado
-- Con la v1.0.0 publicada y `v1` apuntando a ella, el README, la guía de uso y la web dejan de avisar de que el cactus y `go run …@latest workflow` necesitan una versión aún sin publicar, y los ejemplos de versión exacta pasan a `@v1.0.0`.
-- `release/checksums.txt` sale de `main`: describe los binarios de la v1.0.0 ya publicada (va en su etiqueta) y no los de `main`, que ha cambiado. La próxima versión lo vuelve a generar en su commit.
+- La imagen de vista previa (`og.png`) coloca las fases de dos especies por fila para que quepan las cuatro.
+- La web describía el cactus como «una semilla» al empezar; su primera fase es la **yema**.
+- Con la v1.0.0 publicada y `v1` apuntando a ella, el README, la guía de uso y la web dejaron de avisar de que el cactus y `go run …@latest workflow` necesitaban una versión aún sin publicar, y los ejemplos de versión exacta pasaron a `@v1.0.0`.
+- `release/checksums.txt` vuelve con los SHA-256 de los binarios de la 1.1.0 (tras la 1.0.0 había salido de `main`).
+- Versión de la CLI: `1.1.0`.
## [1.0.0] - 2026-10-26
@@ -182,7 +193,8 @@ Primera versión: el MVP.
- Las acciones de los workflows suben de versión para evitar Node 20 (`checkout@v7`, `setup-go@v7`, `configure-pages@v6`, `upload-pages-artifact@v5` y `deploy-pages@v5`) (PR #2).
-[Sin publicar]: https://github.com/BertMarti/commitling/compare/v1.0.0...HEAD
+[Sin publicar]: https://github.com/BertMarti/commitling/compare/v1.1.0...HEAD
+[1.1.0]: https://github.com/BertMarti/commitling/compare/v1.0.0...v1.1.0
[1.0.0]: https://github.com/BertMarti/commitling/compare/v0.7.0...v1.0.0
[0.7.0]: https://github.com/BertMarti/commitling/compare/v0.6.0...v0.7.0
[0.6.0]: https://github.com/BertMarti/commitling/compare/v0.5.0...v0.6.0
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index eb8794a..359ade8 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -21,7 +21,7 @@ go run ./cmd/commitling render --fixture testdata/events.json --out out/commitli
# Con tu usuario real (GITHUB_TOKEN es opcional, sube el límite de peticiones)
go run ./cmd/commitling render --user tu-usuario --out out/commitling.svg
-# Otra especie (moss por defecto, mushroom o cactus)
+# Otra especie (moss por defecto, mushroom, cactus o pumpkin)
go run ./cmd/commitling render --fixture testdata/events.json --species mushroom --out out/hongo.svg
# Web con todas las fases, ánimos y accesorios (y og.png)
@@ -87,26 +87,27 @@ Archivos exactos, en este orden:
1. `internal/creature/creature.go`: añade la constante de `Species` **después** de `Mushroom` (así el valor cero sigue siendo el brote de musgo) y una entrada, en el mismo orden, en cada uno de estos cuatro sitios: `AllSpecies`, `speciesNames` (nombre en español), `speciesSlugs` (ASCII: es lo que se escribe en `species:` y en `--species`) y `speciesStageNames` (los nombres en español de sus cinco fases). Si quieres alias, añádelos al `switch` de `SpeciesByName`.
2. Un archivo nuevo en `internal/creature/` (como `mushroom.go`) con su `[...]stageArt` de cinco mapas 16×16 (`body`, `face`, `hat`, `scarf`, `flower`), y engánchalo en `artFor` de `internal/creature/sprites.go` (un `switch`: añade un `case`). La cara (8×4) va sobre un relleno, la fila de la bufanda debe ser un tramo continuo y el gorro y la flor no pueden tapar la cara. Usa solo la paleta de 5 colores y no copies mapas de otra especie ni de ninguna obra ajena.
-3. `cmd/commitling/main.go`: el mensaje «especie no válida … (usa moss, mushroom o cactus)» y la ayuda de `--species` (en el comentario del archivo y en `usage`) listan los valores; añade el nuevo. Igual el texto de la entrada `species` de `action.yml`.
+3. `cmd/commitling/main.go`: el mensaje «especie no válida … (usa moss, mushroom, cactus o pumpkin)» y la ayuda de `--species` (en el comentario del archivo y en `usage`) listan los valores; añade el nuevo. Igual el texto de la entrada `species` de `action.yml`.
4. La galería (`internal/gallery`), la tarjeta SVG y la imagen Open Graph (`internal/og`) recorren `AllSpecies`, así que la nueva especie sale sin más cambios: genera la web y la imagen y revísalas en claro y oscuro.
5. Actualiza las tablas de especies del `README.md` y de `docs/USO.md` (valores de `species` y nombres de las fases), el `CHANGELOG.md` y `MEMORY.md`.
Tests que deben pasar (y qué tocar en ellos):
-- `internal/creature`: `TestSpeciesNamesAndSlugs` (fija `len(AllSpecies) != 3`: sube el número; comprueba nombres, slugs únicos y cinco nombres de fase), `TestSpeciesByName` (añade los alias), `TestAnchorsFitTheBody` (cara sobre relleno, bufanda continua, gorro y flor sin tapar la cara, para todas las especies y fases), `TestDrawUsesPalette` (solo colores de la paleta), `TestSpeciesDrawDifferently` (cada especie se dibuja distinta y de forma determinista, y cada fase distinta de las demás) y un test como `TestCactusMapsAreNotTheOthers` para tu especie (ningún mapa igual a los de las demás).
+- `internal/creature`: `TestSpeciesNamesAndSlugs` (fija `len(AllSpecies) != 4`: sube el número; comprueba nombres, slugs únicos y cinco nombres de fase), `TestSpeciesByName` (añade los alias), `TestAnchorsFitTheBody` (cara sobre relleno, bufanda continua, gorro y flor sin tapar la cara, para todas las especies y fases), `TestDrawUsesPalette` (solo colores de la paleta), `TestSpeciesDrawDifferently` (cada especie se dibuja distinta y de forma determinista, y cada fase distinta de las demás) y un test como `TestCactusMapsAreNotTheOthers` para tu especie (ningún mapa igual a los de las demás).
- `internal/render`: la tarjeta es XML válido, determinista y el texto cabe, para todas las especies (`TestMushroomCard` es el modelo).
- `internal/gallery`: `TestBuildShowsBothSpecies`, `TestBuildIsDeterministic`, `TestGalleryAltsAreUnique` y `TestPageHasSkipLinkAndSpeciesNav`.
- `internal/og`: las cinco fases de cada especie aparecen (`TestStageRowsShowAllStages`) y todo cabe en el marco.
- `cmd/commitling`: la especie inválida sigue fallando antes de tocar la red y el mensaje lista los valores.
- `internal/gallery/action_test.go`: `action.yml` y la tabla de entradas del README siguen coincidiendo.
-- `.github/workflows/action-test.yml` prueba `species: mushroom` y `species: cactus`; añade un paso para la nueva. También el bucle `for sp in …` de los workflows generados en `ci.yml`, la lista `ALLOWED.species` de `internal/gallery/install.js` y la de `internal/gallery/testdata/install.test.js` (y el número de casos de `TestInstallJSMatchesGo`).
-- `internal/og`: si la fila de fases ya no cabe sobre el nombre de la web, ajusta `stageY`, `stageRowH` y `siteY` (con tres especies, 380, 56 y 556).
+- `.github/workflows/action-test.yml` prueba `species: mushroom`, `species: cactus` y `species: pumpkin`; el bucle `for sp in …` de `ci.yml`, la lista `ALLOWED.species` de `internal/gallery/install.js` y la de `internal/gallery/testdata/install.test.js` cubren las cuatro especies (96 casos en `TestInstallJSMatchesGo`).
+- `internal/og`: la imagen OG coloca dos especies por fila (`stagePos`); con más de cuatro hay que añadir una fila y comprobar que cabe sobre `siteY`.
- `internal/gallery`: el número de archivos de `TestBuild` y los `alt` únicos (`TestGalleryAltsAreUnique`): un nombre de fase no puede repetir el de otra especie con las mismas estadísticas (por eso la primera fase del cactus es «Yema» y no «Semilla»).
+- `internal/gallery`: `TestCardsAreByteIdenticalToV100` (huellas de `testdata/golden/v1.0.0-sha256.txt`) y `TestFullCardsAreByteIdenticalToV050` deben seguir pasando sin tocar las listas: las especies que ya estaban no pueden cambiar.
- Comprueba que las especies existentes no cambian ni un byte: genera la galería en `main` y en tu rama y compara con `diff -r` (solo deben aparecer archivos nuevos de tu especie, `index.html`, `og.png` y los scripts).
## Regla de diseño: personaje original
-Las criaturas son **diseño propio**: un brote de musgo con ojos, una seta pequeña con ojos y un cactus con ojos en su maceta, dibujados píxel a píxel. Está prohibido imitar criaturas de videojuegos, anime o marcas, y no se aceptan sprites, nombres ni siluetas tomados de otras obras.
+Las criaturas son **diseño propio**: un brote de musgo con ojos, una seta pequeña con ojos, un cactus con ojos en su maceta y una calabaza de huerto con ojos, dibujados píxel a píxel. Está prohibido imitar criaturas de videojuegos, anime o marcas, y no se aceptan sprites, nombres ni siluetas tomados de otras obras.
Respeta también el estilo «papel y píxel» de [AGENTS.md](AGENTS.md): paleta de la criatura limitada a 5 colores (`#2b2724`, `#f3efe6`, `#7fb069`, `#e6aa68`, `#ca3c25`), píxeles nítidos, sin degradados ni sombras, animaciones suaves y respetando `prefers-reduced-motion`.
diff --git a/MEMORY.md b/MEMORY.md
index 7c6027d..57871ab 100644
--- a/MEMORY.md
+++ b/MEMORY.md
@@ -1,7 +1,9 @@
# MEMORY.md · commitling
-Última actualización: 2026-10-26 por Claude Code (Opus 5.5, jefe de la fase 5, rama `v1-2026-10-26`)
+Última actualización: 2026-10-31 por Claude Code (Opus 5.5, jefe de la fase 6, rama `v1.1-2026-10-31`)
## Estado actual
+**Versión 1.1.0 (fase 6, 31/10/2026, rama `v1.1-2026-10-31`, PR contra `main`)**: v1.0.0 publicada y `v1` apuntando a ella. Cuarta especie **calabaza** (`pumpkin`; fases Pepita, Plántula, Calabacita, Calabaza y Calabaza gigante) en la Action, la CLI, la galería, los dos generadores y la OG (ahora dos especies por fila). Musgo, hongo y cactus byte a byte iguales (galería de `main` contra la rama: 180 SVG y favicon idénticos; 12 combinaciones de la CLI idénticas; huellas de v0.5.0). De `action.yml` solo cambia la `description` de `species`. El último commit lleva `release/checksums.txt` de la 1.1.0. Avisos «la calabaza llega con la 1.1.0» en README, `docs/USO.md` y el instalador de la web: los quita la rama `v1.1-tras-publicar` (que además borra `release/checksums.txt` de `main`), que se fusiona después de publicar y mover `v1`. Pasos exactos para el orquestador en el informe de la fase 6.
+
**Tras publicar la v1.0.0 (rama `v1-tras-publicar`, se fusiona después de la release y de mover `v1`)**: sin avisos de «aún no publicada» (README, `docs/USO.md`, web), ejemplos de versión exacta `@v1.0.0` y sin `release/checksums.txt` en `main` (va en la etiqueta v1.0.0; regenerarlo con la misma versión daría SHA-256 distintos de los subidos porque la plantilla embebida cambió). `@main` compila siempre con Go.
**Versión 1.0.0 (fase 5, 26/10/2026, rama `v1-2026-10-26`, PR contra `main`)**: la 0.8.0 nunca se publicó y se salta: todo lo de las fases 1 a 4 sale como **v1.0.0** (CLI, Action, web y docs en 1.0.0; CHANGELOG `[1.0.0] - 2026-10-26`). El commit lleva `release/checksums.txt` de la 1.0.0 (generado tras el último cambio de código; la plantilla de la web va embebida en el binario), así que al publicar la release el workflow «Release» sube los binarios y la Action deja de compilar. Antes de publicarla, el paso `bin` recibe un 404 y compila con Go (probado en el job `binario`, que ahora prueba también la release real). El job `action` fuerza la ruta de `go run` (`COMMITLING_RELEASE_URL` a un puerto cerrado) para que el chequeo del Go parcheado no dependa de si la release existe. Lo que solo es cierto tras publicar está en la rama `v1-tras-publicar` (sin PR): quita los avisos del cactus y del `go run …@latest`, pone los ejemplos `@v1.0.0` y borra `release/checksums.txt`. `v1` sigue en v0.7.0 hasta que el orquestador la mueva.
@@ -173,7 +175,13 @@ Pulido de v0.4.0 (rama `agent/builder/v0.4-pulido`, un PR contra `main`, tras la
- **Cactus**: primera fase «Yema» (no «Semilla»: repetiría el `alt` del musgo con las mismas estadísticas, `TestGalleryAltsAreUnique`). Los mapas de Luna no valían (una fila de 15 píxeles y «con brazos» sin brazos): los redibujó Claude. La OG se reajustó (`stageY` 380, `stageRowH` 56, `siteY` 556) para tres filas.
- **Acciones por SHA** también en `action.yml` (el `setup-go` que corre en los perfiles); los workflows que genera la web para las personas siguen con `@v7` (legibles y suyos).
+### Fase 6 (31/10/2026)
+- **Calabaza** como cuarta especie (receta de `CONTRIBUTING.md`): encaja con las otras (planta de huerto), usa la paleta tal cual (miel = calabaza, musgo = tallo y hojas, papel = pepita, acento = gajos). Primera fase «Pepita» (no «Semilla», por `TestGalleryAltsAreUnique`). Los gajos son píxeles de acento y se curvan alrededor de la cara; la pepita lleva borde miel para que se lea en oscuro (el contorno pasa a papel). Mapas de Claude; Luna hizo el cableado, los tests y las docs.
+- **OG con dos especies por fila** (`stagePos`): con cuatro filas de una especie no cabía sobre el nombre de la web; con una quinta especie habría que añadir una fila.
+- **`release/checksums.txt` en el último commit del PR** (como en la 1.0.0): antes de publicar, el paso `bin` recibe 404 y compila; tras publicar, usa el binario. La rama `v1.1-tras-publicar` lo borra de `main` junto con los avisos.
+
## Siguiente paso
+-7. v1.1.0: fusionar el PR de `v1.1-2026-10-31`; publicar la release `v1.1.0` sobre el SHA del merge; esperar a que «Release» suba los 6 binarios y `checksums.txt`; mover `v1`; probar el perfil; PR de `v1.1-tras-publicar`. Ver `PLAN.md`.
-6. v1.0.0: fusionar el PR de `v1-2026-10-26`; publicar la release `v1.0.0` desde ese commit y mover `v1` (órdenes en el README); comprobar que «Release» sube 6 binarios y `checksums.txt`; abrir y fusionar el PR de `v1-tras-publicar`. Ver `PLAN.md`.
-5. Fase 3: fusionar el PR de `fase3-2026-10-25`; tras el despliegue, abrir la web con la consola (Chromium) y comprobar 0 violaciones de CSP/Trusted Types al usar la demo, el generador y el selector de tema; después, la release `v0.8.0` (notas en `CHANGELOG.md`) y mover `v1`. Opcional: activar el binario precompilado (`docs/DESARROLLO.md`).
-4. v0.8.0: fusionar el PR de `mejoras-2026-10-24`; tras el despliegue, abrir la web con la consola y comprobar que la CSP no bloquea nada (demo, generador, descarga); crear la release `v0.8.0` y mover `v1`; activar «Private vulnerability reporting». Ver `PLAN.md`.
@@ -234,3 +242,4 @@ Pulido de v0.4.0 (rama `agent/builder/v0.4-pulido`, un PR contra `main`, tras la
- 2026-10-24 (tarde) Claude Code · Opus 5.5 (jefe) con OpenCode (GPT-5.6 Luna) (fase2-2026-10-24): generador del workflow en la web (`install.js`, paridad con `generate.Install`), `commitling workflow`, errores de la API accionables y anotación `::error`, push con aviso de permisos, `::add-mask::` del token, actionlint en el CI, CSS en `style.css` (CSP `style-src 'self'`).
- 2026-10-26 Claude Code · Opus 5.5 (jefe) con OpenCode (GPT-5.6 Luna) (fase3-2026-10-25): especie cactus (mapas rehechos por Claude), selector de tema (Luna, revisado y corregido por Claude), Trusted Types, acciones por SHA, binario precompilado preparado (paso `bin`, `build-release.sh`, `release.yml`, job `binario`), OG para tres especies, docs.
- 2026-10-26 Claude Code · Opus 5.5 (jefe) con OpenCode (Gemini 3 Flash) (v1-2026-10-26): versión 1.0.0 (CLI, web, docs, CHANGELOG), `release/checksums.txt` de la 1.0.0, jobs `action` y `binario` independientes del estado de la release, docs sin promesas falsas (Marketplace, «`v1` aún no existe»), rama `v1-tras-publicar`; notas de la release redactadas por Gemini y revisadas por Claude.
+- 2026-10-31 Claude Code · Opus 5.5 (jefe) con OpenCode (GPT-5.6 Luna) (v1.1-2026-10-31): cuarta especie calabaza (mapas de Claude; cableado, tests y docs de Luna, revisados), OG en dos columnas, versión 1.1.0, `release/checksums.txt` de la 1.1.0 y rama `v1.1-tras-publicar`.
diff --git a/PLAN.md b/PLAN.md
index 92c69b6..d028844 100644
--- a/PLAN.md
+++ b/PLAN.md
@@ -1,6 +1,6 @@
# PLAN.md · commitling
-Hoja de ruta priorizada. Fechada el 24/10/2026 y actualizada el 26/10/2026 (versión 1.0.0). Las etiquetas, releases y mover `v1` los hace Alberto (o el orquestador en su nombre).
+Hoja de ruta priorizada. Fechada el 24/10/2026 y actualizada el 31/10/2026 (versión 1.1.0). Las etiquetas, releases y mover `v1` los hace Alberto (o el orquestador en su nombre).
## Hecho: versión 1.0.0 (26/10/2026, de v0.7.0 a v1.0.0; la 0.8.0 nunca se publicó)
- **Fase 1 (24/10, «Blindada»)**: CSP estricta en la web y sin scripts en línea, CI con job «seguridad» (govulncheck, staticcheck, gitleaks), permisos mínimos, dependabot, `SECURITY.md`, `go 1.27.3` (la Action compilaba con Go 1.27.0, con 11 vulnerabilidades), escritura atómica del SVG, README para personas usuarias y `docs/DESARROLLO.md`.
@@ -9,7 +9,12 @@ Hoja de ruta priorizada. Fechada el 24/10/2026 y actualizada el 26/10/2026 (vers
- **Fase 4 (26/10)**: README, `docs/USO.md`, CHANGELOG y web sin contradicciones.
- **Fase 5 (26/10, rama `v1-2026-10-26`)**: versión 1.0.0 en la CLI, la web y la documentación; CHANGELOG `[1.0.0]`; `release/checksums.txt` de la 1.0.0 (binario precompilado activo al publicar); el job `action` del CI fuerza la ruta de `go run` y el job `binario` prueba también la release real; fuera la falsa promesa del Marketplace y el aviso obsoleto de «`v1` aún no existe» en `docs/USO.md`.
+## Hecho: versión 1.1.0 (31/10/2026, rama `v1.1-2026-10-31`)
+- **Cuarta especie: la calabaza** (`species: pumpkin`), de la lista «Después de la 1.0»: Pepita, Plántula, Calabacita, Calabaza y Calabaza gigante, en la Action, la CLI, la galería, los generadores de la web y la imagen OG (dos especies por fila). Musgo, hongo y cactus byte a byte iguales.
+- Mantenimiento: Go 1.27.3 sigue siendo el último parche (sin cambios en `go.mod`), govulncheck limpio, 0 alertas y 0 PRs de Dependabot.
+
## Siguiente (por orden)
+0. **Orquestador/Alberto (1.1.0)**: fusionar el PR de `v1.1-2026-10-31`, publicar `v1.1.0` sobre el SHA del merge, esperar a que «Release» suba los binarios, mover `v1` y fusionar el PR de `v1.1-tras-publicar` (pasos exactos en el informe de la fase 6).
1. **Orquestador/Alberto**: fusionar el PR de `v1-2026-10-26`, publicar la release `v1.0.0` desde ese commit (título y notas en el informe de la fase 5) y mover `v1` a ella (orden exacta en el README, «Lo que tienes que hacer tú»). Comprobar que el workflow «Release» sube los 6 binarios y `checksums.txt`.
2. ~~**Orquestador**: PR de la rama `v1-tras-publicar`~~ Hecho: avisos de «aún no publicada» fuera, ejemplos `@v1.0.0` y `release/checksums.txt` borrado de `main`.
3. Tras el despliegue: abrir https://bertmarti.github.io/commitling/ con la consola y comprobar 0 violaciones de CSP/Trusted Types (demo, generador con un usuario real, selector de tema). La primera ejecución del perfil de BertMarti tras mover `v1` no debe tener el paso de `setup-go` (usa el binario).
@@ -17,6 +22,6 @@ Hoja de ruta priorizada. Fechada el 24/10/2026 y actualizada el 26/10/2026 (vers
## Después de la 1.0
- **Alberto**: publicar la Action en el GitHub Marketplace (hay que aceptar el acuerdo de desarrollador en la web al editar la release: casilla «Publish this Action to the GitHub Marketplace»). `action.yml` ya cumple los requisitos (`TestActionMetadataIsMarketplaceReady`).
-- Una cuarta especie (receta en `CONTRIBUTING.md`, al día con lo aprendido en el cactus).
+- ~~Una cuarta especie~~ Hecho en la 1.1.0 (calabaza).
- Modo «año en píxeles» con la actividad de todo el año (requiere otra fuente de datos: la API de eventos solo da 90 días; probablemente GraphQL con token).
- Atestaciones de procedencia de los binarios (`actions/attest-build-provenance`) como capa extra para quien quiera verificarlos a mano; la Action seguiría confiando solo en `release/checksums.txt`.
diff --git a/README.md b/README.md
index 1bfcbbe..4e3d1b4 100644
--- a/README.md
+++ b/README.md
@@ -26,7 +26,7 @@ commitling es una GitHub Action que lee tu actividad pública, calcula tu experi
- Crece con tus commits, pull requests, issues y demás actividad pública.
- Cambia de ánimo según tu actividad reciente y tu racha.
- Desbloquea gorro, bufanda y flor.
-- Tiene tema claro u oscuro y tres especies: brote de musgo, hongo o cactus.
+- Tiene tema claro u oscuro y cuatro especies: brote de musgo, hongo, cactus o calabaza.
## 🎮 Pruébalo sin instalar nada
@@ -103,7 +103,7 @@ Puedes añadir estas entradas dentro de `with:`:
| `user` | `${{ github.repository_owner }}` | Usuario cuya actividad pública se dibuja |
| `out` | `commitling.svg` | Archivo donde se guarda el SVG |
| `theme` | `light` | Usa `light` (papel) o `dark` (tinta) |
-| `species` | `moss` | Elige `moss` (brote de musgo), `mushroom` (hongo) o `cactus` |
+| `species` | `moss` | Elige `moss` (brote de musgo), `mushroom` (hongo), `cactus` o `pumpkin` (calabaza) |
| `size` | `full` | Usa la tarjeta completa o la insignia `compact` de 200x60 |
| `token` | `${{ github.token }}` | Token de la API; normalmente basta el del workflow |
| `fixture` | vacío | Archivo de eventos para probar sin red |
@@ -113,25 +113,28 @@ La salida `path` contiene la ruta absoluta del SVG generado. La Action crea el a
## Especies
-Las tres especies son originales y tienen las mismas reglas, fases, ánimos y accesorios.
+Las cuatro especies son originales y tienen las mismas reglas, fases, ánimos y accesorios.
-| Fase | Desde | Brote de musgo (`moss`) | Hongo (`mushroom`) | Cactus (`cactus`) |
-|---|---|---|---|---|
-| 1 | 0 XP | Semilla | Espora | Yema |
-| 2 | 100 XP | Brote | Botón | Bolita |
-| 3 | 400 XP | Retoño | Seta | Cactus |
-| 4 | 1.000 XP | Arbusto | Seta grande | Cactus con brazos |
-| 5 | 2.500 XP | Árbol ancestral | Corro de setas | Cactus centenario |
+| Fase | Desde | Brote de musgo (`moss`) | Hongo (`mushroom`) | Cactus (`cactus`) | Calabaza (`pumpkin`) |
+|---|---|---|---|---|---|
+| 1 | 0 XP | Semilla | Espora | Yema | Pepita |
+| 2 | 100 XP | Brote | Botón | Bolita | Plántula |
+| 3 | 400 XP | Retoño | Seta | Cactus | Calabacita |
+| 4 | 1.000 XP | Arbusto | Seta grande | Cactus con brazos | Calabaza |
+| 5 | 2.500 XP | Árbol ancestral | Corro de setas | Cactus centenario | Calabaza gigante |
moss: árbol ancestral, radiante
mushroom: corro de setas, radiante
cactus: cactus centenario, radiante
+
pumpkin: calabaza gigante, radiante
-`species` también acepta `musgo`, `hongo` y `seta`, sin distinguir mayúsculas.
+La calabaza llega con la versión 1.1.0. Si al usarla el paso falla con «especie no válida», es que `@v1` aún no apunta a la 1.1.0: vuelve a intentarlo cuando se publique.
+
+`species` también acepta `musgo`, `hongo`, `seta` y `calabaza`, sin distinguir mayúsculas.
### Tarjeta compacta
@@ -151,6 +154,7 @@ Si la añades como un paso más, suma `commitling-compact.svg` al `git add` del
+
@@ -250,7 +254,7 @@ Sí. Solo cuentan los últimos 90 días, así que la XP y los accesorios pueden
¿Cómo cambio a la seta o al tema oscuro?
-Añade `species: mushroom` (o `species: cactus`) o `theme: dark` en `with:`. Para el tema oscuro, genera las dos imágenes y usa el bloque `` de esta página.
+Añade `species: mushroom` (o `species: cactus` o `species: pumpkin`) o `theme: dark` en `with:`. Para el tema oscuro, genera las dos imágenes y usa el bloque `` de esta página.
¿La criatura es de algún videojuego?
diff --git a/action.yml b/action.yml
index 3e42e03..8710b5e 100644
--- a/action.yml
+++ b/action.yml
@@ -23,7 +23,7 @@ inputs:
required: false
default: ${{ github.token }}
species:
- description: Especie de la criatura, moss (brote de musgo, la predeterminada), mushroom (hongo) o cactus.
+ description: Especie de la criatura, moss (brote de musgo, la predeterminada), mushroom (hongo), cactus o pumpkin (calabaza).
required: false
default: moss
size:
diff --git a/cmd/commitling/main.go b/cmd/commitling/main.go
index 0fd0117..2af2c98 100644
--- a/cmd/commitling/main.go
+++ b/cmd/commitling/main.go
@@ -1,9 +1,9 @@
// Command commitling draws a pixel-art creature that grows with your public
// GitHub activity.
//
-// commitling render --user [--theme light|dark] [--species moss|mushroom|cactus] [--size full|compact] [--keep-on-error] [--now RFC3339] --out commitling.svg
+// commitling render --user [--theme light|dark] [--species moss|mushroom|cactus|pumpkin] [--size full|compact] [--keep-on-error] [--now RFC3339] --out commitling.svg
// commitling render --fixture testdata/events.json --out commitling.svg
-// commitling workflow --user [--species moss|mushroom|cactus] [--theme light|dark|both] [--size full|compact]
+// commitling workflow --user [--species moss|mushroom|cactus|pumpkin] [--theme light|dark|both] [--size full|compact]
// commitling gallery --out site/
// commitling og --out og.png
// commitling version
@@ -28,14 +28,14 @@ import (
)
// version is the CLI version; it can be overridden with -ldflags "-X main.version=...".
-var version = "1.0.0"
+var version = "1.1.0"
const usage = `commitling: una mascota pixel-art que crece con tus commits.
Uso:
- commitling render --user [--theme light|dark] [--species moss|mushroom|cactus] [--size full|compact] [--keep-on-error] [--now RFC3339] --out
- commitling render --fixture [--user ] [--theme light|dark] [--species moss|mushroom|cactus] [--size full|compact] [--now RFC3339] --out
- commitling workflow [--user ] [--species moss|mushroom|cactus] [--theme light|dark|both] [--size full|compact]
+ commitling render --user [--theme light|dark] [--species moss|mushroom|cactus|pumpkin] [--size full|compact] [--keep-on-error] [--now RFC3339] --out
+ commitling render --fixture [--user ] [--theme light|dark] [--species moss|mushroom|cactus|pumpkin] [--size full|compact] [--now RFC3339] --out
+ commitling workflow [--user ] [--species moss|mushroom|cactus|pumpkin] [--theme light|dark|both] [--size full|compact]
commitling gallery --out
commitling og --out
commitling version
@@ -112,7 +112,7 @@ func runRender(args []string, stdout, stderr io.Writer) error {
user := fs.String("user", "", "usuario de GitHub")
fixture := fs.String("fixture", "", "archivo JSON de eventos para trabajar sin red")
theme := fs.String("theme", "light", "tema: light o dark")
- speciesFlag := fs.String("species", "moss", "especie: moss (brote de musgo), mushroom (hongo) o cactus")
+ speciesFlag := fs.String("species", "moss", "especie: moss (brote de musgo), mushroom (hongo), cactus o pumpkin (calabaza)")
sizeFlag := fs.String("size", "full", "tamaño: full (480x200) o compact (insignia de 200x60)")
nowFlag := fs.String("now", "", "fecha de referencia en RFC3339 (por defecto, ahora; con --fixture, el último evento)")
out := fs.String("out", "commitling.svg", "archivo SVG de salida (- para la salida estándar)")
@@ -255,7 +255,7 @@ func runWorkflow(args []string, stdout, stderr io.Writer) error {
fs := flag.NewFlagSet("workflow", flag.ContinueOnError)
fs.SetOutput(stderr)
user := fs.String("user", "", "usuario de GitHub (por defecto, el dueño del repositorio donde corre el workflow)")
- speciesFlag := fs.String("species", "moss", "especie: moss (brote de musgo), mushroom (hongo) o cactus")
+ speciesFlag := fs.String("species", "moss", "especie: moss (brote de musgo), mushroom (hongo), cactus o pumpkin (calabaza)")
theme := fs.String("theme", "light", "tema: light, dark o both (claro y oscuro, según quien mire tu perfil)")
sizeFlag := fs.String("size", "full", "tamaño: full (480x200) o compact (insignia de 200x60)")
if err := fs.Parse(args); err != nil {
diff --git a/cmd/commitling/main_test.go b/cmd/commitling/main_test.go
index 3546db7..ee8c80f 100644
--- a/cmd/commitling/main_test.go
+++ b/cmd/commitling/main_test.go
@@ -94,7 +94,7 @@ func TestMissingFixture(t *testing.T) {
func TestVersionAndGallery(t *testing.T) {
var stdout, stderr bytes.Buffer
- if err := run([]string{"version"}, &stdout, &stderr); err != nil || stdout.String() != "commitling 1.0.0\n" {
+ if err := run([]string{"version"}, &stdout, &stderr); err != nil || stdout.String() != "commitling 1.1.0\n" {
t.Fatalf("version: %v %q", err, stdout.String())
}
dir := t.TempDir()
@@ -191,7 +191,7 @@ func TestRenderInvalidSpeciesFailsEarlyAndClearly(t *testing.T) {
t.Errorf("%q: err = %v, want a usage error", bad, err)
continue
}
- for _, want := range []string{"especie no válida", bad, "moss", "mushroom", "cactus"} {
+ for _, want := range []string{"especie no válida", bad, "moss", "mushroom", "cactus", "pumpkin"} {
if !strings.Contains(err.Error(), want) {
t.Errorf("%q: message %q should contain %q", bad, err, want)
}
@@ -409,7 +409,7 @@ func TestRenderMatchesGenerateRender(t *testing.T) {
t.Fatal(err)
}
now := time.Date(2026, 9, 29, 12, 0, 0, 0, time.UTC)
- for _, sp := range []string{"moss", "mushroom", "cactus"} {
+ for _, sp := range []string{"moss", "mushroom", "cactus", "pumpkin"} {
for _, th := range []string{"light", "dark"} {
var stdout, stderr bytes.Buffer
args := []string{"render", "--fixture", fixture, "--user", "octoexample", "--species", sp, "--theme", th,
diff --git a/docs/DESARROLLO.md b/docs/DESARROLLO.md
index 60ad627..2f70979 100644
--- a/docs/DESARROLLO.md
+++ b/docs/DESARROLLO.md
@@ -16,7 +16,7 @@ go run ./cmd/commitling render --fixture testdata/events.json --out out/commitli
# Tema oscuro y fecha de referencia fija, a la salida estándar
go run ./cmd/commitling render --fixture testdata/events.json --theme dark --now 2026-10-01T12:00:00Z --out -
-# Otra especie (moss por defecto, mushroom o cactus)
+# Otra especie (moss por defecto, mushroom, cactus o pumpkin)
go run ./cmd/commitling render --fixture testdata/events.json --species mushroom --out out/hongo.svg
# Si la API tiene una caída transitoria y out/commitling.svg ya existe, lo conserva (aviso, código de salida 0)
@@ -31,7 +31,7 @@ go run ./cmd/commitling og --out out/og.png
go run ./cmd/commitling version
```
-Con `--fixture` y sin `--now`, se usa la fecha del último evento del fixture. `--species` acepta `moss`, `mushroom` o `cactus` y `--size` acepta `full` o `compact`.
+Con `--fixture` y sin `--now`, se usa la fecha del último evento del fixture. `--species` acepta `moss`, `mushroom`, `cactus` o `pumpkin` y `--size` acepta `full` o `compact`.
## Stack
diff --git a/docs/USO.md b/docs/USO.md
index 937a079..b71c110 100644
--- a/docs/USO.md
+++ b/docs/USO.md
@@ -28,14 +28,14 @@ Antes de instalar nada puedes ver todas las fases y ánimos en la web del proyec
https://bertmarti.github.io/commitling/
-Arriba a la izquierda puedes elegir el **tema de la web** (Sistema, Claro u Oscuro); se recuerda en tu navegador con la clave `commitling:theme` y «Sistema» la borra. Ahí verás las tres especies, cada una con sus 5 fases y los 4 ánimos de cada una (20 tarjetas por especie, en versión clara y oscura), los accesorios, las reglas y las instrucciones de instalación con botones para copiar. También hay una criatura «en vivo» del autor del proyecto, que se actualiza cada día.
+Arriba a la izquierda puedes elegir el **tema de la web** (Sistema, Claro u Oscuro); se recuerda en tu navegador con la clave `commitling:theme` y «Sistema» la borra. Ahí verás las cuatro especies, cada una con sus 5 fases y los 4 ánimos de cada una (20 tarjetas por especie, en versión clara y oscura), los accesorios, las reglas y las instrucciones de instalación con botones para copiar. También hay una criatura «en vivo» del autor del proyecto, que se actualiza cada día.
### Generar la tuya en la web (sin instalar nada)
Arriba de todo, en la sección **Pruébalo con tu usuario**, puedes ver tu propia criatura al instante:
1. Escribe tu usuario de GitHub (vale con o sin `@`).
-2. Elige la especie (brote de musgo, hongo o cactus) y el tema (claro u oscuro).
+2. Elige la especie (brote de musgo, hongo, cactus o calabaza) y el tema (claro u oscuro).
3. Pulsa **Dibujar** (o Intro). En unos segundos aparece tu criatura. Si cambias la especie o el tema, se redibuja sola, sin volver a preguntar a GitHub.
4. Con **Descargar SVG** te llevas la imagen (`commitling-.svg`), y debajo tienes el **workflow ya relleno** con tu usuario, la especie y el tema que has elegido, con botón de copiar. Pégalo en `.github/workflows/commitling.yml` de tu repositorio de perfil y sigue los pasos de la sección 3.
@@ -151,7 +151,7 @@ Dentro de `with:` puedes ajustar:
| `user` | el dueño del repositorio | Usuario de GitHub cuya actividad se dibuja |
| `out` | `commitling.svg` | Nombre del archivo de imagen |
| `theme` | `light` | `light` (papel) o `dark` (tinta) |
-| `species` | `moss` | Especie de la criatura: `moss` (brote de musgo) o `mushroom` (hongo) |
+| `species` | `moss` | Especie de la criatura: `moss` (brote de musgo), `mushroom` (hongo), `cactus` o `pumpkin` (calabaza) |
| `size` | `full` | Tamaño de la tarjeta: `full` (480x200, la de siempre) o `compact` (insignia de 200x60 para firmas y barras laterales) |
| `token` | el token del propio workflow | Suele bastar; no hace falta que lo cambies |
| `fixture` | vacío | Archivo de eventos de ejemplo para probar sin conexión (solo para desarrollo) |
@@ -159,7 +159,7 @@ Dentro de `with:` puedes ajustar:
### Elegir la especie
-Hay tres especies, todas de diseño propio y con las mismas reglas (XP, ánimos y accesorios). Por defecto tu criatura es un **brote de musgo** (`moss`). Si prefieres el **hongo** (`mushroom`), una seta pequeña de sombrero miel, añade `species: mushroom` en `with:` (o `species: cactus` para el **cactus**, un cactus con ojos en su maceta de barro):
+Hay cuatro especies, todas de diseño propio y con las mismas reglas (XP, ánimos y accesorios). Por defecto tu criatura es un **brote de musgo** (`moss`). Si prefieres el **hongo** (`mushroom`), una seta pequeña de sombrero miel, añade `species: mushroom` en `with:` (o `species: cactus` para el **cactus**, o `species: pumpkin` para la **calabaza**):
```yaml
- uses: BertMarti/commitling@v1
@@ -170,13 +170,15 @@ Hay tres especies, todas de diseño propio y con las mismas reglas (XP, ánimos
Solo cambian el dibujo y el nombre de las fases:
-| Fase | Desde | Brote de musgo (`moss`) | Hongo (`mushroom`) | Cactus (`cactus`) |
-|---|---|---|---|---|
-| 1 | 0 XP | Semilla | Espora | Yema |
-| 2 | 100 XP | Brote | Botón | Bolita |
-| 3 | 400 XP | Retoño | Seta | Cactus |
-| 4 | 1.000 XP | Arbusto | Seta grande | Cactus con brazos |
-| 5 | 2.500 XP | Árbol ancestral | Corro de setas | Cactus centenario |
+| Fase | Desde | Brote de musgo (`moss`) | Hongo (`mushroom`) | Cactus (`cactus`) | Calabaza (`pumpkin`) |
+|---|---|---|---|---|---|
+| 1 | 0 XP | Semilla | Espora | Yema | Pepita |
+| 2 | 100 XP | Brote | Botón | Bolita | Plántula |
+| 3 | 400 XP | Retoño | Seta | Cactus | Calabacita |
+| 4 | 1.000 XP | Arbusto | Seta grande | Cactus con brazos | Calabaza |
+| 5 | 2.500 XP | Árbol ancestral | Corro de setas | Cactus centenario | Calabaza gigante |
+
+La calabaza llega con la versión 1.1.0. Si al usarla el paso falla con «especie no válida», es que `@v1` aún no apunta a la 1.1.0: vuelve a intentarlo cuando se publique.
Valores que acepta `species` (sin distinguir mayúsculas ni espacios alrededor):
@@ -185,8 +187,9 @@ Valores que acepta `species` (sin distinguir mayúsculas ni espacios alrededor):
| Brote de musgo | `moss`, `musgo`, `brote de musgo` (y vacío, que es la predeterminada) |
| Hongo | `mushroom`, `hongo`, `seta`, `setas` |
| Cactus | `cactus` |
+| Calabaza | `pumpkin`, `calabaza` |
-Cualquier otro valor (por ejemplo `dragon`) hace fallar el paso a propósito, con el mensaje `especie no válida "dragon" (usa moss, mushroom o cactus)`, para que un error tipográfico no pase desapercibido, y no escribe ningún archivo. Puedes ver todas las fases de las tres especies en la [galería](https://bertmarti.github.io/commitling/#galeria).
+Cualquier otro valor (por ejemplo `dragon`) hace fallar el paso a propósito, con el mensaje `especie no válida "dragon" (usa moss, mushroom, cactus o pumpkin)`, para que un error tipográfico no pase desapercibido, y no escribe ningún archivo. Puedes ver todas las fases de las cuatro especies en la [galería](https://bertmarti.github.io/commitling/#galeria).
¿Varias a la vez? Sí: usa la Action dos veces con `out` distinto (por ejemplo `commitling.svg` y `commitling-hongo.svg`, cada uno con su `species`), suma el segundo archivo al `git add` y enseña la imagen que quieras en el README.
@@ -201,7 +204,7 @@ Por defecto la tarjeta mide 480x200 (`size: full`). Si la quieres más pequeña,
size: compact
```
-Verás la compacta en las cinco fases de las tres especies en la [galería](https://bertmarti.github.io/commitling/#compacta), y en el generador de la web (sección «Pruébalo con tu usuario») el selector **Tamaño** te la enseña con tu usuario y rellena el workflow con `size: compact`.
+Verás la compacta en las cinco fases de las cuatro especies en la [galería](https://bertmarti.github.io/commitling/#compacta), y en el generador de la web (sección «Pruébalo con tu usuario») el selector **Tamaño** te la enseña con tu usuario y rellena el workflow con `size: compact`.
Sin `size`, o con `size: full`, el dibujo es exactamente el de siempre, byte a byte. Cualquier otro valor (por ejemplo `grande`) hace fallar el paso a propósito, con el mensaje `tamaño no válido "grande" (usa full o compact)`. Puedes usar la Action dos veces (una con cada tamaño) con `out` distinto y suma los dos archivos al `git add`.
@@ -284,7 +287,7 @@ Ejemplo: en los últimos 90 días has hecho 20 commits (200 XP), abierto 2 pull
| Arbusto | 1.000 XP |
| Árbol ancestral | 2.500 XP |
-En el hongo, las cinco fases se llaman Espora, Botón, Seta, Seta grande y Corro de setas, y en el cactus Yema, Bolita, Cactus, Cactus con brazos y Cactus centenario, con las mismas XP. Con 285 XP estarías en **Brote** (fase 2 de 5) y te faltarían 115 XP para ser Retoño. La tarjeta muestra la XP total, la meta siguiente y una barra de progreso (24 casillas) hasta la próxima fase; en el Árbol ancestral la barra va siempre llena.
+En el hongo, las cinco fases se llaman Espora, Botón, Seta, Seta grande y Corro de setas; en el cactus, Yema, Bolita, Cactus, Cactus con brazos y Cactus centenario; y en la calabaza, Pepita, Plántula, Calabacita, Calabaza y Calabaza gigante, con las mismas XP. Con 285 XP estarías en **Brote** (fase 2 de 5) y te faltarían 115 XP para ser Retoño. La tarjeta muestra la XP total, la meta siguiente y una barra de progreso (24 casillas) hasta la próxima fase; en el Árbol ancestral la barra va siempre llena.
### Ánimos
@@ -378,7 +381,7 @@ go run ./cmd/commitling version
**Conservar el SVG ante una caída de la API (`--keep-on-error`).** Con esta opción, si la API de GitHub sigue fallando tras los reintentos por una causa **transitoria** (error de red, 5xx o límite de peticiones: 429, o 403 con cabeceras de límite) y el archivo de `--out` ya existe y es un SVG completo, commitling lo deja intacto, escribe un aviso («la API de GitHub no responde… se conserva … sin cambios») y termina con código 0. Los fallos **permanentes** siguen siendo un error aunque haya archivo previo: 401 (token inválido o caducado), 404 (usuario inexistente), 422 y 403 sin cabeceras de límite (permisos). Sin archivo previo, con un archivo vacío o cortado, o con `--out -`, el error también se mantiene, y no afecta a otros errores (un `--fixture` que no existe, una especie no válida…). En la Action equivale a la entrada `keep-on-error`, que está activada por defecto.
-**La imagen de vista previa (`og`).** Es el PNG de 1200×630 píxeles que muestran Slack, X, LinkedIn o Telegram cuando alguien pega el enlace de la web del proyecto (formato «Open Graph»). Enseña una criatura grande y las cinco fases de las tres especies, en la paleta de la criatura, sin suavizado y en unos 3 KB. `commitling og --out og.png` la dibuja donde le digas (con `--out -` va a la salida estándar); no necesita red ni usuario, y sale siempre igual. Solo te hace falta si mantienes tu propia web: `commitling gallery` ya la genera como `og.png` dentro de la carpeta de salida y las etiquetas `og:image` de la página apuntan a ella. Ten en cuenta que las redes sociales guardan las vistas previas en caché, así que un cambio puede tardar en verse.
+**La imagen de vista previa (`og`).** Es el PNG de 1200×630 píxeles que muestran Slack, X, LinkedIn o Telegram cuando alguien pega el enlace de la web del proyecto (formato «Open Graph»). Enseña una criatura grande y las cinco fases de las cuatro especies, en la paleta de la criatura, sin suavizado y en unos 3 KB. `commitling og --out og.png` la dibuja donde le digas (con `--out -` va a la salida estándar); no necesita red ni usuario, y sale siempre igual. Solo te hace falta si mantienes tu propia web: `commitling gallery` ya la genera como `og.png` dentro de la carpeta de salida y las etiquetas `og:image` de la página apuntan a ella. Ten en cuenta que las redes sociales guardan las vistas previas en caché, así que un cambio puede tardar en verse.
## 8. Preguntas frecuentes
diff --git a/internal/creature/creature.go b/internal/creature/creature.go
index 1c5784b..d894a09 100644
--- a/internal/creature/creature.go
+++ b/internal/creature/creature.go
@@ -2,10 +2,11 @@
// growth stage, its mood and the accessories it has unlocked, plus the pixel
// maps used to draw it.
//
-// All three species are original designs. The default one is a small moss
+// All four species are original designs. The default one is a small moss
// sprout with eyes that grows from a honey-coloured seed into an ancient tree;
-// the others are a little mushroom that grows from a spore into a fairy ring
-// and a cactus with eyes in a clay-coloured pot.
+// the others are a little mushroom that grows from a spore into a fairy ring,
+// a cactus with eyes in a clay-coloured pot and a garden pumpkin that grows
+// from a seed into a giant pumpkin.
package creature
import (
@@ -23,20 +24,22 @@ const (
MossSprout Species = iota
Mushroom
Cactus
+ Pumpkin
)
// AllSpecies lists every species, default first.
-var AllSpecies = []Species{MossSprout, Mushroom, Cactus}
+var AllSpecies = []Species{MossSprout, Mushroom, Cactus, Pumpkin}
-var speciesNames = [...]string{"Brote de musgo", "Hongo", "Cactus"}
+var speciesNames = [...]string{"Brote de musgo", "Hongo", "Cactus", "Calabaza"}
-var speciesSlugs = [...]string{"moss", "mushroom", "cactus"}
+var speciesSlugs = [...]string{"moss", "mushroom", "cactus", "pumpkin"}
// Names of the stages of each species, in growth order.
var speciesStageNames = [...][5]string{
MossSprout: {"Semilla", "Brote", "Retoño", "Arbusto", "Árbol ancestral"},
Mushroom: {"Espora", "Botón", "Seta", "Seta grande", "Corro de setas"},
Cactus: {"Yema", "Bolita", "Cactus", "Cactus con brazos", "Cactus centenario"},
+ Pumpkin: {"Pepita", "Plántula", "Calabacita", "Calabaza", "Calabaza gigante"},
}
// Name is the Spanish name of the species.
diff --git a/internal/creature/creature_test.go b/internal/creature/creature_test.go
index 57b0073..b4775e2 100644
--- a/internal/creature/creature_test.go
+++ b/internal/creature/creature_test.go
@@ -216,7 +216,7 @@ func TestDrawChangesWithMoodAndAccessories(t *testing.T) {
}
func TestSpeciesNamesAndSlugs(t *testing.T) {
- if len(AllSpecies) != 3 || AllSpecies[0] != MossSprout {
+ if len(AllSpecies) != 4 || AllSpecies[0] != MossSprout {
t.Fatalf("AllSpecies = %v, want the default first", AllSpecies)
}
if (Creature{}).Species != MossSprout {
@@ -246,6 +246,9 @@ func TestSpeciesNamesAndSlugs(t *testing.T) {
if Cactus.StageName(Seed) != "Yema" || Cactus.StageName(Sprout) != "Bolita" || Cactus.StageName(Sapling) != "Cactus" || Cactus.StageName(Shrub) != "Cactus con brazos" || Cactus.StageName(Ancient) != "Cactus centenario" {
t.Error("unexpected cactus stage names")
}
+ if Pumpkin.StageName(Seed) != "Pepita" || Pumpkin.StageName(Sprout) != "Plántula" || Pumpkin.StageName(Sapling) != "Calabacita" || Pumpkin.StageName(Shrub) != "Calabaza" || Pumpkin.StageName(Ancient) != "Calabaza gigante" {
+ t.Error("unexpected pumpkin stage names")
+ }
}
func TestSpeciesByName(t *testing.T) {
@@ -253,6 +256,7 @@ func TestSpeciesByName(t *testing.T) {
"": MossSprout, " ": MossSprout, "moss": MossSprout, "MOSS": MossSprout, "musgo": MossSprout, "Brote de musgo": MossSprout,
"mushroom": Mushroom, " Mushroom ": Mushroom, "hongo": Mushroom, "seta": Mushroom,
"cactus": Cactus, " CACTUS ": Cactus,
+ "pumpkin": Pumpkin, " PUMPKIN ": Pumpkin, "calabaza": Pumpkin, "Calabaza": Pumpkin,
}
for in, want := range ok {
got, found := SpeciesByName(in)
@@ -336,3 +340,14 @@ func TestCactusMapsAreNotTheOthers(t *testing.T) {
}
}
}
+
+// The pumpkin is its own design: it must not reuse another species' maps.
+func TestPumpkinMapsAreNotTheOthers(t *testing.T) {
+ for _, st := range Stages {
+ for name, other := range map[string]stageArt{"moss": mossArt[st], "mushroom": mushroomArt[st], "cactus": cactusArt[st]} {
+ if pumpkinArt[st].body == other.body {
+ t.Errorf("stage %d: pumpkin body equals the %s body", st, name)
+ }
+ }
+ }
+}
diff --git a/internal/creature/pumpkin.go b/internal/creature/pumpkin.go
new file mode 100644
index 0000000..9200cdd
--- /dev/null
+++ b/internal/creature/pumpkin.go
@@ -0,0 +1,137 @@
+package creature
+
+// Pixel maps of the pumpkin. K ink, P paper, G moss, H honey, R accent,
+// . transparent. An original design: a garden pumpkin with eyes. It grows
+// from a cream seed with a curl of a shoot (the face is on the seed) into a
+// seedling still wearing its seed coat between two round leaves, a small
+// striped green pumpkin, a ribbed orange pumpkin with a leaf and a tendril,
+// and a giant pumpkin that fills the whole map. The ribs are accent pixels and
+// curve around the face, which always sits on a fill; the hat sits on the
+// stem, the flower on a leaf or a tendril and the scarf wraps the lower part.
+var pumpkinArt = [...]stageArt{
+ Seed: {
+ body: [Size]string{
+ "................",
+ "................",
+ "................",
+ "................",
+ ".........KKK....",
+ "........KGGGK...",
+ "........KGKK....",
+ "......KKKGK.....",
+ ".....KHHHHK.....",
+ "....KHPPPPHK....",
+ "...KHPPPPPPHK...",
+ "...KHPPPPPPHK...",
+ "...KHPPPPPPHK...",
+ "...KHPPPPPPHK...",
+ "....KHHHHHHK....",
+ ".....KKKKKK.....",
+ },
+ face: Point{4, 10},
+ hat: Point{5, 4},
+ scarfRow: 14,
+ scarfTail: Point{9, 15},
+ flower: Point{11, 6},
+ },
+ Sprout: {
+ body: [Size]string{
+ "................",
+ "................",
+ "................",
+ "......KKKK......",
+ "..KKKKHPPHKKKK..",
+ ".KGGGGKHHKGGGGK.",
+ "KGGGGGGKKGGGGGGK",
+ "KGGGGGGGGGGGGGGK",
+ "KGGGGGGGGGGGGGGK",
+ "KGGGGGGGGGGGGGGK",
+ ".KGGGGGGGGGGGGK.",
+ "..KKKKKGGKKKKK..",
+ "......KGGK......",
+ ".....KKGGKK.....",
+ "...KKHHHHHHKK...",
+ "..KKKKKKKKKKKK..",
+ },
+ face: Point{4, 7},
+ hat: Point{5, 0},
+ scarfRow: 11,
+ scarfTail: Point{9, 12},
+ flower: Point{12, 3},
+ },
+ Sapling: {
+ body: [Size]string{
+ "................",
+ "................",
+ "................",
+ "................",
+ ".......KK.......",
+ "......KGGKKKK...",
+ "......KGGKGGGK..",
+ "...KKKKGGKKKK...",
+ "..KPGGGKKGGGPK..",
+ "..KPGGGGGGGGPK..",
+ "..KPGGGGGGGGPK..",
+ "..KPGGGGGGGGPK..",
+ "..KPGGGGGGGGPK..",
+ "...KPGGGGGGPK...",
+ "....KKKKKKKK....",
+ "................",
+ },
+ face: Point{4, 9},
+ hat: Point{5, 3},
+ scarfRow: 13,
+ scarfTail: Point{9, 14},
+ flower: Point{11, 4},
+ },
+ Shrub: {
+ body: [Size]string{
+ "................",
+ "................",
+ ".......KK.......",
+ "..GG..KGGK.KKK..",
+ ".G..G.KGGKKGGGK.",
+ ".G.G..KGGKGGGK..",
+ "..GKKKKGGKKKK...",
+ "..KHRHHKKHHRHK..",
+ ".KHRHHHHHHHHRHK.",
+ ".KHRHHHHHHHHRHK.",
+ ".KHRHHHHHHHHRHK.",
+ ".KHRHHHHHHHHRHK.",
+ ".KHRHHHHHHHHRHK.",
+ ".KHRHHHHHHHHRHK.",
+ "..KHRHHHHHHRHK..",
+ "...KKKKKKKKKK...",
+ },
+ face: Point{4, 9},
+ hat: Point{5, 2},
+ scarfRow: 13,
+ scarfTail: Point{9, 14},
+ flower: Point{11, 3},
+ },
+ Ancient: {
+ body: [Size]string{
+ "......KK........",
+ "..KKKKGGK...GG..",
+ ".KGGGKGGK..G..G.",
+ "KGGGGKGGK..G.G..",
+ ".KKKKKGGKKKKG...",
+ ".KHHHRHKKHRHHHK.",
+ "KHPHRHHHHHHRHHHK",
+ "KHPRHHHHHHHHRHHK",
+ "KHHRHHHHHHHHRHHK",
+ "KHHRHHHHHHHHRHHK",
+ "KHHRHHHHHHHHRHHK",
+ "KHHRHHHHHHHHRHHK",
+ "KHHRHHHHHHHHRHHK",
+ "KHHHRHHHHHHRHHHK",
+ ".KHHHRHHHHRHHHK.",
+ "..KKKKKKKKKKKK..",
+ },
+ face: Point{4, 8},
+ hat: Point{5, 0},
+ scarfRow: 13,
+ scarfTail: Point{9, 14},
+ flower: Point{12, 1},
+ },
+}
diff --git a/internal/creature/sprites.go b/internal/creature/sprites.go
index 43b520b..ed8efe4 100644
--- a/internal/creature/sprites.go
+++ b/internal/creature/sprites.go
@@ -98,6 +98,8 @@ func artFor(sp Species, st Stage) stageArt {
return mushroomArt[st]
case Cactus:
return cactusArt[st]
+ case Pumpkin:
+ return pumpkinArt[st]
default:
return mossArt[st]
}
diff --git a/internal/gallery/gallery.go b/internal/gallery/gallery.go
index 4cc0190..971f772 100644
--- a/internal/gallery/gallery.go
+++ b/internal/gallery/gallery.go
@@ -205,7 +205,8 @@ func Build(dir, version string) (int, error) {
intros := map[creature.Species]string{
creature.MossSprout: "La especie predeterminada: un brote de musgo con ojos que acaba siendo un árbol ancestral.",
creature.Mushroom: "Una seta pequeña con ojos que empieza siendo una espora y acaba en un corro de setas. Se elige con species: mushroom.",
- creature.Cactus: "Un cactus pequeño en su maceta que empieza siendo una semilla y acaba en un cactus centenario. Se elige con species: cactus.",
+ creature.Cactus: "Un cactus pequeño en su maceta que empieza siendo una yema y acaba en un cactus centenario. Se elige con species: cactus.",
+ creature.Pumpkin: "Una calabaza de huerto con ojos que empieza siendo una pepita y acaba en una calabaza gigante. Se elige con species: pumpkin.",
}
for _, sp := range creature.AllSpecies {
// The default species keeps the file names of the first release.
diff --git a/internal/gallery/gallery_test.go b/internal/gallery/gallery_test.go
index 9ac58f6..083ad0b 100644
--- a/internal/gallery/gallery_test.go
+++ b/internal/gallery/gallery_test.go
@@ -40,7 +40,7 @@ func TestBuild(t *testing.T) {
// dark, plus favicon, og.png, generator.js, page.js, theme.js, install.js, style.css and index (the hero of the header is
// one of the accessory cards), plus the compact badges: per species, one
// per stage, light and dark.
- if want := 3*(20+4)*2 + 8 + 3*5*2; n != want {
+ if want := 4*(20+4)*2 + 8 + 4*5*2; n != want {
t.Fatalf("Build wrote %d files, want %d", n, want)
}
@@ -518,6 +518,18 @@ func TestPageDoesNotOverflowOnPhones(t *testing.T) {
if got := rules.propIn(t, phone, "td:first-child", "white-space"); got != "normal" {
t.Errorf("on a phone the first column of a table must be able to wrap, white-space = %q", got)
}
+ // The stages table lists the names of every species in one cell
+ // («Semilla / Espora / Yema / Pepita»): with nowrap it pushed the page
+ // wider between ~720 and ~1350 px. The names wrap; «desde 2.500 XP» does not.
+ if got := rules.prop(".fases td:first-child", "white-space"); got != "normal" {
+ t.Errorf("the names of the stages table must wrap at every width, white-space = %q", got)
+ }
+ if got := rules.prop(".fases td:last-child", "white-space"); got != "nowrap" {
+ t.Errorf("the XP of the stages table must stay on one line, white-space = %q", got)
+ }
+ if !strings.Contains(readRepoFile(t, "internal/gallery/index.html.tmpl"), `
`+"\n
Fases
") {
+ t.Error(`the stages table must have class="fases"`)
+ }
for _, r := range rules {
last := r.sel[strings.LastIndex(r.sel, " ")+1:]
@@ -585,6 +597,43 @@ func TestFullCardsAreByteIdenticalToV050(t *testing.T) {
}
}
+// A new species must not change a single byte of what v1.0.0 drew for the
+// species it already had: every card (full and compact, light and dark) of
+// the moss sprout, the mushroom and the cactus, and favicon.svg.
+// testdata/golden/v1.0.0-sha256.txt holds their SHA-256, taken from the
+// v1.0.0 gallery.
+func TestCardsAreByteIdenticalToV100(t *testing.T) {
+ list, err := os.ReadFile("../../testdata/golden/v1.0.0-sha256.txt")
+ if err != nil {
+ t.Fatal(err)
+ }
+ dir := t.TempDir()
+ if _, err := Build(dir, "test"); err != nil {
+ t.Fatal(err)
+ }
+ n := 0
+ for _, line := range strings.Split(strings.TrimSpace(strings.ReplaceAll(string(list), "\r\n", "\n")), "\n") {
+ sum, name, ok := strings.Cut(line, " ")
+ if !ok {
+ t.Fatalf("bad line in the golden list: %q", line)
+ }
+ data, err := os.ReadFile(filepath.Join(dir, filepath.FromSlash(name)))
+ if err != nil {
+ t.Errorf("%s: %v", name, err)
+ continue
+ }
+ if got := fmt.Sprintf("%x", sha256.Sum256(data)); got != sum {
+ t.Errorf("%s changed: sha256 %s, v1.0.0 had %s", name, got, sum)
+ }
+ n++
+ }
+ // Three species × (20 stage cards + 4 accessory cards + 5 compact
+ // badges) × light and dark, plus favicon.svg.
+ if want := 3*(20+4+5)*2 + 1; n != want {
+ t.Errorf("the golden list has %d files, want %d", n, want)
+ }
+}
+
// The gallery shows the compact badge for every stage of both species, light
// and dark, at its real size, with its own text alternative.
func TestGalleryShowsTheCompactBadge(t *testing.T) {
diff --git a/internal/gallery/index.html.tmpl b/internal/gallery/index.html.tmpl
index af7e6cb..4ccfcb4 100644
--- a/internal/gallery/index.html.tmpl
+++ b/internal/gallery/index.html.tmpl
@@ -17,7 +17,7 @@
-
+
@@ -204,7 +204,7 @@
Experiencia
{{range .XP}}
{{.Name}}
{{.When}}
{{end}}
-
+
Fases
{{range .Stages}}
{{.Name}}
{{.When}}
{{end}}
@@ -235,6 +235,7 @@
{{range $i, $c := .GenSpecies}}
{{end}}
+
La calabaza llega con la versión 1.1.0: si tu workflow falla con «especie no válida», es que @v1 aún no la incluye; vuelve a ejecutarlo cuando se publique.