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,133 @@ jobs:
277371 pattern : image-digest-*
278372 path : dist/digests
279373 merge-multiple : true
374+
375+ - name : Fetch MistServer digest from DockerHub
376+ id : mistserver
377+ run : |
378+ set -euo pipefail
379+
380+ # Get manifest digest from DockerHub
381+ DIGEST=$(curl -sI \
382+ -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
383+ "https://registry-1.docker.io/v2/livepeerframeworks/mistserver/manifests/latest" \
384+ | grep -i docker-content-digest \
385+ | awk '{print $2}' \
386+ | tr -d '\r')
387+
388+ if [[ -z "$DIGEST" ]]; then
389+ echo "WARNING: Could not fetch MistServer digest from DockerHub, using placeholder"
390+ DIGEST="sha256:TODO-fetch-failed"
391+ fi
392+
393+ echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT"
394+ echo "Fetched MistServer 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+ svcver=$(jq -r .service_version "$f")
440+
441+ cat >> dist/manifest.yaml <<YAML
442+ - name: ${name}
443+ service_version: ${svcver}
444+ deployment_method: systemd
445+ artifacts:
446+ - arch: linux-amd64
447+ file: frameworks-${name}-${{ github.ref_name }}-linux-amd64.tar.gz
448+ - arch: linux-arm64
449+ file: frameworks-${name}-${{ github.ref_name }}-linux-arm64.tar.gz
450+ YAML
295451 done
452+
453+ # Interfaces (webapp/website reference implementations)
454+ echo "" >> dist/manifest.yaml
455+ echo "interfaces:" >> dist/manifest.yaml
456+ for f in dist/digests/*.json; do
457+ name=$(jq -r .name "$f")
458+ # Only include webapp/website
459+ if [[ "$name" != "webapp" && "$name" != "website" ]]; then
460+ continue
461+ fi
462+
463+ image=$(jq -r .image "$f")
464+ digest=$(jq -r .digest "$f")
465+
466+ # Determine build dir and bundle name
467+ if [[ "$name" == "webapp" ]]; then
468+ bundle="webapp-build.tar.gz"
469+ else
470+ bundle="website-build.tar.gz"
471+ fi
472+
473+ cat >> dist/manifest.yaml <<YAML
474+ - name: ${name}
475+ type: reference-implementation
476+ deployment_options: [docker, systemd]
477+ image: ${image}
478+ digest: ${digest}
479+ static_bundle: ${bundle}
480+ note: "White-label example - fork and customize for production deployments"
481+ YAML
482+ done
483+
484+ # External Dependencies (MistServer from separate repo)
485+ echo "" >> dist/manifest.yaml
486+ cat >> dist/manifest.yaml <<YAML
487+ external_dependencies:
488+ - name: mistserver
489+ repository: github.com/Livepeer-FrameWorks/mistserver
490+ image: docker.io/livepeerframeworks/mistserver:latest
491+ digest: "${{ steps.mistserver.outputs.digest }}"
492+ compatibility_level: backwards-compatible
493+ required_by: [helmsman]
494+ deployment_options: [native-preferred, docker]
495+ note: "Built from separate fork - pinning optional due to backwards compatibility"
496+ YAML
497+
498+ # Infrastructure Requirements (from config/infrastructure.yaml)
499+ echo "" >> dist/manifest.yaml
500+ cat config/infrastructure.yaml >> dist/manifest.yaml
296501 - name : Upload manifest artifact
297502 uses : actions/upload-artifact@v4
298503 with :
@@ -304,6 +509,12 @@ jobs:
304509 pattern : cli-*
305510 path : dist/cli
306511 merge-multiple : true
512+ - name : Download service binary artifacts
513+ uses : actions/download-artifact@v4
514+ with :
515+ pattern : binary-*
516+ path : dist/binaries
517+ merge-multiple : true
307518 - name : Download webapp bundle
308519 uses : actions/download-artifact@v4
309520 with :
@@ -323,6 +534,7 @@ jobs:
323534 files : |
324535 dist/manifest.yaml
325536 dist/cli/*
537+ dist/binaries/*.tar.gz
326538 dist/webapp-build.tar.gz
327539 dist/website-build.tar.gz
328540 env :
@@ -369,25 +581,44 @@ jobs:
369581 # Commit and push
370582 git config user.name "github-actions"
371583 git config user.email "[email protected] " 372- git checkout -b "${RELEASE_BRANCH}"
584+
585+ # Fetch branch if it exists
586+ git fetch origin "${RELEASE_BRANCH}" || true
587+
588+ # Create or reset to new content
589+ git checkout -B "${RELEASE_BRANCH}"
373590 git add releases/${{ github.ref_name }}.yaml channels/${channel}.yaml
374591 git commit -m "Release manifest for ${{ github.ref_name }}"
375- git push origin "${RELEASE_BRANCH}"
592+ git push --force origin "${RELEASE_BRANCH}"
376593
377- - name : Create PR to env repo
594+ - name : Create or update PR to env repo
378595 env :
379596 GH_TOKEN : ${{ secrets.GITOPS_REPO_TOKEN }}
380597 run : |
381598 cd env-repo
382- gh pr create \
599+
600+ # Check if PR already exists
601+ PR_NUMBER=$(gh pr list \
383602 --repo "${{ env.ENV_REPO }}" \
384- --base main \
385603 --head "${RELEASE_BRANCH}" \
386- --title "Release ${{ github.ref_name }} manifest" \
387- --body "This PR adds the release manifest for ${{ github.ref_name }}.
604+ --base main \
605+ --json number \
606+ --jq '.[0].number' 2>/dev/null || echo "")
607+
608+ if [[ -n "$PR_NUMBER" ]]; then
609+ echo "PR #${PR_NUMBER} already exists and was updated via branch push"
610+ echo "https://github.com/${{ env.ENV_REPO }}/pull/${PR_NUMBER}"
611+ else
612+ gh pr create \
613+ --repo "${{ env.ENV_REPO }}" \
614+ --base main \
615+ --head "${RELEASE_BRANCH}" \
616+ --title "Release ${{ github.ref_name }} manifest" \
617+ --body "This PR adds the release manifest for ${{ github.ref_name }}.
388618
389619 It maps each service to image@digest and includes component versions.
390620
391621 **Platform Version:** \`${{ github.ref_name }}\`
392622 **Git Commit:** \`${{ github.sha }}\`
393623 **Release Date:** \`$(date -u '+%Y-%m-%dT%H:%M:%SZ')\`"
624+ fi
0 commit comments