Skip to content

Replace mattn/go-sqlite3 with modernc.org/sqlite to remove CGO dependency #150

Description

@mrhillsman

Summary

Replace the CGO-based SQLite driver (github.com/mattn/go-sqlite3) with the pure-Go driver (modernc.org/sqlite). This eliminates the CGO build requirement, which is the sole blocker for cross-compiling to macOS and Linux ARM targets (see #TBD).

Background

The project currently uses github.com/mattn/go-sqlite3, which requires CGO_ENABLED=1 and a C compiler at build time. This has several consequences:

  • Cross-compilation is impractical — building for macOS or Linux ARM from a Linux amd64 CI runner requires platform-specific C cross-compilers (osxcross, aarch64-linux-gnu-gcc), adding significant CI complexity
  • Docker images need sqlite-libs at runtime (currently installed in both Dockerfile and Dockerfile.ci)
  • E2E test helper (testutil/binary.go) must explicitly set CGO_ENABLED=1 when building the test binary

modernc.org/sqlite is a mechanically translated pure-Go port of SQLite. It uses the same database/sql interface with the driver name "sqlite" instead of "sqlite3". DSN pragma syntax differs slightly.

Scope of change

The CGO surface is well-contained in this codebase:

File What changes
internal/audit/audit.go Import: mattn/go-sqlite3modernc.org/sqlite. Driver name: "sqlite3""sqlite". DSN pragma syntax: ?_journal_mode=WAL&_foreign_keys=on?_pragma=journal_mode(WAL)&_pragma=foreign_keys(on)
internal/audit/audit.go In-memory DSN: file::memory:?mode=memory&cache=shared&_foreign_keys=onfile::memory:?mode=memory&cache=shared&_pragma=foreign_keys(on)
internal/audit/migrate_test.go Same driver name and DSN adjustments as above
go.mod Remove github.com/mattn/go-sqlite3, add modernc.org/sqlite
.goreleaser.yaml Remove CGO_ENABLED=1 from env (or set to 0)
Dockerfile Remove CGO_ENABLED=1 from build command
Dockerfile Remove sqlite-libs from runtime apk add
Dockerfile.ci Remove sqlite-libs from runtime dependencies
internal/testutil/binary.go Remove CGO_ENABLED=1 from test binary build env

Implementation plan

1. Swap the driver (TDD)

Tests first:

  • Update internal/audit/audit_test.go and migrate_test.go driver name and DSN references
  • Run tests — they will fail because the new driver isn't imported yet

Implementation:

  • go get modernc.org/sqlite
  • In internal/audit/audit.go:
    • Replace _ "github.com/mattn/go-sqlite3" with _ "modernc.org/sqlite"
    • Change sql.Open("sqlite3", dsn)sql.Open("sqlite", dsn)
    • Update DSN pragma syntax for both file-based and in-memory paths
  • go mod tidy to remove mattn/go-sqlite3

Verify: go test ./internal/audit/... passes

2. Remove CGO build requirements

  • .goreleaser.yaml: change CGO_ENABLED=1 to CGO_ENABLED=0
  • Dockerfile: remove CGO_ENABLED=1 from go build, remove sqlite-libs from apk add
  • Dockerfile.ci: remove sqlite-libs install
  • internal/testutil/binary.go: remove CGO_ENABLED=1 from build env, update comment

Verify: go test ./... passes, go build ./cmd/virtwork succeeds without CGO

3. Validate existing audit database compatibility

  • modernc.org/sqlite reads/writes the same SQLite file format
  • Existing virtwork.db files will continue to work without migration
  • WAL journal mode and foreign keys behave identically

Acceptance criteria

  • go test ./... passes with CGO_ENABLED=0
  • go build ./cmd/virtwork succeeds with CGO_ENABLED=0
  • Existing virtwork.db files are readable by the new driver
  • No mattn/go-sqlite3 references remain in the codebase
  • No CGO_ENABLED=1 references remain in build files
  • Docker build succeeds without C compiler or sqlite-libs

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions