name: Console Service Pipeline on: push: branches: - main paths: - ".github/workflows/pipeline.yaml" - "Dockerfile" - "deploy/single-node/**" - "package.json" - "yarn.lock" - "scripts/github-actions/build-and-push-frontend-image.sh" - "scripts/github-actions/compute-frontend-release-metadata.sh" - "scripts/github-actions/render-frontend-build-args.sh" - "scripts/github-actions/render-frontend-runtime-env.sh" - "scripts/github-actions/prepare-frontend-build-context.sh" - "scripts/github-actions/run-console-deploy-playbook.sh" - "scripts/github-actions/run-cloudflare-svc-plus-dns-playbook.sh" - "scripts/github-actions/verify-frontend-release-over-ssh.sh" - "scripts/github-actions/verify-frontend-release.sh" - "scripts/prebuild.sh" - "contentlayer.config.ts" - "next.config.js" - "next.config.mjs" - "src/**" - "public/**" workflow_dispatch: inputs: target_host: description: Ansible host or alias required: false default: "jp-xhttp-contabo.svc.plus" type: string run_apply: description: Apply deployment required: true default: true type: boolean permissions: contents: read packages: write concurrency: group: console-pipeline-${{ github.ref_name }} cancel-in-progress: false env: CANONICAL_DOMAIN: www.svc.plus SERVED_DOMAINS: www.svc.plus,console.svc.plus APP_BASE_URL: https://www.svc.plus NEXT_PUBLIC_APP_BASE_URL: https://www.svc.plus NEXT_PUBLIC_SITE_URL: https://www.svc.plus RUNTIME_HOSTNAME: www.svc.plus NEXT_RUNTIME_HOSTNAME: www.svc.plus NEXT_PUBLIC_RUNTIME_ENVIRONMENT: prod NEXT_PUBLIC_RUNTIME_REGION: cn ACCOUNT_SERVICE_URL: https://accounts.svc.plus CLOUDFLARE_ZONE_TAG: bf3427f83a2c52c8285ab3d741a6ee27 CLOUDFLARE_WEB_ANALYTICS_SITE_TAG: 0973e84ec8872c67c570f8072e92e21b CLOUDFLARE_ACCOUNT_ID: e71be5efb76a6c54f78f008da4404f00 GHCR_REGISTRY: ghcr.io GHCR_USERNAME: ${{ secrets.GHCR_USERNAME }} GHCR_PASSWORD: ${{ secrets.GHCR_TOKEN }} INTERNAL_SERVICE_TOKEN: ${{ secrets.INTERNAL_SERVICE_TOKEN }} CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_DNS_API_TOKEN: ${{ secrets.CLOUDFLARE_DNS_API_TOKEN }} jobs: prep: name: Prep runs-on: ubuntu-latest outputs: target_host: ${{ steps.inputs.outputs.target_host }} run_apply: ${{ steps.inputs.outputs.run_apply }} image_tag: ${{ steps.metadata.outputs.image_tag }} image_ref: ${{ steps.metadata.outputs.image_ref }} image_latest_ref: ${{ steps.metadata.outputs.image_latest_ref }} ghcr_namespace: ${{ steps.metadata.outputs.ghcr_namespace }} push_latest: ${{ steps.push.outputs.push_latest }} steps: - name: Check Out Repository uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Resolve Inputs id: inputs env: EVENT_NAME: ${{ github.event_name }} INPUT_TARGET_HOST: ${{ inputs.target_host }} INPUT_RUN_APPLY: ${{ inputs.run_apply }} run: bash scripts/github-actions/resolve-workflow-inputs.sh - name: Compute Image Metadata id: metadata run: | bash scripts/github-actions/compute-frontend-release-metadata.sh - name: Resolve Push Latest id: push env: REF: ${{ github.ref }} run: bash scripts/github-actions/resolve-push-latest.sh build: name: Build runs-on: ubuntu-latest needs: prep outputs: image_ref: ${{ steps.publish.outputs.image_ref }} image_tag: ${{ steps.publish.outputs.image_tag }} steps: - name: Check Out Repository uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Set Up Docker Buildx run: bash scripts/github-actions/setup-docker-buildx.sh - name: Log In To GHCR env: GHCR_TOKEN: ${{ secrets.GHCR_TOKEN }} run: bash scripts/github-actions/login-ghcr.sh - name: Publish Frontend Image id: publish env: IMAGE_REF: ${{ needs.prep.outputs.image_ref }} IMAGE_TAG: ${{ needs.prep.outputs.image_tag }} IMAGE_LATEST_REF: ${{ needs.prep.outputs.image_latest_ref }} PUSH_LATEST: ${{ needs.prep.outputs.push_latest }} run: bash scripts/github-actions/publish-frontend-image.sh deploy: name: Deploy runs-on: ubuntu-latest needs: - prep - build env: TARGET_HOST: ${{ needs.prep.outputs.target_host }} RUN_APPLY: ${{ needs.prep.outputs.run_apply }} FRONTEND_IMAGE: ${{ needs.build.outputs.image_ref }} steps: - name: Check Out Repository uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Check Out Playbooks Repository uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: # Intentionally pinned: playbooks@main regressed deploy reliability on 2026-04-12. # Any future bump must pass a full Deploy + Validate run before becoming the default. repository: x-evor/playbooks ref: 80c545a95c3b16459f6494ed13d951faac57bfa8 path: playbooks token: ${{ github.token }} - name: Set Up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.11" - name: Install Ansible run: | python -m pip install --upgrade pip python -m pip install ansible - name: Configure SSH For Deploy Host env: SINGLE_NODE_VPS_SSH_PRIVATE_KEY: ${{ secrets.SINGLE_NODE_VPS_SSH_PRIVATE_KEY }} TARGET_HOST: ${{ needs.prep.outputs.target_host }} run: bash scripts/github-actions/configure-ssh-for-deploy.sh - name: Run Deploy Playbook working-directory: playbooks env: ANSIBLE_HOST_KEY_CHECKING: "False" run: bash ../scripts/github-actions/run-console-deploy-playbook.sh validate: name: Validate runs-on: ubuntu-latest needs: - prep - build - deploy if: ${{ always() && needs.deploy.result == 'success' }} env: EXPECTED_FRONTEND_IMAGE: ${{ needs.build.outputs.image_ref }} TARGET_HOST: ${{ needs.prep.outputs.target_host }} SINGLE_NODE_VPS_SSH_PRIVATE_KEY: ${{ secrets.SINGLE_NODE_VPS_SSH_PRIVATE_KEY }} steps: - name: Check Out Repository uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Configure SSH For Validate Host run: bash scripts/github-actions/configure-ssh-for-deploy.sh - name: Verify Frontend Release On Host run: bash scripts/github-actions/verify-frontend-release-over-ssh.sh update_dns: name: Update DNS runs-on: ubuntu-latest needs: - prep - build - deploy if: ${{ always() && needs.deploy.result == 'success' }} continue-on-error: true env: TARGET_HOST: ${{ needs.prep.outputs.target_host }} RUN_APPLY: ${{ needs.prep.outputs.run_apply }} FRONTEND_IMAGE: ${{ needs.build.outputs.image_ref }} steps: - name: Check Out Repository uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Check Out Playbooks Repository uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: repository: x-evor/playbooks ref: 80c545a95c3b16459f6494ed13d951faac57bfa8 path: playbooks token: ${{ github.token }} - name: Set Up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.11" - name: Install Ansible run: | python -m pip install --upgrade pip python -m pip install ansible - name: Update Cloudflare svc.plus DNS working-directory: playbooks env: ANSIBLE_HOST_KEY_CHECKING: "False" run: bash ../scripts/github-actions/run-cloudflare-svc-plus-dns-playbook.sh