Files
pezkuwi-telegram-miniapp/.github/workflows/deploy.yml
T
pezkuwichain b2b4751f7a ops: version the ownership gate and this repo's branch protection
Two pieces of infrastructure existed only on servers, as single copies with no
history and no review:

  /usr/local/bin/supabase-deploy-functions   the gate both projects deploy through
  /opt/supabase-self-hosted/functions-registry.json   who owns which function name

The gate exists to stop silent drift between what a repo declares and what a
server actually runs. Leaving it unversioned meant the mechanism was drifting the
same way it was built to prevent — and losing that host would have lost it
entirely, along with any record of why it works the way it does.

Both are now in ops/ and synced to the host by CI on every deploy, installed
before the functions they validate so a change to the gate ships together with
them. Verified identical to the server copies by checksum before committing, so
this captures the running state rather than replacing it.

Branch protection gets the same treatment: apply-repo-settings.sh is idempotent
and has a --check mode that reports drift without changing anything. It was
applied by hand through the API today, so nothing here reflected it.

Documented alongside: why the registry lives in this repo even though pwap-web
depends on it (a pwap-web change needs a PR here — deliberate friction, since
the registry is the record of who owns what and should not be edited on a server
where nobody sees the change), and why listing each CI job individually in the
protection rules is a weakness here that pwap-web avoids with an aggregate job.
2026-07-31 10:02:14 -07:00

163 lines
6.0 KiB
YAML

name: Deploy
on:
workflow_run:
workflows: ["CI"]
types: [completed]
branches: [main]
workflow_dispatch:
concurrency:
group: deploy
cancel-in-progress: true
env:
VITE_SUPABASE_URL: ${{ secrets.VITE_SUPABASE_URL }}
VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY }}
VITE_DEPOSIT_TON_ADDRESS: ${{ secrets.VITE_DEPOSIT_TON_ADDRESS }}
VITE_DEPOSIT_POLKADOT_ADDRESS: ${{ secrets.VITE_DEPOSIT_POLKADOT_ADDRESS }}
jobs:
deploy:
name: Deploy to VPS
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run build
# VPS1 (telegram.pezkuwichain.io) is currently running Zagros testnet - deployed manually
# Re-enable this step when testnet period is over
# - name: Deploy to telegram.pezkuwichain.io
# uses: appleboy/scp-action@v1.0.0
# with:
# host: ${{ secrets.VPS1_HOST }}
# username: ${{ secrets.VPS1_USER }}
# key: ${{ secrets.VPS1_SSH_KEY }}
# source: 'dist/*'
# target: '/var/www/telegram.pezkuwichain.io'
# strip_components: 1
- name: Deploy to telegram.pezkiwi.app
uses: appleboy/scp-action@v1.0.0
with:
host: ${{ secrets.VPS2_HOST }}
username: ${{ secrets.VPS2_USER }}
key: ${{ secrets.VPS2_SSH_KEY }}
source: 'dist/*'
target: '/var/www/telegram.pezkiwi.app'
strip_components: 1
- name: Cleanup old assets on VPS
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.VPS2_HOST }}
username: ${{ secrets.VPS2_USER }}
key: ${{ secrets.VPS2_SSH_KEY }}
script: bash /opt/cleanup-miniapp.sh
# Edge functions live in a Supabase volume shared with other projects, so they
# are written through the ownership gate on the host rather than copied in
# directly. The gate refuses any name this project does not own, which is what
# stops a repeat of the 2026-06-28 incident where another project's deploy
# silently replaced telegram-auth and broke sign-in for a month.
deploy-functions:
name: Deploy edge functions (self-hosted)
needs: deploy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# telegram-bot and ask are excluded on purpose: both Telegram bots and the
# news.pex.mom assistant are served from the cloud project, not from here.
# Stale copies of both still sit in this volume from before that split and
# take no traffic. They are deployed by the deploy-cloud-functions job.
- name: Package functions
run: |
tar czf functions.tgz -C supabase \
--exclude='functions/telegram-bot' \
--exclude='functions/ask' \
functions
- name: Copy to staging on VPS
uses: appleboy/scp-action@v1.0.0
with:
host: ${{ secrets.VPS2_HOST }}
username: ${{ secrets.VPS2_USER }}
key: ${{ secrets.VPS2_SSH_KEY }}
source: 'functions.tgz'
target: '/opt/miniapp-deploy-staging'
# The gate and its registry are versioned here, so the server copy is
# replaced from source control on every deploy rather than edited in
# place. They previously existed only at /usr/local/bin — one copy, no
# history, no review, gone with the server. The mechanism built to stop
# silent drift was itself drifting.
- name: Sync ownership gate to host
uses: appleboy/scp-action@v1.0.0
with:
host: ${{ secrets.VPS2_HOST }}
username: ${{ secrets.VPS2_USER }}
key: ${{ secrets.VPS2_SSH_KEY }}
source: 'ops/supabase-deploy-functions,ops/functions-registry.json'
target: '/opt/miniapp-deploy-staging'
strip_components: 1
- name: Deploy through ownership gate
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.VPS2_HOST }}
username: ${{ secrets.VPS2_USER }}
key: ${{ secrets.VPS2_SSH_KEY }}
script: |
set -e
BASE=/opt/miniapp-deploy-staging
# Install the gate before using it, so a change to the gate ships in
# the same deploy as the functions it validates.
install -m 755 "$BASE/supabase-deploy-functions" /usr/local/bin/supabase-deploy-functions
install -m 644 "$BASE/functions-registry.json" /opt/supabase-self-hosted/functions-registry.json
trap 'rm -rf "$BASE"' EXIT
rm -rf "$BASE/functions"
tar xzf "$BASE/functions.tgz" -C "$BASE"
supabase-deploy-functions \
--project pezkuwi-telegram-miniapp \
--src "$BASE/functions" \
--restart
# The two Telegram bots and the news.pex.mom assistant run on the cloud
# Supabase project, reached by webhook at
# vbhftvdayqfmcgmzdxfv.supabase.co/functions/v1/telegram-bot?bot=krd|dks.
# These were deployed by hand for months, which let the source in git drift
# behind what was actually running — badly enough that on 2026-07-21 the live
# function body had to be pulled back out of the deployed bundle to recover it.
deploy-cloud-functions:
name: Deploy edge functions (cloud)
needs: deploy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy telegram-bot and ask
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
run: |
set -e
if [ -z "$SUPABASE_ACCESS_TOKEN" ]; then
echo "::error::SUPABASE_ACCESS_TOKEN is not set — the bots would silently keep running old code"
exit 1
fi
for fn in telegram-bot ask; do
npx --yes supabase@latest functions deploy "$fn" \
--project-ref vbhftvdayqfmcgmzdxfv \
--no-verify-jwt
done