Repository navigation
348 lines (309 loc) · 14.5 KB
/
Copy pathci.yml
File metadata and controls
348 lines (309 loc) · 14.5 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
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
# Copyright 2026 the adbcbridge authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
name: CI
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
# One version, everywhere: every manifest and install snippet must agree with
# CMakeLists.txt (scripts/bump_version.py lists them). Cheap, and it turns a
# half-bumped release into a red check instead of a wrong number on PyPI.
version:
name: Version agreement
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- run: python3 scripts/bump_version.py --check
# The Rust crate ships a copy of the C sources (rust/csrc, kept by
# rust/sync-csrc.sh); a change to src/ that is not copied there would only
# surface in the Release workflow's crate job, after the tag.
- name: Bundled C sources match src/
run: diff -rq src rust/csrc/src
# docs/compatibility.json is generated from docs/COMPATIBILITY.md and the harness's
# DBS dict. Regenerating it here means the published data cannot drift from the
# table, and the generator's own count assertion (53 / 45 / 48) fails the build if a
# row changes without the figures quoted in README.md and docs/index.md changing too.
- name: Generated compatibility data is current
run: |
python3 scripts/gen_compatibility_json.py
git diff --exit-code -- docs/compatibility.json \
|| { echo "docs/compatibility.json is stale: run scripts/gen_compatibility_json.py"; exit 1; }
# PostgreSQL through psqlodbc, on a real server: pins the temporal precision and
# zone mapping, the current-schema option, schema-level GetObjects and the array
# ingest path (tests/test_postgres.py, tests/test_pg_array_ingest.py) -- behaviour
# that only a PostgreSQL server can verify, and that the SQLite job cannot.
postgres:
name: PostgreSQL (psqlodbc)
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: adbc
POSTGRES_PASSWORD: adbc
POSTGRES_DB: adbc
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U adbc"
--health-interval 5s
--health-timeout 5s
--health-retries 20
steps:
- uses: actions/checkout@v7
- name: Install unixODBC and psqlodbc
run: |
sudo apt-get update
sudo apt-get install -y unixodbc-dev odbc-postgresql
- name: Configure
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
- name: Build
run: cmake --build build --parallel 4
- name: Set up Python
uses: actions/setup-python@v7
with:
python-version: "3.12"
- name: Install Python dependencies
run: python -m pip install --upgrade pip "adbc-driver-manager>=1.7" pyarrow pytest
- name: Run the PostgreSQL tests
env:
PG_URI: postgresql://adbc:adbc@127.0.0.1:5432/adbc
POSTGRES_ODBC_DRIVER: /usr/lib/x86_64-linux-gnu/odbc/psqlodbcw.so
ADBC_ODBC_DRIVER: ${{ github.workspace }}/build/libadbc_driver_odbc.so
run: python -m pytest tests/test_postgres.py tests/test_pg_array_ingest.py -v -rs
build:
name: Build (${{ matrix.os }}${{ matrix.arch && format(' {0}', matrix.arch) || '' }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
arch: [""]
include:
# 32-bit Windows is a supported target: the Access, Excel and Text ODBC
# drivers on a machine with 32-bit Office are 32-bit only, and ODBC cannot
# mix bitness. SQLLEN is 32 bits wide there, which is what this job checks.
- os: windows-latest
arch: Win32
steps:
- uses: actions/checkout@v7
# Linux and macOS need an ODBC driver manager; Windows ships one.
- name: Install unixODBC (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y unixodbc-dev
- name: Install unixODBC (macOS)
if: runner.os == 'macOS'
run: brew install unixodbc
- name: Configure (macOS)
if: runner.os == 'macOS'
run: |
cmake -S . -B build \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/dist" \
-DCMAKE_PREFIX_PATH="$(brew --prefix unixodbc)"
- name: Configure
if: runner.os != 'macOS' && matrix.arch == ''
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/dist"
# Release, not Debug: the configuration people ship is the one that has to pass.
# An x86 /O2 /Ob2 test failure (see ResolvePartitionCount) was invisible to a Debug-only job.
- name: Configure (Windows x86)
if: matrix.arch == 'Win32'
run: cmake -S . -B build -A Win32 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/dist"
- name: Build
run: cmake --build build --config ${{ matrix.arch == 'Win32' && 'Release' || 'Debug' }} --parallel 4
- name: C unit tests
run: ctest --test-dir build -C ${{ matrix.arch == 'Win32' && 'Release' || 'Debug' }} --output-on-failure
- name: Install
run: cmake --install build --config ${{ matrix.arch == 'Win32' && 'Release' || 'Debug' }}
- name: Show driver manifest
if: runner.os != 'Windows'
run: cat dist/etc/adbc/drivers/odbc.toml
- name: Show driver manifest (Windows)
if: runner.os == 'Windows'
run: type dist\etc\adbc\drivers\odbc.toml
shell: cmd
# The end-to-end tests need an actual ODBC driver; SQLite is the cheapest
# one to install, so they run on Linux only.
- name: Install SQLite ODBC driver (Linux)
if: runner.os == 'Linux'
run: sudo apt-get install -y libsqliteodbc
- name: Set up Python
if: runner.os == 'Linux'
uses: actions/setup-python@v7
with:
python-version: "3.12"
- name: Install Python dependencies (Linux)
if: runner.os == 'Linux'
run: python -m pip install --upgrade pip "adbc-driver-manager>=1.7" pyarrow
- name: Run tests (Linux)
if: runner.os == 'Linux'
env:
SQLITE_ODBC_DRIVER: /usr/lib/x86_64-linux-gnu/odbc/libsqlite3odbc.so
ADBC_ODBC_DRIVER: ${{ github.workspace }}/build/libadbc_driver_odbc.so
run: python tests/test_sqlite.py
# install.sh --drivers: the bridge into ~/.local plus the four open-licence
# drivers (apt for three, ClickHouse's checksummed tarball for the fourth).
# Its own build tree, so the CI build above is left alone; the manifest it
# writes to ~/.config/adbc/drivers is what driver="odbc" resolves through.
- name: Driver bootstrap (install.sh --drivers, Linux)
if: runner.os == 'Linux'
env:
BUILD_DIR: ${{ github.workspace }}/build-install
run: |
set -o pipefail
./install.sh --drivers | tee install.log
grep -E '^ (sqlite|postgres|mysql|clickhouse) +ok ' install.log | wc -l | grep -qx 4
python - <<'PY'
import os, glob, ctypes
import adbc_driver_manager.dbapi as dbapi
with dbapi.connect(driver="odbc", db_kwargs={"uri": "Driver=SQLite3;Database=:memory:;"}) as conn:
with conn.cursor() as cur:
cur.execute("SELECT 1 AS one")
assert cur.fetch_arrow_table().to_pydict() == {"one": [1]}
for name in ("PostgreSQL Unicode", "MariaDB Unicode"):
# No server in this job: a driver that loads and answers the handshake is enough.
try:
dbapi.connect(driver="odbc", db_kwargs={"uri": f"Driver={name};Server=127.0.0.1;Port=1;Uid=x;Pwd=x;"})
except Exception as e:
assert "SQLDriverConnect" in str(e), e # reached the driver; refused by the (absent) server
ch = glob.glob(os.path.expanduser("~/.local/odbc-drivers/clickhouse-odbc-*/lib/libclickhouseodbcw.so"))
assert ch, "clickhouse-odbc not unpacked"
ctypes.CDLL(ch[0])
print("driver bootstrap OK")
PY
# The Python package (python/) is a thin wrapper around the library built
# above; ADBC_ODBC_DRIVER points its driver lookup at that build.
- name: Test the Python package (Linux)
if: runner.os == 'Linux'
env:
SQLITE_ODBC_DRIVER: /usr/lib/x86_64-linux-gnu/odbc/libsqlite3odbc.so
ADBC_ODBC_DRIVER: ${{ github.workspace }}/build/libadbc_driver_odbc.so
run: |
python -m pip install pytest
python -m pip install -e python
python -m pytest python/tests -q
# The Go binding (go/) drives the same library through arrow-adbc's cgo
# driver manager; its tests skip themselves unless both variables are set.
- name: Set up Go (Linux)
if: runner.os == 'Linux'
uses: actions/setup-go@v7
with:
go-version-file: go/go.mod
cache-dependency-path: go/go.sum
- name: Build and test the Go package (Linux)
if: runner.os == 'Linux'
working-directory: go
env:
SQLITE_ODBC_DRIVER: /usr/lib/x86_64-linux-gnu/odbc/libsqlite3odbc.so
ADBC_ODBC_DRIVER: ${{ github.workspace }}/build/libadbc_driver_odbc.so
run: |
go vet ./...
go build ./...
go test -v ./... 2>&1 | tee go-test.log
grep -q -- '--- PASS' go-test.log || { echo "::error::no Go test ran (all skipped?)"; exit 1; }
# The Rust crate, the .NET package and the Java library each carry a test
# suite that opens SQLite through the library built above; every suite
# skips itself without the two variables, so each step checks that tests
# really ran.
- name: Set up Rust (Linux)
if: runner.os == 'Linux'
uses: dtolnay/rust-toolchain@stable
- name: Test the Rust crate (Linux)
if: runner.os == 'Linux'
working-directory: rust
env:
SQLITE_ODBC_DRIVER: /usr/lib/x86_64-linux-gnu/odbc/libsqlite3odbc.so
ADBC_ODBC_DRIVER: ${{ github.workspace }}/build/libadbc_driver_odbc.so
run: |
cargo test 2>&1 | tee cargo-test.log
grep -E 'test result: ok\. [1-9][0-9]* passed' cargo-test.log >/dev/null || { echo "::error::no Rust test passed (all skipped or filtered?)"; exit 1; }
- name: Set up .NET (Linux)
if: runner.os == 'Linux'
uses: actions/setup-dotnet@v6
with:
dotnet-version: "8.0.x"
- name: Test the .NET package (Linux)
if: runner.os == 'Linux'
env:
SQLITE_ODBC_DRIVER: /usr/lib/x86_64-linux-gnu/odbc/libsqlite3odbc.so
ADBC_ODBC_DRIVER: ${{ github.workspace }}/build/libadbc_driver_odbc.so
run: |
dotnet test csharp/AdbcBridge.Tests -c Release --nologo 2>&1 | tee dotnet-test.log
grep -E 'Passed! .*Skipped: +0,' dotnet-test.log >/dev/null || { echo "::error::.NET tests did not all pass with zero skipped"; exit 1; }
- name: Set up Java (Linux)
if: runner.os == 'Linux'
uses: actions/setup-java@v6
with:
distribution: temurin
java-version: "17"
cache: maven
- name: Test the Java library (Linux)
if: runner.os == 'Linux'
env:
SQLITE_ODBC_DRIVER: /usr/lib/x86_64-linux-gnu/odbc/libsqlite3odbc.so
ADBC_ODBC_DRIVER: ${{ github.workspace }}/build/libadbc_driver_odbc.so
run: |
mvn -B -q -f java/pom.xml test
# -q hides Surefire's summary; the XML reports carry the counts regardless.
python3 - <<'PY'
import glob, sys, xml.etree.ElementTree as ET
files = glob.glob("java/target/surefire-reports/TEST-*.xml")
tests = failures = errors = skipped = 0
for f in files:
r = ET.parse(f).getroot()
tests += int(r.get("tests", 0)); failures += int(r.get("failures", 0))
errors += int(r.get("errors", 0)); skipped += int(r.get("skipped", 0))
print(f"surefire: {tests} tests, {failures} failures, {errors} errors, {skipped} skipped, {len(files)} report(s)")
if tests == 0 or failures or errors or skipped:
print("::error::Java tests did not all run and pass"); sys.exit(1)
PY
# Check that the installed manifest really lets the driver manager find
# the driver by name. Both install flavours are covered: the prefix
# given at configure time (dist, installed above) and a prefix given at
# install time with `cmake --install --prefix` (dist-relocated), which is
# what the README's install instructions use. The latter is a regression
# test: the manifest used to freeze the library path at configure time,
# so a relocated install shipped a manifest pointing at the old prefix.
- name: Check driver manifest discovery (Linux)
if: runner.os == 'Linux'
env:
SQLITE_ODBC_DRIVER: /usr/lib/x86_64-linux-gnu/odbc/libsqlite3odbc.so
run: |
cmake --install build --config Debug --prefix "${{ github.workspace }}/dist-relocated"
cat dist-relocated/etc/adbc/drivers/odbc.toml
cat > check_manifest.py <<'PY'
import os
import adbc_driver_manager.dbapi as dbapi
uri = f"Driver={os.environ['SQLITE_ODBC_DRIVER']};Database=:memory:;"
with dbapi.connect(driver="odbc", db_kwargs={"uri": uri}) as conn:
with conn.cursor() as cur:
cur.execute("SELECT 1 AS one")
assert cur.fetch_arrow_table().to_pydict() == {"one": [1]}
print("manifest discovery OK:", os.environ["ADBC_DRIVER_PATH"])
PY
for prefix in dist dist-relocated; do
ADBC_DRIVER_PATH="${{ github.workspace }}/$prefix/etc/adbc/drivers" \
python check_manifest.py
done