-
Notifications
You must be signed in to change notification settings - Fork 0
148 lines (140 loc) · 6.27 KB
/
Copy pathrelease.yml
File metadata and controls
148 lines (140 loc) · 6.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# El tag ES la publicación. `git push origin vX.Y.Z` y esto publica los tres paquetes solo.
#
# Tres paquetes de UN tag, y está bien que sea así: son el mismo contrato en tres idiomas,
# generados del mismo `schema/`. Una versión de python que no fuera la misma que la de typescript
# sería dos wires que dicen ser uno. `scripts/the-version` es quien no lo deja pasar.
#
# El orden importa fuera de este archivo: los otros dos repos dependen de éste por ruta hasta que
# haya una versión publicada. Este repo publica primero, siempre.
name: release
on:
push:
tags: ["v*"]
# Para ensayar el camino sin quemar un número: corre guard + gates + build, y publish queda
# skipped porque no es un tag.
workflow_dispatch:
permissions:
contents: read
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
jobs:
# Una versión de PyPI es permanente: un artefacto cuyo nombre miente no se deshace. Así que la
# primera pregunta es si el tag y lo que el árbol declara dicen lo mismo.
guard:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.declared.outputs.version }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
# En dos líneas y no en una: `echo "version=$(scripts/the-version)"` se come el exit code
# del script — echo sale 0 — y el paso queda VERDE con la versión vacía, que es exactamente
# lo que un guard no puede hacer. Una asignación sí propaga el estado bajo `bash -e`.
- id: declared
run: |
version=$(scripts/the-version)
echo "version=$version" >> "$GITHUB_OUTPUT"
- name: el tag y la versión declarada son el mismo número
if: github.ref_type == 'tag'
run: |
tag="${GITHUB_REF_NAME#v}"
declared="${{ steps.declared.outputs.version }}"
if [ "$tag" != "$declared" ]; then
echo "el tag dice $tag y el árbol declara $declared — uno de los dos está mal" >&2
exit 1
fi
echo "v$declared"
# El gate de verdad, el mismo que corre en cada push: regenerar, diffear, y las tres suites.
gates:
uses: ./.github/workflows/ci.yml
publish-pypi:
needs: [guard, gates]
# PyPI no tiene trusted publisher para `pinecall-protocol`, y un publisher lo da de alta una
# PERSONA en pypi.org: no hay API. Hasta entonces este job no corre — un release que se pinta
# rojo en un paquete que nadie instala de un registro (el runtime toma `../protocol/python` por
# ruta) es un gate que enseña a ignorar el rojo. Para encenderlo: pypi.org → Your projects →
# Publishing → pending publisher (owner `pinecall`, repo `protocol`, workflow `release.yml`,
# environment `pypi`), y después `gh variable set PUBLISH_PYPI --body true`.
if: github.ref_type == 'tag' && vars.PUBLISH_PYPI == 'true'
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write # Trusted Publishing: OIDC, sin token guardado en ningún lado
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- run: uv build
working-directory: python
- uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: python/dist
skip-existing: true # idempotente: relanzar el workflow no es un error
publish-npm:
needs: [guard, gates]
if: github.ref_type == 'tag'
runs-on: ubuntu-latest
environment: npm
permissions:
id-token: write
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 11.2.2
- uses: actions/setup-node@v4
with:
node-version: 24
registry-url: https://registry.npmjs.org
# setup-node con node 24 trae un npm que NO conoce el intercambio OIDC del registro y cae
# EN SILENCIO a un publish anónimo: 404 en el PUT, después de haber dicho que firmó la
# provenance. El éxito de la firma no prueba nada sobre el auth.
- run: npm install -g npm@latest
- run: pnpm install --frozen-lockfile
working-directory: typescript
- run: pnpm run build
working-directory: typescript
# Empaqueta pnpm y publica npm, y cada mitad está acá por un motivo distinto.
#
# `npm pack` NO aplica `publishConfig` — comprobado: el manifiesto que arma sigue diciendo
# `main: ./src/index.ts`, un archivo que `files: ["dist"]` no manda. Un `npm publish` directo
# publicaría un paquete cuyo import falla, en un número que ya no se puede reusar. `pnpm pack`
# sí lo aplica (dist/index.js) y además reescribe cualquier `workspace:`.
#
# Y publica npm porque es npm quien habla OIDC con el registro. El tarball viaja tal cual:
# `npm publish <tgz>` no reempaqueta nada.
- run: pnpm pack --pack-destination /tmp/pkg
working-directory: typescript
# npm nunca toma dos veces el mismo número: si ya está publicado, volver a pedirlo es un run
# rojo que no dice nada nuevo. Esto hace que mover un tag — para corregir ESTE archivo, que
# es lo único que se corre del ref y no de HEAD — no queme el release entero.
- name: publicar, salvo que esta versión ya esté en npm
working-directory: typescript
run: |
version="${{ needs.guard.outputs.version }}"
if npm view "@pinecall/protocol@$version" version >/dev/null 2>&1; then
echo "@pinecall/protocol@$version ya está en npm: el tag sólo lo registra"
exit 0
fi
npm publish --provenance --access public /tmp/pkg/*.tgz
publish-gem:
needs: [guard, gates]
# Igual que PyPI: rubygems.org contesta `No trusted publisher configured for this workflow`.
# Alta en rubygems.org → perfil → trusted publishers (pending), y `gh variable set PUBLISH_GEM
# --body true`. El gem de este repo tampoco lo instala nadie todavía.
if: github.ref_type == 'tag' && vars.PUBLISH_GEM == 'true'
runs-on: ubuntu-latest
environment: rubygems
permissions:
id-token: write
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: "3.2"
- uses: rubygems/release-gem@v1
with:
await-release: false
working-directory: ruby