mirror of
https://github.com/pezkuwichain/pezkuwi-telegram-miniapp.git
synced 2026-11-01 16:31:09 +00:00
ci: deploy over pinned, single-purpose keys; root installs the gate
The deploy reached VPS3 as root with one key, handed to appleboy actions referenced by tag, and on every run it reinstalled the edge-function ownership gate and its registry as root. That gate guards every project's functions in the shared volume, so any change merged here could rewrite the rule that protects pwap's functions. Now CI connects as miniapp-deploy with two keys, each pinned on the host (authorized_keys restrict,command=) to a script in ops/host: - MINIAPP_SITE_DEPLOY_KEY -> miniapp-site-receive: the site tar on stdin, into /var/www/telegram.pezkiwi.app, nothing else. - MINIAPP_FUNCTIONS_DEPLOY_KEY -> miniapp-functions-ssh: `gate-sha`, or `deploy` with functions.tgz on stdin, run through sudo for exactly those arguments. Root installs the gate and registry from ops/; the deploy checks the host's copies against them and refuses to deploy functions while they differ. The asset cleanup that ran after each deploy (/opt/cleanup-miniapp.sh, only on the host) protected lazy chunks by mtime and so removed live ones when a build was shipped more than two hours after it was made; it took seven pages down for about two minutes on 2026-10-09 while this was being tested, and they were restored from the same tar. The receive script now keeps every file of the new build and removes other assets only after four hours. Its tests cover that (red with the keep rule removed) and the refusals; CI runs them with shellcheck, and apply-repo-settings.sh requires the job. VPS3's host key is pinned. The commented-out VPS1 deploy is gone; that host no longer exists. Every action is pinned to a commit SHA.
This commit is contained in:
1 parent
909dacd1af
commit
f6d53f51b1
11 files changed
+263
-104
No files matched your search
@@ -12,7 +12,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
||||
|
||||
- name: Create or update PR
|
||||
env:
|
||||
|
||||
@@ -25,8 +25,8 @@ jobs:
|
||||
name: ESLint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
@@ -37,8 +37,8 @@ jobs:
|
||||
name: TypeScript
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
@@ -49,8 +49,8 @@ jobs:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
@@ -62,14 +62,25 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [lint, typecheck, test]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
- run: npm ci
|
||||
- run: npm run build
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: dist
|
||||
path: dist/
|
||||
|
||||
# The scripts the deploy keys are pinned to are the whole of what CI can do
|
||||
# on the host, so they are checked with every change.
|
||||
host-scripts:
|
||||
name: Host deploy scripts
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
||||
- run: |
|
||||
shellcheck -S warning ops/host/miniapp-* ops/host/test-site-receive.sh
|
||||
bash ops/host/test-site-receive.sh
|
||||
@@ -14,9 +14,9 @@ jobs:
|
||||
name: Code Complexity
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
@@ -35,9 +35,9 @@ jobs:
|
||||
name: Duplicate Code Detection
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
|
||||
@@ -26,9 +26,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
@@ -37,35 +37,19 @@ jobs:
|
||||
|
||||
- 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
|
||||
env:
|
||||
DEPLOY_KEY: ${{ secrets.MINIAPP_SITE_DEPLOY_KEY }}
|
||||
# VPS3's host key, read from the host itself; any other key fails.
|
||||
KNOWN_HOST: "217.77.6.126 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMxxRmMEYyng+B9O3jcUCMnUa31SyWQ+l7aj18/soWtJ"
|
||||
run: |
|
||||
umask 077
|
||||
printf '%s\n' "$DEPLOY_KEY" > "$RUNNER_TEMP/deploy_key"
|
||||
printf '%s\n' "$KNOWN_HOST" > "$RUNNER_TEMP/known_hosts"
|
||||
# The key is pinned on the host to ops/host/miniapp-site-receive
|
||||
# (authorized_keys restrict,command=): the site tar on stdin, then the
|
||||
# cleanup of earlier deploys' assets; nothing else.
|
||||
tar -C dist -cf - . | ssh -i "$RUNNER_TEMP/deploy_key" -o IdentitiesOnly=yes -o BatchMode=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile="$RUNNER_TEMP/known_hosts" miniapp-deploy@217.77.6.126 deploy
|
||||
|
||||
# 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
|
||||
@@ -77,7 +61,7 @@ jobs:
|
||||
needs: deploy
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
||||
|
||||
# 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.
|
||||
@@ -90,51 +74,27 @@ jobs:
|
||||
--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
|
||||
|
||||
# The ownership gate and its registry guard every project's functions
|
||||
# in the shared volume, so this deploy does not install them; root does,
|
||||
# from ops/ here. What the deploy does is refuse to run when the host's
|
||||
# copy is not the one in this repository.
|
||||
- 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
|
||||
env:
|
||||
DEPLOY_KEY: ${{ secrets.MINIAPP_FUNCTIONS_DEPLOY_KEY }}
|
||||
# VPS3's host key, read from the host itself; any other key fails.
|
||||
KNOWN_HOST: "217.77.6.126 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMxxRmMEYyng+B9O3jcUCMnUa31SyWQ+l7aj18/soWtJ"
|
||||
run: |
|
||||
umask 077
|
||||
printf '%s\n' "$DEPLOY_KEY" > "$RUNNER_TEMP/deploy_key"
|
||||
printf '%s\n' "$KNOWN_HOST" > "$RUNNER_TEMP/known_hosts"
|
||||
# The key is pinned on the host to ops/host/miniapp-functions-ssh.
|
||||
want="$(sha256sum ops/supabase-deploy-functions ops/functions-registry.json | awk '{print $1}')"
|
||||
have="$(ssh -i "$RUNNER_TEMP/deploy_key" -o IdentitiesOnly=yes -o BatchMode=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile="$RUNNER_TEMP/known_hosts" miniapp-deploy@217.77.6.126 gate-sha)"
|
||||
if [ "$want" != "$have" ]; then
|
||||
echo "::error::the host's ownership gate or registry is not the one in ops/; install them as root (ops/host/README.md), then re-run"
|
||||
exit 1
|
||||
fi
|
||||
ssh -i "$RUNNER_TEMP/deploy_key" -o IdentitiesOnly=yes -o BatchMode=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile="$RUNNER_TEMP/known_hosts" miniapp-deploy@217.77.6.126 deploy < functions.tgz
|
||||
|
||||
# The two Telegram bots and the news.pex.mom assistant run on the cloud
|
||||
# Supabase project, reached by webhook at
|
||||
@@ -147,7 +107,7 @@ jobs:
|
||||
needs: deploy
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
||||
|
||||
- name: Deploy telegram-bot and ask
|
||||
env:
|
||||
|
||||
@@ -16,9 +16,9 @@ jobs:
|
||||
name: Dependency Audit
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
@@ -41,10 +41,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'pull_request'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
||||
|
||||
- name: Dependency Review
|
||||
uses: actions/dependency-review-action@v4
|
||||
uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4.9.0
|
||||
with:
|
||||
fail-on-severity: high
|
||||
deny-licenses: GPL-3.0, AGPL-3.0
|
||||
@@ -53,12 +53,12 @@ jobs:
|
||||
name: Secret Scan
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: TruffleHog Secret Scan
|
||||
uses: trufflesecurity/trufflehog@main
|
||||
uses: trufflesecurity/trufflehog@e76dff8a7e57d6df0c60ad8cbf5d5cba505a8fc9 # v3.99.2
|
||||
with:
|
||||
path: ./
|
||||
base: ${{ github.event.repository.default_branch }}
|
||||
|
||||
Reference in new issue
Block a user