222 lines
6.2 KiB
Markdown
222 lines
6.2 KiB
Markdown
|
|
# AGENTS.md - Development Guidelines for my-log-wynter
|
||
|
|
|
||
|
|
## Project Overview
|
||
|
|
|
||
|
|
my-log-wynter is a Go CLI application that extends the `codeberg.org/danjones000/my-log` CLI library with custom commands. It integrates with youtube-dl/yt-dlp for video metadata fetching. It uses and `github.com/lrstanley/go-ytdlp` for yt-dlp integration.
|
||
|
|
|
||
|
|
## Build/Lint/Test Commands
|
||
|
|
|
||
|
|
### Building
|
||
|
|
```bash
|
||
|
|
# Build all packages
|
||
|
|
go build ./...
|
||
|
|
|
||
|
|
# Build the CLI binary
|
||
|
|
go build -o my-log ./cmd/my-log
|
||
|
|
|
||
|
|
# Run the CLI
|
||
|
|
go run ./cmd/my-log
|
||
|
|
```
|
||
|
|
|
||
|
|
### Testing
|
||
|
|
```bash
|
||
|
|
# Run all tests
|
||
|
|
go test ./...
|
||
|
|
|
||
|
|
# Run a single test by name
|
||
|
|
go test -run <TestName> ./...
|
||
|
|
|
||
|
|
# Run tests with verbose output
|
||
|
|
go test -v ./...
|
||
|
|
|
||
|
|
# Run tests with coverage
|
||
|
|
go test -cover ./...
|
||
|
|
|
||
|
|
# Run benchmarks
|
||
|
|
go test -bench=. ./...
|
||
|
|
```
|
||
|
|
|
||
|
|
### Linting
|
||
|
|
```bash
|
||
|
|
# Run golangci-lint (comprehensive linter)
|
||
|
|
golangci-lint run ./...
|
||
|
|
|
||
|
|
# Run golangci-lint with auto-fix
|
||
|
|
golangci-lint run ./... --fix
|
||
|
|
|
||
|
|
# Run go vet
|
||
|
|
go vet ./...
|
||
|
|
|
||
|
|
# Format code (gofmt)
|
||
|
|
gofmt -w .
|
||
|
|
gofmt -d .
|
||
|
|
|
||
|
|
# Check for unused imports and other issues
|
||
|
|
goimports -w .
|
||
|
|
```
|
||
|
|
|
||
|
|
### Dependencies
|
||
|
|
```bash
|
||
|
|
# Update dependencies
|
||
|
|
go get -u ./...
|
||
|
|
|
||
|
|
# Tidy go.mod
|
||
|
|
go mod tidy
|
||
|
|
|
||
|
|
# List dependencies
|
||
|
|
go list -m all
|
||
|
|
```
|
||
|
|
|
||
|
|
## Code Style Guidelines
|
||
|
|
|
||
|
|
### General Conventions
|
||
|
|
- Follow [Effective Go](https://go.dev/doc/effective_go) and [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments)
|
||
|
|
- Use Go 1.26.0 or later (as specified in go.mod)
|
||
|
|
- Run `go fmt` and `gofmt` before committing
|
||
|
|
- Run `golangci-lint run ./...` before committing
|
||
|
|
|
||
|
|
### Naming Conventions
|
||
|
|
- **Packages**: Use short, lowercase names (e.g., `ytdlp`, not `yt-dlp` or `youtube_dlp`)
|
||
|
|
- **Variables/Functions**: Use camelCase (e.g., `fetchURL`, not `fetch_url`)
|
||
|
|
- **Exported Functions/Types**: Use PascalCase (e.g., `Fetch`, not `fetch`)
|
||
|
|
- **Constants**: Use PascalCase (e.g., `MaxRetries`) or camelCase for unexported (e.g., `maxRetries`)
|
||
|
|
- **Acronyms**: Keep original casing (e.g., `URL`, not `Url`; `ID`, not `Id`)
|
||
|
|
- **Files**: Use lowercase with underscores for multiple words (e.g., `fetch_video.go`)
|
||
|
|
|
||
|
|
### Import Organization
|
||
|
|
Imports should be organized in three groups separated by blank lines:
|
||
|
|
1. Standard library packages
|
||
|
|
2. External/third-party packages
|
||
|
|
3. Internal packages (if applicable)
|
||
|
|
|
||
|
|
```go
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"fmt"
|
||
|
|
"os"
|
||
|
|
|
||
|
|
"codeberg.org/danjones000/my-log/cli"
|
||
|
|
"github.com/lrstanley/go-ytdlp"
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Error Handling
|
||
|
|
- Return errors where possible; avoid panics except for truly unrecoverable conditions
|
||
|
|
- Wrap errors with context using `fmt.Errorf("description: %w", err)` or `%v`
|
||
|
|
- Handle errors explicitly; don't ignore them with `_`
|
||
|
|
- Place error checks immediately after the operation that can fail
|
||
|
|
|
||
|
|
```go
|
||
|
|
// Good
|
||
|
|
result, err := fetchURL(ctx, url)
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("failed to fetch %s: %w", url, err)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Avoid
|
||
|
|
result, _ := fetchURL(ctx, url) // Don't ignore errors
|
||
|
|
```
|
||
|
|
|
||
|
|
### Context Usage
|
||
|
|
- Pass `context.Context` as the first parameter for functions that may timeout or be cancelled
|
||
|
|
- Use `context.Background()` for top-level operations and `context.WithTimeout()` for bounded operations
|
||
|
|
- Check for context cancellation with `ctx.Err()` when appropriate
|
||
|
|
|
||
|
|
```go
|
||
|
|
func Fetch(ctx context.Context, url string) (*Result, error) {
|
||
|
|
// ...
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Types and Interfaces
|
||
|
|
- Define interfaces close to where they are used
|
||
|
|
- Prefer concrete types unless interface polymorphism is needed
|
||
|
|
- Use pointer receivers (`*T`) for methods that modify the receiver or when nil is meaningful
|
||
|
|
|
||
|
|
### Testing Conventions
|
||
|
|
- Test files should be named `*_test.go`
|
||
|
|
- Test functions should start with `Test` (e.g., `TestFetch`)
|
||
|
|
- Benchmark functions should start with `Benchmark` (e.g., `BenchmarkFetch`)
|
||
|
|
- Use the github.com/nalgeon/be for assertions: `be.Equal`, `be.Err`, and `be.True`
|
||
|
|
- Use table-driven tests when testing multiple cases:
|
||
|
|
|
||
|
|
```go
|
||
|
|
func TestFetch(t *testing.T) {
|
||
|
|
tests := []struct {
|
||
|
|
name string
|
||
|
|
url string
|
||
|
|
wantErr bool
|
||
|
|
}{
|
||
|
|
{"valid youtube", "https://youtube.com/watch?v=xxx", false},
|
||
|
|
{"invalid url", "not-a-url", true},
|
||
|
|
}
|
||
|
|
for _, tt := range tests {
|
||
|
|
t.Run(tt.name, func(t *testing.T) {
|
||
|
|
// test logic
|
||
|
|
be.Equal(t, 2*5, 10)
|
||
|
|
be.True(t, !false)
|
||
|
|
be.Err(t, err, nil) // No error
|
||
|
|
be.Err(t, ioErr, io.EOF) // ioErr should be an io.EOF
|
||
|
|
be.Err(t, otherErr, "bad stuff") // error message contains "bad stuff"
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Concurrency
|
||
|
|
- Use goroutines with `go` keyword for concurrent operations
|
||
|
|
- Use `sync.WaitGroup` or channels for synchronization
|
||
|
|
- Prefer `errgroup` for coordinating multiple goroutines with error handling
|
||
|
|
- Never leak goroutines; ensure they can complete or be cancelled
|
||
|
|
|
||
|
|
### Documentation
|
||
|
|
- Document exported functions, types, and constants with doc comments
|
||
|
|
- Comments should start with the identifier name (no need to repeat the name)
|
||
|
|
- Keep comments concise but explanatory
|
||
|
|
|
||
|
|
```go
|
||
|
|
// Fetch retrieves video metadata from yt-dlp compatible sources.
|
||
|
|
func Fetch(ctx context.Context, url string) (*Info, error) {
|
||
|
|
// ...
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Logging
|
||
|
|
- Print user-friendly messages to stderr
|
||
|
|
|
||
|
|
## Project Structure
|
||
|
|
|
||
|
|
```
|
||
|
|
my-log-wynter/
|
||
|
|
├── cmd/my-log/ # CLI entry point
|
||
|
|
│ └── main.go
|
||
|
|
├── ytdlp/ # yt-dlp integration package
|
||
|
|
│ └── fetch.go
|
||
|
|
├── go.mod
|
||
|
|
├── go.sum
|
||
|
|
└── AGENTS.md # This file
|
||
|
|
```
|
||
|
|
|
||
|
|
## Common Tasks
|
||
|
|
|
||
|
|
### Adding a new command
|
||
|
|
1. Add the command to the my-log library (this project imports it)
|
||
|
|
2. Update this repository's code as needed
|
||
|
|
|
||
|
|
### Adding a new yt-dlp feature
|
||
|
|
1. Modify `ytdlp/fetch.go`
|
||
|
|
2. Add tests in `ytdlp/fetch_test.go`
|
||
|
|
3. Run tests with `go test -v ./ytdlp/`
|
||
|
|
|
||
|
|
## Pre-commit Checklist
|
||
|
|
- [ ] Code is formatted: `gofmt -w .`
|
||
|
|
- [ ] Imports are organized: `goimports -w .`
|
||
|
|
- [ ] No lint errors: `golangci-lint run ./...`
|
||
|
|
- [ ] Tests pass: `go test ./...`
|
||
|
|
- [ ] Code builds: `go build ./...`
|
||
|
|
|
||
|
|
## Git Commit Guidelines
|
||
|
|
- **Format**: Prepend commit messages with a gitmoji emoji (see https://gitmoji.dev)
|
||
|
|
- **Style**: Write detailed commit messages that explain what changed and why
|
||
|
|
- **Examples**: `✨ Add JSON export functionality for log entries`, `🐛 Fix date parsing for RFC3339 timestamps`, `📝 Update README with configuration examples`
|
||
|
|
- Do not commit files not yet staged unless explicitly asked to do so.
|