Allow to specify CLI db path as a flag

This commit is contained in:
Sung 2025-10-12 13:20:27 -07:00
commit 1ca640812e
4 changed files with 89 additions and 7 deletions

View file

@ -23,6 +23,7 @@ import (
)
var apiEndpointFlag string
var dbPathFlag string
var root = &cobra.Command{
Use: "dnote",
@ -36,6 +37,7 @@ var root = &cobra.Command{
func init() {
root.PersistentFlags().StringVar(&apiEndpointFlag, "api-endpoint", "", "the API endpoint to connect to (defaults to value in config)")
root.PersistentFlags().StringVar(&dbPathFlag, "dbPath", "", "the path to the database file (defaults to standard location)")
}
// GetRoot returns the root command
@ -48,6 +50,11 @@ func GetAPIEndpointFlag() string {
return apiEndpointFlag
}
// GetDBPathFlag returns the value of the --dbpath flag
func GetDBPathFlag() string {
return dbPathFlag
}
// Register adds a new command
func Register(cmd *cobra.Command) {
root.AddCommand(cmd)

View file

@ -59,7 +59,12 @@ func checkLegacyDBPath() (string, bool) {
return "", false
}
func getDBPath(paths context.Paths) string {
func getDBPath(paths context.Paths, customPath string) string {
// If custom path is provided, use it
if customPath != "" {
return customPath
}
legacyDnoteDir, ok := checkLegacyDBPath()
if ok {
return fmt.Sprintf("%s/%s", legacyDnoteDir, consts.DnoteDBFileName)
@ -71,7 +76,7 @@ func getDBPath(paths context.Paths) string {
// newBaseCtx creates a minimal context with paths and database connection.
// This base context is used for file and database initialization before
// being enriched with config values by setupCtx.
func newBaseCtx(versionTag string) (context.DnoteCtx, error) {
func newBaseCtx(versionTag, customDBPath string) (context.DnoteCtx, error) {
dnoteDir := getLegacyDnotePath(dirs.Home)
paths := context.Paths{
Home: dirs.Home,
@ -81,7 +86,7 @@ func newBaseCtx(versionTag string) (context.DnoteCtx, error) {
LegacyDnote: dnoteDir,
}
dbPath := getDBPath(paths)
dbPath := getDBPath(paths, customDBPath)
db, err := database.Open(dbPath)
if err != nil {
@ -98,8 +103,8 @@ func newBaseCtx(versionTag string) (context.DnoteCtx, error) {
}
// Init initializes the Dnote environment and returns a new dnote context
func Init(versionTag, apiEndpoint string) (*context.DnoteCtx, error) {
ctx, err := newBaseCtx(versionTag)
func Init(versionTag, apiEndpoint, customDBPath string) (*context.DnoteCtx, error) {
ctx, err := newBaseCtx(versionTag, customDBPath)
if err != nil {
return nil, errors.Wrap(err, "initializing a context")
}

View file

@ -46,7 +46,7 @@ var apiEndpoint string
var versionTag = "master"
func main() {
// Parse flags early to check if --api-endpoint was provided
// Parse flags early to check if --api-endpoint and --dbpath were provided
root.GetRoot().ParseFlags(os.Args[1:])
// Use flag value if provided, otherwise use ldflags value
@ -55,7 +55,9 @@ func main() {
endpoint = flagValue
}
ctx, err := infra.Init(versionTag, endpoint)
dbPath := root.GetDBPathFlag()
ctx, err := infra.Init(versionTag, endpoint, dbPath)
if err != nil {
panic(errors.Wrap(err, "initializing context"))
}

View file

@ -501,3 +501,71 @@ func TestRemoveBook(t *testing.T) {
})
}
}
func TestDBPathFlag(t *testing.T) {
// Helper function to verify database contents
verifyDatabase := func(t *testing.T, dbPath, expectedBook, expectedNote string) *database.DB {
ok, err := utils.FileExists(dbPath)
if err != nil {
t.Fatal(errors.Wrapf(err, "checking if custom db exists at %s", dbPath))
}
if !ok {
t.Errorf("custom database was not created at %s", dbPath)
}
db, err := database.Open(dbPath)
if err != nil {
t.Fatal(errors.Wrapf(err, "opening db at %s", dbPath))
}
var noteCount, bookCount int
database.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount)
database.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), &noteCount)
assert.Equalf(t, bookCount, 1, fmt.Sprintf("%s book count mismatch", dbPath))
assert.Equalf(t, noteCount, 1, fmt.Sprintf("%s note count mismatch", dbPath))
var book database.Book
database.MustScan(t, "getting book", db.QueryRow("SELECT label FROM books"), &book.Label)
assert.Equalf(t, book.Label, expectedBook, fmt.Sprintf("%s book label mismatch", dbPath))
var note database.Note
database.MustScan(t, "getting note", db.QueryRow("SELECT body FROM notes"), &note.Body)
assert.Equalf(t, note.Body, expectedNote, fmt.Sprintf("%s note body mismatch", dbPath))
return db
}
// Setup - use two different custom database paths
customDBPath1 := "./tmp/custom-test1.db"
customDBPath2 := "./tmp/custom-test2.db"
defer testutils.RemoveDir(t, "./tmp")
customOpts := testutils.RunDnoteCmdOptions{
Env: []string{
fmt.Sprintf("XDG_CONFIG_HOME=%s", testDir),
fmt.Sprintf("XDG_DATA_HOME=%s", testDir),
fmt.Sprintf("XDG_CACHE_HOME=%s", testDir),
},
}
// Execute - add different notes to each database
testutils.RunDnoteCmd(t, customOpts, binaryName, "--dbPath", customDBPath1, "add", "db1-book", "-c", "content in db1")
testutils.RunDnoteCmd(t, customOpts, binaryName, "--dbPath", customDBPath2, "add", "db2-book", "-c", "content in db2")
// Test both databases
db1 := verifyDatabase(t, customDBPath1, "db1-book", "content in db1")
defer db1.Close()
db2 := verifyDatabase(t, customDBPath2, "db2-book", "content in db2")
defer db2.Close()
// Verify that the databases are independent
var db1HasDB2Book int
db1.QueryRow("SELECT count(*) FROM books WHERE label = ?", "db2-book").Scan(&db1HasDB2Book)
assert.Equal(t, db1HasDB2Book, 0, "db1 should not have db2's book")
var db2HasDB1Book int
db2.QueryRow("SELECT count(*) FROM books WHERE label = ?", "db1-book").Scan(&db2HasDB1Book)
assert.Equal(t, db2HasDB1Book, 0, "db2 should not have db1's book")
}