Skip to content

Build a CLI app to track your tasks and manage your to-do list in go

License

Notifications You must be signed in to change notification settings

kamplianitis/task-tracker-go

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Task Tracker CLI

A lightweight command-line application for managing tasks and to-do lists, built in Go with persistent JSON storage.

Challenge: This project fulfills the roadmap.sh Task Tracker project, a beginner-level CLI development challenge.

Overview

This project demonstrates fundamental Go concepts including:

  • Struct types and receiver methods
  • Error handling patterns
  • Package organization and encapsulation
  • JSON serialization with custom field mapping
  • CLI argument parsing with os.Args
  • File I/O operations

Architecture

Core Components

task-tracker/ — Package providing task management logic:

  • task.go — Defines Task struct and lifecycle methods (UpdateTask, ListTask)
  • taskList.go — Manages collections via TaskList with CRUD operations and JSON persistence
  • taskState.go — Enumerates task statuses (TODO, INPROGRESS, DONE)

main.go — CLI entry point that:

  • Loads tasks from tasks.json on startup
  • Routes commands to appropriate TaskList methods
  • Persists changes to disk after mutations (not on read-only list)

Design Decisions

  1. Exported Fields with JSON Tags: Task fields are public (ID, Description, etc.) to enable direct JSON marshaling/unmarshaling without custom methods. This trades encapsulation for simplicity.

  2. UUID-Based Identification: Each task gets a unique UUID (github.com/google/uuid) generated at creation time. Commands accept UUID strings as arguments.

  3. Stateless Status Sentinel: NoStatusChange = -1 is a sentinel value used in update operations to indicate "do not change status."

  4. Single-File Persistence: All tasks stored in tasks.json (created in working directory). Missing file on startup is treated gracefully (empty list).

  5. Changed Flag Optimization: Tasks only save to disk after mutating commands (add, update, delete, mark-*), not after read-only list.

Building & Running

cd /path/to/task-tracker-go
go build -o tasktracker .
./tasktracker <command> [args]

Or run directly without building:

go run . <command> [args]

Commands

Add a Task

./tasktracker add "Buy groceries"

Creates a new task with status TODO. Generates a unique UUID.

List Tasks

# List all tasks
./tasktracker list

# Filter by status
./tasktracker list todo
./tasktracker list in-progress
./tasktracker list done

Output format: <uuid>: [status] description

Update a Task Description

./tasktracker update <uuid> "Buy groceries and cook dinner"

Replaces the description; status unchanged.

Mark Task Status

./tasktracker mark-in-progress <uuid>
./tasktracker mark-done <uuid>

Updates task status without changing description.

Delete a Task

./tasktracker delete <uuid>

Permanently removes the task.

JSON Storage

Tasks persist in tasks.json with this structure:

[
  {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "description": "Buy groceries",
    "status": "todo",
    "created_at": "2026-01-24T10:30:00Z",
    "updated_at": "2026-01-24T10:30:00Z"
  }
]

The file is automatically created on first mutation. Loading a malformed file returns an error.

Makefile Commands

A Makefile is provided for common development tasks:

# Build the binary
make build

# Run all tests
make test

# Run tests with coverage report
make coverage

# Clean build artifacts
make clean

# Install to GOPATH/bin
make install

# Generate package documentation
make docs

# Show all available commands
make help

Available Targets

  • make build — Compiles the binary to task-tracker
  • make test — Runs all tests with verbose output
  • make coverage — Generates coverage report (shows percentage breakdown by function)
  • make clean — Removes binary and coverage files
  • make install — Installs binary to $GOPATH/bin (available as task-tracker-go)
  • make docs — Displays package documentation using go doc
  • make help — Shows usage examples

Implementation Notes & Tips

JSON Serialization Gotchas

  • Field Visibility: Only exported (capitalized) struct fields are serialized by encoding/json. Initially, lowercase id, description fields were private, causing empty JSON output.
  • Tag Format: JSON struct tags must use quotes: json:"id" not json:id.
  • Unmarshal Validation: Status strings ("todo", "in-progress", "done") are loaded from JSON but are only validated if you add custom unmarshaling.

Argument Parsing Pattern

This CLI uses simple os.Args indexing rather than a framework like Cobra:

if len(os.Args) < 2 { return }  // Check args exist
cmd := os.Args[1]               // Get command
args := os.Args[2:]             // Get remaining args

For multi-word arguments, use strings.Join(args, " ").

Error Handling

  • UUID parsing errors are caught and reported: if err := uuid.Parse(...)
  • File I/O errors distinguish between missing files (OK, start empty) and other errors (fatal)
  • Task lookup errors are explicit: "Task Not Found", "Update failed"

Persistence Strategy

Instead of saving after every operation, we track a changed flag:

changed := false
switch cmd {
case "add":
    taskList.AddNewTask(desc)
    changed = true
case "list":
    taskList.List(category)  // changed stays false
}
if changed {
    taskList.SaveToJSON(dataFile)
}

This avoids disk I/O for read-only commands.

Status Management

Task states are constants:

const (
    TODO TaskState = iota      // 0
    INPROGRESS                 // 1
    DONE                       // 2
)
const NoStatusChange = -1      // Sentinel for partial updates

Mapping to/from JSON strings uses TaskStatus map for bidirectional lookup.

Learning Resources

  • Go type system and receiver methods
  • Struct tags and reflection (for JSON encoding)
  • Error handling and nil checks
  • Package exports (public vs. private)
  • Standard library: encoding/json, os, fmt, uuid

Challenge Requirements (roadmap.sh)

This implementation fulfills all requirements from the Task Tracker project:

✅ Core Features

  • Add tasks./tasktracker add "description"
  • Update tasks./tasktracker update <uuid> "new description"
  • Delete tasks./tasktracker delete <uuid>
  • Mark as in-progress./tasktracker mark-in-progress <uuid>
  • Mark as done./tasktracker mark-done <uuid>
  • List all tasks./tasktracker list
  • List by status./tasktracker list todo|in-progress|done

✅ Task Properties

Each task persists with:

  • id — UUID generated at creation
  • description — Task description string
  • status — One of todo, in-progress, done
  • created_at — RFC3339 timestamp on creation
  • updated_at — RFC3339 timestamp on update

✅ Constraints & Best Practices

  • CLI via positional arguments — Uses os.Args for command routing
  • JSON file storage — Single tasks.json file in current directory
  • Graceful missing file — Auto-creates on first mutation; starts empty if missing
  • Native Go modules only — Uses only fmt, os, encoding/json, time, uuid (external UUID library only)
  • Error handling — Validates UUIDs, handles missing files, reports invalid operations
  • Edge cases — UUID parsing, task lookup, status validation

Future Enhancements

  • Custom data file path (environment variable or flag)
  • Task filtering/searching by keyword
  • Priority levels or due dates
  • Batch operations (mark multiple tasks)
  • Colored terminal output
  • Persistent timestamps as location-aware time instead of UTC

About

Build a CLI app to track your tasks and manage your to-do list in go

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •