Publishing from GitHub Actions
El objetivo es que lanzar una versión sea esto y nada más:
git tag -a v1.2.0 -m "Pegado con formato y atajo configurable."
git push origin v1.2.0Y que compilar, sacar las notas, crear la versión, subir los binarios y publicar lo haga el CI.
Abajo tienes el workflow completo, trozo a trozo y con el porqué de cada pieza. Pégalo en .github/workflows/publicar.yml, cambia APP por el slug de tu ficha y guarda tu clave como secreto.
Antes: el secreto
Crea la clave en claves de API con ámbito publish y nada más, atada a esta aplicación y con caducidad — 365 días está bien para un CI. Guárdala en Settings → Secrets and variables → Actions → New repository secret con el nombre FULGEON_TOKEN.
Sólo publish porque es lo único que hace este workflow. Y atada a la app porque limita el destrozo: si el token se escapa —un registro, un fork, un runner ajeno— quien lo tenga puede publicar en esta ficha y en ninguna otra de tu cuenta.
Cuándo se dispara
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
version:
description: 'Versión a publicar (por ejemplo 1.2.0)'
required: true
ensayo:
description: 'Ensayo: no publica nada'
type: boolean
default: trueCon los tags v* para lo normal, y con ejecución manual para probarlo sin crear un tag. En la manual, la casilla ensayo viene marcada por defecto: enseña lo que haría y no publica nada.
Permisos y concurrencia
permissions:
contents: read
concurrency:
group: fulgeon-publicar
cancel-in-progress: falseEl workflow no escribe en el repositorio —sólo lee el código y el tag—, así que pide el permiso mínimo. Y una publicación empezada no se cancela: si entran dos tags seguidos, el segundo espera. Cancelar a mitad dejaría un borrador incompleto.
Descargar el código
- name: Descargar el código
uses: actions/checkout@v4
with:
fetch-depth: 0fetch-depth: 0 no es capricho: sin el historial completo no se puede leer el cuerpo del tag anotado ni buscar el tag anterior, y de ahí salen las notas.
Compilar
Aquí va tu build. Lo único que importa es que deje los binarios en dist/ y que el nombre diga en qué plataforma corren: el CLI deduce el destino de la extensión (.dmg → macOS, .msi → Windows, .AppImage → Linux) y del nombre (…-arm64…, …-x86_64…). Un .zip no lo deduce nadie: renómbralo o dile el destino a mano.
Compilar los tres sistemas en un solo runner casi nunca sale. Lo normal es un job por plataforma (strategy.matrix) que suba sus binarios con actions/upload-artifact, y que este job los recoja con actions/download-artifact antes de publicar. Publicar se hace una vez y con todo junto: una versión son todos sus archivos.
Versión, canal y notas
El paso Preparar versión y notas saca tres cosas del tag:
- La versión: el nombre del tag sin la
vinicial. - El canal, del propio tag:
*-alpha*esalpha;*-beta*y*-rc*sonbeta; el resto,stable. Unav1.3.0-beta.1no es una estable. - Las notas, de tres fuentes en orden de preferencia:
- El cuerpo del tag anotado. La fuente buena: las notas se escriben al etiquetar, cuando aún te acuerdas de qué cambió.
- La sección de
CHANGELOG.mdcuyo título mencione esa versión. Entra en el primer encabezado que la nombre y sale en el siguiente del mismo nivel o superior, así que los «### Añadido» y «### Corregido» de dentro se conservan. - Último recurso: los commits desde el tag anterior. Feo, pero mejor que publicar sin decir qué cambia.
Si ninguna de las tres llega a diez caracteres, el CLI se planta antes de subir nada. Las notas quedan en NOTAS-VERSION.md, que es lo que se le pasa después con --changelog @NOTAS-VERSION.md.
Ensayo
- name: Ensayo
env:
FULGEON_TOKEN: ${{ secrets.FULGEON_TOKEN }}
run: |
npx --yes @fulgeon/cli@1 publish \
--app "$APP" \
--version "${{ steps.datos.outputs.version }}" \
--channel "${{ steps.datos.outputs.canal }}" \
--changelog @NOTAS-VERSION.md \
--file 'dist/*.dmg' \
--file 'dist/*.exe' \
--file 'dist/*.AppImage' \
--dry-runComprueba clave, ámbitos, app, canal, notas, archivos y destinos, y enseña la tabla de lo que subiría. No crea nada. Cuesta segundos y evita descubrir un error después de subir 400 MB.
El paso empieza mirando que el secreto exista y falla con un mensaje claro si falta, en vez de dejar que el error salga cinco líneas más abajo disfrazado de otra cosa.
Publicar
El mismo comando sin --dry-run, y sólo cuando no era un ensayo. Crea la versión en borrador, sube los binarios y la publica.
Si algo falla a mitad, la versión se queda en borrador —invisible— y el CLI te dice cuántos archivos llegaron: se termina desde el panel, o se borra el borrador y se relanza. Nada queda publicado a medias.
Añade --draft si prefieres revisarla en el panel antes de que se vea.
El resumen del job
El último paso escribe en $GITHUB_STEP_SUMMARY la versión, el canal, el enlace a la ficha y las notas. Así el resultado se lee desde la propia pestaña de Actions sin abrir el registro entero.
Sobre los forks
Los secretos no viajan a los forks, y eso es a propósito. Este workflow sólo se dispara con tags del repositorio: un pull request de fuera no puede publicar nada.
La acción compuesta
Si prefieres un solo paso a llamar a npx, existe como acción pública: JaviHG/fulgeon-publish-action, con las etiquetas v1 y v1.0.0. Instala el CLI, crea la versión, sube los archivos con sus hashes y publica:
- uses: JaviHG/fulgeon-publish-action@v1
with:
token: ${{ secrets.FULGEON_TOKEN }}
app: clipio
version: ${{ github.ref_name }}
changelog: |
Arranque más rápido en frío.
Arreglado el icono de la bandeja en Windows 11.
files: |
dist/*.dmg
dist/*.exe
channel: stableEntradas: token, app, version, changelog (multilínea, o @ruta para leer un fichero del repositorio), files (un patrón por línea), channel, targets, draft, dry-run, api y cli-version. Salidas: url y version-id.