Thank you for your interest in contributing to tmpo! This document provides guidelines and instructions for contributing to the project.
- Go 1.25 or higher
- Git
-
Fork the repository
-
Clone your fork:
git clone https://github.com/YOUR_USERNAME/tmpo.git cd tmpo -
Add the upstream repository:
git remote add upstream https://github.com/DylanDevelops/tmpo.git
# Build for local development
go build -o tmpo .
# Run the binary
./tmpo --helpTo prevent corrupting your real tmpo data during development, use the TMPO_DEV environment variable:
# Enable development mode (uses ~/.tmpo-dev/ instead of ~/.tmpo/)
export TMPO_DEV=1
# Now all commands use the development database
./tmpo start "Testing new feature"
./tmpo status
./tmpo stopData Locations:
- Production mode (default):
- Database:
~/.tmpo/tmpo.db - Global config:
~/.tmpo/config.yaml
- Database:
- Development mode (
TMPO_DEV=1):- Database:
~/.tmpo-dev/tmpo.db - Global config:
~/.tmpo-dev/config.yaml
- Database:
Note
The export TMPO_DEV=1 command only applies to your current terminal session. When you close the terminal, it resets to production mode. This is intentional for safety - you must explicitly enable dev mode each time.
Making it persistent (optional):
If you prefer to always use dev mode, add it to your shell profile:
# For zsh (macOS default)
echo 'export TMPO_DEV=1' >> ~/.zshrc
# For bash
echo 'export TMPO_DEV=1' >> ~/.bashrcThen restart your terminal or run source ~/.zshrc (or source ~/.bashrc).
Benefits of development mode:
- Your real time tracking data and settings stay safe
- You can test database and config changes without risk
- You can easily clean up test data and config (
rm -rf ~/.tmpo-dev/)
To build with version information injected (useful for testing version display):
go build -ldflags "-X github.com/DylanDevelops/tmpo/cmd/utilities.Version=0.1.0 \
-X github.com/DylanDevelops/tmpo/cmd/utilities.Commit=$(git rev-parse --short HEAD) \
-X github.com/DylanDevelops/tmpo/cmd/utilities.Date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
-o tmpo .Note
This is an example - you can modify the version number (e.g., 0.1.0) or any other injected values to suit your testing needs.
This is useful when you want to:
- Test version display locally (
./tmpo --version) - Build a binary with specific version info
- Verify version injection is working correctly
For production releases, goreleaser handles version injection automatically.
# Run all tests
go test -v ./...
# Run tests with coverage
go test -v -cover ./...# Build with goreleaser (for testing release builds)
goreleaser build --snapshot --cleantmpo/
├── cmd/ # CLI commands (Using Cobra)
│ ├── root.go # Root command with RootCmd() constructor
│ ├── tracking/ # Time tracking commands (start, stop, pause, resume, status)
│ ├── entries/ # Entry management (edit, delete, manual)
│ ├── history/ # History commands (log, stats, export)
│ ├── milestones/ # Milestone management (start, finish, status, list)
│ ├── setup/ # Setup commands (init)
│ ├── config/ # Global configuration (config/settings/preferences)
│ └── utilities/ # Utility commands (version)
├── internal/
│ ├── settings/ # Configuration management (.tmporc and global config)
│ ├── storage/ # SQLite database layer
│ ├── project/ # Project detection logic
│ ├── export/ # Export functionality (CSV, JSON)
│ ├── currency/ # Currency formatting and symbol handling
│ ├── update/ # Update checking from GitHub releases
│ └── ui/ # UI helpers (formatting, colors, printing)
├── docs/ # User documentation
│ ├── usage.md
│ └── configuration.md
├── main.go
└── README.md
cmd/: Contains all CLI command implementations using Cobracmd/tracking/: Time tracking commands (start, stop, pause, resume, status)cmd/entries/: Entry management commands (edit, delete, manual)cmd/history/: History and reporting commands (log, stats, export)cmd/setup/: Setup and initialization commands (init)cmd/utilities/: Utility commands and version information (version)cmd/config/: Global configuration command (config/settings/preferences)cmd/milestones/: Milestone management commands (start, finish, status, list)
internal/settings/: Configuration management (.tmporcfiles and globalconfig.yaml)internal/storage/: SQLite database operations, models, and migrationsinternal/project/: Project name detection logic (git/directory/config)internal/export/: Export functionality (CSV, JSON)internal/currency/: Currency formatting and symbol handling for billinginternal/update/: Update checking from GitHub releasesinternal/ui/: UI helpers for formatting, colors, and terminal outputdocs/: User-facing documentation and guides
All user data is stored locally in:
~/.tmpo/ # Production (default)
├── tmpo.db # SQLite database
├── config.yaml # Global configuration (optional)
└── projects.yaml # Global projects registry (optional)
~/.tmpo-dev/ # Development (when TMPO_DEV=1)
├── tmpo.db # SQLite database
├── config.yaml # Global configuration (optional)
└── projects.yaml # Global projects registry (optional)
The database schema includes:
- 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 for information on using the development database during local development.
When a user runs tmpo start, the project name is detected in this priority order:
--projectflag - Explicitly specified global project (e.g.,tmpo start --project "My Project").tmporcfile - Searches current directory and all parent directories- Git repository - Uses
git rev-parse --show-toplevelto find repo root - Directory name - Falls back to current directory name
Global Projects:
Users can create global projects with tmpo init --global, which stores project configurations in ~/.tmpo/projects.yaml. These projects can be tracked from any directory using the --project flag.
This logic lives in internal/project/detect.go.
When you need to modify the database schema, use the migration system in internal/storage/migrations.go:
-
Add a migration constant:
const ( Migration001_UTCTimestamps = "001_utc_timestamps" Migration002_YourFeature = "002_your_feature" // New migration )
-
Create your migration function:
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() }
-
Register in
runMigrations():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.
Create a feature branch from main:
git checkout -b feature/your-feature-nameUse descriptive branch names such as:
feature/add-pause-commandfix/status-display-bugdocs/update-readme
- Follow standard Go conventions and use
gofmt - Write clear, descriptive commit messages
- Add comments for complex logic
- Keep functions focused and modular
Use clear, imperative commit messages:
Add pause/resume functionality
- Implement pause command to temporarily stop tracking
- Add resume command to continue paused sessions
- Update status command to show paused state
-
Ensure all tests pass:
go test -v ./... -
Commit your changes with descriptive messages
-
Push to your fork:
git push origin feature/your-feature-name
-
Open a Pull Request against the
mainbranch -
Describe your changes and link any related issues
- Provide a clear description of the changes
- Reference any related issues (e.g., "Fixes #123")
- Ensure tests pass and code builds successfully
- Be responsive to feedback and questions
Reviews can take a few iterations, especially for large contributions. Don't be disheartened if you feel it takes time - we just want to ensure each contribution is high-quality and that any outstanding questions are resolved, captured or documented for posterity.
We appreciate packaging efforts for various package managers and distributions! However, to keep maintenance focused on the core application and avoid setting precedent for supporting every distribution method, we have the following policy:
- GoReleaser configuration (
.goreleaser.yml) - handles official releases for multiple platforms - Core build files - Go modules, source code, and build scripts
Distribution-specific packaging should be maintained outside this repository:
- Nix flakes and modules - Contribute to nixpkgs or maintain in a separate repo
- Homebrew formulas - Maintained in our custom tap repository at
DylanDevelops/homebrew-tmpo. Oncetmpomeets the required repository popularity metrics, we will submit it tohomebrew-core. - Linux packages - AUR (Arch), APT/RPM repos, Snap, Flatpak, etc.
- System configuration - Systemd units, init scripts, etc.
- Other package managers - Scoop (Windows), Chocolatey, etc.
Maintaining distribution-specific packaging in-tree creates ongoing maintenance burden:
- Each config format change requires updating multiple package definitions
- Testing across different package managers becomes complex
- Sets precedent for accepting every packaging request
- Most package ecosystems prefer maintaining packages in their own repositories anyway
If you'd like to package tmpo for your preferred distribution:
- Create the package in the appropriate repository (nixpkgs, AUR, etc.)
- Open an issue with the link to your package
- We'll add a link in our installation documentation to help users find it
This way, tmpo remains accessible across platforms while keeping maintenance focused on what we do best—building a great time tracking tool.
We may consider dedicated support for specific platforms in the future if we see a large user base, but for now, community-maintained packages with documentation links work best for everyone.
When reporting bugs or requesting features, please:
- Check existing issues first to avoid duplicates
- Use the issue templates provided
- Include relevant details:
- tmpo version (
tmpo --version) - Operating system
- Steps to reproduce (for bugs)
- Expected vs actual behavior
- tmpo version (
Feel free to open an issue for questions or discussions about:
- Feature ideas
- Implementation approaches
- Project architecture
Be respectful and constructive in all interactions. We're all here to make tmpo better!
Thank you for contributing to tmpo!