Skip to main content

Welcome Contributors!

Thank you for your interest in contributing to Camera Workflow! This guide will help you get started with contributing code, documentation, bug reports, and feature requests.

Getting Started

1. Fork and Clone

# Fork the repository on GitHub, then clone your fork
git clone https://github.com/YOUR_USERNAME/Camera-Workflow.git
cd Camera-Workflow

# Add upstream remote
git remote add upstream https://github.com/Azilone/Camera-Workflow.git

2. Set Up Development Environment

1

Install Go 1.21+

Download from go.dev
go version  # Verify installation
2

Install Dependencies

Install FFmpeg and ImageMagick:macOS:
brew install ffmpeg imagemagick
Ubuntu/Debian:
sudo apt install ffmpeg imagemagick
Arch Linux:
sudo pacman -S ffmpeg imagemagick
3

Install Go Modules

make deps
4

Run Tests

Verify your environment is set up correctly:
make test

3. Create a Feature Branch

# Update your fork
git fetch upstream
git checkout main
git merge upstream/main

# Create feature branch
git checkout -b feature/your-feature-name

Development Workflow

Making Changes

Follow Go best practices and project conventions:
  • Use gofmt for formatting (automatic with make fmt)
  • Write clear, descriptive variable names
  • Add comments for exported functions
  • Keep functions focused and small
// Good: Clear function with documentation

// CalculateChecksum computes the xxHash64 checksum for the given file.
// Returns an error if the file cannot be read.
func CalculateChecksum(path string) (uint64, error) {
    file, err := os.Open(path)
    if err != nil {
        return 0, fmt.Errorf("open file: %w", err)
    }
    defer file.Close()
    
    return hasher.CalculateReader(file)
}
Add tests for new functionality:
func TestCalculateChecksum(t *testing.T) {
    // Create test file
    tmpfile := createTempFile(t, "test content")
    
    // Test checksum calculation
    sum, err := CalculateChecksum(tmpfile)
    if err != nil {
        t.Fatalf("unexpected error: %v", err)
    }
    
    if sum == 0 {
        t.Error("expected non-zero checksum")
    }
}
Run tests:
make test
Before committing, run:
make check
# Runs: fmt + vet + test
Ensure code quality:
  • make fmt - Format code
  • make vet - Check for issues
  • make test - Run tests
  • make lint - Run all linters
If you add new features or change behavior:
  • Update relevant *.mdx documentation files
  • Add docstrings to exported functions
  • Update AGENTS.md if architecture changes
  • Add examples to examples/ if applicable

Commit Guidelines

Commit Message Format

Use clear, descriptive commit messages:
<type>: <subject>

<body>

<footer>
Types:
  • feat: New feature
  • fix: Bug fix
  • docs: Documentation
  • test: Tests
  • refactor: Code refactoring
  • perf: Performance improvement
  • chore: Maintenance

Commit Examples

Good commits:
feat: add adaptive worker scaling for video conversion

Implements dynamic worker pool adjustment based on CPU
and memory usage during video conversions.

Closes #123
fix: handle edge case in date extraction

Fixes crash when EXIF data contains invalid dates.
docs: update building.mdx with cross-compile info

Submitting Changes

1

Push to Your Fork

git push origin feature/your-feature-name
2

Create Pull Request

  1. Go to the Camera Workflow repository
  2. Click “New Pull Request”
  3. Select your fork and branch
  4. Fill out the PR template
3

PR Description

Include:
  • What the change does
  • Why the change is needed
  • How it was implemented
  • Testing performed
  • Related issue numbers (e.g., “Closes #123”)
4

Address Review Feedback

Respond to reviewer comments and update your PR:
# Make changes based on feedback
git add .
git commit -m "fix: address review feedback"
git push origin feature/your-feature-name

Code Style Guide

Go Code Conventions

Variables:
// Good
var hasher checksum.Hasher
var maxWorkers int
var outputPath string

// Avoid
var h checksum.Hasher  // Too short
var max_workers int    // Use camelCase
Functions:
// Exported (public)
func CalculateChecksum(path string) (uint64, error) {}

// Unexported (private)
func validatePath(path string) error {}
Constants:
const (
    DefaultQualityAVIF = 80
    DefaultQualityWebP = 85
)
Always wrap errors with context:
// Good
file, err := os.Open(path)
if err != nil {
    return fmt.Errorf("open file %s: %w", path, err)
}

// Avoid
if err != nil {
    return err  // No context
}
Use custom error types for specific cases:
type ConversionError struct {
    Path string
    Err  error
}

func (e *ConversionError) Error() string {
    return fmt.Sprintf("conversion failed for %s: %v", e.Path, e.Err)
}
Document all exported symbols:
// NewConverter creates a new Converter instance with the provided
// configuration and logger. It initializes the security checker,
// statistics tracker, and checksum hasher.
func NewConverter(cfg *config.Config, log *logger.Logger) *Converter {
    // ...
}
Use examples for complex functions:
// GetFileDate extracts the creation date from file metadata.
// It tries multiple sources in priority order:
//  1. macOS mdls metadata (most reliable for RAW files)
//  2. EXIF data (for images)
//  3. Video metadata (for videos)
//  4. File modification time (fallback)
//
// Example:
//  date, err := GetFileDate("/path/to/photo.jpg")
//  if err != nil {
//      log.Error(err)
//  }
func GetFileDate(path string) (time.Time, error) {
    // ...
}

Testing Requirements

Unit Tests

All new code should include unit tests:
func TestNewFeature(t *testing.T) {
    tests := []struct {
        name    string
        input   string
        want    string
        wantErr bool
    }{
        {
            name:    "valid input",
            input:   "test",
            want:    "expected output",
            wantErr: false,
        },
        {
            name:    "invalid input",
            input:   "",
            want:    "",
            wantErr: true,
        },
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := NewFeature(tt.input)
            
            if (err != nil) != tt.wantErr {
                t.Errorf("error = %v, wantErr %v", err, tt.wantErr)
                return
            }
            
            if got != tt.want {
                t.Errorf("got %v, want %v", got, tt.want)
            }
        })
    }
}

Test Coverage

Maintain coverage for critical code:
make test-coverage
open coverage.html
Aim for 70%+ coverage on new code, especially for security and conversion logic.

Bug Reports

Reporting Bugs

Create a GitHub Issue with: Template:
## Bug Description
A clear description of what the bug is.

## Steps to Reproduce
1. Run command: `media-converter /source /dest`
2. Observe error: ...

## Expected Behavior
What should happen.

## Actual Behavior
What actually happens.

## Environment
- OS: macOS 14.2 / Ubuntu 22.04 / Windows 11
- Go version: `go version`
- FFmpeg version: `ffmpeg -version`
- ImageMagick version: `magick -version`

## Logs
Paste relevant logs here

## Additional Context
Any other relevant information.

Security Vulnerabilities

Do not open public issues for security vulnerabilities.Email security issues to: [security contact - add if available]

Feature Requests

Proposing Features

Create a GitHub Discussion or issue: Template:
## Feature Description
Describe the feature you'd like to see.

## Use Case
Explain why this feature is needed and who would benefit.

## Proposed Implementation
(Optional) How you think it could be implemented.

## Alternatives Considered
(Optional) Other approaches you've considered.

Project Structure

Understand the codebase organization:
camera-workflow/
├── cmd/                    # CLI commands (Cobra)
│   └── root.go            # Main command definition
├── internal/              # Internal packages (not importable externally)
│   ├── api/              # JSON API for frontend
│   ├── checksum/         # File checksumming
│   ├── config/           # Configuration management
│   ├── converter/        # Core conversion logic
│   ├── logger/           # Logging system
│   ├── security/         # Security checks
│   └── utils/            # Utility functions
├── apps/raycast/         # Raycast extension
├── scripts/              # Build and utility scripts
├── examples/             # Example configurations
├── docs/                 # Documentation (Mintlify)
├── main.go               # Application entry point
├── Makefile              # Build automation
└── go.mod                # Go module definition

Communication

Where to Ask Questions

  • GitHub Discussions - General questions, ideas, help
  • GitHub Issues - Bug reports, feature requests
  • Pull Requests - Code review discussions

Code Review Process

  1. Automated Checks - CI runs tests and linters
  2. Maintainer Review - Project maintainers review code
  3. Feedback - Address comments and suggestions
  4. Approval - At least one maintainer approval required
  5. Merge - Squash and merge to main branch

Recognition

Contributors are recognized in:
  • GitHub Contributors list
  • Release notes
  • Project documentation
Thank you for contributing to Camera Workflow! 🎉

Next Steps

Architecture

Understand the system design

Building

Learn how to build the project

Testing

Run tests and check coverage

GitHub Repository

Visit the repository

Build docs developers (and LLMs) love