2020 fail-fast : false
2121 matrix :
2222 service :
23- - { name: gateway, context: api_gateway, dockerfile: api_gateway/Dockerfile }
23+ - { name: bridge, context: api_gateway, dockerfile: api_gateway/Dockerfile }
2424 - { name: quartermaster, context: api_tenants, dockerfile: api_tenants/Dockerfile }
2525 - { name: commodore, context: api_control, dockerfile: api_control/Dockerfile }
2626 - { name: purser, context: api_billing, dockerfile: api_billing/Dockerfile }
7373 with :
7474 context : .
7575 file : ${{ matrix.service.dockerfile }}
76+ platforms : linux/amd64,linux/arm64
7677 push : true
7778 provenance : true
7879 sbom : true
9495 cat > dist/image-${{ matrix.service.name }}.json <<JSON
9596 {
9697 "name": "${{ matrix.service.name }}",
97- "image": "${{ env.REGISTRY }} /frameworks-${{ matrix.service.name }}:${{ github.ref_name }}",
98+ "image": "docker.io/livepeerframeworks /frameworks-${{ matrix.service.name }}:${{ github.ref_name }}",
9899 "digest": "${{ steps.build.outputs.digest }}",
99100 "service_version": "${{ steps.svcver.outputs.version }}",
100101 "git_commit": "${{ github.sha }}"
@@ -197,6 +198,7 @@ jobs:
197198 password : ${{ secrets.DOCKERHUB_TOKEN }}
198199
199200 - name : Build and push Docker image
201+ id : build
200202 uses : docker/build-push-action@v5
201203 with :
202204 context : .
@@ -211,6 +213,24 @@ jobs:
211213 cache-from : type=gha,scope=${{ matrix.webapp.name }}
212214 cache-to : type=gha,mode=max,scope=${{ matrix.webapp.name }}
213215
216+ - name : Write digest artifact
217+ run : |
218+ mkdir -p dist
219+ cat > dist/image-${{ matrix.webapp.name }}.json <<JSON
220+ {
221+ "name": "${{ matrix.webapp.name }}",
222+ "image": "docker.io/livepeerframeworks/frameworks-${{ matrix.webapp.name }}:${{ github.ref_name }}",
223+ "digest": "${{ steps.build.outputs.digest }}",
224+ "git_commit": "${{ github.sha }}"
225+ }
226+ JSON
227+
228+ - name : Upload digest artifact
229+ uses : actions/upload-artifact@v4
230+ with :
231+ name : image-digest-${{ matrix.webapp.name }}
232+ path : dist/image-${{ matrix.webapp.name }}.json
233+
214234 - name : Upload static bundle artifact
215235 uses : actions/upload-artifact@v4
216236 with :
@@ -265,9 +285,83 @@ jobs:
265285 name : cli-${{ matrix.goos }}-${{ matrix.goarch }}
266286 path : dist/cli/*
267287
288+ build-service-binaries :
289+ name : Build Service Binaries
290+ runs-on : ubuntu-latest
291+ strategy :
292+ fail-fast : false
293+ matrix :
294+ service :
295+ - { name: bridge, context: api_gateway, cmd_path: ./cmd/bridge }
296+ - { name: quartermaster, context: api_tenants, cmd_path: ./cmd/quartermaster }
297+ - { name: commodore, context: api_control, cmd_path: ./cmd/commodore }
298+ - { name: purser, context: api_billing, cmd_path: ./cmd/purser }
299+ - { name: foghorn, context: api_balancing, cmd_path: ./cmd/foghorn }
300+ - { name: decklog, context: api_firehose, cmd_path: cmd/decklog/main.go }
301+ - { name: periscope-ingest, context: api_analytics_ingest, cmd_path: ./cmd/periscope }
302+ - { name: periscope-query, context: api_analytics_query, cmd_path: ./cmd/periscope }
303+ - { name: signalman, context: api_realtime, cmd_path: ./cmd/signalman }
304+ - { name: helmsman, context: api_sidecar, cmd_path: ./cmd/helmsman }
305+ arch :
306+ - { goos: linux, goarch: amd64 }
307+ - { goos: linux, goarch: arm64 }
308+ steps :
309+ - name : Checkout
310+ uses : actions/checkout@v4
311+
312+ - name : Setup Go
313+ uses : actions/setup-go@v5
314+ with :
315+ go-version : ' 1.22'
316+ cache-dependency-path : ${{ matrix.service.context }}/go.sum
317+
318+ - name : Read service version
319+ id : svcver
320+ run : |
321+ VERSION_FILE="${{ matrix.service.context }}/VERSION"
322+ if [[ -f "$VERSION_FILE" ]]; then
323+ echo "version=$(tr -d '\n' < "$VERSION_FILE")" >> "$GITHUB_OUTPUT"
324+ else
325+ echo "version=0.0.0" >> "$GITHUB_OUTPUT"
326+ fi
327+
328+ - name : Build binary
329+ env :
330+ GOOS : ${{ matrix.arch.goos }}
331+ GOARCH : ${{ matrix.arch.goarch }}
332+ CGO_ENABLED : 0
333+ run : |
334+ set -euo pipefail
335+ cd ${{ matrix.service.context }}
336+
337+ BINARY_NAME="frameworks-${{ matrix.service.name }}-${{ matrix.arch.goos }}-${{ matrix.arch.goarch }}"
338+ OUTPUT="frameworks-${{ matrix.service.name }}-${{ github.ref_name }}-${{ matrix.arch.goos }}-${{ matrix.arch.goarch }}"
339+
340+ go build -ldflags "\
341+ -X frameworks/pkg/version.Version=${{ github.ref_name }} \
342+ -X frameworks/pkg/version.GitCommit=${{ github.sha }} \
343+ -X frameworks/pkg/version.BuildDate=$(date -u '+%Y-%m-%dT%H:%M:%SZ') \
344+ -X frameworks/pkg/version.ComponentName=${{ matrix.service.name }} \
345+ -X frameworks/pkg/version.ComponentVersion=${{ steps.svcver.outputs.version }} \
346+ -s -w" \
347+ -o "${BINARY_NAME}" \
348+ ${{ matrix.service.cmd_path }}
349+
350+ # Create output directory
351+ mkdir -p ../dist/binaries/${{ matrix.service.name }}
352+
353+ # Package binary with platform version in filename
354+ tar czf "../dist/binaries/${{ matrix.service.name }}/${OUTPUT}.tar.gz" "${BINARY_NAME}"
355+
356+ - name : Upload binary artifact
357+ uses : actions/upload-artifact@v4
358+ with :
359+ name : binary-${{ matrix.service.name }}-${{ matrix.arch.goos }}-${{ matrix.arch.goarch }}
360+ path : dist/binaries/${{ matrix.service.name }}/*.tar.gz
361+
268362 manifest :
269363 name : Generate & Publish Manifest
270- needs : [build-images, build-webapps, build-cli]
364+ needs : [build-images, build-webapps, build-cli, build-service-binaries ]
271365 runs-on : ubuntu-latest
272366 steps :
273367 - uses : actions/checkout@v4
@@ -277,22 +371,121 @@ jobs:
277371 pattern : image-digest-*
278372 path : dist/digests
279373 merge-multiple : true
374+
375+ - name : Fetch MistServer metadata from GitHub Release
376+ id : mistserver
377+ run : |
378+ set -euo pipefail
379+
380+ # Fetch Docker metadata from MistServer GitHub Release
381+ BASE_URL="https://github.com/Livepeer-FrameWorks/mistserver/releases/download/${{ github.ref_name }}"
382+
383+ DIGEST=$(curl -sL "${BASE_URL}/docker-digest.txt" || echo "")
384+ TAG=$(curl -sL "${BASE_URL}/docker-tag.txt" || echo "")
385+
386+ if [[ -z "$DIGEST" || -z "$TAG" ]]; then
387+ echo "WARNING: Could not fetch MistServer metadata, using fallback"
388+ DIGEST="sha256:unknown"
389+ TAG="latest"
390+ fi
391+
392+ echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT"
393+ echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
394+ echo "Fetched MistServer metadata - Tag: ${TAG}, Digest: ${DIGEST}"
395+
280396 - name : Generate manifest
281397 run : |
282- echo "platform_version: ${{ github.ref_name }}" > dist/manifest.yaml
283- echo "git_commit: ${{ github.sha }}" >> dist/manifest.yaml
284- echo "release_date: $(date -u '+%Y-%m-%dT%H:%M:%SZ')" >> dist/manifest.yaml
398+ set -euo pipefail
399+
400+ # Header
401+ cat > dist/manifest.yaml <<YAML
402+ platform_version: ${{ github.ref_name }}
403+ git_commit: ${{ github.sha }}
404+ release_date: $(date -u '+%Y-%m-%dT%H:%M:%SZ')
405+
406+ YAML
407+
408+ # Services (microservices with service_version field)
285409 echo "services:" >> dist/manifest.yaml
286410 for f in dist/digests/*.json; do
287411 name=$(jq -r .name "$f")
412+ # Skip webapp/website - they go in interfaces section
413+ if [[ "$name" == "webapp" || "$name" == "website" ]]; then
414+ continue
415+ fi
416+
288417 image=$(jq -r .image "$f")
289418 digest=$(jq -r .digest "$f")
290419 svcver=$(jq -r .service_version "$f")
291- echo " - name: ${name}" >> dist/manifest.yaml
292- echo " service_version: ${svcver}" >> dist/manifest.yaml
293- echo " image: ${image}" >> dist/manifest.yaml
294- echo " digest: ${digest}" >> dist/manifest.yaml
420+
421+ cat >> dist/manifest.yaml <<YAML
422+ - name: ${name}
423+ service_version: ${svcver}
424+ image: ${image}
425+ digest: ${digest}
426+ YAML
427+ done
428+
429+ # Native Binaries (for bare-metal deployment)
430+ echo "" >> dist/manifest.yaml
431+ echo "native_binaries:" >> dist/manifest.yaml
432+ for f in dist/digests/*.json; do
433+ name=$(jq -r .name "$f")
434+ # Skip webapp/website - they don't have native binaries
435+ if [[ "$name" == "webapp" || "$name" == "website" ]]; then
436+ continue
437+ fi
438+
439+ cat >> dist/manifest.yaml <<YAML
440+ - name: ${name}
441+ artifacts:
442+ - arch: linux-amd64
443+ file: frameworks-${name}-${{ github.ref_name }}-linux-amd64.tar.gz
444+ - arch: linux-arm64
445+ file: frameworks-${name}-${{ github.ref_name }}-linux-arm64.tar.gz
446+ YAML
295447 done
448+
449+ # Interfaces (webapp/website reference implementations)
450+ echo "" >> dist/manifest.yaml
451+ echo "interfaces:" >> dist/manifest.yaml
452+ for f in dist/digests/*.json; do
453+ name=$(jq -r .name "$f")
454+ # Only include webapp/website
455+ if [[ "$name" != "webapp" && "$name" != "website" ]]; then
456+ continue
457+ fi
458+
459+ image=$(jq -r .image "$f")
460+ digest=$(jq -r .digest "$f")
461+
462+ # Determine build dir and bundle name
463+ if [[ "$name" == "webapp" ]]; then
464+ bundle="webapp-build.tar.gz"
465+ else
466+ bundle="website-build.tar.gz"
467+ fi
468+
469+ cat >> dist/manifest.yaml <<YAML
470+ - name: ${name}
471+ image: ${image}
472+ digest: ${digest}
473+ static_bundle: ${bundle}
474+ YAML
475+ done
476+
477+ # External Dependencies (MistServer from separate repo)
478+ echo "" >> dist/manifest.yaml
479+ cat >> dist/manifest.yaml <<YAML
480+ external_dependencies:
481+ - name: mistserver
482+ image: docker.io/livepeerframeworks/mistserver:${{ steps.mistserver.outputs.tag }}
483+ digest: "${{ steps.mistserver.outputs.digest }}"
484+ YAML
485+
486+ # Infrastructure Requirements (from config/infrastructure.yaml)
487+ echo "" >> dist/manifest.yaml
488+ cat config/infrastructure.yaml >> dist/manifest.yaml
296489 - name : Upload manifest artifact
297490 uses : actions/upload-artifact@v4
298491 with :
@@ -304,6 +497,12 @@ jobs:
304497 pattern : cli-*
305498 path : dist/cli
306499 merge-multiple : true
500+ - name : Download service binary artifacts
501+ uses : actions/download-artifact@v4
502+ with :
503+ pattern : binary-*
504+ path : dist/binaries
505+ merge-multiple : true
307506 - name : Download webapp bundle
308507 uses : actions/download-artifact@v4
309508 with :
@@ -323,6 +522,7 @@ jobs:
323522 files : |
324523 dist/manifest.yaml
325524 dist/cli/*
525+ dist/binaries/*.tar.gz
326526 dist/webapp-build.tar.gz
327527 dist/website-build.tar.gz
328528 env :
@@ -369,25 +569,44 @@ jobs:
369569 # Commit and push
370570 git config user.name "github-actions"
371571 git config user.email "[email protected] " 372- git checkout -b "${RELEASE_BRANCH}"
572+
573+ # Fetch branch if it exists
574+ git fetch origin "${RELEASE_BRANCH}" || true
575+
576+ # Create or reset to new content
577+ git checkout -B "${RELEASE_BRANCH}"
373578 git add releases/${{ github.ref_name }}.yaml channels/${channel}.yaml
374579 git commit -m "Release manifest for ${{ github.ref_name }}"
375- git push origin "${RELEASE_BRANCH}"
580+ git push --force origin "${RELEASE_BRANCH}"
376581
377- - name : Create PR to env repo
582+ - name : Create or update PR to env repo
378583 env :
379584 GH_TOKEN : ${{ secrets.GITOPS_REPO_TOKEN }}
380585 run : |
381586 cd env-repo
382- gh pr create \
587+
588+ # Check if PR already exists
589+ PR_NUMBER=$(gh pr list \
383590 --repo "${{ env.ENV_REPO }}" \
384- --base main \
385591 --head "${RELEASE_BRANCH}" \
386- --title "Release ${{ github.ref_name }} manifest" \
387- --body "This PR adds the release manifest for ${{ github.ref_name }}.
592+ --base main \
593+ --json number \
594+ --jq '.[0].number' 2>/dev/null || echo "")
595+
596+ if [[ -n "$PR_NUMBER" ]]; then
597+ echo "PR #${PR_NUMBER} already exists and was updated via branch push"
598+ echo "https://github.com/${{ env.ENV_REPO }}/pull/${PR_NUMBER}"
599+ else
600+ gh pr create \
601+ --repo "${{ env.ENV_REPO }}" \
602+ --base main \
603+ --head "${RELEASE_BRANCH}" \
604+ --title "Release ${{ github.ref_name }} manifest" \
605+ --body "This PR adds the release manifest for ${{ github.ref_name }}.
388606
389607 It maps each service to image@digest and includes component versions.
390608
391609 **Platform Version:** \`${{ github.ref_name }}\`
392610 **Git Commit:** \`${{ github.sha }}\`
393611 **Release Date:** \`$(date -u '+%Y-%m-%dT%H:%M:%SZ')\`"
612+ fi
0 commit comments