Skip to content

feat: add pure-go sqlite support via build tags when cgo is disabled - #1072

Open
LXSCA7 wants to merge 1 commit into
gotify:masterfrom
LXSCA7:feat/purego-sqlite-support
Open

LXSCA7 wants to merge 1 commit into
gotify:masterfrom
LXSCA7:feat/purego-sqlite-support

Conversation

@LXSCA7

@LXSCA7 LXSCA7 commented Oct 12, 2026

Copy link
Copy Markdown

Description

This PR enables pure-Go SQLite support via github.com/glebarez/sqlite (backed by modernc.org/sqlite) when compiling with CGO_ENABLED=0.

Motivation

Compiling Gotify with CGO_ENABLED=0 currently fails at runtime because mattn/go-sqlite3 falls back to stub functions that panic (Binary was compiled with 'CGO_ENABLED=0', go-sqlite3 requires cgo to work. This is a stub).

This limits running Gotify in environments where dynamic C libraries / glibc linking or cross-compilation toolchains are problematic (e.g. minimal FROM scratch containers, Alpine environments, Termux/Android, or embedded architectures).

Changes

  • Added conditional SQLite dialectors using Go build tags (//go:build cgo and //go:build !cgo).
  • Standard builds (CGO_ENABLED=1, official Docker images, release workflows) remain 100% unchanged using gorm.io/driver/sqlite.
  • Builds with CGO_ENABLED=0 automatically resolve to github.com/glebarez/sqlite without requiring CGO or external C compilers.
  • Updated database/migration_test.go to use the abstracted dialector.

Tests

  • Verified test suite passes under standard build (CGO_ENABLED=1).
  • Verified test suite passes without CGO (CGO_ENABLED=0).
  • Validated full binary cross-compilation and startup:
    CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -o gotify_arm64 .
Test output (standard: go test -v ./database/... ./test/testdb/...)

go test -v ./database/... ./test/testdb/...

=== RUN   TestDatabaseSuite
=== RUN   TestDatabaseSuite/TestApplication
=== RUN   TestDatabaseSuite/TestCleanupExpiredClients
=== RUN   TestDatabaseSuite/TestClient
=== RUN   TestDatabaseSuite/TestClientExpiresAtMemoization
=== RUN   TestDatabaseSuite/TestClientSelectHidesExpired
=== RUN   TestDatabaseSuite/TestDeleteAppDeletesMessages
=== RUN   TestDatabaseSuite/TestDeleteUserDeletesApplicationsAndClientsAndPluginConfs
=== RUN   TestDatabaseSuite/TestGetMessagesSince
=== RUN   TestDatabaseSuite/TestMessage
=== RUN   TestDatabaseSuite/TestPing_onClosedDB
=== RUN   TestDatabaseSuite/TestPing_onValidDB
=== RUN   TestDatabaseSuite/TestPluginConf
=== RUN   TestDatabaseSuite/TestUser
=== RUN   TestDatabaseSuite/TestUserPlugins
--- PASS: TestDatabaseSuite (0.32s)
    --- PASS: TestDatabaseSuite/TestApplication (0.01s)
    --- PASS: TestDatabaseSuite/TestCleanupExpiredClients (0.01s)
    --- PASS: TestDatabaseSuite/TestClient (0.01s)
    --- PASS: TestDatabaseSuite/TestClientExpiresAtMemoization (0.01s)
    --- PASS: TestDatabaseSuite/TestClientSelectHidesExpired (0.01s)
    --- PASS: TestDatabaseSuite/TestDeleteAppDeletesMessages (0.01s)
    --- PASS: TestDatabaseSuite/TestDeleteUserDeletesApplicationsAndClientsAndPluginConfs (0.01s)
    --- PASS: TestDatabaseSuite/TestGetMessagesSince (0.22s)    --- PASS: TestDatabaseSuite/TestMessage (0.01s)
    --- PASS: TestDatabaseSuite/TestPing_onClosedDB (0.01s)             --- PASS: TestDatabaseSuite/TestPing_onValidDB (0.01s)
    --- PASS: TestDatabaseSuite/TestPluginConf (0.01s)                  --- PASS: TestDatabaseSuite/TestUser (0.01s)
    --- PASS: TestDatabaseSuite/TestUserPlugins (0.01s)
=== RUN   TestInvalidDialect                                        --- PASS: TestInvalidDialect (0.00s)
=== RUN   TestCreateSqliteFolder                                    --- PASS: TestCreateSqliteFolder (0.01s)
=== RUN   TestWithAlreadyExistingSqliteFolder
--- PASS: TestWithAlreadyExistingSqliteFolder (0.01s)
=== RUN   TestPanicsOnMkdirError
--- PASS: TestPanicsOnMkdirError (0.00s)
=== RUN   TestMigrateSortKey                                        {"level":"info","count":4,"time":"2026-10-12T15:31:40-03:00","message":"Migrating application sort keys"}                               --- PASS: TestMigrateSortKey (0.00s)=== RUN   TestMigration
=== RUN   TestMigration/TestMigration                               --- PASS: TestMigration (0.00s)
    --- PASS: TestMigration/TestMigration (0.00s)
PASS
ok      github.com/gotify/server/v3/database    0.742s
=== RUN   Test_WithDefault
--- PASS: Test_WithDefault (0.00s)                                  === RUN   TestDatabaseSuite
=== RUN   TestDatabaseSuite/Test_Apps
=== RUN   TestDatabaseSuite/Test_Clients
=== RUN   TestDatabaseSuite/Test_Messages
=== RUN   TestDatabaseSuite/Test_Users
--- PASS: TestDatabaseSuite (0.00s)
    --- PASS: TestDatabaseSuite/Test_Apps (0.00s)
    --- PASS: TestDatabaseSuite/Test_Clients (0.00s)
    --- PASS: TestDatabaseSuite/Test_Messages (0.00s)
    --- PASS: TestDatabaseSuite/Test_Users (0.00s)
PASS
ok      github.com/gotify/server/v3/test/testdb 0.812s

CGO_ENABLED=0 go test -v ./database/... ./test/testdb/...

=== RUN   TestDatabaseSuite
=== RUN   TestDatabaseSuite/TestApplication
=== RUN   TestDatabaseSuite/TestCleanupExpiredClients
=== RUN   TestDatabaseSuite/TestClient
=== RUN   TestDatabaseSuite/TestClientExpiresAtMemoization
=== RUN   TestDatabaseSuite/TestClientSelectHidesExpired
=== RUN   TestDatabaseSuite/TestDeleteAppDeletesMessages
=== RUN   TestDatabaseSuite/TestDeleteUserDeletesApplicationsAndClientsAndPluginConfs
=== RUN   TestDatabaseSuite/TestGetMessagesSince
=== RUN   TestDatabaseSuite/TestMessage
=== RUN   TestDatabaseSuite/TestPing_onClosedDB
=== RUN   TestDatabaseSuite/TestPing_onValidDB
=== RUN   TestDatabaseSuite/TestPluginConf
=== RUN   TestDatabaseSuite/TestUser
=== RUN   TestDatabaseSuite/TestUserPlugins
--- PASS: TestDatabaseSuite (0.36s)
    --- PASS: TestDatabaseSuite/TestApplication (0.01s)
    --- PASS: TestDatabaseSuite/TestCleanupExpiredClients (0.01s)
    --- PASS: TestDatabaseSuite/TestClient (0.01s)
    --- PASS: TestDatabaseSuite/TestClientExpiresAtMemoization (0.01s)
    --- PASS: TestDatabaseSuite/TestClientSelectHidesExpired (0.01s)
    --- PASS: TestDatabaseSuite/TestDeleteAppDeletesMessages (0.01s)
    --- PASS: TestDatabaseSuite/TestDeleteUserDeletesApplicationsAndClientsAndPluginConfs (0.01s)
    --- PASS: TestDatabaseSuite/TestGetMessagesSince (0.25s)
    --- PASS: TestDatabaseSuite/TestMessage (0.01s)
    --- PASS: TestDatabaseSuite/TestPing_onClosedDB (0.01s)
    --- PASS: TestDatabaseSuite/TestPing_onValidDB (0.01s)
    --- PASS: TestDatabaseSuite/TestPluginConf (0.01s)
    --- PASS: TestDatabaseSuite/TestUser (0.01s)
    --- PASS: TestDatabaseSuite/TestUserPlugins (0.01s)
=== RUN   TestInvalidDialect
--- PASS: TestInvalidDialect (0.00s)
=== RUN   TestCreateSqliteFolder
--- PASS: TestCreateSqliteFolder (0.01s)
=== RUN   TestWithAlreadyExistingSqliteFolder
--- PASS: TestWithAlreadyExistingSqliteFolder (0.01s)
=== RUN   TestPanicsOnMkdirError
--- PASS: TestPanicsOnMkdirError (0.00s)
=== RUN   TestMigrateSortKey
{"level":"info","count":4,"time":"2026-10-12T15:32:22-03:00","message":"Migrating application sort keys"}
--- PASS: TestMigrateSortKey (0.00s)
=== RUN   TestMigration
=== RUN   TestMigration/TestMigration
--- PASS: TestMigration (0.01s)
    --- PASS: TestMigration/TestMigration (0.01s)
PASS
ok  	github.com/gotify/server/v3/database	1.356s
=== RUN   Test_WithDefault
--- PASS: Test_WithDefault (0.01s)
=== RUN   TestDatabaseSuite
=== RUN   TestDatabaseSuite/Test_Apps
=== RUN   TestDatabaseSuite/Test_Clients
=== RUN   TestDatabaseSuite/Test_Messages
=== RUN   TestDatabaseSuite/Test_Users
--- PASS: TestDatabaseSuite (0.01s)
    --- PASS: TestDatabaseSuite/Test_Apps (0.00s)
    --- PASS: TestDatabaseSuite/Test_Clients (0.00s)
    --- PASS: TestDatabaseSuite/Test_Messages (0.00s)
    --- PASS: TestDatabaseSuite/Test_Users (0.00s)
PASS
ok  	github.com/gotify/server/v3/test/testdb	0.584s

@LXSCA7
LXSCA7 requested a review from a team as a code owner October 12, 2026 19:10
@codecov

codecov Bot commented Oct 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 76.56%. Comparing base (6a49e9c) to head (d3c02f5).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1072      +/-   ##
==========================================
+ Coverage   76.54%   76.56%   +0.01%     
==========================================
  Files          68       69       +1     
  Lines        3714     3716       +2     
==========================================
+ Hits         2843     2845       +2     
  Misses        658      658              
  Partials      213      213              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@jmattheis

Copy link
Copy Markdown
Member

Hey, sorry I'm quiet busy currently. I think I rather not support two different sqlite implementations, as this just increase the maintenance burden. If something like this is added, it'll be the default and we'd drop the CGO sqlite lib.

I think as of now, I'd rather not change this, as gotify/server still depends on CGO due to plugins. If the plugin system is reworked (#825), then I'd be open to including something like this into gotify, so gotify won't have any CGO depnedencies which greatly reduces the building complexity of this project.

@LXSCA7

LXSCA7 commented Oct 27, 2026

Copy link
Copy Markdown
Author

hey @jmattheis, fair enough!

totally get the maintenance concern of having two drivers side by side. dropping CGO completely once the IPC plugin rework lands is definitely the cleaner path.

i'll keep this build on my fork for my current setup and keep an eye on #825 to see where i can help out with that migration.

feel free to close this PR. appreciate the review!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants