ops(sql-runner): add whitelisted SQL workflow + stuck-leads cleanup doc
.github/workflows/sql-runner.yml — универсальный SQL-runner для прод-операций через GitHub Actions (workflow_dispatch). Whitelist: SELECT/WITH/EXPLAIN (read-only) + targeted UPDATE/DELETE на 5 таблицах при confirm_mutating=true. docs/ops/2026-05-29-stage5-stuck-leads-cleanup.md — шаблон rollback log + инструкции для cleanup 2 застрявших supplier_leads (id=1110, 1157, ~256k failed_webhook_jobs). Root cause: поставщик crm.bp-gr.ru шлёт B1+SMS combo, constraint chk_supplier_projects_b1_not_for_sms запрещает (Finding 2 Stage 5). Task 1 plan 2026-05-29-supplier-webhook-fast-fail-and-stuck-cleanup.md. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
name: Run whitelisted SQL on liderra.ru
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
sql:
|
||||
description: 'SQL query (SELECT only by default; UPDATE/DELETE need confirm_mutating=true)'
|
||||
required: true
|
||||
type: string
|
||||
confirm_mutating:
|
||||
description: 'Подтверждаю UPDATE/DELETE на проде'
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
run:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
LIDERRA_HOST: 111.88.246.137
|
||||
LIDERRA_USER: ubuntu
|
||||
SQL: ${{ github.event.inputs.sql }}
|
||||
CONFIRM_MUT: ${{ github.event.inputs.confirm_mutating }}
|
||||
|
||||
steps:
|
||||
- name: Whitelist check
|
||||
run: |
|
||||
set -euo pipefail
|
||||
SQL_LOWER=$(echo "$SQL" | tr '[:upper:]' '[:lower:]' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
||||
|
||||
# Allow: SELECT / WITH (CTE) / \d / EXPLAIN
|
||||
READ_RE='^(select |with |explain |\\d|\\df|\\di|\\dt)'
|
||||
|
||||
# Mutating allowed if confirm=true: targeted UPDATE/DELETE on specific tables
|
||||
MUTATING_RE='^(update supplier_leads|update failed_webhook_jobs|update scheduler_heartbeats|delete from failed_webhook_jobs|delete from incidents_log) '
|
||||
|
||||
if [[ "$SQL_LOWER" =~ $READ_RE ]]; then
|
||||
echo "::notice::SELECT/read-only — allowed."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$SQL_LOWER" =~ $MUTATING_RE ]]; then
|
||||
if [[ "$CONFIRM_MUT" != "true" ]]; then
|
||||
echo "::error::Mutating SQL requires confirm_mutating=true."
|
||||
exit 1
|
||||
fi
|
||||
echo "::warning::Mutating SQL authorized."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "::error::SQL not in whitelist: $SQL_LOWER"
|
||||
exit 1
|
||||
|
||||
- name: Setup SSH key
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
echo "${{ secrets.LIDERRA_SSH_KEY }}" > ~/.ssh/liderra_deploy
|
||||
chmod 600 ~/.ssh/liderra_deploy
|
||||
ssh-keyscan -H ${{ env.LIDERRA_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
|
||||
|
||||
- name: Run on prod
|
||||
run: |
|
||||
set -o pipefail
|
||||
SQL_B64=$(printf '%s' "$SQL" | base64 -w0)
|
||||
ssh -i ~/.ssh/liderra_deploy ${{ env.LIDERRA_USER }}@${{ env.LIDERRA_HOST }} \
|
||||
"SQL_B64='$SQL_B64' bash -s" <<'REMOTE' | tee /tmp/sql.log
|
||||
SQL=$(echo "$SQL_B64" | base64 -d)
|
||||
echo "=== Running on $(hostname) at $(date -u) ==="
|
||||
echo "SQL: $SQL"
|
||||
echo
|
||||
sudo -u postgres psql -d liderra -c "$SQL"
|
||||
RC=$?
|
||||
echo
|
||||
echo "=== Exit code: $RC ==="
|
||||
exit $RC
|
||||
REMOTE
|
||||
|
||||
- name: Summary
|
||||
if: always()
|
||||
run: |
|
||||
{
|
||||
echo "## SQL on prod"
|
||||
echo
|
||||
echo '```sql'
|
||||
echo "$SQL"
|
||||
echo '```'
|
||||
echo
|
||||
echo '```'
|
||||
cat /tmp/sql.log 2>/dev/null
|
||||
echo '```'
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Cleanup
|
||||
if: always()
|
||||
run: rm -f ~/.ssh/liderra_deploy
|
||||
Reference in New Issue
Block a user