Expand release workflow manifest gen #9
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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: gateway, 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 }} | |
| 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": "${{ env.REGISTRY }}/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": "${{ env.REGISTRY }}/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/* | |
| manifest: | |
| name: Generate & Publish Manifest | |
| needs: [build-images, build-webapps, build-cli] | |
| 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 GHCR | |
| id: mistserver | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| # Query GHCR API for latest MistServer digest | |
| DIGEST=$(curl -sL \ | |
| -H "Authorization: Bearer ${GITHUB_TOKEN}" \ | |
| -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \ | |
| "https://ghcr.io/v2/livepeer-frameworks/mistserver/manifests/latest" \ | |
| | jq -r '.config.digest // empty') | |
| if [[ -z "$DIGEST" ]]; then | |
| echo "WARNING: Could not fetch MistServer digest from GHCR, 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 | |
| # 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: ghcr.io/livepeer-frameworks/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 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/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]" | |
| 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-with-lease origin "${RELEASE_BRANCH}" | |
| - name: Create PR to env repo | |
| env: | |
| GH_TOKEN: ${{ secrets.GITOPS_REPO_TOKEN }} | |
| run: | | |
| cd env-repo | |
| 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')\`" |