Skip to content

Expand release workflow manifest gen #14

Expand release workflow manifest gen

Expand release workflow manifest gen #14

Workflow file for this run

name: Release Platform
on:
push:
tags:
- 'v*'
permissions:
contents: write # create release, upload assets
packages: write # push images to GHCR
env:
REGISTRY: ghcr.io/livepeer-frameworks
jobs:
build-images:
name: Build & Push Images
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
service:
- { name: bridge, context: api_gateway, dockerfile: api_gateway/Dockerfile }
- { name: quartermaster, context: api_tenants, dockerfile: api_tenants/Dockerfile }
- { name: commodore, context: api_control, dockerfile: api_control/Dockerfile }
- { name: purser, context: api_billing, dockerfile: api_billing/Dockerfile }
- { name: foghorn, context: api_balancing, dockerfile: api_balancing/Dockerfile }
- { name: decklog, context: api_firehose, dockerfile: api_firehose/Dockerfile }
- { name: periscope-ingest, context: api_analytics_ingest, dockerfile: api_analytics_ingest/Dockerfile }
- { name: periscope-query, context: api_analytics_query, dockerfile: api_analytics_query/Dockerfile }
- { name: signalman, context: api_realtime, dockerfile: api_realtime/Dockerfile }
- { name: helmsman, context: api_sidecar, dockerfile: api_sidecar/Dockerfile }
- { name: forms-api, context: api_forms, dockerfile: api_forms/Dockerfile }
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Log in to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Read service component version (optional)
id: svcver
run: |
VERSION_FILE="${{ matrix.service.context }}/VERSION"
PACKAGE_JSON="${{ matrix.service.context }}/package.json"
if [[ -f "$VERSION_FILE" ]]; then
echo "version=$(tr -d '\n' < "$VERSION_FILE")" >> "$GITHUB_OUTPUT"
elif [[ -f "$PACKAGE_JSON" ]]; then
echo "version=$(jq -r .version "$PACKAGE_JSON")" >> "$GITHUB_OUTPUT"
else
echo "version=0.0.0" >> "$GITHUB_OUTPUT"
fi
- name: Build & push ${{ matrix.service.name }}
id: build
uses: docker/build-push-action@v5
with:
context: .
file: ${{ matrix.service.dockerfile }}
platforms: linux/amd64,linux/arm64
push: true
provenance: true
sbom: true
build-args: |
VERSION=${{ github.ref_name }}
GIT_COMMIT=${{ github.sha }}
BUILD_DATE=${{ github.event.repository.updated_at }}
COMPONENT_NAME=${{ matrix.service.name }}
COMPONENT_VERSION=${{ steps.svcver.outputs.version }}
tags: |
${{ env.REGISTRY }}/frameworks-${{ matrix.service.name }}:${{ github.ref_name }}
livepeerframeworks/frameworks-${{ matrix.service.name }}:${{ github.ref_name }}
cache-from: type=gha,scope=${{ matrix.service.name }}
cache-to: type=gha,mode=max,scope=${{ matrix.service.name }}
- name: Write digest artifact
run: |
mkdir -p dist
cat > dist/image-${{ matrix.service.name }}.json <<JSON
{
"name": "${{ matrix.service.name }}",
"image": "docker.io/livepeerframeworks/frameworks-${{ matrix.service.name }}:${{ github.ref_name }}",
"digest": "${{ steps.build.outputs.digest }}",
"service_version": "${{ steps.svcver.outputs.version }}",
"git_commit": "${{ github.sha }}"
}
JSON
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: image-digest-${{ matrix.service.name }}
path: dist/image-${{ matrix.service.name }}.json
build-webapps:
name: Build Web Applications
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
webapp:
- { name: webapp, context: website_application, env_prefix: VITE, build_dir: build }
- { name: website, context: website_marketing, env_prefix: VITE, build_dir: dist }
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 9
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
cache-dependency-path: 'pnpm-lock.yaml'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build player package
run: |
cd npm_player
pnpm run build
- name: Build application
env:
# webapp (website_application) variables
VITE_AUTH_URL: ${{ secrets.VITE_AUTH_URL }}
VITE_GRAPHQL_HTTP_URL: ${{ secrets.VITE_GRAPHQL_HTTP_URL }}
VITE_GRAPHQL_WS_URL: ${{ secrets.VITE_GRAPHQL_WS_URL }}
VITE_RTMP_DOMAIN: ${{ secrets.VITE_RTMP_DOMAIN }}
VITE_HTTP_DOMAIN: ${{ secrets.VITE_HTTP_DOMAIN }}
VITE_CDN_DOMAIN: ${{ secrets.VITE_CDN_DOMAIN }}
VITE_RTMP_PATH: ${{ secrets.VITE_RTMP_PATH }}
VITE_HLS_PATH: ${{ secrets.VITE_HLS_PATH }}
VITE_WEBRTC_PATH: ${{ secrets.VITE_WEBRTC_PATH }}
VITE_EMBED_PATH: ${{ secrets.VITE_EMBED_PATH }}
VITE_MARKETING_SITE_URL: ${{ secrets.VITE_MARKETING_SITE_URL }}
VITE_TURNSTILE_AUTH_SITE_KEY: ${{ secrets.VITE_TURNSTILE_AUTH_SITE_KEY }}
# website (website_marketing) variables
VITE_APP_URL: ${{ secrets.VITE_APP_URL }}
VITE_CONTACT_API_URL: ${{ secrets.VITE_CONTACT_API_URL }}
VITE_GATEWAY_URL: ${{ secrets.VITE_GATEWAY_URL }}
VITE_GITHUB_URL: ${{ secrets.VITE_GITHUB_URL }}
VITE_LIVEPEER_URL: ${{ secrets.VITE_LIVEPEER_URL }}
VITE_LIVEPEER_EXPLORER_URL: ${{ secrets.VITE_LIVEPEER_EXPLORER_URL }}
VITE_CONTACT_EMAIL: ${{ secrets.VITE_CONTACT_EMAIL }}
VITE_FORUM_URL: ${{ secrets.VITE_FORUM_URL }}
VITE_DISCORD_URL: ${{ secrets.VITE_DISCORD_URL }}
VITE_DEMO_STREAM_NAME: ${{ secrets.VITE_DEMO_STREAM_NAME }}
VITE_COMPANY_NAME: ${{ secrets.VITE_COMPANY_NAME }}
VITE_DOMAIN: ${{ secrets.VITE_DOMAIN }}
VITE_TURNSTILE_FORMS_SITE_KEY: ${{ secrets.VITE_TURNSTILE_FORMS_SITE_KEY }}
run: |
cd ${{ matrix.webapp.context }}
pnpm run build
- name: Create static bundle
run: |
cd ${{ matrix.webapp.context }}
tar czf ../${{ matrix.webapp.name }}-build.tar.gz ${{ matrix.webapp.build_dir }}/
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Log in to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
id: build
uses: docker/build-push-action@v5
with:
context: .
file: ${{ matrix.webapp.context }}/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
provenance: true
sbom: true
tags: |
${{ env.REGISTRY }}/frameworks-${{ matrix.webapp.name }}:${{ github.ref_name }}
livepeerframeworks/frameworks-${{ matrix.webapp.name }}:${{ github.ref_name }}
cache-from: type=gha,scope=${{ matrix.webapp.name }}
cache-to: type=gha,mode=max,scope=${{ matrix.webapp.name }}
- name: Write digest artifact
run: |
mkdir -p dist
cat > dist/image-${{ matrix.webapp.name }}.json <<JSON
{
"name": "${{ matrix.webapp.name }}",
"image": "docker.io/livepeerframeworks/frameworks-${{ matrix.webapp.name }}:${{ github.ref_name }}",
"digest": "${{ steps.build.outputs.digest }}",
"git_commit": "${{ github.sha }}"
}
JSON
- name: Upload digest artifact
uses: actions/upload-artifact@v4
with:
name: image-digest-${{ matrix.webapp.name }}
path: dist/image-${{ matrix.webapp.name }}.json
- name: Upload static bundle artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.webapp.name }}-bundle
path: ${{ matrix.webapp.name }}-build.tar.gz
build-cli:
name: Build CLI binaries
runs-on: ubuntu-latest
strategy:
matrix:
include:
- goos: linux
goarch: amd64
ext: ""
- goos: linux
goarch: arm64
ext: ""
- goos: darwin
goarch: amd64
ext: ""
- goos: darwin
goarch: arm64
ext: ""
steps:
- uses: actions/checkout@v4
- name: Sanitize platform tag to component version
id: compver
run: |
ver="${GITHUB_REF_NAME#v}"
echo "value=${ver}" >> "$GITHUB_OUTPUT"
- uses: actions/setup-go@v5
with:
go-version: '1.22'
cache-dependency-path: cli/go.sum
- name: Build CLI ${{ matrix.goos }}/${{ matrix.goarch }}
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
CGO_ENABLED: 0
run: |
set -euo pipefail
cd cli
out="frameworks-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.ext }}"
go build -ldflags "-X frameworks/pkg/version.Version=${{ github.ref_name }} -X frameworks/pkg/version.GitCommit=${{ github.sha }} -X frameworks/pkg/version.BuildDate=$(date -u '+%Y-%m-%dT%H:%M:%SZ') -X frameworks/pkg/version.ComponentName=cli -X frameworks/pkg/version.ComponentVersion=${{ steps.compver.outputs.value }}" -o "$out" .
cd ..
mkdir -p dist/cli
mv "cli/$out" "dist/cli/$out"
- name: Upload CLI artifacts
uses: actions/upload-artifact@v4
with:
name: cli-${{ matrix.goos }}-${{ matrix.goarch }}
path: dist/cli/*
build-service-binaries:
name: Build Service Binaries
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
service:
- { name: bridge, context: api_gateway, cmd_path: ./cmd/bridge }
- { name: quartermaster, context: api_tenants, cmd_path: ./cmd/quartermaster }
- { name: commodore, context: api_control, cmd_path: ./cmd/commodore }
- { name: purser, context: api_billing, cmd_path: ./cmd/purser }
- { name: foghorn, context: api_balancing, cmd_path: ./cmd/foghorn }
- { name: decklog, context: api_firehose, cmd_path: cmd/decklog/main.go }
- { name: periscope-ingest, context: api_analytics_ingest, cmd_path: ./cmd/periscope }
- { name: periscope-query, context: api_analytics_query, cmd_path: ./cmd/periscope }
- { name: signalman, context: api_realtime, cmd_path: ./cmd/signalman }
- { name: helmsman, context: api_sidecar, cmd_path: ./cmd/helmsman }
arch:
- { goos: linux, goarch: amd64 }
- { goos: linux, goarch: arm64 }
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
cache-dependency-path: ${{ matrix.service.context }}/go.sum
- name: Read service version
id: svcver
run: |
VERSION_FILE="${{ matrix.service.context }}/VERSION"
if [[ -f "$VERSION_FILE" ]]; then
echo "version=$(tr -d '\n' < "$VERSION_FILE")" >> "$GITHUB_OUTPUT"
else
echo "version=0.0.0" >> "$GITHUB_OUTPUT"
fi
- name: Build binary
env:
GOOS: ${{ matrix.arch.goos }}
GOARCH: ${{ matrix.arch.goarch }}
CGO_ENABLED: 0
run: |
set -euo pipefail
cd ${{ matrix.service.context }}
BINARY_NAME="frameworks-${{ matrix.service.name }}-${{ matrix.arch.goos }}-${{ matrix.arch.goarch }}"
OUTPUT="frameworks-${{ matrix.service.name }}-${{ github.ref_name }}-${{ matrix.arch.goos }}-${{ matrix.arch.goarch }}"
go build -ldflags "\
-X frameworks/pkg/version.Version=${{ github.ref_name }} \
-X frameworks/pkg/version.GitCommit=${{ github.sha }} \
-X frameworks/pkg/version.BuildDate=$(date -u '+%Y-%m-%dT%H:%M:%SZ') \
-X frameworks/pkg/version.ComponentName=${{ matrix.service.name }} \
-X frameworks/pkg/version.ComponentVersion=${{ steps.svcver.outputs.version }} \
-s -w" \
-o "${BINARY_NAME}" \
${{ matrix.service.cmd_path }}
# Create output directory
mkdir -p ../dist/binaries/${{ matrix.service.name }}
# Package binary with platform version in filename
tar czf "../dist/binaries/${{ matrix.service.name }}/${OUTPUT}.tar.gz" "${BINARY_NAME}"
- name: Upload binary artifact
uses: actions/upload-artifact@v4
with:
name: binary-${{ matrix.service.name }}-${{ matrix.arch.goos }}-${{ matrix.arch.goarch }}
path: dist/binaries/${{ matrix.service.name }}/*.tar.gz
manifest:
name: Generate & Publish Manifest
needs: [build-images, build-webapps, build-cli, build-service-binaries]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download image digest artifacts
uses: actions/download-artifact@v4
with:
pattern: image-digest-*
path: dist/digests
merge-multiple: true
- name: Fetch MistServer digest from DockerHub
id: mistserver
run: |
set -euo pipefail
# Get manifest digest from DockerHub
DIGEST=$(curl -sI \
-H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
"https://registry-1.docker.io/v2/livepeerframeworks/mistserver/manifests/latest" \
| grep -i docker-content-digest \
| awk '{print $2}' \
| tr -d '\r')
if [[ -z "$DIGEST" ]]; then
echo "WARNING: Could not fetch MistServer digest from DockerHub, using placeholder"
DIGEST="sha256:TODO-fetch-failed"
fi
echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT"
echo "Fetched MistServer digest: ${DIGEST}"
- name: Generate manifest
run: |
set -euo pipefail
# Header
cat > dist/manifest.yaml <<YAML
platform_version: ${{ github.ref_name }}
git_commit: ${{ github.sha }}
release_date: $(date -u '+%Y-%m-%dT%H:%M:%SZ')
YAML
# Services (microservices with service_version field)
echo "services:" >> dist/manifest.yaml
for f in dist/digests/*.json; do
name=$(jq -r .name "$f")
# Skip webapp/website - they go in interfaces section
if [[ "$name" == "webapp" || "$name" == "website" ]]; then
continue
fi
image=$(jq -r .image "$f")
digest=$(jq -r .digest "$f")
svcver=$(jq -r .service_version "$f")
cat >> dist/manifest.yaml <<YAML
- name: ${name}
service_version: ${svcver}
image: ${image}
digest: ${digest}
YAML
done
# Native Binaries (for bare-metal deployment)
echo "" >> dist/manifest.yaml
echo "native_binaries:" >> dist/manifest.yaml
for f in dist/digests/*.json; do
name=$(jq -r .name "$f")
# Skip webapp/website - they don't have native binaries
if [[ "$name" == "webapp" || "$name" == "website" ]]; then
continue
fi
svcver=$(jq -r .service_version "$f")
cat >> dist/manifest.yaml <<YAML
- name: ${name}
service_version: ${svcver}
deployment_method: systemd
artifacts:
- arch: linux-amd64
file: frameworks-${name}-${{ github.ref_name }}-linux-amd64.tar.gz
- arch: linux-arm64
file: frameworks-${name}-${{ github.ref_name }}-linux-arm64.tar.gz
YAML
done
# Interfaces (webapp/website reference implementations)
echo "" >> dist/manifest.yaml
echo "interfaces:" >> dist/manifest.yaml
for f in dist/digests/*.json; do
name=$(jq -r .name "$f")
# Only include webapp/website
if [[ "$name" != "webapp" && "$name" != "website" ]]; then
continue
fi
image=$(jq -r .image "$f")
digest=$(jq -r .digest "$f")
# Determine build dir and bundle name
if [[ "$name" == "webapp" ]]; then
bundle="webapp-build.tar.gz"
else
bundle="website-build.tar.gz"
fi
cat >> dist/manifest.yaml <<YAML
- name: ${name}
type: reference-implementation
deployment_options: [docker, systemd]
image: ${image}
digest: ${digest}
static_bundle: ${bundle}
note: "White-label example - fork and customize for production deployments"
YAML
done
# External Dependencies (MistServer from separate repo)
echo "" >> dist/manifest.yaml
cat >> dist/manifest.yaml <<YAML
external_dependencies:
- name: mistserver
repository: github.com/Livepeer-FrameWorks/mistserver
image: docker.io/livepeerframeworks/mistserver:latest
digest: "${{ steps.mistserver.outputs.digest }}"
compatibility_level: backwards-compatible
required_by: [helmsman]
deployment_options: [native-preferred, docker]
note: "Built from separate fork - pinning optional due to backwards compatibility"
YAML
# Infrastructure Requirements (from config/infrastructure.yaml)
echo "" >> dist/manifest.yaml
cat config/infrastructure.yaml >> dist/manifest.yaml
- name: Upload manifest artifact
uses: actions/upload-artifact@v4
with:
name: release-manifest
path: dist/manifest.yaml
- name: Download CLI artifacts
uses: actions/download-artifact@v4
with:
pattern: cli-*
path: dist/cli
merge-multiple: true
- name: Download service binary artifacts
uses: actions/download-artifact@v4
with:
pattern: binary-*
path: dist/binaries
merge-multiple: true
- name: Download webapp bundle
uses: actions/download-artifact@v4
with:
name: webapp-bundle
path: dist/
- name: Download website bundle
uses: actions/download-artifact@v4
with:
name: website-bundle
path: dist/
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
name: ${{ github.ref_name }}
generate_release_notes: true
files: |
dist/manifest.yaml
dist/cli/*
dist/binaries/*.tar.gz
dist/webapp-build.tar.gz
dist/website-build.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
update-env:
name: Update GitOps Environment Repo
needs: [manifest]
runs-on: ubuntu-latest
env:
ENV_REPO: Livepeer-FrameWorks/gitops
RELEASE_BRANCH: release/${{ github.ref_name }}
steps:
- name: Checkout env repo
uses: actions/checkout@v4
with:
repository: ${{ env.ENV_REPO }}
token: ${{ secrets.GITOPS_REPO_TOKEN }}
path: env-repo
- name: Download manifest artifact
uses: actions/download-artifact@v4
with:
name: release-manifest
path: dist
- name: Commit and push manifest to env repo
run: |
set -euo pipefail
cd env-repo
# Create release manifest
mkdir -p releases
cp ../dist/manifest.yaml releases/${{ github.ref_name }}.yaml
# Update channel pointer
channel="stable"
if [[ "${{ github.ref_name }}" == *"-rc."* ]]; then channel="rc"; fi
mkdir -p channels
cat > channels/${channel}.yaml <<YAML
platform_version: ${{ github.ref_name }}
manifest: releases/${{ github.ref_name }}.yaml
updated_at: $(date -u '+%Y-%m-%dT%H:%M:%SZ')
YAML
# Commit and push
git config user.name "github-actions"
git config user.email "[email protected]"
# Fetch branch if it exists
git fetch origin "${RELEASE_BRANCH}" || true
# Create or reset to new content
git checkout -B "${RELEASE_BRANCH}"
git add releases/${{ github.ref_name }}.yaml channels/${channel}.yaml
git commit -m "Release manifest for ${{ github.ref_name }}"
git push --force origin "${RELEASE_BRANCH}"
- name: Create or update PR to env repo
env:
GH_TOKEN: ${{ secrets.GITOPS_REPO_TOKEN }}
run: |
cd env-repo
# Check if PR already exists
PR_NUMBER=$(gh pr list \
--repo "${{ env.ENV_REPO }}" \
--head "${RELEASE_BRANCH}" \
--base main \
--json number \
--jq '.[0].number' 2>/dev/null || echo "")
if [[ -n "$PR_NUMBER" ]]; then
echo "PR #${PR_NUMBER} already exists and was updated via branch push"
echo "https://github.com/${{ env.ENV_REPO }}/pull/${PR_NUMBER}"
else
gh pr create \
--repo "${{ env.ENV_REPO }}" \
--base main \
--head "${RELEASE_BRANCH}" \
--title "Release ${{ github.ref_name }} manifest" \
--body "This PR adds the release manifest for ${{ github.ref_name }}.
It maps each service to image@digest and includes component versions.
**Platform Version:** \`${{ github.ref_name }}\`
**Git Commit:** \`${{ github.sha }}\`
**Release Date:** \`$(date -u '+%Y-%m-%dT%H:%M:%SZ')\`"
fi