From 330e565d2934fba2be9cef0f84b5581217abdba5 Mon Sep 17 00:00:00 2001 From: Dylan Ravel Date: Sun, 11 Jan 2026 17:10:12 -0800 Subject: [PATCH] Update CONTRIBUTING.md with database migration guidelines Expanded the documentation to include instructions for adding and managing database migrations in the project. Clarified the structure of the database schema, added details about UTC timestamp storage, and updated the description of the internal storage module to mention migrations. --- CONTRIBUTING.md | 73 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7d9714c..6674fef 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -161,7 +161,7 @@ tmpo/ - **`cmd/config/`**: Global configuration command (config/settings/preferences) - **`cmd/milestones/`**: Milestone management commands (start, finish, status, list) - **`internal/settings/`**: Configuration management (`.tmporc` files and global `config.yaml`) -- **`internal/storage/`**: SQLite database operations and models +- **`internal/storage/`**: SQLite database operations, models, and migrations - **`internal/project/`**: Project name detection logic (git/directory/config) - **`internal/export/`**: Export functionality (CSV, JSON) - **`internal/currency/`**: Currency formatting and symbol handling for billing @@ -185,10 +185,14 @@ All user data is stored locally in: The database schema includes: -- Time entries (start/end times, project, description, hourly rate) -- Project metadata (derived from entries) +- **time_entries**: Time tracking entries (start/end times, project, description, hourly rate, milestone) +- **milestones**: Project milestones for organizing work +- **settings**: Migration tracking and other metadata (e.g., `001_utc_timestamps`) - Automatic indexing for fast queries +> [!IMPORTANT] +> All timestamps are stored in UTC and converted to the user's configured timezone for display. + > [!NOTE] > See [Development Mode](#development-mode) for information on using the development database during local development. @@ -202,6 +206,69 @@ When a user runs `tmpo start`, the project name is detected in this priority ord This logic lives in `internal/project/detect.go`. +### Database Migrations + +When you need to modify the database schema, use the migration system in `internal/storage/migrations.go`: + +1. **Add a migration constant:** + + ```go + const ( + Migration001_UTCTimestamps = "001_utc_timestamps" + Migration002_YourFeature = "002_your_feature" // New migration + ) + ``` + +2. **Create your migration function:** + + ```go + func (d *Database) migrateYourFeature() error { + completed, err := d.hasMigrationRun(Migration002_YourFeature) + if err != nil || completed { + return err + } + + tx, err := d.db.Begin() + if err != nil { + return fmt.Errorf("failed to begin transaction: %w", err) + } + defer func() { + if err != nil { + tx.Rollback() + } + }() + + // Your migration logic here + + // Mark complete + _, err = tx.Exec( + "INSERT OR REPLACE INTO settings (key, value, updated_at) VALUES (?, ?, ?)", + Migration002_YourFeature, "completed", time.Now().UTC(), + ) + if err != nil { + return err + } + + return tx.Commit() + } + ``` + +3. **Register in `runMigrations()`:** + + ```go + func (d *Database) runMigrations() error { + if err := d.migrateTimestampsToUTC(); err != nil { + return fmt.Errorf("timestamp UTC migration failed: %w", err) + } + if err := d.migrateYourFeature(); err != nil { + return fmt.Errorf("your feature migration failed: %w", err) + } + return nil + } + ``` + +**Important:** Migrations run automatically on `Initialize()` and are wrapped in transactions for safety. If a migration fails, all changes are rolled back. + ## Making Changes ### Branching