fix(ci/deploy): pre-apply partitioned migrations via postgres superuser + e2e CWD fix

Workflow run 26564909645 failed: migration 2026_05_27_120000_create_project_routing_snapshots_table
hit 'SET ROLE crm_migrator' failure (pgsql conn = crm_app_user, not member of crm_migrator).
Failed SET ROLE poisoned transaction → subsequent CREATE TABLE failed SQLSTATE[25P02].

Fix in deploy.yml:
  New step 'Pre-apply partitioned migrations via postgres superuser' runs CREATE TABLE
  + indexes + RLS + GRANTs + partitions + system_settings insert via sudo -u postgres psql,
  then marks migration as ran in migrations table. Idempotent (checks both migrations
  table AND information_schema). Established prod pattern (memory: paused_at migration 26.05).

Side fix in tools/enforce-override-limit.test.mjs:
  CLI e2e tests used 'node tools/enforce-override-limit.mjs' without cwd, failed when
  vitest ran from app/. Added cwd: projectRoot via fileURLToPath(import.meta.url).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Дмитрий
2026-05-28 12:33:47 +03:00
parent fe922c3091
commit ceb47eff10
3 changed files with 94 additions and 10 deletions
+79
View File
@@ -91,6 +91,85 @@ jobs:
scp -i ~/.ssh/liderra_deploy -o StrictHostKeyChecking=accept-new \
/tmp/deploy.tgz ${{ env.LIDERRA_USER }}@${{ env.LIDERRA_HOST }}:/tmp/deploy.tgz
- name: Pre-apply partitioned migrations via postgres superuser
# Workaround for partitioned-table migrations:
# 2026_05_27_120000_create_project_routing_snapshots_table.php has SET ROLE crm_migrator
# which fails when pgsql connection = crm_app_user (not a member of crm_migrator),
# poisoning the transaction. Established prod pattern (memory: paused_at migration 26.05):
# apply schema via sudo -u postgres psql + insert into migrations table.
# Idempotent — skips if already applied.
run: |
ssh -i ~/.ssh/liderra_deploy ${{ env.LIDERRA_USER }}@${{ env.LIDERRA_HOST }} 'bash -s' <<'REMOTE'
set -euo pipefail
MIG_NAME='2026_05_27_120000_create_project_routing_snapshots_table'
ALREADY=$(sudo -u postgres psql -d liderra -tAc \
"SELECT 1 FROM migrations WHERE migration = '${MIG_NAME}' LIMIT 1")
if [ "${ALREADY}" = "1" ]; then
echo "Migration ${MIG_NAME} already in migrations table — skipping."
exit 0
fi
TABLE_EXISTS=$(sudo -u postgres psql -d liderra -tAc \
"SELECT 1 FROM information_schema.tables WHERE table_name='project_routing_snapshots' LIMIT 1")
if [ "${TABLE_EXISTS}" != "1" ]; then
echo "Applying CREATE TABLE project_routing_snapshots via postgres superuser..."
sudo -u postgres psql -d liderra -v ON_ERROR_STOP=1 <<'PSQL'
BEGIN;
CREATE TABLE project_routing_snapshots (
snapshot_date DATE NOT NULL,
project_id BIGINT NOT NULL,
tenant_id BIGINT NOT NULL,
daily_limit INT NOT NULL CHECK (daily_limit >= 0),
delivery_days_mask INT NOT NULL CHECK (delivery_days_mask BETWEEN 0 AND 127),
regions INT[] NOT NULL DEFAULT '{}',
signal_type TEXT NOT NULL CHECK (signal_type IN ('call','site','sms')),
signal_identifier TEXT,
sms_senders JSONB,
sms_keyword TEXT,
expected_volume INT NOT NULL CHECK (expected_volume >= 0),
delivered_count INT NOT NULL DEFAULT 0 CHECK (delivered_count >= 0),
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
PRIMARY KEY (snapshot_date, project_id),
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
) PARTITION BY RANGE (snapshot_date);
ALTER TABLE project_routing_snapshots OWNER TO crm_migrator;
CREATE INDEX project_routing_snapshots_tenant_date_idx
ON project_routing_snapshots (tenant_id, snapshot_date);
CREATE INDEX project_routing_snapshots_signal_idx
ON project_routing_snapshots (snapshot_date, signal_type, lower(signal_identifier));
ALTER TABLE project_routing_snapshots ENABLE ROW LEVEL SECURITY;
CREATE POLICY project_routing_snapshots_tenant_isolation
ON project_routing_snapshots
USING (tenant_id = current_setting('app.current_tenant_id', true)::bigint);
GRANT SELECT, INSERT, UPDATE ON project_routing_snapshots TO crm_app_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON project_routing_snapshots TO crm_supplier_worker;
CREATE TABLE project_routing_snapshots_y2026_m05
PARTITION OF project_routing_snapshots
FOR VALUES FROM ('2026-05-01') TO ('2026-06-01');
CREATE TABLE project_routing_snapshots_y2026_m06
PARTITION OF project_routing_snapshots
FOR VALUES FROM ('2026-06-01') TO ('2026-07-01');
ALTER TABLE project_routing_snapshots_y2026_m05 OWNER TO crm_migrator;
ALTER TABLE project_routing_snapshots_y2026_m06 OWNER TO crm_migrator;
INSERT INTO system_settings (key, value, type, description, updated_at)
VALUES ('partition_retention_months_project_routing_snapshots', '3', 'int',
'Retention в месяцах для project_routing_snapshots (90 дней)', NOW())
ON CONFLICT (key) DO NOTHING;
COMMIT;
PSQL
else
echo "Table project_routing_snapshots already exists but migration not marked — marking only."
fi
# Mark migration as applied so Laravel migrate skips it
NEXT_BATCH=$(sudo -u postgres psql -d liderra -tAc "SELECT COALESCE(MAX(batch),0)+1 FROM migrations")
sudo -u postgres psql -d liderra -c \
"INSERT INTO migrations (migration, batch) VALUES ('${MIG_NAME}', ${NEXT_BATCH}) ON CONFLICT (migration) DO NOTHING;"
echo "Marked ${MIG_NAME} as applied (batch ${NEXT_BATCH})"
REMOTE
- name: Extract + run redeploy.sh on prod
run: |
ssh -i ~/.ssh/liderra_deploy ${{ env.LIDERRA_USER }}@${{ env.LIDERRA_HOST }} 'bash -s' <<'REMOTE'