-
Notifications
You must be signed in to change notification settings - Fork 0
443 lines (416 loc) · 18.9 KB
/
Copy pathci.yml
File metadata and controls
443 lines (416 loc) · 18.9 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
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
# Run the full test suite on the three platforms most consumers actually
# use. Cross-build coverage for the long tail of modernc-supported targets
# is in build_all_targets below.
test:
name: test
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
# AGENTS.md / docs promise the two most recent Go releases;
# keep both here so a regression on the older line surfaces.
go: ['1.25.x', '1.26.x']
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
- uses: ./.github/actions/setup
with:
go-version: ${{ matrix.go }}
- name: go test ./...
run: go test -count=1 -timeout 5m ./...
# Race-mode test pass — linux/amd64 only because race-detection support is
# spotty on other targets and the bulk of the locking surface lives in
# platform-independent Go code anyway.
race:
name: race
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: ./.github/actions/setup
- name: go test -race
run: go test -race -count=1 -timeout 10m ./...
# Static analysis: go vet (always available) plus staticcheck (matches the
# modernc/sqlite CI lint pass).
lint:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: ./.github/actions/setup
- name: go vet
# unsafeptr=false silences the uintptr↔unsafe.Pointer false positives
# in the modernc-derived wrapper (see .golangci.yml + justfile vet
# recipe for the same suppression).
run: go vet -unsafeptr=false ./...
- name: gofmt -d
run: |
out=$(gofmt -d $(find . -name '*.go' -not -path './.*/*'))
if [ -n "$out" ]; then echo "$out"; exit 1; fi
- name: staticcheck
# We invoke staticcheck directly rather than dominikh/staticcheck-action
# because the action's latest tag (v1) still runs on Node.js 20, which
# GitHub is deprecating. Upstream has not cut a Node.js 24 release;
# running the binary on the existing toolchain bypasses the deprecated
# action entirely and produces identical lint output.
run: |
go install honnef.co/go/tools/cmd/staticcheck@latest
staticcheck ./...
- name: golangci-lint
# v8 of the action defaults to golangci-lint v2.x, which is the
# major version `.golangci.yml` is written against (`version: "2"`).
# We also pin the binary to a v2 release explicitly so a future
# action revision that flips the default cannot silently downgrade
# us back into the v1 series. v2.x is built with Go 1.25, which
# matches go.mod and avoids the "Go language version used to build
# golangci-lint is lower than the targeted Go version" error that
# v1.x throws on Go 1.25 modules.
uses: golangci/golangci-lint-action@v9
with:
version: v2.4.0
args: --timeout 5m
- name: gopls modernize
# Surfaces Go-version-bump idioms (range-over-int, reflect.TypeFor,
# strings.SplitSeq, sync.WaitGroup.Go, etc.). Skips the forked
# modernc/glebarez files we keep verbatim per CLAUDE.md — those
# follow upstream patterns and shouldn't drift on style.
#
# `^go:` strips Go's auto-toolchain breadcrumbs (`go: downloading
# ...`, `go: switching to go1.X`) — newer gopls releases require
# a newer Go than we pin in go.mod, so `go run @latest` will
# auto-download and that chatter would otherwise trip the
# `[ -n "$out" ]` gate.
run: |
out=$(go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest ./... 2>&1 \
| grep -v -E '^/[^:]*/(sqlite|vtab|rows)\.go:' \
| grep -v -E '^/[^:]*/gorm/(sqlite|migrator)\.go:' \
| grep -v '^exit status' \
| grep -v '^go: ' \
|| true)
if [ -n "$out" ]; then echo "$out"; exit 1; fi
# Cross-build every modernc-supported GOOS/GOARCH so we catch
# platform-specific compile errors without running the (often unavailable)
# test runtime there. The matrix mirrors modernc/sqlite's builder.json.
build_all_targets:
name: build (${{ matrix.goos }}/${{ matrix.goarch }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- { goos: darwin, goarch: amd64 }
- { goos: darwin, goarch: arm64 }
- { goos: freebsd, goarch: amd64 }
- { goos: freebsd, goarch: arm64 }
- { goos: linux, goarch: 386 }
- { goos: linux, goarch: amd64 }
- { goos: linux, goarch: arm }
- { goos: linux, goarch: arm64 }
- { goos: linux, goarch: loong64 }
- { goos: linux, goarch: ppc64le }
- { goos: linux, goarch: riscv64 }
- { goos: linux, goarch: s390x }
- { goos: windows, goarch: 386 }
- { goos: windows, goarch: amd64 }
- { goos: windows, goarch: arm64 }
steps:
- uses: actions/checkout@v5
- uses: ./.github/actions/setup
- name: cross build
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
# modernc.org/sqlite/vec is only transpiled for the same OS set we
# build the package on (linux/darwin/freebsd/openbsd/windows).
# `go build ./vec/...` on excluded targets fails because the
# underlying lib hasn't been generated there; the `|| true` is
# deliberate so the cross-build matrix doesn't fail on platforms
# we never claimed to support for vec.
CGO_ENABLED: '0'
run: |
go build ./
go build ./fts/...
go build ./vfs/...
go build ./vec/... || echo "vec not supported on ${GOOS}/${GOARCH} — skipping"
# Cross-build every PUBLISHED sub-module (own go.mod, module path
# gosqlite.org/<sub> — gorm, blobstore, vfs/crypto). Discovered, not
# hand-listed; mirrors `just cross-build`'s pubmods loop. Build the
# package (`./`), not `./...`: some module examples import
# gosqlite.org/vec, which isn't transpiled on every target.
for f in $(find . -mindepth 2 -name go.mod -not -path './.*/*' -not -path './examples/*'); do
grep -q '^module gosqlite[.]org/' "$f" || continue
(cd "$(dirname "$f")" && go build ./)
done
# Run the SQL conformance suite under tests/sql/. This is our methodical
# feature-by-feature proof that every documented SQLite SQL surface the
# driver exposes actually works. See dev/coverage/sql.md for the matrix.
sql_conformance:
name: sql-conformance
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
- uses: ./.github/actions/setup
- name: go test ./tests/sql/...
run: go test -count=1 -timeout 2m -v ./tests/sql/...
# Run the vendored subset of modernc.org/sqlite's own test suite against
# this fork. See dev/upstream/modernc.md for what's in/out and why.
# The `modernc_upstream` build tag gates this so the default `go test`
# path stays fast.
modernc_upstream:
name: modernc-upstream
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: ./.github/actions/setup
- name: go test -tags=modernc_upstream
run: go test -tags=modernc_upstream -count=1 -timeout 5m -v .
# Run the vendored subset of mattn/go-sqlite3's test suite against this
# fork. See dev/upstream/mattn.md for what's in/out and why — the
# subset is small because mattn's tests probe mattn-internal types
# heavily, not the database/sql contract. This lane is a canary for
# the mattn-compat surface (math UDFs, unlock-notify semantics, DSN
# flag handling), not a parity proof.
mattn_upstream:
name: mattn-upstream
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: ./.github/actions/setup
- name: go test -tags=mattn_upstream
run: go test -tags=mattn_upstream -count=1 -timeout 5m -v -run 'TestMath|TestUnlock' .
# Every sub-module has its OWN go.mod (joined to the root via `replace
# gosqlite.org`), so its private deps never reach the root graph: blobstore's
# codec, vfs/crypto's adiantum + x/crypto, xorm-compat's xorm.io/xorm, gorm's
# dialector deps. Each is linted (vet + staticcheck + golangci-lint + modernize)
# and tested in its own module context. (xorm-compat is the CI-enforced proof
# gosqlite.org is a drop-in xorm SQLite driver — see dev/upstream/xorm.md.)
#
# The matrix is DISCOVERED, not hand-listed: `discover` finds every joined
# sub-module's go.mod and emits the dir list as JSON; `submodules` fans out
# over it. Adding or removing a module needs no edit here — it mirrors
# `just test-submodules`. gorm's own unit tests run here, so the gorm-upstream
# job below stays focused on the upstream integration suite.
discover:
name: discover submodules
runs-on: ubuntu-latest
outputs:
mods: ${{ steps.find.outputs.mods }}
steps:
- uses: actions/checkout@v5
- id: find
# Joined sub-modules = own go.mod + a `replace gosqlite.org` directive,
# minus the hidden reference clones (.xorm, …) and the examples/* modules.
run: |
mods=$(for f in $(find . -mindepth 2 -name go.mod -not -path './.*/*' -not -path './examples/*'); do
grep -q 'replace gosqlite.org ' "$f" && dirname "$f" | sed 's|^[.]/||'
done | sort | jq -R . | jq -sc .)
echo "mods=$mods" >> "$GITHUB_OUTPUT"
echo "discovered submodules: $mods"
submodules:
name: submodule (${{ matrix.dir }})
needs: discover
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
dir: ${{ fromJson(needs.discover.outputs.mods) }}
steps:
- uses: actions/checkout@v5
- uses: ./.github/actions/setup
# Same lint gate as the root `lint` job, in the sub-module's own context.
# gofmt is not repeated here — the root job's `gofmt -d` already covers
# every .go file in the repo, sub-modules included.
- name: go vet
working-directory: ${{ matrix.dir }}
run: go vet -unsafeptr=false ./...
- name: staticcheck
working-directory: ${{ matrix.dir }}
run: |
go install honnef.co/go/tools/cmd/staticcheck@latest
staticcheck ./...
- name: golangci-lint
uses: golangci/golangci-lint-action@v9
with:
version: v2.4.0
# Runs in the sub-module dir; pin the repo-root config explicitly so
# resolution can't drift (rather than relying on the walk-up), and so
# its exclusions apply uniformly across modules.
args: --timeout 5m --config ${{ github.workspace }}/.golangci.yml
working-directory: ${{ matrix.dir }}
- name: gopls modernize
working-directory: ${{ matrix.dir }}
# Forked upstream files (gorm's sqlite.go/migrator.go) are excluded by
# path tail, since modernize runs with the sub-module as the working dir.
run: |
out=$(go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest ./... 2>&1 \
| grep -v -E '(^|/)(sqlite|vtab|rows|migrator)\.go:' \
| grep -v '^exit status' | grep -v '^go: ' || true)
if [ -n "$out" ]; then echo "$out"; exit 1; fi
- name: go test (${{ matrix.dir }} module)
working-directory: ${{ matrix.dir }}
run: go test -count=1 -timeout 5m -v ./...
# Standing guard that every PUBLISHED sub-module pins the same gosqlite.org
# core version. A lagging pin is exactly what once made `go get
# gosqlite.org/gorm` fail with an ambiguous import (the submodule required an
# old core that still bundled gorm/). Mirrors `just check-pins`; `just release`
# keeps them in lockstep by construction.
module_pins:
name: module-pins
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: core-version pins are in lockstep
# Mirrors `just check-pins`; identical associative-array-free logic so
# the guard behaves the same on any bash.
run: |
vers=""
while IFS= read -r f; do
grep -q '^module gosqlite[.]org/' "$f" || continue
v=$(grep -oE 'gosqlite\.org v[0-9][^[:space:]]*' "$f" | awk '{print $2}' | head -n1 || true)
[ -n "$v" ] || continue
printf ' %-14s gosqlite.org %s\n' "$(dirname "$f")" "$v"
vers="${vers}${v}"$'\n'
done < <(find . -mindepth 2 -name go.mod -not -path './.*/*' -not -path './examples/*' | sort)
distinct=$(printf '%s' "$vers" | sed '/^$/d' | sort -u | wc -l | tr -d ' ')
if [ "$distinct" -gt 1 ]; then
echo "::error::published sub-modules pin different core versions"
exit 1
fi
echo "all published sub-modules pin the same gosqlite.org core version"
# Run gorm.io/gorm's own integration test suite against our dialector.
# The setup, reasoning, and current pass/fail counts live in
# dev/upstream/gorm.md; this job is the CI-enforced version of the
# recipe documented there. We pin gorm to the same version go.mod
# depends on, so the suite stays in lockstep with our `gorm/` package.
gorm_upstream:
name: gorm-upstream (v1.31.1)
runs-on: ubuntu-latest
env:
GORM_VERSION: v1.31.1
WORK_DIR: ${{ github.workspace }}/.gorm-upstream
steps:
- uses: actions/checkout@v5
with:
path: sqlite
# This job checks our repo out into ./sqlite (so gorm can be cloned
# into a sibling dir), so the local action lives under ./sqlite —
# not the workspace root, where every other job's checkout puts it.
# cache-dependency-path forwards to actions/setup-go so its
# toolchain cache restore finds our go.sum at the non-root checkout
# location; every other job inherits the default empty value.
- uses: ./sqlite/.github/actions/setup
with:
cache-dependency-path: sqlite/go.sum
# gorm's own unit tests (incl. the DDL parser) run in the `submodules`
# matrix above; this job stays focused on the upstream integration suite.
- name: Clone gorm at pinned version
run: |
mkdir -p "$WORK_DIR"
git clone --depth 1 --branch "$GORM_VERSION" \
https://github.com/go-gorm/gorm.git "$WORK_DIR/gorm"
- name: Write shim module re-exporting our dialector
# The upstream tests import gorm.io/driver/sqlite. We expose
# gosqlite.org/gorm under that path via a tiny shim,
# then append WAL + busy_timeout DSN flags so modernc's pure-Go
# file locks don't return SQLITE_BUSY on concurrent goroutines —
# mattn/go-sqlite3 (which gorm's tests were written against)
# serializes via libc-level locks instead.
run: |
mkdir -p "$WORK_DIR/shim"
cat > "$WORK_DIR/shim/go.mod" <<EOF
module gorm.io/driver/sqlite
go 1.25.0
require (
gosqlite.org/gorm v0.0.0
gorm.io/gorm $GORM_VERSION
)
replace gosqlite.org/gorm => ${{ github.workspace }}/sqlite/gorm
replace gosqlite.org => ${{ github.workspace }}/sqlite
EOF
cat > "$WORK_DIR/shim/sqlite.go" <<'EOF'
package sqlite
import (
"strings"
sqlitegorm "gosqlite.org/gorm"
"gorm.io/gorm"
)
const DriverName = sqlitegorm.DriverName
type (
Dialector = sqlitegorm.Dialector
Config = sqlitegorm.Config
)
const extraFlags = "_busy_timeout=5000&_journal=WAL&_sync=NORMAL"
func withFlags(dsn string) string {
if dsn == "" || strings.HasPrefix(dsn, ":memory:") || strings.Contains(dsn, "busy_timeout") {
return dsn
}
sep := "?"
if strings.Contains(dsn, "?") {
sep = "&"
}
return dsn + sep + extraFlags
}
func Open(dsn string) gorm.Dialector { return sqlitegorm.Open(withFlags(dsn)) }
func New(cfg Config) gorm.Dialector {
cfg.DSN = withFlags(cfg.DSN)
return sqlitegorm.New(cfg)
}
EOF
(cd "$WORK_DIR/shim" && go mod tidy)
- name: Wire shim into gorm/tests module
run: |
cat >> "$WORK_DIR/gorm/tests/go.mod" <<EOF
replace gorm.io/driver/sqlite => $WORK_DIR/shim
replace gosqlite.org/gorm => ${{ github.workspace }}/sqlite/gorm
replace gosqlite.org => ${{ github.workspace }}/sqlite
EOF
(cd "$WORK_DIR/gorm/tests" && go mod tidy)
- name: Run gorm upstream test suite
run: |
cd "$WORK_DIR/gorm/tests"
# tee duplicates output to a file we parse for PASS/FAIL counts
# below. Two pitfalls we work around here:
# 1. GitHub Actions prefixes every line with a UTC timestamp,
# even in the file written by tee — so we cannot anchor our
# grep with `^--- PASS:`. We use an unanchored match.
# 2. `go test`'s exit code is masked by tee's (always 0). We
# capture it via PIPESTATUS before the next command resets
# $?, and disable errexit around the pipeline so a
# legitimate test-fail doesn't abort our reporter.
set +e
go test -count=1 -timeout 5m -v ./... 2>&1 | tee /tmp/gorm-upstream.log
rc=${PIPESTATUS[0]}
set -e
# `grep -c` exits 1 on zero matches; `|| true` keeps the
# outer script alive so we can report the actual counts.
PASS=$(grep -c -- '--- PASS:' /tmp/gorm-upstream.log || true)
FAIL=$(grep -c -- '--- FAIL:' /tmp/gorm-upstream.log || true)
SKIP=$(grep -c -- '--- SKIP:' /tmp/gorm-upstream.log || true)
echo "--- summary ---"
echo "PASS=$PASS FAIL=$FAIL SKIP=$SKIP go-test-exit=$rc"
if [ "$rc" -ne 0 ]; then
echo "::error::go test exited with $rc"
exit "$rc"
fi
if [ "$FAIL" -ne 0 ]; then
echo "::error::gorm upstream suite reports $FAIL failures"
exit 1
fi
# PASS count includes sub-test lines (which are indented in the
# file too) — for gorm v1.31.1 the total is ~15000. Floor at
# 10000 to catch a "no tests ran at all" regression without
# being brittle on minor gorm bumps.
if [ "$PASS" -lt 10000 ]; then
echo "::error::gorm upstream PASS count regressed: got $PASS, expected >= 10000"
exit 1
fi