-
-
Notifications
You must be signed in to change notification settings - Fork 1
296 lines (259 loc) · 11.6 KB
/
Copy pathci.yml
File metadata and controls
296 lines (259 loc) · 11.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
name: pgclone CI
on:
push:
branches: [ main, 'fix/**', 'feature/**' ]
paths-ignore:
- '**.md'
- 'docs/**'
- 'LICENSE'
- '.gitignore'
- '.gitattributes'
- '.editorconfig'
- '.github/FUNDING.yml'
- '.github/ISSUE_TEMPLATE/**'
- '.github/PULL_REQUEST_TEMPLATE.md'
- '.github/CODEOWNERS'
- '.github/dependabot.yml'
- '**.png'
- '**.jpg'
- '**.jpeg'
- '**.gif'
- '**.svg'
- '**.webp'
- 'release-v*.md'
pull_request:
branches: [ main ]
paths-ignore:
- '**.md'
- 'docs/**'
- 'LICENSE'
- '.gitignore'
- '.gitattributes'
- '.editorconfig'
- '.github/FUNDING.yml'
- '.github/ISSUE_TEMPLATE/**'
- '.github/PULL_REQUEST_TEMPLATE.md'
- '.github/CODEOWNERS'
- '.github/dependabot.yml'
- '**.png'
- '**.jpg'
- '**.jpeg'
- '**.gif'
- '**.svg'
- '**.webp'
- 'release-v*.md'
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
test:
name: "PostgreSQL ${{ matrix.pg_version }}"
runs-on: ubuntu-22.04
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
pg_version: [14, 15, 16, 17, 18]
services:
source-db:
image: postgres:${{ matrix.pg_version }}
env:
POSTGRES_PASSWORD: testpass
POSTGRES_DB: source_db
ports:
- 5433:5432
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 5s
--health-timeout 5s
--health-retries 10
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install PostgreSQL ${{ matrix.pg_version }}
run: |
PG_VER=${{ matrix.pg_version }}
sudo apt-get update -qq
sudo apt-get install -y postgresql-common ca-certificates curl gnupg
sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y
sudo apt-get update -qq
if ! sudo apt-get install -y postgresql-$PG_VER postgresql-server-dev-$PG_VER libpq-dev build-essential; then
echo "Primary install failed for PG${PG_VER}. Trying fallback..."
apt-cache policy "postgresql-$PG_VER" || true
sudo apt-get install -y "postgresql-$PG_VER" postgresql-server-dev-all libpq-dev build-essential
fi
/usr/lib/postgresql/$PG_VER/bin/pg_config --version || pg_config --version
- name: Build and install pgclone
run: |
PG_CONFIG=/usr/lib/postgresql/${{ matrix.pg_version }}/bin/pg_config
make PG_CONFIG=$PG_CONFIG CFLAGS="-Isrc"
sudo make install PG_CONFIG=$PG_CONFIG
- name: Start target PostgreSQL
run: |
PG_VER=${{ matrix.pg_version }}
PGBIN=/usr/lib/postgresql/$PG_VER/bin
PGDATA="$RUNNER_TEMP/pg_target_$PG_VER"
PGLOG="$RUNNER_TEMP/pg_target_$PG_VER.log"
sudo systemctl stop postgresql 2>/dev/null || true
rm -rf "$PGDATA"
"$PGBIN/initdb" -D "$PGDATA" --username=postgres --auth=trust
{
echo "port = 5434"
echo "listen_addresses = 'localhost'"
echo "shared_buffers = '128MB'"
echo "max_connections = 50"
echo "huge_pages = off"
echo "shared_preload_libraries = 'pgclone'"
echo "max_worker_processes = 32"
echo "unix_socket_directories = '/tmp'"
echo "logging_collector = off"
} >> "$PGDATA/postgresql.conf"
if ! "$PGBIN/pg_ctl" -D "$PGDATA" -l "$PGLOG" -w -t 30 start; then
echo "=== PostgreSQL failed to start ==="
cat "$PGLOG"
exit 1
fi
"$PGBIN/createdb" -h localhost -p 5434 -U postgres target_db
- name: Seed source database
run: |
PGPASSWORD=testpass psql -h localhost -p 5433 -U postgres -d source_db \
-f test/fixtures/seed.sql
- name: Install pgTAP and create extensions
run: |
PG_CONFIG=/usr/lib/postgresql/${{ matrix.pg_version }}/bin/pg_config
git clone --depth 1 https://github.com/theory/pgtap.git /tmp/pgtap
cd /tmp/pgtap && make PG_CONFIG=$PG_CONFIG && sudo make install PG_CONFIG=$PG_CONFIG
psql -h localhost -p 5434 -U postgres -d target_db <<SQL
CREATE EXTENSION IF NOT EXISTS pgtap;
CREATE EXTENSION IF NOT EXISTS pgclone;
SELECT pgclone.version();
SQL
psql -h localhost -p 5434 -U postgres -d target_db \
-c "ALTER DATABASE target_db SET app.source_conninfo = 'host=localhost port=5433 dbname=source_db user=postgres password=testpass';"
- name: "Test: pgTAP (66 sync tests)"
run: |
psql -h localhost -p 5434 -U postgres -d target_db \
-f test/pgclone_test.sql
- name: "Test: Loopback-DDL (roles, verify, report, DDM, diff, preflight)"
env:
PGHOST: localhost
PGPORT: 5434
SOURCE_CONNINFO: "host=localhost port=5433 dbname=source_db user=postgres password=testpass"
run: bash test/test_loopback.sh
- name: "Test: pgclone.database_create"
run: |
SOURCE_CONNINFO="host=localhost port=5433 dbname=source_db user=postgres password=testpass"
psql -h localhost -p 5434 -U postgres -d postgres \
-c "CREATE EXTENSION IF NOT EXISTS pgclone;"
psql -h localhost -p 5434 -U postgres -d postgres \
-c "DROP DATABASE IF EXISTS clone_test_db;" 2>/dev/null || true
# Create and clone
psql -h localhost -p 5434 -U postgres -d postgres -v ON_ERROR_STOP=1 \
-c "SELECT pgclone.database_create('${SOURCE_CONNINFO}', 'clone_test_db', true);"
# Verify
DB_EXISTS=$(psql -h localhost -p 5434 -U postgres -d postgres -tAc \
"SELECT 1 FROM pg_database WHERE datname = 'clone_test_db'")
[ "$DB_EXISTS" = "1" ] || { echo "FAIL: DB not created"; exit 1; }
ROW_COUNT=$(psql -h localhost -p 5434 -U postgres -d clone_test_db -tAc \
"SELECT count(*) FROM test_schema.customers")
[ "$ROW_COUNT" -ge 1 ] || { echo "FAIL: no data"; exit 1; }
# Idempotent re-clone
psql -h localhost -p 5434 -U postgres -d postgres -v ON_ERROR_STOP=1 \
-c "SELECT pgclone.database_create('${SOURCE_CONNINFO}', 'clone_test_db', false);"
# Cleanup
psql -h localhost -p 5434 -U postgres -d postgres \
-c "DROP DATABASE IF EXISTS clone_test_db;" 2>/dev/null || true
echo "ALL database_create tests PASSED"
- name: "Test: Async functions (bgworker)"
run: |
SOURCE_CONNINFO="host=localhost port=5433 dbname=source_db user=postgres password=testpass"
PG="psql -h localhost -p 5434 -U postgres -d target_db"
PASS=0; FAIL=0
run_test() {
if eval "$2" 2>/dev/null; then
echo " PASS: $1"; PASS=$((PASS + 1))
else
echo " FAIL: $1"; FAIL=$((FAIL + 1))
fi
}
# Clean slate — drop leftover tables from sync tests
$PG -c "SELECT pgclone.clear_jobs();" 2>/dev/null || true
$PG <<SQL 2>/dev/null
DROP TABLE IF EXISTS public.simple_test CASCADE;
DROP TABLE IF EXISTS public.simple_test_copy CASCADE;
DROP TABLE IF EXISTS public.simple_test_empty CASCADE;
DROP TABLE IF EXISTS public.async_renamed CASCADE;
DROP TABLE IF EXISTS test_schema.customers_lite CASCADE;
DROP TABLE IF EXISTS test_schema.active_only CASCADE;
SQL
# 1: table_async basic
echo "---- Async: table_async basic ----"
JOB=$($PG -tAc "SELECT pgclone.table_async('${SOURCE_CONNINFO}', 'public', 'simple_test', true);")
run_test "returns job_id" "[ -n '$JOB' ] && [ '$JOB' -gt 0 ]"
for i in $(seq 1 30); do
S=$($PG -tAc "SELECT status FROM pgclone.jobs_view WHERE job_id=$JOB;" 2>/dev/null | tr -d '[:space:]')
[ "$S" = "completed" ] || [ "$S" = "failed" ] && break; sleep 1
done
run_test "job completed" "[ '$S' = 'completed' ]"
RC=$($PG -tAc "SELECT count(*)::int FROM public.simple_test;" 2>/dev/null | tr -d '[:space:]')
run_test "simple_test has 5 rows" "[ '$RC' = '5' ]"
# 2: table_async with target name
echo "---- Async: table_async target name ----"
JOB2=$($PG -tAc "SELECT pgclone.table_async('${SOURCE_CONNINFO}', 'public', 'simple_test', true, 'async_renamed');")
for i in $(seq 1 30); do
S2=$($PG -tAc "SELECT status FROM pgclone.jobs_view WHERE job_id=$JOB2;" 2>/dev/null | tr -d '[:space:]')
[ "$S2" = "completed" ] || [ "$S2" = "failed" ] && break; sleep 1
done
run_test "renamed job completed" "[ '$S2' = 'completed' ]"
TE=$($PG -tAc "SELECT count(*) FROM pg_tables WHERE schemaname='public' AND tablename='async_renamed';" 2>/dev/null | tr -d '[:space:]')
run_test "async_renamed exists" "[ '$TE' = '1' ]"
# 3: schema_async
echo "---- Async: schema_async ----"
$PG -c "DROP SCHEMA IF EXISTS test_schema CASCADE;" 2>/dev/null || true
JOB3=$($PG -tAc "SELECT pgclone.schema_async('${SOURCE_CONNINFO}', 'test_schema', true);")
for i in $(seq 1 60); do
S3=$($PG -tAc "SELECT status FROM pgclone.jobs_view WHERE job_id=$JOB3;" 2>/dev/null | tr -d '[:space:]')
[ "$S3" = "completed" ] || [ "$S3" = "failed" ] && break; sleep 1
done
run_test "schema_async completed" "[ '$S3' = 'completed' ]"
CC=$($PG -tAc "SELECT count(*)::int FROM test_schema.customers;" 2>/dev/null | tr -d '[:space:]')
run_test "customers has 10 rows" "[ '$CC' = '10' ]"
# 4: progress/jobs/view
echo "---- Async: progress & jobs ----"
PR=$($PG -tAc "SELECT pgclone.progress($JOB);" 2>/dev/null)
run_test "progress returns JSON" "echo '$PR' | grep -q 'job_id'"
JJ=$($PG -tAc "SELECT pgclone.jobs();" 2>/dev/null)
run_test "jobs returns JSON" "echo '$JJ' | grep -q 'job_id'"
VC=$($PG -tAc "SELECT count(*) FROM pgclone.jobs_view;" 2>/dev/null | tr -d '[:space:]')
run_test "jobs_view has rows" "[ '$VC' -ge 1 ]"
# 5: clear_jobs
echo "---- Async: clear_jobs ----"
CL=$($PG -tAc "SELECT pgclone.clear_jobs();" 2>/dev/null | tr -d '[:space:]')
run_test "clear_jobs works" "[ '$CL' -ge 1 ]"
echo ""
echo "============================================"
echo "ASYNC: $PASS passed, $FAIL failed"
echo "============================================"
[ $FAIL -eq 0 ] || exit 1
- name: "Test: Snapshot-keeper resilience (issue #9)"
env:
PGHOST: localhost
PGPORT: 5434
SOURCE_HOST: localhost
SOURCE_PORT: 5433
SOURCE_DB: source_db
SOURCE_USER: postgres
SOURCE_PW: testpass
run: |
# The test script defaults its target connection to
# `psql -U postgres -d target_db`, which matches the CI
# target DB created on port 5434.
bash test/test_snapshot_keeper.sh
- name: Show logs on failure
if: failure()
run: |
PG_VER=${{ matrix.pg_version }}
PGLOG="$RUNNER_TEMP/pg_target_$PG_VER.log"
echo "=== PostgreSQL $PG_VER log ==="
[ -f "$PGLOG" ] && tail -150 "$PGLOG" || echo "No log found"
echo "=== Port usage ==="
sudo ss -tlnp | grep -E '5432|5433|5434' || true