docs(README): retract image-not-published warning; chrome-selkies is live #205
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: Container Images - Build, Sign & Publish (v2.0) | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - develop | |
| tags: | |
| - 'v*' | |
| paths: | |
| - 'api/**' | |
| - 'agents/k8s-agent/**' | |
| - 'ui/**' | |
| - '.github/workflows/container-images.yml' | |
| pull_request: | |
| branches: | |
| - main | |
| - develop | |
| workflow_dispatch: | |
| env: | |
| REGISTRY: ghcr.io | |
| IMAGE_PREFIX: ghcr.io/${{ github.repository_owner }}/streamspace | |
| permissions: | |
| contents: write | |
| packages: write | |
| id-token: write | |
| security-events: write | |
| attestations: write | |
| jobs: | |
| build-and-sign-k8s-agent: | |
| name: Build & Sign K8s Agent (v2.0) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GitHub Container Registry | |
| if: github.event_name != 'pull_request' | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Install Cosign | |
| if: github.event_name != 'pull_request' | |
| uses: sigstore/cosign-installer@v3 | |
| with: | |
| cosign-release: 'v2.2.2' | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.IMAGE_PREFIX }}-k8s-agent | |
| tags: | | |
| type=ref,event=branch | |
| type=ref,event=pr | |
| type=semver,pattern={{version}} | |
| type=semver,pattern={{major}}.{{minor}} | |
| type=semver,pattern={{major}} | |
| type=sha,prefix=sha- | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| - name: Set build variables | |
| id: vars | |
| run: | | |
| echo "VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}" >> $GITHUB_OUTPUT | |
| echo "COMMIT=${{ github.sha }}" >> $GITHUB_OUTPUT | |
| echo "BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> $GITHUB_OUTPUT | |
| - name: Build and push K8s Agent image | |
| id: build | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: ./agents/k8s-agent | |
| file: ./agents/k8s-agent/Dockerfile | |
| platforms: linux/amd64,linux/arm64 | |
| push: ${{ github.event_name != 'pull_request' }} | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| build-args: | | |
| VERSION=${{ steps.vars.outputs.VERSION }} | |
| COMMIT=${{ steps.vars.outputs.COMMIT }} | |
| BUILD_DATE=${{ steps.vars.outputs.BUILD_DATE }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| provenance: true | |
| sbom: true | |
| - name: Sign K8s Agent image | |
| if: github.event_name != 'pull_request' | |
| env: | |
| COSIGN_EXPERIMENTAL: "true" | |
| run: | | |
| # Get the first tag from the metadata action | |
| IMAGE_TAG=$(echo "${{ steps.meta.outputs.tags }}" | head -n1) | |
| echo "Image tag: $IMAGE_TAG" | |
| # Retry logic for manifest propagation | |
| MAX_RETRIES=5 | |
| RETRY_DELAY=2 | |
| for i in $(seq 1 $MAX_RETRIES); do | |
| echo "Attempt $i of $MAX_RETRIES to fetch manifest digest..." | |
| # Get the manifest digest using docker manifest inspect | |
| # This returns the actual registry digest for multi-platform manifests | |
| DIGEST=$(docker manifest inspect "$IMAGE_TAG" 2>&1 | jq -r '.digest // empty' || echo "") | |
| # If jq didn't find .digest, try extracting from Docker-Content-Digest header | |
| if [ -z "$DIGEST" ]; then | |
| DIGEST=$(docker buildx imagetools inspect "$IMAGE_TAG" 2>&1 | grep -oP 'Digest:\s*\K(sha256:[a-f0-9]{64})' | head -n1 || echo "") | |
| fi | |
| # Validate digest format | |
| if [[ "$DIGEST" =~ ^sha256:[0-9a-f]{64}$ ]]; then | |
| echo "✅ Successfully extracted digest: $DIGEST" | |
| break | |
| else | |
| echo "⚠️ Invalid digest format (attempt $i): $DIGEST" | |
| if [ $i -eq $MAX_RETRIES ]; then | |
| echo "ERROR: Failed to get valid digest after $MAX_RETRIES attempts" | |
| echo "Full docker manifest inspect output:" | |
| docker manifest inspect "$IMAGE_TAG" || true | |
| echo "Full buildx imagetools output:" | |
| docker buildx imagetools inspect "$IMAGE_TAG" || true | |
| exit 1 | |
| fi | |
| echo "Waiting ${RETRY_DELAY}s before retry..." | |
| sleep $RETRY_DELAY | |
| RETRY_DELAY=$((RETRY_DELAY * 2)) # Exponential backoff | |
| fi | |
| done | |
| IMAGE_REF="${{ env.IMAGE_PREFIX }}-k8s-agent@${DIGEST}" | |
| echo "Image reference for signing: $IMAGE_REF" | |
| cosign sign --yes "$IMAGE_REF" | |
| - name: Generate SBOM for K8s Agent | |
| if: github.event_name != 'pull_request' | |
| uses: anchore/sbom-action@v0 | |
| with: | |
| path: ./agents/k8s-agent | |
| artifact-name: streamspace-k8s-agent-sbom.spdx.json | |
| output-file: sbom-k8s-agent.spdx.json | |
| format: spdx-json | |
| - name: Attest K8s Agent SBOM | |
| if: github.event_name != 'pull_request' | |
| env: | |
| COSIGN_EXPERIMENTAL: "true" | |
| run: | | |
| IMAGE_TAG=$(echo "${{ steps.meta.outputs.tags }}" | head -n1) | |
| # Retry logic for manifest propagation | |
| MAX_RETRIES=5 | |
| RETRY_DELAY=2 | |
| for i in $(seq 1 $MAX_RETRIES); do | |
| echo "Attempt $i of $MAX_RETRIES to fetch manifest digest for SBOM attestation..." | |
| DIGEST=$(docker manifest inspect "$IMAGE_TAG" 2>&1 | jq -r '.digest // empty' || echo "") | |
| if [ -z "$DIGEST" ]; then | |
| DIGEST=$(docker buildx imagetools inspect "$IMAGE_TAG" 2>&1 | grep -oP 'Digest:\s*\K(sha256:[a-f0-9]{64})' | head -n1 || echo "") | |
| fi | |
| if [[ "$DIGEST" =~ ^sha256:[0-9a-f]{64}$ ]]; then | |
| echo "✅ Successfully extracted digest: $DIGEST" | |
| break | |
| else | |
| if [ $i -eq $MAX_RETRIES ]; then | |
| echo "ERROR: Failed to get valid digest after $MAX_RETRIES attempts" | |
| exit 1 | |
| fi | |
| echo "Waiting ${RETRY_DELAY}s before retry..." | |
| sleep $RETRY_DELAY | |
| RETRY_DELAY=$((RETRY_DELAY * 2)) | |
| fi | |
| done | |
| IMAGE_REF="${{ env.IMAGE_PREFIX }}-k8s-agent@${DIGEST}" | |
| echo "Using digest for SBOM attestation: $DIGEST" | |
| cosign attest --yes --type spdxjson \ | |
| --predicate sbom-k8s-agent.spdx.json \ | |
| "$IMAGE_REF" | |
| - name: Upload K8s Agent SBOM | |
| if: github.event_name != 'pull_request' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: sbom-k8s-agent | |
| path: sbom-k8s-agent.spdx.json | |
| retention-days: 90 | |
| build-and-sign-api: | |
| name: Build & Sign API | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GitHub Container Registry | |
| if: github.event_name != 'pull_request' | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Install Cosign | |
| if: github.event_name != 'pull_request' | |
| uses: sigstore/cosign-installer@v3 | |
| with: | |
| cosign-release: 'v2.2.2' | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.IMAGE_PREFIX }}-api | |
| tags: | | |
| type=ref,event=branch | |
| type=ref,event=pr | |
| type=semver,pattern={{version}} | |
| type=semver,pattern={{major}}.{{minor}} | |
| type=semver,pattern={{major}} | |
| type=sha,prefix=sha- | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| - name: Set build variables | |
| id: vars | |
| run: | | |
| echo "VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}" >> $GITHUB_OUTPUT | |
| echo "COMMIT=${{ github.sha }}" >> $GITHUB_OUTPUT | |
| echo "BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> $GITHUB_OUTPUT | |
| - name: Build and push API image | |
| id: build | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: ./api | |
| file: ./api/Dockerfile | |
| platforms: linux/amd64,linux/arm64 | |
| push: ${{ github.event_name != 'pull_request' }} | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| build-args: | | |
| VERSION=${{ steps.vars.outputs.VERSION }} | |
| COMMIT=${{ steps.vars.outputs.COMMIT }} | |
| BUILD_DATE=${{ steps.vars.outputs.BUILD_DATE }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| provenance: true | |
| sbom: true | |
| - name: Sign API image | |
| if: github.event_name != 'pull_request' | |
| env: | |
| COSIGN_EXPERIMENTAL: "true" | |
| run: | | |
| # Get the first tag from the metadata action | |
| IMAGE_TAG=$(echo "${{ steps.meta.outputs.tags }}" | head -n1) | |
| echo "Image tag: $IMAGE_TAG" | |
| # Retry logic for manifest propagation | |
| MAX_RETRIES=5 | |
| RETRY_DELAY=2 | |
| for i in $(seq 1 $MAX_RETRIES); do | |
| echo "Attempt $i of $MAX_RETRIES to fetch manifest digest..." | |
| # Get the manifest digest using docker manifest inspect | |
| # This returns the actual registry digest for multi-platform manifests | |
| DIGEST=$(docker manifest inspect "$IMAGE_TAG" 2>&1 | jq -r '.digest // empty' || echo "") | |
| # If jq didn't find .digest, try extracting from Docker-Content-Digest header | |
| if [ -z "$DIGEST" ]; then | |
| DIGEST=$(docker buildx imagetools inspect "$IMAGE_TAG" 2>&1 | grep -oP 'Digest:\s*\K(sha256:[a-f0-9]{64})' | head -n1 || echo "") | |
| fi | |
| # Validate digest format | |
| if [[ "$DIGEST" =~ ^sha256:[0-9a-f]{64}$ ]]; then | |
| echo "✅ Successfully extracted digest: $DIGEST" | |
| break | |
| else | |
| echo "⚠️ Invalid digest format (attempt $i): $DIGEST" | |
| if [ $i -eq $MAX_RETRIES ]; then | |
| echo "ERROR: Failed to get valid digest after $MAX_RETRIES attempts" | |
| echo "Full docker manifest inspect output:" | |
| docker manifest inspect "$IMAGE_TAG" || true | |
| echo "Full buildx imagetools output:" | |
| docker buildx imagetools inspect "$IMAGE_TAG" || true | |
| exit 1 | |
| fi | |
| echo "Waiting ${RETRY_DELAY}s before retry..." | |
| sleep $RETRY_DELAY | |
| RETRY_DELAY=$((RETRY_DELAY * 2)) # Exponential backoff | |
| fi | |
| done | |
| IMAGE_REF="${{ env.IMAGE_PREFIX }}-api@${DIGEST}" | |
| echo "Image reference for signing: $IMAGE_REF" | |
| cosign sign --yes "$IMAGE_REF" | |
| - name: Generate SBOM for API | |
| if: github.event_name != 'pull_request' | |
| uses: anchore/sbom-action@v0 | |
| with: | |
| path: ./api | |
| artifact-name: streamspace-api-sbom.spdx.json | |
| output-file: sbom-api.spdx.json | |
| format: spdx-json | |
| - name: Attest API SBOM | |
| if: github.event_name != 'pull_request' | |
| env: | |
| COSIGN_EXPERIMENTAL: "true" | |
| run: | | |
| IMAGE_TAG=$(echo "${{ steps.meta.outputs.tags }}" | head -n1) | |
| # Retry logic for manifest propagation | |
| MAX_RETRIES=5 | |
| RETRY_DELAY=2 | |
| for i in $(seq 1 $MAX_RETRIES); do | |
| echo "Attempt $i of $MAX_RETRIES to fetch manifest digest for SBOM attestation..." | |
| DIGEST=$(docker manifest inspect "$IMAGE_TAG" 2>&1 | jq -r '.digest // empty' || echo "") | |
| if [ -z "$DIGEST" ]; then | |
| DIGEST=$(docker buildx imagetools inspect "$IMAGE_TAG" 2>&1 | grep -oP 'Digest:\s*\K(sha256:[a-f0-9]{64})' | head -n1 || echo "") | |
| fi | |
| if [[ "$DIGEST" =~ ^sha256:[0-9a-f]{64}$ ]]; then | |
| echo "✅ Successfully extracted digest: $DIGEST" | |
| break | |
| else | |
| if [ $i -eq $MAX_RETRIES ]; then | |
| echo "ERROR: Failed to get valid digest after $MAX_RETRIES attempts" | |
| exit 1 | |
| fi | |
| echo "Waiting ${RETRY_DELAY}s before retry..." | |
| sleep $RETRY_DELAY | |
| RETRY_DELAY=$((RETRY_DELAY * 2)) | |
| fi | |
| done | |
| IMAGE_REF="${{ env.IMAGE_PREFIX }}-api@${DIGEST}" | |
| echo "Using digest for SBOM attestation: $DIGEST" | |
| cosign attest --yes --type spdxjson \ | |
| --predicate sbom-api.spdx.json \ | |
| "$IMAGE_REF" | |
| - name: Upload API SBOM | |
| if: github.event_name != 'pull_request' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: sbom-api | |
| path: sbom-api.spdx.json | |
| retention-days: 90 | |
| build-and-sign-ui: | |
| name: Build & Sign UI | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GitHub Container Registry | |
| if: github.event_name != 'pull_request' | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Install Cosign | |
| if: github.event_name != 'pull_request' | |
| uses: sigstore/cosign-installer@v3 | |
| with: | |
| cosign-release: 'v2.2.2' | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.IMAGE_PREFIX }}-ui | |
| tags: | | |
| type=ref,event=branch | |
| type=ref,event=pr | |
| type=semver,pattern={{version}} | |
| type=semver,pattern={{major}}.{{minor}} | |
| type=semver,pattern={{major}} | |
| type=sha,prefix=sha- | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| - name: Set build variables | |
| id: vars | |
| run: | | |
| echo "VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}" >> $GITHUB_OUTPUT | |
| echo "COMMIT=${{ github.sha }}" >> $GITHUB_OUTPUT | |
| echo "BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> $GITHUB_OUTPUT | |
| - name: Build and push UI image | |
| id: build | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: ./ui | |
| file: ./ui/Dockerfile | |
| platforms: linux/amd64,linux/arm64 | |
| push: ${{ github.event_name != 'pull_request' }} | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| build-args: | | |
| VERSION=${{ steps.vars.outputs.VERSION }} | |
| COMMIT=${{ steps.vars.outputs.COMMIT }} | |
| BUILD_DATE=${{ steps.vars.outputs.BUILD_DATE }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| provenance: true | |
| sbom: true | |
| - name: Sign UI image | |
| if: github.event_name != 'pull_request' | |
| env: | |
| COSIGN_EXPERIMENTAL: "true" | |
| run: | | |
| # Get the first tag from the metadata action | |
| IMAGE_TAG=$(echo "${{ steps.meta.outputs.tags }}" | head -n1) | |
| echo "Image tag: $IMAGE_TAG" | |
| # Retry logic for manifest propagation | |
| MAX_RETRIES=5 | |
| RETRY_DELAY=2 | |
| for i in $(seq 1 $MAX_RETRIES); do | |
| echo "Attempt $i of $MAX_RETRIES to fetch manifest digest..." | |
| # Get the manifest digest using docker manifest inspect | |
| # This returns the actual registry digest for multi-platform manifests | |
| DIGEST=$(docker manifest inspect "$IMAGE_TAG" 2>&1 | jq -r '.digest // empty' || echo "") | |
| # If jq didn't find .digest, try extracting from Docker-Content-Digest header | |
| if [ -z "$DIGEST" ]; then | |
| DIGEST=$(docker buildx imagetools inspect "$IMAGE_TAG" 2>&1 | grep -oP 'Digest:\s*\K(sha256:[a-f0-9]{64})' | head -n1 || echo "") | |
| fi | |
| # Validate digest format | |
| if [[ "$DIGEST" =~ ^sha256:[0-9a-f]{64}$ ]]; then | |
| echo "✅ Successfully extracted digest: $DIGEST" | |
| break | |
| else | |
| echo "⚠️ Invalid digest format (attempt $i): $DIGEST" | |
| if [ $i -eq $MAX_RETRIES ]; then | |
| echo "ERROR: Failed to get valid digest after $MAX_RETRIES attempts" | |
| echo "Full docker manifest inspect output:" | |
| docker manifest inspect "$IMAGE_TAG" || true | |
| echo "Full buildx imagetools output:" | |
| docker buildx imagetools inspect "$IMAGE_TAG" || true | |
| exit 1 | |
| fi | |
| echo "Waiting ${RETRY_DELAY}s before retry..." | |
| sleep $RETRY_DELAY | |
| RETRY_DELAY=$((RETRY_DELAY * 2)) # Exponential backoff | |
| fi | |
| done | |
| IMAGE_REF="${{ env.IMAGE_PREFIX }}-ui@${DIGEST}" | |
| echo "Image reference for signing: $IMAGE_REF" | |
| cosign sign --yes "$IMAGE_REF" | |
| - name: Generate SBOM for UI | |
| if: github.event_name != 'pull_request' | |
| uses: anchore/sbom-action@v0 | |
| with: | |
| path: ./ui | |
| artifact-name: streamspace-ui-sbom.spdx.json | |
| output-file: sbom-ui.spdx.json | |
| format: spdx-json | |
| - name: Attest UI SBOM | |
| if: github.event_name != 'pull_request' | |
| env: | |
| COSIGN_EXPERIMENTAL: "true" | |
| run: | | |
| IMAGE_TAG=$(echo "${{ steps.meta.outputs.tags }}" | head -n1) | |
| # Retry logic for manifest propagation | |
| MAX_RETRIES=5 | |
| RETRY_DELAY=2 | |
| for i in $(seq 1 $MAX_RETRIES); do | |
| echo "Attempt $i of $MAX_RETRIES to fetch manifest digest for SBOM attestation..." | |
| DIGEST=$(docker manifest inspect "$IMAGE_TAG" 2>&1 | jq -r '.digest // empty' || echo "") | |
| if [ -z "$DIGEST" ]; then | |
| DIGEST=$(docker buildx imagetools inspect "$IMAGE_TAG" 2>&1 | grep -oP 'Digest:\s*\K(sha256:[a-f0-9]{64})' | head -n1 || echo "") | |
| fi | |
| if [[ "$DIGEST" =~ ^sha256:[0-9a-f]{64}$ ]]; then | |
| echo "✅ Successfully extracted digest: $DIGEST" | |
| break | |
| else | |
| if [ $i -eq $MAX_RETRIES ]; then | |
| echo "ERROR: Failed to get valid digest after $MAX_RETRIES attempts" | |
| exit 1 | |
| fi | |
| echo "Waiting ${RETRY_DELAY}s before retry..." | |
| sleep $RETRY_DELAY | |
| RETRY_DELAY=$((RETRY_DELAY * 2)) | |
| fi | |
| done | |
| IMAGE_REF="${{ env.IMAGE_PREFIX }}-ui@${DIGEST}" | |
| echo "Using digest for SBOM attestation: $DIGEST" | |
| cosign attest --yes --type spdxjson \ | |
| --predicate sbom-ui.spdx.json \ | |
| "$IMAGE_REF" | |
| - name: Upload UI SBOM | |
| if: github.event_name != 'pull_request' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: sbom-ui | |
| path: sbom-ui.spdx.json | |
| retention-days: 90 | |
| security-scan: | |
| name: Security Scan | |
| runs-on: ubuntu-latest | |
| if: github.event_name != 'pull_request' | |
| needs: [build-and-sign-k8s-agent, build-and-sign-api, build-and-sign-ui] | |
| strategy: | |
| matrix: | |
| component: [k8s-agent, api, ui] | |
| steps: | |
| - name: Install Cosign | |
| uses: sigstore/cosign-installer@v3 | |
| with: | |
| cosign-release: 'v2.2.2' | |
| - name: Verify image signature | |
| env: | |
| COSIGN_EXPERIMENTAL: "true" | |
| run: | | |
| cosign verify ${{ env.IMAGE_PREFIX }}-${{ matrix.component }}:latest | |
| - name: Run Trivy vulnerability scanner | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| image-ref: ${{ env.IMAGE_PREFIX }}-${{ matrix.component }}:latest | |
| format: 'sarif' | |
| output: 'trivy-${{ matrix.component }}-results.sarif' | |
| severity: 'CRITICAL,HIGH' | |
| - name: Upload Trivy results to GitHub Security | |
| uses: github/codeql-action/upload-sarif@v3 | |
| with: | |
| sarif_file: 'trivy-${{ matrix.component }}-results.sarif' | |
| category: 'trivy-${{ matrix.component }}' | |
| - name: Generate Trivy report | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| image-ref: ${{ env.IMAGE_PREFIX }}-${{ matrix.component }}:latest | |
| format: 'table' | |
| severity: 'CRITICAL,HIGH,MEDIUM' | |
| update-helm-chart: | |
| name: Update Helm Chart | |
| runs-on: ubuntu-latest | |
| needs: [build-and-sign-k8s-agent, build-and-sign-api, build-and-sign-ui] | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract version | |
| id: version | |
| run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT | |
| - name: Update Chart.yaml | |
| run: | | |
| VERSION=${{ steps.version.outputs.VERSION }} | |
| sed -i "s/^version:.*/version: ${VERSION#v}/" chart/Chart.yaml | |
| sed -i "s/^appVersion:.*/appVersion: \"${VERSION}\"/" chart/Chart.yaml | |
| - name: Update values.yaml | |
| run: | | |
| VERSION=${{ steps.version.outputs.VERSION }} | |
| sed -i "s/tag: \".*\"/tag: \"${VERSION}\"/" chart/values.yaml | |
| - name: Commit changes | |
| run: | | |
| git config --local user.email "github-actions[bot]@users.noreply.github.com" | |
| git config --local user.name "github-actions[bot]" | |
| git add chart/Chart.yaml chart/values.yaml | |
| git commit -m "chore: bump chart version to ${{ steps.version.outputs.VERSION }}" || echo "No changes to commit" | |
| git push origin HEAD:main || echo "No changes to push" | |
| create-release: | |
| name: Create GitHub Release | |
| runs-on: ubuntu-latest | |
| needs: [security-scan, update-helm-chart] | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Extract version | |
| id: version | |
| run: | | |
| VERSION=${GITHUB_REF#refs/tags/} | |
| echo "VERSION=$VERSION" >> $GITHUB_OUTPUT | |
| echo "VERSION_NUM=${VERSION#v}" >> $GITHUB_OUTPUT | |
| - name: Generate changelog | |
| id: changelog | |
| run: | | |
| PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "") | |
| if [ -z "$PREV_TAG" ]; then | |
| CHANGELOG=$(git log --pretty=format:"- %s (%h)" --no-merges) | |
| else | |
| CHANGELOG=$(git log ${PREV_TAG}..HEAD --pretty=format:"- %s (%h)" --no-merges) | |
| fi | |
| echo "$CHANGELOG" > CHANGELOG.txt | |
| - name: Set up Helm | |
| uses: azure/setup-helm@v4 | |
| with: | |
| version: 'v3.14.0' | |
| - name: Package Helm chart | |
| run: | | |
| helm package chart/ --version ${{ steps.version.outputs.VERSION_NUM }} --app-version ${{ steps.version.outputs.VERSION }} | |
| mv streamspace-${{ steps.version.outputs.VERSION_NUM }}.tgz streamspace-helm-chart.tgz | |
| - name: Download SBOMs | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: sboms | |
| - name: Create Release Notes | |
| run: | | |
| cat > RELEASE_NOTES.md <<EOF | |
| # StreamSpace ${{ steps.version.outputs.VERSION }} (v2.0 Multi-Platform Architecture) | |
| ## Architecture | |
| StreamSpace v2.0 features a **multi-platform agent-based architecture**: | |
| - Centralized Control Plane (API + UI) | |
| - Platform-specific agents (K8s, Docker, VMs, Cloud) | |
| - WebSocket-based agent communication | |
| - VNC proxy tunneling | |
| ## Container Images | |
| All images are multi-platform (linux/amd64, linux/arm64) and signed with Cosign: | |
| - K8s Agent: \`${{ env.IMAGE_PREFIX }}-k8s-agent:${{ steps.version.outputs.VERSION }}\` | |
| - Control Plane API: \`${{ env.IMAGE_PREFIX }}-api:${{ steps.version.outputs.VERSION }}\` | |
| - Web UI: \`${{ env.IMAGE_PREFIX }}-ui:${{ steps.version.outputs.VERSION }}\` | |
| ## Security | |
| ✅ All images are signed with Cosign (keyless) | |
| ✅ SBOMs generated and attached | |
| ✅ Vulnerability scanned with Trivy | |
| ### Verifying Image Signatures | |
| \`\`\`bash | |
| cosign verify ${{ env.IMAGE_PREFIX }}-k8s-agent:${{ steps.version.outputs.VERSION }} | |
| cosign verify ${{ env.IMAGE_PREFIX }}-api:${{ steps.version.outputs.VERSION }} | |
| cosign verify ${{ env.IMAGE_PREFIX }}-ui:${{ steps.version.outputs.VERSION }} | |
| \`\`\` | |
| ## Installation | |
| ### Using Helm | |
| \`\`\`bash | |
| helm install streamspace streamspace-helm-chart.tgz \\ | |
| --namespace streamspace \\ | |
| --create-namespace | |
| \`\`\` | |
| ## What's Changed | |
| $(cat CHANGELOG.txt) | |
| ## Documentation | |
| - [README](https://github.com/${{ github.repository }}/blob/main/README.md) | |
| - [Architecture](https://github.com/${{ github.repository }}/blob/main/docs/ARCHITECTURE.md) | |
| - [Helm Chart](https://github.com/${{ github.repository }}/blob/main/chart/README.md) | |
| EOF | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| body_path: RELEASE_NOTES.md | |
| files: | | |
| streamspace-helm-chart.tgz | |
| sboms/**/*.spdx.json | |
| draft: false | |
| prerelease: ${{ contains(steps.version.outputs.VERSION, '-') }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| build-summary: | |
| name: Build Summary (v2.0) | |
| runs-on: ubuntu-latest | |
| needs: [build-and-sign-k8s-agent, build-and-sign-api, build-and-sign-ui] | |
| if: always() | |
| steps: | |
| - name: Generate summary | |
| run: | | |
| echo "## 🐳 StreamSpace v2.0 Container Images Built" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Architecture: Multi-Platform Agent-Based" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Images" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✅ \`${{ env.IMAGE_PREFIX }}-k8s-agent:latest\` (K8s Agent)" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✅ \`${{ env.IMAGE_PREFIX }}-api:latest\` (Control Plane API)" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✅ \`${{ env.IMAGE_PREFIX }}-ui:latest\` (Web UI)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Platforms" >> $GITHUB_STEP_SUMMARY | |
| echo "- linux/amd64" >> $GITHUB_STEP_SUMMARY | |
| echo "- linux/arm64" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Security Features" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✅ Signed with Cosign" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✅ SBOM generated" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✅ Provenance attestation" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✅ Vulnerability scanned" >> $GITHUB_STEP_SUMMARY |