Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,21 @@ jobs:
with:
go-version: "1.26"

- name: Install SQLite dev headers
run: sudo apt-get install -y libsqlite3-dev

# CGO_ENABLED=0 throughout, matching .goreleaser.yaml: the SQLite driver
# is pure Go, and testing the mode that ships is the point — a cgo-only
# driver once compiled here and then died at startup in every release
# archive, because nothing exercised the released build configuration.
- name: Run Go tests
# ./... rather than ./internal/... so cmd/devcloud's ServicePlugin
# conformance test runs in CI too.
run: CGO_ENABLED=1 go test ./... -v
run: go test ./... -v
env:
CGO_ENABLED: "1"
CGO_ENABLED: "0"

- name: Build Go binaries
run: |
CGO_ENABLED=1 go build -o dist/devcloud ./cmd/devcloud
CGO_ENABLED=1 go build -o dist/codegen ./cmd/codegen
CGO_ENABLED=0 go build -o dist/devcloud ./cmd/devcloud
CGO_ENABLED=0 go build -o dist/codegen ./cmd/codegen

# internal/generated is committed but derived. The Go tests check the fidelity
# manifest's shape — floors, registered services, the CRUD registry — none of
Expand Down
4 changes: 0 additions & 4 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,6 @@ jobs:
with:
go-version: "1.26"

- name: Install SQLite dev headers
if: matrix.language == 'go'
run: sudo apt-get install -y libsqlite3-dev

- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
Expand Down
7 changes: 3 additions & 4 deletions .github/workflows/compat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@ jobs:
with:
go-version: "1.26"

- name: Install SQLite dev headers
run: sudo apt-get install -y libsqlite3-dev

# CGO_ENABLED=0 is the mode .goreleaser.yaml publishes, so these tests
# double as the smoke test for the binary users actually download.
- name: Build devcloud binary
run: CGO_ENABLED=1 go build -o dist/devcloud ./cmd/devcloud
run: CGO_ENABLED=0 go build -o dist/devcloud ./cmd/devcloud

- name: Set up Python
uses: actions/setup-python@v7
Expand Down
3 changes: 0 additions & 3 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ jobs:
with:
go-version: "1.26"

- name: Install SQLite dev headers
run: sudo apt-get install -y libsqlite3-dev

- name: Run golangci-lint
uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9.3.0
with:
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,10 @@ jobs:
with:
go-version-file: go.mod
cache-dependency-path: go.sum
- name: Install SQLite dev headers
run: sudo apt-get install -y libsqlite3-dev
- name: Run Go tests
run: CGO_ENABLED=1 go test ./...
# CGO_ENABLED=0, the mode GoReleaser publishes in — a gate that tests a
# build configuration the release does not use vouches for nothing.
run: CGO_ENABLED=0 go test ./...

# The Go tests cannot see a stale fidelity manifest — that is the whole reason
# ci.yml has a codegen-drift job — so waiting only on `test` would let a tag
Expand Down Expand Up @@ -128,10 +128,10 @@ jobs:
with:
go-version-file: go.mod
cache-dependency-path: go.sum
- name: Install SQLite dev headers
run: sudo apt-get install -y libsqlite3-dev
- name: Build devcloud binary
run: CGO_ENABLED=1 go build -o dist/devcloud ./cmd/devcloud
# Same build configuration as .goreleaser.yaml, so the 775 tests below
# exercise the artifact this tag is about to publish.
run: CGO_ENABLED=0 go build -o dist/devcloud ./cmd/devcloud
- name: Set up Python
uses: actions/setup-python@v7
with:
Expand Down
7 changes: 2 additions & 5 deletions .github/workflows/smithy-sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,14 @@ jobs:
with:
go-version: "1.26"

- name: Install SQLite dev headers
run: sudo apt-get install -y libsqlite3-dev

- name: Download latest Smithy models
# --refresh re-downloads the committed models; without it every model
# already in the tree is skipped and this job can never find a change.
run: bash scripts/download-smithy-models.sh --refresh

- name: Run code generation
run: |
CGO_ENABLED=1 go run ./cmd/codegen \
go run ./cmd/codegen \
-models ./smithy-models \
-output ./internal/generated \
-templates ./internal/codegen/templates \
Expand All @@ -53,7 +50,7 @@ jobs:

- name: Run tests
if: steps.changes.outputs.changed == 'true'
run: CGO_ENABLED=1 go test ./... -v
run: CGO_ENABLED=0 go test ./... -v

- name: Create Pull Request
if: steps.changes.outputs.changed == 'true'
Expand Down
7 changes: 5 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,19 @@ repos:
language: system
types: [go]
exclude: ^internal/generated/
# CGO_ENABLED=0, matching .goreleaser.yaml and the CI workflows: it is the
# mode releases are built in, and no dependency needs a C toolchain, so a
# contributor without one still gets these hooks.
- id: go-vet
name: go vet
entry: bash -c 'CGO_ENABLED=1 go vet ./...'
entry: bash -c 'CGO_ENABLED=0 go vet ./...'
language: system
pass_filenames: false
types: [go]
exclude: ^internal/generated/
- id: go-build
name: go build
entry: bash -c 'CGO_ENABLED=1 go build ./...'
entry: bash -c 'CGO_ENABLED=0 go build ./...'
language: system
pass_filenames: false
types: [go]
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build:
go build -o dist/codegen ./cmd/codegen

test:
CGO_ENABLED=1 go test ./... -v
CGO_ENABLED=0 go test ./... -v

test-compat:
cd tests/compatibility && pip install -q -r requirements.txt && pytest -v
Expand Down
5 changes: 5 additions & 0 deletions changes/unreleased/Fixed-20260809-190000.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
kind: Fixed
body: Downloadable binaries now start. Releases were built with CGO_ENABLED=0 while the SQLite driver required cgo, so every tar.gz/zip binary, the Homebrew formula and the versioned `*-alpine` images exited at startup with "go-sqlite3 requires cgo to work. This is a stub" — only the rolling `latest` image, built separately with cgo, worked. The driver is now pure Go, so no build needs a C toolchain or SQLite headers
time: 2026-08-09T19:00:00.000000+09:00
custom:
Issue: "128"
7 changes: 4 additions & 3 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
# Stage 1: Build Go binary
FROM golang:1.26-alpine AS go-builder
RUN apk add --no-cache gcc musl-dev sqlite-dev
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
ENV CGO_ENABLED=1
# No C toolchain, no SQLite headers: the driver is pure Go. That is the same
# property that lets the released binaries build without one.
ENV CGO_ENABLED=0
RUN go build -o /devcloud ./cmd/devcloud
RUN go build -o /codegen ./cmd/codegen

# Stage 2: Runtime
FROM alpine:3.20
RUN apk add --no-cache sqlite-libs ca-certificates su-exec
RUN apk add --no-cache ca-certificates su-exec
RUN adduser -D -H -h /app appuser
WORKDIR /app
COPY --from=go-builder /devcloud /app/devcloud
Expand Down
3 changes: 1 addition & 2 deletions docker/Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
FROM golang:1.26-alpine
RUN apk add --no-cache gcc musl-dev sqlite-dev
RUN adduser -D -H -h /app appuser
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN chown -R appuser:appuser /app
USER appuser
ENV CGO_ENABLED=1
ENV CGO_ENABLED=0
RUN go build -o /app/devcloud ./cmd/devcloud
EXPOSE 4747
CMD ["/app/devcloud"]
2 changes: 1 addition & 1 deletion docs/release.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ The rest are only caught here.
generator overwrites the outputs it still emits but never removes one it has stopped
emitting, so regenerating in place leaves a retired file looking current.
- [ ] **boto3 compatibility passes** — `make test-compat`.
- [ ] **Go tests pass** — `CGO_ENABLED=1 go test ./...`.
- [ ] **Go tests pass** — `CGO_ENABLED=0 go test ./...`.
- [ ] **`main` is green**, including lint and CodeQL.
- [ ] **Every unreleased fragment carries an issue number** —
`grep -L 'Issue: "[0-9]' changes/unreleased/*.yaml` prints nothing.
Expand Down
11 changes: 0 additions & 11 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,6 @@ Common issues and how to resolve them. If your problem isn't listed here, check

## Installation & Build

### `sqlite3.h: No such file or directory` during `make build`

DevCloud requires SQLite development headers because the server uses CGO for SQLite access.

- **macOS**: `brew install sqlite3`
- **Ubuntu / Debian**: `sudo apt-get install libsqlite3-dev`
- **Fedora / RHEL**: `sudo dnf install sqlite-devel`
- **Alpine**: `apk add sqlite-dev build-base`

Then rebuild with `CGO_ENABLED=1 make build`.

### `go: module github.com/skyoo2003/devcloud: Go 1.26 required`

Upgrade Go to 1.26 or later. Check with `go version`. See [go.dev/dl](https://go.dev/dl/).
Expand Down
11 changes: 10 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,24 @@ go 1.26
toolchain go1.26.1

require (
github.com/mattn/go-sqlite3 v1.14.49
github.com/stretchr/testify v1.11.1
gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.56.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/mattn/go-isatty v0.0.24 // indirect
github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
golang.org/x/sys v0.47.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
modernc.org/libc v1.74.4 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
)
52 changes: 50 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,25 +1,73 @@
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3 h1:LMLX+LgTNWpfvCBdFebv6EsYotImrt/Ppc5cXIriCSo=
github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3/go.mod h1:jl5iWTm0/hd5PjEYEOuwAJ57L/CibdZfrqZ5XA5GrCk=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-sqlite3 v1.14.49 h1:B8jBHC3xhxZgxztrgruTuLucebnULQnx4W7cF7SAE9w=
github.com/mattn/go-sqlite3 v1.14.49/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w=
github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI=
github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A=
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
modernc.org/cc/v4 v4.29.1 h1:MKgdCV3WykTSPqpVrnxdEDS0HEd2FHpKZDzxzU5LyeI=
modernc.org/cc/v4 v4.29.1/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
modernc.org/ccgo/v4 v4.34.6 h1:sBgfIwyN0TQ9C5hwIeuqyeAKyMWnbvj2fvpF4L11uzU=
modernc.org/ccgo/v4 v4.34.6/go.mod h1:SZ8YcN9NG7XVsQYdm6jYBvi8PQP1qi+kqB6OhjqI3Fk=
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/gc/v3 v3.1.4 h1:2g65LGVSmFQrXeITAw97x7hCRvZFcyE1uDP+7Vng7JI=
modernc.org/gc/v3 v3.1.4/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
modernc.org/libc v1.74.4 h1:fX1Omw4o2/1C2iRkkIsrQTasJQldLhRmuPreXLoWs9k=
modernc.org/libc v1.74.4/go.mod h1:eeQAS9W3sZeKYMFubydxJpII9ybHWshk+7or7bLG9co=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg=
modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.56.0 h1:/D8e2RfFqoy/Zc6PuC76U28zFwmI/sYx1Kjm4yEn9e0=
modernc.org/sqlite v1.56.0/go.mod h1:yCJ2cmAaIkHQ25oXWrF8H4O1lIfPYPR26yCEDj2P3pQ=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
34 changes: 21 additions & 13 deletions internal/storage/sqlite/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,22 @@ import (
"path/filepath"
"strings"

sqlite3 "github.com/mattn/go-sqlite3"
sqlite "modernc.org/sqlite"
)

// driverName is a sqlite3 driver variant that registers the NUMTEXT collation
// on every connection. NUMTEXT orders TEXT columns holding numeric strings by
// true numeric value (exact across DynamoDB's full 38-digit precision), which
// a float CAST cannot do past 2^53.
const driverName = "sqlite3_numtext"

// driverName is the driver modernc.org/sqlite registers on import. It is a pure
// Go translation of SQLite, chosen because DevCloud's released binaries are
// built with CGO_ENABLED=0: a cgo-only driver still compiles under that flag
// and then exits at startup, which is how every tagged binary through v0.2.0
// shipped unable to open its own database.
const driverName = "sqlite"
Comment thread
skyoo2003 marked this conversation as resolved.

// NUMTEXT orders TEXT columns holding numeric strings by true numeric value
// (exact across DynamoDB's full 38-digit precision), which a float CAST cannot
// do past 2^53. Registration is process-wide here, so every connection carries
// it without a per-connection hook.
func init() {
sql.Register(driverName, &sqlite3.SQLiteDriver{
ConnectHook: func(conn *sqlite3.SQLiteConn) error {
return conn.RegisterCollation("NUMTEXT", compareNumericText)
},
})
sqlite.MustRegisterCollationUtf8("NUMTEXT", compareNumericText)
}

func compareNumericText(a, b string) int {
Expand Down Expand Up @@ -56,7 +57,14 @@ func Open(dbPath string, migrations []Migration) (*Store, error) {
return nil, fmt.Errorf("create data dir: %w", err)
}

db, err := sql.Open(driverName, dbPath)
// busy_timeout has to travel in the DSN so every pooled connection gets it.
// It is per-connection state, unlike journal_mode below, which the database
// file remembers — a single Exec here would configure whichever connection
// happened to answer and leave the rest at SQLite's default of no waiting.
// mattn/go-sqlite3 applied 5s unconditionally; modernc.org/sqlite installs
// no busy handler unless asked, so without this a second concurrent writer
// fails instantly with SQLITE_BUSY instead of waiting its turn.
db, err := sql.Open(driverName, dbPath+"?_pragma=busy_timeout(5000)")
Comment thread
skyoo2003 marked this conversation as resolved.
if err != nil {
return nil, fmt.Errorf("open sqlite: %w", err)
}
Expand Down
Loading