Skip to content

Commit 34c052e

Browse files
committed
Expand release workflow manifest gen
1 parent 4fd1f5f commit 34c052e

File tree

4 files changed

+315
-18
lines changed

4 files changed

+315
-18
lines changed

.github/workflows/release.yml

Lines changed: 247 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ jobs:
7373
with:
7474
context: .
7575
file: ${{ matrix.service.dockerfile }}
76+
platforms: linux/amd64,linux/arm64
7677
push: true
7778
provenance: true
7879
sbom: true
@@ -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": "${{ env.REGISTRY }}/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: gateway, 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,134 @@ jobs:
277371
pattern: image-digest-*
278372
path: dist/digests
279373
merge-multiple: true
374+
375+
- name: Fetch MistServer digest from GHCR
376+
id: mistserver
377+
env:
378+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
379+
run: |
380+
set -euo pipefail
381+
382+
# Query GHCR API for latest MistServer digest
383+
DIGEST=$(curl -sL \
384+
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
385+
-H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
386+
"https://ghcr.io/v2/livepeer-frameworks/mistserver/manifests/latest" \
387+
| jq -r '.config.digest // empty')
388+
389+
if [[ -z "$DIGEST" ]]; then
390+
echo "WARNING: Could not fetch MistServer digest from GHCR, using placeholder"
391+
DIGEST="sha256:TODO-fetch-failed"
392+
fi
393+
394+
echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT"
395+
echo "Fetched MistServer digest: ${DIGEST}"
396+
280397
- name: Generate manifest
281398
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
399+
set -euo pipefail
400+
401+
# Header
402+
cat > dist/manifest.yaml <<YAML
403+
platform_version: ${{ github.ref_name }}
404+
git_commit: ${{ github.sha }}
405+
release_date: $(date -u '+%Y-%m-%dT%H:%M:%SZ')
406+
407+
YAML
408+
409+
# Services (microservices with service_version field)
285410
echo "services:" >> dist/manifest.yaml
286411
for f in dist/digests/*.json; do
287412
name=$(jq -r .name "$f")
413+
# Skip webapp/website - they go in interfaces section
414+
if [[ "$name" == "webapp" || "$name" == "website" ]]; then
415+
continue
416+
fi
417+
288418
image=$(jq -r .image "$f")
289419
digest=$(jq -r .digest "$f")
290420
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
421+
422+
cat >> dist/manifest.yaml <<YAML
423+
- name: ${name}
424+
service_version: ${svcver}
425+
image: ${image}
426+
digest: ${digest}
427+
YAML
428+
done
429+
430+
# Native Binaries (for bare-metal deployment)
431+
echo "" >> dist/manifest.yaml
432+
echo "native_binaries:" >> dist/manifest.yaml
433+
for f in dist/digests/*.json; do
434+
name=$(jq -r .name "$f")
435+
# Skip webapp/website - they don't have native binaries
436+
if [[ "$name" == "webapp" || "$name" == "website" ]]; then
437+
continue
438+
fi
439+
440+
svcver=$(jq -r .service_version "$f")
441+
442+
cat >> dist/manifest.yaml <<YAML
443+
- name: ${name}
444+
service_version: ${svcver}
445+
deployment_method: systemd
446+
artifacts:
447+
- arch: linux-amd64
448+
file: frameworks-${name}-${{ github.ref_name }}-linux-amd64.tar.gz
449+
- arch: linux-arm64
450+
file: frameworks-${name}-${{ github.ref_name }}-linux-arm64.tar.gz
451+
YAML
452+
done
453+
454+
# Interfaces (webapp/website reference implementations)
455+
echo "" >> dist/manifest.yaml
456+
echo "interfaces:" >> dist/manifest.yaml
457+
for f in dist/digests/*.json; do
458+
name=$(jq -r .name "$f")
459+
# Only include webapp/website
460+
if [[ "$name" != "webapp" && "$name" != "website" ]]; then
461+
continue
462+
fi
463+
464+
image=$(jq -r .image "$f")
465+
digest=$(jq -r .digest "$f")
466+
467+
# Determine build dir and bundle name
468+
if [[ "$name" == "webapp" ]]; then
469+
bundle="webapp-build.tar.gz"
470+
else
471+
bundle="website-build.tar.gz"
472+
fi
473+
474+
cat >> dist/manifest.yaml <<YAML
475+
- name: ${name}
476+
type: reference-implementation
477+
deployment_options: [docker, systemd]
478+
image: ${image}
479+
digest: ${digest}
480+
static_bundle: ${bundle}
481+
note: "White-label example - fork and customize for production deployments"
482+
YAML
295483
done
484+
485+
# External Dependencies (MistServer from separate repo)
486+
echo "" >> dist/manifest.yaml
487+
cat >> dist/manifest.yaml <<YAML
488+
external_dependencies:
489+
- name: mistserver
490+
repository: github.com/Livepeer-FrameWorks/mistserver
491+
image: ghcr.io/livepeer-frameworks/mistserver:latest
492+
digest: "${{ steps.mistserver.outputs.digest }}"
493+
compatibility_level: backwards-compatible
494+
required_by: [helmsman]
495+
deployment_options: [native-preferred, docker]
496+
note: "Built from separate fork - pinning optional due to backwards compatibility"
497+
YAML
498+
499+
# Infrastructure Requirements (from config/infrastructure.yaml)
500+
echo "" >> dist/manifest.yaml
501+
cat config/infrastructure.yaml >> dist/manifest.yaml
296502
- name: Upload manifest artifact
297503
uses: actions/upload-artifact@v4
298504
with:
@@ -304,6 +510,12 @@ jobs:
304510
pattern: cli-*
305511
path: dist/cli
306512
merge-multiple: true
513+
- name: Download service binary artifacts
514+
uses: actions/download-artifact@v4
515+
with:
516+
pattern: binary-*
517+
path: dist/binaries
518+
merge-multiple: true
307519
- name: Download webapp bundle
308520
uses: actions/download-artifact@v4
309521
with:
@@ -323,6 +535,7 @@ jobs:
323535
files: |
324536
dist/manifest.yaml
325537
dist/cli/*
538+
dist/binaries/*/*.tar.gz
326539
dist/webapp-build.tar.gz
327540
dist/website-build.tar.gz
328541
env:
@@ -369,25 +582,44 @@ jobs:
369582
# Commit and push
370583
git config user.name "github-actions"
371584
git config user.email "[email protected]"
372-
git checkout -b "${RELEASE_BRANCH}"
585+
586+
# Fetch branch if it exists
587+
git fetch origin "${RELEASE_BRANCH}" || true
588+
589+
# Create or reset to new content
590+
git checkout -B "${RELEASE_BRANCH}"
373591
git add releases/${{ github.ref_name }}.yaml channels/${channel}.yaml
374592
git commit -m "Release manifest for ${{ github.ref_name }}"
375-
git push origin "${RELEASE_BRANCH}"
593+
git push --force origin "${RELEASE_BRANCH}"
376594
377-
- name: Create PR to env repo
595+
- name: Create or update PR to env repo
378596
env:
379597
GH_TOKEN: ${{ secrets.GITOPS_REPO_TOKEN }}
380598
run: |
381599
cd env-repo
382-
gh pr create \
600+
601+
# Check if PR already exists
602+
PR_NUMBER=$(gh pr list \
383603
--repo "${{ env.ENV_REPO }}" \
384-
--base main \
385604
--head "${RELEASE_BRANCH}" \
386-
--title "Release ${{ github.ref_name }} manifest" \
387-
--body "This PR adds the release manifest for ${{ github.ref_name }}.
605+
--base main \
606+
--json number \
607+
--jq '.[0].number' 2>/dev/null || echo "")
608+
609+
if [[ -n "$PR_NUMBER" ]]; then
610+
echo "PR #${PR_NUMBER} already exists and was updated via branch push"
611+
echo "https://github.com/${{ env.ENV_REPO }}/pull/${PR_NUMBER}"
612+
else
613+
gh pr create \
614+
--repo "${{ env.ENV_REPO }}" \
615+
--base main \
616+
--head "${RELEASE_BRANCH}" \
617+
--title "Release ${{ github.ref_name }} manifest" \
618+
--body "This PR adds the release manifest for ${{ github.ref_name }}.
388619
389620
It maps each service to image@digest and includes component versions.
390621
391622
**Platform Version:** \`${{ github.ref_name }}\`
392623
**Git Commit:** \`${{ github.sha }}\`
393624
**Release Date:** \`$(date -u '+%Y-%m-%dT%H:%M:%SZ')\`"
625+
fi

0 commit comments

Comments
 (0)