2727 - minor
2828 - major
2929
30- env :
31- REGISTRY : ghcr.io
32- IMAGE_NAME : ${{ github.repository }}
33-
3430permissions :
3531 contents : write
36- packages : write
3732
3833jobs :
3934 release :
5550 app-id : ${{ secrets.PINEFORGE_APP_ID }}
5651 private-key : ${{ secrets.PINEFORGE_APP_PRIVATE_KEY }}
5752 owner : pineforge-4pass
58- repositories : pineforge-engine,pineforge-release
53+ repositories : pineforge-engine
5954
6055 - name : Checkout (full history)
6156 uses : actions/checkout@v4
@@ -137,8 +132,6 @@ jobs:
137132 env :
138133 TAG : ${{ steps.bump.outputs.tag }}
139134 PREV : ${{ steps.prev.outputs.prev }}
140- REGISTRY : ${{ env.REGISTRY }}
141- IMAGE : ${{ env.IMAGE_NAME }}
142135 run : |
143136 set -euo pipefail
144137 {
@@ -155,61 +148,13 @@ jobs:
155148 fi
156149 echo
157150 echo
158- echo "### Container image "
151+ echo "### Prebuilt libraries "
159152 echo
160- echo '```'
161- echo "docker pull ${REGISTRY}/${IMAGE}:${TAG#v}"
162- echo '```'
153+ echo "Per-arch static-lib + headers tarballs are attached below (C ABI)."
154+ echo "For the full PineScript -> backtest image, use \`ghcr.io/pineforge-4pass/pineforge-release\`."
163155 } > RELEASE_NOTES.md
164156 echo "path=RELEASE_NOTES.md" >> "$GITHUB_OUTPUT"
165157
166- - name : Build context metadata
167- id : ctx
168- run : |
169- echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
170- echo "date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_OUTPUT"
171-
172- - name : Set up QEMU
173- uses : docker/setup-qemu-action@v3
174-
175- - name : Set up Buildx
176- uses : docker/setup-buildx-action@v3
177-
178- - name : Log in to GHCR
179- uses : docker/login-action@v3
180- with :
181- registry : ${{ env.REGISTRY }}
182- username : ${{ github.actor }}
183- password : ${{ secrets.GITHUB_TOKEN }}
184-
185- - name : Compute image tags
186- id : meta
187- uses : docker/metadata-action@v5
188- with :
189- images : ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
190- tags : |
191- type=semver,pattern={{version}},value=${{ steps.bump.outputs.tag }}
192- type=semver,pattern={{major}}.{{minor}},value=${{ steps.bump.outputs.tag }}
193- type=semver,pattern={{major}},value=${{ steps.bump.outputs.tag }}
194- type=raw,value=latest
195- type=sha,format=short,prefix=,value=${{ steps.ctx.outputs.sha }}
196-
197- - name : Build and push image
198- uses : docker/build-push-action@v6
199- with :
200- context : .
201- file : docker/Dockerfile
202- platforms : linux/amd64,linux/arm64
203- push : true
204- tags : ${{ steps.meta.outputs.tags }}
205- labels : ${{ steps.meta.outputs.labels }}
206- build-args : |
207- PINEFORGE_VERSION=${{ steps.bump.outputs.next }}
208- PINEFORGE_GIT_SHA=${{ steps.ctx.outputs.sha }}
209- PINEFORGE_BUILD_DATE=${{ steps.ctx.outputs.date }}
210- cache-from : type=gha
211- cache-to : type=gha,mode=max
212-
213158 - name : Create GitHub Release
214159 env :
215160 GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
@@ -221,28 +166,6 @@ jobs:
221166 --notes-file "${NOTES}" \
222167 --verify-tag
223168
224- # Every engine release notifies the pineforge-release hub, which bumps its
225- # ENGINE_VERSION pin, rebuilds the combined image (FROM this engine image +
226- # the bundled codegen), and fans out to the MCPs. The hub is the sole
227- # downstream entry point now — we no longer dispatch to mcp-public directly.
228- #
229- # FAIL LOUD (no continue-on-error): a dropped notify means downstream never
230- # rebuilds, and that silent failure is exactly the bug this replaces. The
231- # credential is the org GitHub App (no PAT — minted at runtime, never
232- # expires); `gh api` exits non-zero on HTTP >=4xx so `set -e` fails the job.
233- - name : Dispatch engine-release -> pineforge-release
234- env :
235- GH_TOKEN : ${{ steps.app.outputs.token }}
236- ENGINE_TAG : ${{ steps.bump.outputs.tag }}
237- RUN_ID : ${{ github.run_id }}
238- run : |
239- set -euo pipefail
240- gh api repos/pineforge-4pass/pineforge-release/dispatches \
241- -f event_type=engine-release \
242- -F "client_payload[version]=${ENGINE_TAG}" \
243- -F "client_payload[run_id]=${RUN_ID}"
244- echo "dispatched engine-release -> pineforge-release (${ENGINE_TAG})"
245-
246169 # Prebuilt static-lib + headers tarballs, one per (os, arch). Attached
247170 # to the GitHub Release created above. Tarball layout:
248171 # pineforge-vX.Y.Z-{triple}/
@@ -349,3 +272,34 @@ jobs:
349272 SHASUM : ${{ steps.pack.outputs.sha256 }}
350273 run : |
351274 gh release upload "${TAG}" "${TARBALL}" "${SHASUM}" --clobber
275+
276+ # Notify the pineforge-release hub AFTER the tarballs are uploaded — the hub
277+ # builds FROM the engine static-lib tarball, so it must exist first. The hub
278+ # bumps its ENGINE_VERSION pin, rebuilds the combined image, and fans out to
279+ # the MCPs. FAIL LOUD (no continue-on-error): a dropped notify means downstream
280+ # never rebuilds. Credential is the org GitHub App (no PAT).
281+ notify-hub :
282+ needs : [release, prebuilt]
283+ runs-on : ubuntu-latest
284+ steps :
285+ - name : Mint org App token (hub dispatch)
286+ id : app
287+ uses : actions/create-github-app-token@v1
288+ with :
289+ app-id : ${{ secrets.PINEFORGE_APP_ID }}
290+ private-key : ${{ secrets.PINEFORGE_APP_PRIVATE_KEY }}
291+ owner : pineforge-4pass
292+ repositories : pineforge-release
293+
294+ - name : Dispatch engine-release -> pineforge-release
295+ env :
296+ GH_TOKEN : ${{ steps.app.outputs.token }}
297+ ENGINE_TAG : ${{ needs.release.outputs.tag }}
298+ RUN_ID : ${{ github.run_id }}
299+ run : |
300+ set -euo pipefail
301+ gh api repos/pineforge-4pass/pineforge-release/dispatches \
302+ -f event_type=engine-release \
303+ -F "client_payload[version]=${ENGINE_TAG}" \
304+ -F "client_payload[run_id]=${RUN_ID}"
305+ echo "dispatched engine-release -> pineforge-release (${ENGINE_TAG})"
0 commit comments