# Publicar una versión

Las versiones se publican desde GitHub Actions al subir un tag `vX.Y.Z`
(`.github/workflows/release.yml`). Nadie compila binarios a mano.

## Pasos

1. Actualiza `version` en `cmd/koru/main.go` y `packages/javascript/package.json`.
2. Mueve lo de `## [Unreleased]` de `CHANGELOG.md` a una sección
   `## [X.Y.Z] - AAAA-MM-DD` y actualiza los enlaces del final.
3. Comprueba en local:

   ```sh
   scripts/release.sh vX.Y.Z      # archivos en dist/ (borra dist/ antes)
   scripts/test-install.sh dist   # instalador contra esos archivos
   ```

4. Commit en `main`, y después:

   ```sh
   git tag -a vX.Y.Z -m "koru vX.Y.Z"
   git push origin vX.Y.Z
   ```

## Qué hace el workflow

Todo corre en una sola máquina Linux: Go compila de forma cruzada macOS y
Windows, así que no hay runners de esos sistemas.

1. Ejecuta `scripts/ci.sh`, lo mismo que `ci.yml` y que puedes lanzar en local:
   gofmt, `go vet` para linux, darwin y windows, compilación de los tests para
   darwin y windows, tests con `-race`, módulo JS, shellcheck, sintaxis de
   `install.ps1` (si hay `pwsh`), un APK de prueba y el test del instalador.
2. `scripts/release.sh` compila con `CGO_ENABLED=0 -trimpath` para
   linux, darwin y windows en amd64 y arm64, e inyecta versión y commit con
   `-ldflags`. Falla si el tag no coincide con `cmd/koru/main.go`.
3. Publica por cada plataforma `koru_X.Y.Z_<os>_<arch>.tar.gz` (`.zip` en
   Windows) con su `.sha256`, un `SHA256SUMS` con todos, e `install.sh` /
   `install.ps1`.
4. Añade `selftest.apk` (la app de `examples/selftest` construida con esa
   versión) con su `.sha256`, y lo incluye en `SHA256SUMS`.
5. Firma la procedencia con `actions/attest-build-provenance` (Sigstore). Se
   verifica con `gh attestation verify <archivo> --repo wux4an/koru`.
6. Crea la release con las notas de la sección de `CHANGELOG.md` (falla si no
   existe). Los tags con sufijo (`v1.0.0-rc.1`) salen como prerelease; el resto
   como última versión, que es la que leen los instaladores y la web.
7. Instala la versión publicada con `install.sh` y comprueba `koru version`.
8. Regenera `Formula/koru.rb` (Homebrew) y `bucket/koru.json` (Scoop) con
   `scripts/package-managers.sh`, hace commit en `main` como
   `github-actions[bot]` e instala la fórmula con Homebrew en la misma máquina.
   Este repositorio es a la vez el tap y el bucket, sin repos aparte. Los
   prereleases no actualizan los paquetes.
   Los binarios de macOS y Windows y `install.ps1` no se ejecutan en CI: solo
   se compilan y se comprueba su sintaxis.

También se puede publicar sin subir el tag: *Actions → Release → Run workflow*
en `main` con el tag (`v0.0.1`). Si el tag no existe, el workflow lo crea en ese
commit cuando han pasado todas las comprobaciones. Si ya existe, rehace la
release con ese tag y sustituye los archivos y las notas.

## Builds reproducibles

Mismo commit y misma versión de Go dan los mismos bytes: `-trimpath`,
`-buildid=` vacío, y archivos con fecha del commit, orden fijo y sin
usuario. Para comprobar una release, recompila el tag con la versión de Go del
workflow y compara `SHA256SUMS`.

## Instaladores

- `scripts/install.sh` (POSIX `sh`, Linux y macOS): resuelve la última versión
  siguiendo la redirección de `/releases/latest` (sin la API ni su límite),
  descarga por HTTPS, verifica el SHA-256 antes de instalar y escribe en
  `~/.local/bin` de forma atómica. Opciones: `--version`, `--dir`,
  `KORU_VERSION`, `KORU_INSTALL_DIR`.
- `scripts/install.ps1` (Windows): igual, instala en
  `%LOCALAPPDATA%\Programs\koru` y lo añade al `PATH` del usuario.

El sitio publica ambos en `https://koru.js.org/install.sh` y
`https://koru.js.org/install.ps1` al desplegarse.

## Firma de selftest.apk

Si existe el secret `SELFTEST_SIGNING_KEY` (una identidad PEM creada con
`koru keygen --output selftest.pem`), CI y releases firman `selftest.apk` siempre
con esa clave, y cada versión se instala encima de la anterior. Sin el secret,
cada build crea una clave nueva y hay que desinstalar la app de pruebas antes de
instalar otra; las notas de la release lo avisan.
