Skip to content

Add Gold network package catalog dropdown support #153

Add Gold network package catalog dropdown support

Add Gold network package catalog dropdown support #153

Workflow file for this run

name: Build And Publish Images
on:
push:
branches:
- main
tags:
- "v*"
workflow_dispatch:
env:
REGISTRY: ghcr.io
FRONTEND_IMAGE: ghcr.io/paradox1612/streambridge-frontend
BACKEND_IMAGE: ghcr.io/paradox1612/streambridge-backend
PUBLIC_BASE_URL: ${{ vars.PUBLIC_BASE_URL }}
jobs:
build-and-push:
runs-on: ubuntu-latest
outputs:
backend_changed: ${{ steps.changes.outputs.backend }}
frontend_changed: ${{ steps.changes.outputs.frontend }}
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v5
with:
fetch-depth: 2
- name: Detect image build changes
id: changes
uses: dorny/paths-filter@v4
with:
filters: |
backend:
- 'backend/**'
frontend:
- 'frontend-next/**'
- name: Log in to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
continue-on-error: true
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v4
- name: Log in to GHCR
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker metadata for backend
if: steps.changes.outputs.backend == 'true'
id: backend-meta
uses: docker/metadata-action@v6
with:
images: ${{ env.BACKEND_IMAGE }}
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=sha,format=short
type=ref,event=tag
- name: Docker metadata for frontend
if: steps.changes.outputs.frontend == 'true'
id: frontend-meta
uses: docker/metadata-action@v6
with:
images: ${{ env.FRONTEND_IMAGE }}
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=sha,format=short
type=ref,event=tag
- name: Build and push backend
if: steps.changes.outputs.backend == 'true'
uses: docker/build-push-action@v7
with:
context: ./backend
file: ./backend/Dockerfile
push: true
tags: ${{ steps.backend-meta.outputs.tags }}
labels: ${{ steps.backend-meta.outputs.labels }}
cache-from: type=gha,scope=backend-image
cache-to: type=gha,scope=backend-image,mode=max
- name: Cache frontend Buildx layers
if: steps.changes.outputs.frontend == 'true'
uses: actions/cache@v5
with:
path: ${{ runner.temp }}/.buildx-cache-frontend-next
key: ${{ runner.os }}-buildx-frontend-next-${{ hashFiles('frontend-next/package-lock.json', 'frontend-next/Dockerfile', 'frontend-next/next.config.ts') }}
restore-keys: |
${{ runner.os }}-buildx-frontend-next-
- name: Cache frontend Docker cache mounts
if: steps.changes.outputs.frontend == 'true'
id: frontend-cache-mounts
uses: actions/cache@v5
with:
path: ${{ runner.temp }}/.buildx-cache-mounts-frontend-next
key: ${{ runner.os }}-buildx-cache-mounts-frontend-next-${{ hashFiles('frontend-next/package-lock.json', 'frontend-next/Dockerfile', 'frontend-next/next.config.ts') }}
restore-keys: |
${{ runner.os }}-buildx-cache-mounts-frontend-next-
- name: Restore frontend Docker cache mounts
if: steps.changes.outputs.frontend == 'true'
uses: reproducible-containers/buildkit-cache-dance@4db69aec1c9d8c63a6412a678820f69d2c48569b
with:
builder: ${{ steps.buildx.outputs.name }}
cache-dir: ${{ runner.temp }}/.buildx-cache-mounts-frontend-next
dockerfile: frontend-next/Dockerfile
skip-extraction: ${{ steps.frontend-cache-mounts.outputs.cache-hit }}
- name: Validate frontend public URL
if: steps.changes.outputs.frontend == 'true'
shell: bash
run: |
set -euo pipefail
test -n "${PUBLIC_BASE_URL:-}" || { echo "Set the PUBLIC_BASE_URL repository variable before building the frontend image."; exit 1; }
- name: Build and push frontend
if: steps.changes.outputs.frontend == 'true'
uses: docker/build-push-action@v7
with:
context: ./frontend-next
file: ./frontend-next/Dockerfile
push: true
tags: ${{ steps.frontend-meta.outputs.tags }}
labels: ${{ steps.frontend-meta.outputs.labels }}
build-args: |
NEXT_PUBLIC_API_URL=${{ env.PUBLIC_BASE_URL }}
NEXT_PUBLIC_SITE_URL=${{ env.PUBLIC_BASE_URL }}
NEXT_PUBLIC_GOOGLE_CLIENT_ID=${{ secrets.NEXT_PUBLIC_GOOGLE_CLIENT_ID }}
cache-from: |
type=gha,scope=frontend-next-image
type=local,src=${{ runner.temp }}/.buildx-cache-frontend-next
cache-to: |
type=gha,scope=frontend-next-image,mode=max
type=local,dest=${{ runner.temp }}/.buildx-cache-frontend-next-new,mode=max
- name: Move frontend Buildx cache
if: steps.changes.outputs.frontend == 'true'
shell: bash
run: |
rm -rf "${{ runner.temp }}/.buildx-cache-frontend-next"
mv "${{ runner.temp }}/.buildx-cache-frontend-next-new" "${{ runner.temp }}/.buildx-cache-frontend-next"
build-cloudstream-plugin:
name: Build CloudStream Plugin
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v5
with:
fetch-depth: 2 # needed for paths-filter to diff against previous commit
- name: Check if plugin code changed
id: changes
uses: dorny/paths-filter@v4
with:
filters: |
plugin:
- 'cloudstream-plugin/**'
- name: Set up JDK 17
if: steps.changes.outputs.plugin == 'true'
uses: actions/setup-java@v5
with:
java-version: "17"
distribution: "temurin"
- name: Set up Android SDK
if: steps.changes.outputs.plugin == 'true'
uses: android-actions/setup-android@v4
- name: Install Android SDK packages
if: steps.changes.outputs.plugin == 'true'
shell: bash
run: |
sdkmanager --install \
"platform-tools" \
"platforms;android-33" \
"build-tools;33.0.2"
- name: Cache Gradle
if: steps.changes.outputs.plugin == 'true'
uses: actions/cache@v5
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('cloudstream-plugin/**/*.gradle*', 'cloudstream-plugin/**/gradle-wrapper.properties') }}
restore-keys: ${{ runner.os }}-gradle-
- name: Validate plugin public URL
if: steps.changes.outputs.plugin == 'true'
shell: bash
run: |
set -euo pipefail
test -n "${PUBLIC_BASE_URL:-}" || { echo "Set the PUBLIC_BASE_URL repository variable before building the CloudStream plugin."; exit 1; }
- name: Build .cs3 plugin
if: steps.changes.outputs.plugin == 'true'
run: |
cd cloudstream-plugin
chmod +x gradlew
./gradlew StreamBridgePlugin:make makePluginsJson
env:
ANDROID_HOME: ${{ env.ANDROID_HOME }}
ANDROID_SDK_ROOT: ${{ env.ANDROID_SDK_ROOT }}
GITHUB_REPOSITORY: ${{ github.repository }}
PUBLIC_BASE_URL: ${{ env.PUBLIC_BASE_URL }}
- name: Copy artifacts into repo
if: steps.changes.outputs.plugin == 'true'
shell: bash
run: |
set -euo pipefail
mkdir -p builds
shopt -s globstar nullglob
artifacts=(cloudstream-plugin/**/build/*.cs3)
if [ ${#artifacts[@]} -eq 0 ]; then
echo "No .cs3 artifact produced by the build — failing the job."
exit 1
fi
cp "${artifacts[@]}" builds/
cp cloudstream-plugin/plugins.json builds/plugins.json
- name: Commit and push built artifacts
if: steps.changes.outputs.plugin == 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add builds/
git diff --cached --quiet || git commit -m "chore: CloudStream plugin build ${{ github.sha }}"
git push
deploy-k3s:
name: Deploy To K3s
needs: build-and-push
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/main')
runs-on:
- self-hosted
- k3s
permissions:
contents: read
packages: read
concurrency:
group: streambridge-k3s-deploy
cancel-in-progress: true
env:
KUBECONFIG_PATH: ${{ vars.K3S_KUBECONFIG_PATH }}
VALUES_FILE: ${{ vars.K3S_VALUES_FILE || '/runner-secrets/streambridge-values.yaml' }}
RELEASE_NAME: ${{ vars.K3S_RELEASE_NAME || 'streambridge' }}
RELEASE_NAMESPACE: ${{ vars.K3S_NAMESPACE || 'streambridge' }}
PUBLIC_BASE_URL: ${{ vars.PUBLIC_BASE_URL }}
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Set up kubectl
uses: azure/setup-kubectl@v5
- name: Set up Helm
uses: azure/setup-helm@v5
- name: Validate local deploy prerequisites
shell: bash
run: |
set -euo pipefail
test -f "$VALUES_FILE"
test -n "${PUBLIC_BASE_URL:-}" || { echo "Set the PUBLIC_BASE_URL repository variable before building or deploying."; exit 1; }
if [ -n "${KUBECONFIG_PATH:-}" ]; then
test -f "$KUBECONFIG_PATH"
export KUBECONFIG="$KUBECONFIG_PATH"
fi
kubectl version --client
helm version
- name: Build Helm dependencies
shell: bash
run: |
set -euo pipefail
helm dependency build ./deployment/helm/streambridge
- name: Deploy Helm release with commit image tags
shell: bash
env:
BACKEND_CHANGED: ${{ needs.build-and-push.outputs.backend_changed }}
FRONTEND_CHANGED: ${{ needs.build-and-push.outputs.frontend_changed }}
PAYGATE_WALLET_ADDRESS: ${{ secrets.PAYGATE_WALLET_ADDRESS }}
PIA_USERNAME: ${{ secrets.PIA_USERNAME }}
PIA_PASSWORD: ${{ secrets.PIA_PASSWORD }}
TWENTY_API_KEY: ${{ secrets.TWENTY_API_KEY }}
TWENTY_APP_SECRET: ${{ secrets.TWENTY_APP_SECRET }}
TWENTY_ACCESS_TOKEN_SECRET: ${{ secrets.TWENTY_ACCESS_TOKEN_SECRET }}
TWENTY_LOGIN_TOKEN_SECRET: ${{ secrets.TWENTY_LOGIN_TOKEN_SECRET }}
TWENTY_REFRESH_TOKEN_SECRET: ${{ secrets.TWENTY_REFRESH_TOKEN_SECRET }}
TWENTY_FILE_TOKEN_SECRET: ${{ secrets.TWENTY_FILE_TOKEN_SECRET }}
TWO_CAPTCHA_API_KEY: ${{ secrets.TWO_CAPTCHA_API_KEY }}
GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }}
RESEND_API_KEY: ${{ secrets.RESEND_API_KEY }}
RESEND_FROM_EMAIL: ${{ secrets.RESEND_FROM_EMAIL }}
run: |
set -euo pipefail
if [ -n "${KUBECONFIG_PATH:-}" ]; then
export KUBECONFIG="$KUBECONFIG_PATH"
fi
SHORT_SHA="${GITHUB_SHA::7}"
IMAGE_TAG="sha-${SHORT_SHA}"
current_tag() {
local deployment_name="$1"
local container_name="$2"
local image
image="$(kubectl get deployment "$deployment_name" -n "$RELEASE_NAMESPACE" -o jsonpath="{.spec.template.spec.containers[?(@.name==\"$container_name\")].image}" 2>/dev/null || true)"
if [ -z "$image" ]; then
return 0
fi
printf '%s\n' "${image##*:}"
}
BACKEND_TAG="$IMAGE_TAG"
FRONTEND_TAG="$IMAGE_TAG"
if [ "${BACKEND_CHANGED}" != "true" ]; then
BACKEND_TAG="$(current_tag "$RELEASE_NAME"-streambridge-backend backend)"
BACKEND_TAG="${BACKEND_TAG:-$IMAGE_TAG}"
fi
if [ "${FRONTEND_CHANGED}" != "true" ]; then
FRONTEND_TAG="$(current_tag "$RELEASE_NAME"-streambridge-frontend frontend)"
FRONTEND_TAG="${FRONTEND_TAG:-$IMAGE_TAG}"
fi
# Enable VPN only when PIA credentials are present
PIA_ARGS=""
VPN_ARG="--set vpn.enabled=false"
if [ -n "${PIA_USERNAME:-}" ] && [ -n "${PIA_PASSWORD:-}" ]; then
PIA_ARGS="--set-string backend.secrets.piaUsername=${PIA_USERNAME} --set-string backend.secrets.piaPassword=${PIA_PASSWORD}"
VPN_ARG="--set vpn.enabled=true"
fi
PAYGATE_ARGS=""
if [ -n "${PAYGATE_WALLET_ADDRESS:-}" ]; then
PAYGATE_ARGS="--set-string backend.secrets.paygateWalletAddress=${PAYGATE_WALLET_ADDRESS} --set-string backend.env.paygateEnabled=true"
fi
TWENTY_ARGS=""
if [ -n "${TWENTY_API_KEY:-}" ]; then
TWENTY_ARGS="--set-string backend.secrets.twentyApiKey=${TWENTY_API_KEY}"
fi
TWO_CAPTCHA_ARGS=""
if [ -n "${TWO_CAPTCHA_API_KEY:-}" ]; then
TWO_CAPTCHA_ARGS="--set-string backend.secrets.twoCaptchaApiKey=${TWO_CAPTCHA_API_KEY}"
fi
GOOGLE_ARGS=""
if [ -n "${GOOGLE_CLIENT_ID:-}" ]; then
GOOGLE_ARGS="--set-string backend.secrets.googleClientId=${GOOGLE_CLIENT_ID}"
fi
RESEND_ARGS=""
if [ -n "${RESEND_API_KEY:-}" ]; then
RESEND_ARGS="--set-string backend.secrets.resendApiKey=${RESEND_API_KEY}"
if [ -n "${RESEND_FROM_EMAIL:-}" ]; then
RESEND_ARGS+=" --set-string backend.secrets.resendFromEmail=${RESEND_FROM_EMAIL}"
fi
fi
TWENTY_SECRET_ARGS=""
if [ -n "${TWENTY_APP_SECRET:-}" ]; then
TWENTY_SECRET_ARGS=" --set-string twenty.secrets.appSecret=${TWENTY_APP_SECRET}"
TWENTY_SECRET_ARGS+=" --set-string twenty.secrets.accessTokenSecret=${TWENTY_ACCESS_TOKEN_SECRET}"
TWENTY_SECRET_ARGS+=" --set-string twenty.secrets.loginTokenSecret=${TWENTY_LOGIN_TOKEN_SECRET}"
TWENTY_SECRET_ARGS+=" --set-string twenty.secrets.refreshTokenSecret=${TWENTY_REFRESH_TOKEN_SECRET}"
TWENTY_SECRET_ARGS+=" --set-string twenty.secrets.fileTokenSecret=${TWENTY_FILE_TOKEN_SECRET}"
fi
helm upgrade --install "$RELEASE_NAME" ./deployment/helm/streambridge \
--namespace "$RELEASE_NAMESPACE" \
--create-namespace \
-f "$VALUES_FILE" \
--set-string frontend.image.tag="$FRONTEND_TAG" \
--set-string backend.image.tag="$BACKEND_TAG" \
--set-string backend.env.frontendUrl="$PUBLIC_BASE_URL" \
--set-string backend.env.baseUrl="$PUBLIC_BASE_URL" \
--set-string backend.env.apiBaseUrl="$PUBLIC_BASE_URL" \
$VPN_ARG \
$PIA_ARGS \
$PAYGATE_ARGS \
$TWENTY_ARGS \
$TWO_CAPTCHA_ARGS \
$TWENTY_SECRET_ARGS \
$GOOGLE_ARGS \
$RESEND_ARGS
- name: Wait for rollout
shell: bash
run: |
set -euo pipefail
if [ -n "${KUBECONFIG_PATH:-}" ]; then
export KUBECONFIG="$KUBECONFIG_PATH"
fi
kubectl rollout status deployment/"$RELEASE_NAME"-streambridge-backend -n "$RELEASE_NAMESPACE" --timeout=300s
kubectl rollout status deployment/"$RELEASE_NAME"-streambridge-frontend -n "$RELEASE_NAMESPACE" --timeout=300s
if kubectl get deployment/"$RELEASE_NAME"-streambridge-scheduler -n "$RELEASE_NAMESPACE" >/dev/null 2>&1; then
kubectl rollout status deployment/"$RELEASE_NAME"-streambridge-scheduler -n "$RELEASE_NAMESPACE" --timeout=300s
fi