release.yml 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. name: Release
  2. on:
  3. push:
  4. tags:
  5. - "v*"
  6. permissions:
  7. contents: write
  8. jobs:
  9. test:
  10. runs-on: ubuntu-latest
  11. strategy:
  12. matrix:
  13. node-version: [20, 22]
  14. steps:
  15. - uses: actions/checkout@v4
  16. - uses: pnpm/action-setup@v4
  17. with:
  18. version: 9
  19. - uses: actions/setup-node@v4
  20. with:
  21. node-version: ${{ matrix.node-version }}
  22. cache: pnpm
  23. - run: pnpm install --frozen-lockfile
  24. - run: pnpm build
  25. - run: pnpm --filter @actalk/inkos-core typecheck
  26. - run: pnpm --filter @actalk/inkos typecheck
  27. - run: pnpm test
  28. - run: pnpm verify:publish-manifests
  29. smoke-test:
  30. runs-on: ubuntu-latest
  31. needs: test
  32. steps:
  33. - uses: actions/checkout@v4
  34. - uses: pnpm/action-setup@v4
  35. with:
  36. version: 9
  37. - uses: actions/setup-node@v4
  38. with:
  39. node-version: 22
  40. cache: pnpm
  41. - run: pnpm install --frozen-lockfile
  42. - run: pnpm build
  43. - run: pnpm verify:publish-manifests
  44. - name: Smoke test — CLI help and version
  45. run: |
  46. node packages/cli/dist/index.js --help
  47. node packages/cli/dist/index.js --version
  48. - name: Smoke test — core import
  49. run: node -e "import('@actalk/inkos-core').then(m => { if (!m.PipelineRunner) throw new Error('missing PipelineRunner'); console.log('core import OK') })"
  50. - name: Smoke test — verify no workspace:* in tarballs
  51. run: |
  52. set -euo pipefail
  53. for pkg in packages/core packages/cli; do
  54. echo "--- Checking $pkg ---"
  55. cd "$pkg"
  56. PACKDIR=$(mktemp -d)
  57. npm pack --pack-destination "$PACKDIR" 2>/dev/null
  58. TGZ=$(ls "$PACKDIR"/*.tgz)
  59. PACKED_PKG=$(tar -xOf "$TGZ" package/package.json)
  60. if echo "$PACKED_PKG" | grep -q '"workspace:'; then
  61. echo "FAIL: $pkg tarball still contains workspace: protocol"
  62. echo "$PACKED_PKG" | grep 'workspace:'
  63. exit 1
  64. fi
  65. echo "OK: $pkg tarball is clean"
  66. rm -rf "$PACKDIR"
  67. cd - >/dev/null
  68. done
  69. publish-canary:
  70. runs-on: ubuntu-latest
  71. needs: smoke-test
  72. outputs:
  73. release_version: ${{ steps.versions.outputs.release_version }}
  74. canary_version: ${{ steps.versions.outputs.canary_version }}
  75. steps:
  76. - uses: actions/checkout@v4
  77. - uses: pnpm/action-setup@v4
  78. with:
  79. version: 9
  80. - uses: actions/setup-node@v4
  81. with:
  82. node-version: 22
  83. cache: pnpm
  84. registry-url: https://registry.npmjs.org
  85. - run: pnpm install --frozen-lockfile
  86. - run: pnpm build
  87. - run: pnpm verify:publish-manifests
  88. - name: Derive release versions
  89. id: versions
  90. run: |
  91. RELEASE_VERSION="${GITHUB_REF_NAME#v}"
  92. CANARY_VERSION="${RELEASE_VERSION}-canary.${GITHUB_RUN_NUMBER}.${GITHUB_RUN_ATTEMPT}"
  93. echo "release_version=$RELEASE_VERSION" >> "$GITHUB_OUTPUT"
  94. echo "canary_version=$CANARY_VERSION" >> "$GITHUB_OUTPUT"
  95. - name: Rewrite package versions for canary publish
  96. run: |
  97. node scripts/set-package-versions.mjs "${{ steps.versions.outputs.canary_version }}" --root .
  98. pnpm verify:publish-manifests
  99. - name: Publish core canary
  100. working-directory: packages/core
  101. run: pnpm publish --tag canary --access public --no-git-checks
  102. env:
  103. NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
  104. - name: Publish CLI canary
  105. working-directory: packages/cli
  106. run: pnpm publish --tag canary --access public --no-git-checks
  107. env:
  108. NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
  109. verify-canary:
  110. runs-on: ubuntu-latest
  111. needs: publish-canary
  112. steps:
  113. - uses: actions/setup-node@v4
  114. with:
  115. node-version: 22
  116. registry-url: https://registry.npmjs.org
  117. - name: Verify canary dist-tag and installation
  118. run: |
  119. set -euo pipefail
  120. EXPECTED_CANARY="${{ needs.publish-canary.outputs.canary_version }}"
  121. ACTUAL_CANARY=""
  122. for _ in 1 2 3 4 5 6; do
  123. ACTUAL_CANARY=$(npm view @actalk/inkos@canary version 2>/dev/null || true)
  124. if [ "$ACTUAL_CANARY" = "$EXPECTED_CANARY" ]; then
  125. break
  126. fi
  127. sleep 10
  128. done
  129. if [ "$ACTUAL_CANARY" != "$EXPECTED_CANARY" ]; then
  130. echo "FATAL: canary dist-tag mismatch: expected $EXPECTED_CANARY got ${ACTUAL_CANARY:-<empty>}"
  131. exit 1
  132. fi
  133. TMPDIR=$(mktemp -d)
  134. cd "$TMPDIR"
  135. npm init -y
  136. npm install "@actalk/inkos@$EXPECTED_CANARY"
  137. if grep -q '"workspace:' node_modules/@actalk/inkos/package.json; then
  138. echo "FATAL: canary CLI package still contains workspace: protocol"
  139. cat node_modules/@actalk/inkos/package.json | grep workspace
  140. exit 1
  141. fi
  142. if grep -q '"workspace:' node_modules/@actalk/inkos-core/package.json; then
  143. echo "FATAL: canary core package still contains workspace: protocol"
  144. cat node_modules/@actalk/inkos-core/package.json | grep workspace
  145. exit 1
  146. fi
  147. npx inkos --version
  148. echo "Canary verification passed"
  149. rm -rf "$TMPDIR"
  150. publish-release:
  151. runs-on: ubuntu-latest
  152. needs: [publish-canary, verify-canary]
  153. steps:
  154. - uses: actions/checkout@v4
  155. - uses: pnpm/action-setup@v4
  156. with:
  157. version: 9
  158. - uses: actions/setup-node@v4
  159. with:
  160. node-version: 22
  161. cache: pnpm
  162. registry-url: https://registry.npmjs.org
  163. - run: pnpm install --frozen-lockfile
  164. - run: pnpm build
  165. - run: pnpm verify:publish-manifests
  166. - name: Rewrite package versions for final publish
  167. run: |
  168. node scripts/set-package-versions.mjs "${{ needs.publish-canary.outputs.release_version }}" --root .
  169. pnpm verify:publish-manifests
  170. - name: Publish core latest
  171. working-directory: packages/core
  172. run: pnpm publish --access public --no-git-checks
  173. env:
  174. NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
  175. - name: Publish CLI latest
  176. working-directory: packages/cli
  177. run: pnpm publish --access public --no-git-checks
  178. env:
  179. NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
  180. verify-release:
  181. runs-on: ubuntu-latest
  182. needs: [publish-canary, publish-release]
  183. steps:
  184. - uses: actions/setup-node@v4
  185. with:
  186. node-version: 22
  187. registry-url: https://registry.npmjs.org
  188. - name: Verify latest dist-tag and installation
  189. run: |
  190. set -euo pipefail
  191. EXPECTED_RELEASE="${{ needs.publish-canary.outputs.release_version }}"
  192. ACTUAL_LATEST=""
  193. for _ in 1 2 3 4 5 6; do
  194. ACTUAL_LATEST=$(npm view @actalk/inkos@latest version 2>/dev/null || true)
  195. if [ "$ACTUAL_LATEST" = "$EXPECTED_RELEASE" ]; then
  196. break
  197. fi
  198. sleep 10
  199. done
  200. if [ "$ACTUAL_LATEST" != "$EXPECTED_RELEASE" ]; then
  201. echo "FATAL: latest dist-tag mismatch: expected $EXPECTED_RELEASE got ${ACTUAL_LATEST:-<empty>}"
  202. exit 1
  203. fi
  204. TMPDIR=$(mktemp -d)
  205. cd "$TMPDIR"
  206. npm init -y
  207. npm install "@actalk/inkos@$EXPECTED_RELEASE"
  208. if grep -q '"workspace:' node_modules/@actalk/inkos/package.json; then
  209. echo "FATAL: released CLI package still contains workspace: protocol"
  210. cat node_modules/@actalk/inkos/package.json | grep workspace
  211. exit 1
  212. fi
  213. if grep -q '"workspace:' node_modules/@actalk/inkos-core/package.json; then
  214. echo "FATAL: released core package still contains workspace: protocol"
  215. cat node_modules/@actalk/inkos-core/package.json | grep workspace
  216. exit 1
  217. fi
  218. npx inkos --version
  219. echo "Release verification passed"
  220. rm -rf "$TMPDIR"
  221. github-release:
  222. runs-on: ubuntu-latest
  223. needs: verify-release
  224. steps:
  225. - uses: actions/checkout@v4
  226. with:
  227. fetch-depth: 0
  228. - name: Generate release notes
  229. id: notes
  230. run: |
  231. PREV_TAG=$(git tag --sort=-creatordate | sed -n '2p' || echo "")
  232. if [ -n "$PREV_TAG" ]; then
  233. echo "notes<<EOF" >> "$GITHUB_OUTPUT"
  234. git log --pretty=format:"- %s (%h)" "$PREV_TAG"..HEAD >> "$GITHUB_OUTPUT"
  235. echo "" >> "$GITHUB_OUTPUT"
  236. echo "EOF" >> "$GITHUB_OUTPUT"
  237. else
  238. echo "notes=Initial release" >> "$GITHUB_OUTPUT"
  239. fi
  240. - uses: softprops/action-gh-release@v2
  241. with:
  242. body: ${{ steps.notes.outputs.notes }}
  243. generate_release_notes: true