Lo que lograrás
Construirás un pipeline de release que convierte un tag Git en un artefacto desplegado — firmado, probado, versionado y publicado.
El contrato
- Push de un tag que coincida con
v*.*.*. - CI construye, prueba, firma y publica.
- Se crea un release de GitHub con notas auto-generadas.
- El deployment se registra y verifica.
Paso 1: convenciones de tag
git tag -a v1.4.0 -m "Release 1.4.0"
git tag -s v1.4.0 -m "Release 1.4.0"
Paso 2: configurar el workflow
# .github/workflows/release.yml
name: release
on:
push:
tags: ['v*.*.*']
permissions:
contents: write
packages: write
id-token: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'
- run: npm ci
- run: npm run lint
- run: npm test
- run: npm run build
- run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Paso 3: auto-generar notas de release
- name: Generate notes
id: notes
uses: orhun/git-cliff-action@v2
with:
config: cliff.toml
args: --latest --strip header
- name: Create release
uses: softprops/action-gh-release@v2
with:
body: ${{ steps.notes.outputs.content }}
generate_release_notes: true
Paso 4: matriz de build para releases multi-plataforma
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- run: cargo build --release
- uses: actions/upload-artifact@v4
with:
name: bin-${{ matrix.os }}
path: target/release/myapp*
Paso 5: artefactos firmados
- uses: sigstore/cosign-installer@v3
- run: |
cosign sign-blob --yes \
--output-certificate cert.pem \
--output-signature sig.bin \
dist/myapp-1.4.0.tgz
Paso 6: release de imagen Docker
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v5
with:
push: true
tags: |
ghcr.io/${{ github.repository }}:${{ github.ref_name }}
ghcr.io/${{ github.repository }}:latest
Paso 7: deploy tras publish
- name: Deploy to production
run: ./scripts/deploy.sh
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
Paso 8: notificación
- name: Slack notify
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "Released ${{ github.ref_name }} :rocket:"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
Paso 9: canales de pre-release
jobs:
release:
steps:
- id: classify
run: |
if [[ "${{ github.ref_name }}" =~ -(alpha|beta|rc) ]]; then
echo "tag=next" >> $GITHUB_OUTPUT
else
echo "tag=latest" >> $GITHUB_OUTPUT
fi
- run: npm publish --tag ${{ steps.classify.outputs.tag }}
Paso 10: integrar con release-please
Paso 11: tagear el SHA en artefactos
- name: Get short SHA
id: vars
run: echo "sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- run: docker build -t myapp:${{ github.ref_name }} -t myapp:${{ steps.vars.outputs.sha }} .
Paso 12: protección de tags
gh api -X POST repos/owner/repo/tags/protection \
-F pattern='v*.*.*'
Trampas comunes
- El pipeline corre en cada push.
- Force-push de tags tras release.
- Secretos en logs CI.
- Build desde main en push de tag.