Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ otto/config.yaml
otto/secrets.yaml
otto/.env
otto/*.pem

**/.claude/settings.local.json
5 changes: 5 additions & 0 deletions otto/.golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,11 @@ linters:
- revive
path: example.*_test\.go
text: calls to (.+) only in main[(][)] or init[(][)] functions
# Ignore repetitive type names in oncall module as extensive refactoring would be needed
- linters:
- revive
path: modules/oncall/.*\.go
text: type name will be used as oncall.OnCall.* by other packages, and that is repetitive
# It's okay to not run gosec and perfsprint in a test.
- linters:
- gosec
Expand Down
28 changes: 23 additions & 5 deletions otto/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,30 @@
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Build Commands
- Build: `go build -o otto ./cmd/otto`
- Run: `./otto`
- Test all: `go test ./...`
- Build: `make build` or `go build -o otto ./cmd/otto`
- Run: `make run` or `./otto`
- Test all: `make test` or `go test ./...`
- Test single file: `go test ./path/to/package -run TestName`
- Lint: `golangci-lint run`
- Docker build: `docker build -t otel-otto:latest .`
- Generate test coverage: `make test-coverage`
- Generate HTML coverage report: `make test-coverage-html`
- Check coverage threshold: `make test-coverage-check`
- Lint: `make lint` or `golangci-lint run`
- Auto-fix linting issues: `make lint-fix`
- Format code: `make fmt` (basic) or `make fmt-all` (comprehensive)
- Fix imports ordering: `make fix-imports`
- Fix line length: `make fix-lines`
- Fix comments: `make fix-comments`
- Run all linting and formatting: `make lint-all`
- Install required tools: `make install-tools`
- Docker build: `make docker-build` or `docker build -t otel-otto:latest .`
- Database migrations: `make migrate-up` or `make migrate-down`

## Architecture
- The codebase follows a clean architecture pattern with dependency injection
- Database operations use the Repository pattern with Go generics for type safety
- All major components define interfaces for testability and use constructor-based dependency injection
- Database migrations are handled via the golang-migrate library
- The code is organized into focused packages with clean separation of concerns

## Code Style Guidelines
- License: Include SPDX license header (`// SPDX-License-Identifier: Apache-2.0`) in all Go files
Expand Down
75 changes: 73 additions & 2 deletions otto/Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Makefile for Otto
BINARY := otto
CMD_DIR := ./cmd/otto
COVERAGE_DIR := ./coverage

.PHONY: all build clean run test lint
.PHONY: all build clean run test lint lint-fix format fix-comments fmt fix-imports fix-lines fmt-all lint-all test-coverage test-coverage-html test-coverage-badge test-coverage-check migrate-up migrate-down docker-build install-tools

all: build

Expand All @@ -11,15 +12,85 @@ build:

clean:
rm -f $(BINARY)
rm -rf $(COVERAGE_DIR)

run: build
./$(BINARY)

test:
go test ./...

# Run tests with coverage and output to console
test-coverage:
mkdir -p $(COVERAGE_DIR)
go test -coverprofile=$(COVERAGE_DIR)/coverage.out ./...
go tool cover -func=$(COVERAGE_DIR)/coverage.out

# Run tests with coverage and generate HTML report
test-coverage-html: test-coverage
go tool cover -html=$(COVERAGE_DIR)/coverage.out -o $(COVERAGE_DIR)/coverage.html
@echo "Coverage report generated at $(COVERAGE_DIR)/coverage.html"

# Generate a coverage badge for use in documentation
test-coverage-badge: test-coverage
@go run scripts/cmd/badge/main.go -coverage=$(COVERAGE_DIR)/coverage.out -output=$(COVERAGE_DIR)/coverage-badge.svg
@echo "Coverage badge generated at $(COVERAGE_DIR)/coverage-badge.svg"

# Check if coverage meets threshold
test-coverage-check: test-coverage
@go run scripts/cmd/checker/main.go -coverage=$(COVERAGE_DIR)/coverage.out -threshold=70
@echo "Coverage check passed!"

# Run database migrations up
migrate-up:
@echo "Running migrations up..."
@go run $(CMD_DIR) migrate up

# Run database migrations down
migrate-down:
@echo "Running migrations down..."
@go run $(CMD_DIR) migrate down

# Run linting checks
lint:
golangci-lint run

# Fix linting issues automatically when possible
lint-fix:
golangci-lint run --fix

# Format code with gofumpt (stricter than gofmt)
format:
gofumpt -l -w .

# Fix missing periods in Go comments with godot
fix-comments:
godot -w .

# Fix imports ordering and grouping
fix-imports:
goimports -local go.opentelemetry.io -w .

# Fix line length with golines
fix-lines:
golines -w --max-len=120 .

# Run all code formatters
fmt: format fix-comments

# Run all code formatters and fixers
fmt-all: fix-lines fix-imports format fix-comments

# Run all linting and formatting checks and auto-fix where possible
lint-all: lint-fix fmt-all

# Install required formatting and linting tools
install-tools:
go install mvdan.cc/gofumpt@latest
go install github.com/segmentio/golines@latest
go install golang.org/x/tools/cmd/goimports@latest
go install github.com/tetafro/godot/cmd/godot@latest
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest

docker-build:
docker build -t otel-otto:latest .
docker build -t otel-otto:latest .
43 changes: 43 additions & 0 deletions otto/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,49 @@ This requires:
2. A 1Password Connect API token
3. Items in your 1Password vault for each secret (webhook secret, GitHub App credentials)

## Development

Otto provides several commands to help with development, testing, and building the application.

### Common Commands

```bash
# Build Otto
make build

# Run tests
make test

# Run linting
make lint

# Run database migrations up
make migrate-up

# Run database migrations down
make migrate-down
```

### Test Coverage

Otto provides several Makefile targets to check and report on test coverage:

```bash
# Generate coverage report to console
make test-coverage

# Generate HTML coverage report
make test-coverage-html

# Generate coverage badge SVG
make test-coverage-badge

# Check if coverage meets threshold (70%)
make test-coverage-check
```

The coverage reports will be created in the `./coverage` directory.

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Otto.
21 changes: 21 additions & 0 deletions otto/coverage/coverage-badge.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions otto/db/migrations/000001_create_oncall_tables.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-- Drop tables in reverse order to respect foreign key constraints
DROP TABLE IF EXISTS oncall_escalations;
DROP TABLE IF EXISTS oncall_assignments;
DROP TABLE IF EXISTS oncall_rotations;
DROP TABLE IF EXISTS oncall_users;
60 changes: 60 additions & 0 deletions otto/db/migrations/000001_create_oncall_tables.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
-- Create oncall_users table
CREATE TABLE IF NOT EXISTS oncall_users (
id TEXT PRIMARY KEY,
github_username TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
email TEXT,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Create oncall_rotations table
CREATE TABLE IF NOT EXISTS oncall_rotations (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
description TEXT,
repository TEXT NOT NULL,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Create oncall_assignments table
CREATE TABLE IF NOT EXISTS oncall_assignments (
id TEXT PRIMARY KEY,
rotation_id TEXT NOT NULL,
user_id TEXT NOT NULL,
start_time TIMESTAMP NOT NULL,
end_time TIMESTAMP,
is_current BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (rotation_id) REFERENCES oncall_rotations(id),
FOREIGN KEY (user_id) REFERENCES oncall_users(id)
);

-- Create oncall_escalations table
CREATE TABLE IF NOT EXISTS oncall_escalations (
id TEXT PRIMARY KEY,
assignment_id TEXT NOT NULL,
issue_number INTEGER,
pr_number INTEGER,
repository TEXT NOT NULL,
status TEXT NOT NULL,
escalation_time TIMESTAMP,
resolution_time TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (assignment_id) REFERENCES oncall_assignments(id)
);

-- Create indices for faster lookups
CREATE INDEX IF NOT EXISTS idx_oncall_users_github_username ON oncall_users(github_username);
CREATE INDEX IF NOT EXISTS idx_oncall_rotations_repository ON oncall_rotations(repository);
CREATE INDEX IF NOT EXISTS idx_oncall_assignments_rotation_id ON oncall_assignments(rotation_id);
CREATE INDEX IF NOT EXISTS idx_oncall_assignments_user_id ON oncall_assignments(user_id);
CREATE INDEX IF NOT EXISTS idx_oncall_assignments_is_current ON oncall_assignments(is_current);
CREATE INDEX IF NOT EXISTS idx_oncall_escalations_assignment_id ON oncall_escalations(assignment_id);
CREATE INDEX IF NOT EXISTS idx_oncall_escalations_repository ON oncall_escalations(repository);
CREATE INDEX IF NOT EXISTS idx_oncall_escalations_status ON oncall_escalations(status);
5 changes: 5 additions & 0 deletions otto/db/migrations/000002_test_data.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-- Remove test data
DELETE FROM oncall_escalations;
DELETE FROM oncall_assignments;
DELETE FROM oncall_rotations;
DELETE FROM oncall_users;
19 changes: 19 additions & 0 deletions otto/db/migrations/000002_test_data.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-- Insert test users
INSERT OR IGNORE INTO oncall_users (id, github_username, name, email, is_active)
VALUES
('user-1', 'tester1', 'Test User 1', 'test1@example.com', TRUE),
('user-2', 'tester2', 'Test User 2', 'test2@example.com', TRUE),
('user-3', 'tester3', 'Test User 3', 'test3@example.com', TRUE);

-- Insert test rotations
INSERT OR IGNORE INTO oncall_rotations (id, name, description, repository, is_active)
VALUES
('rotation-1', 'Main Rotation', 'Primary on-call rotation', 'open-telemetry/sig-project-infra', TRUE),
('rotation-2', 'Secondary Rotation', 'Backup on-call rotation', 'open-telemetry/sig-project-infra', TRUE);

-- Insert test assignments
INSERT OR IGNORE INTO oncall_assignments (id, rotation_id, user_id, start_time, end_time, is_current)
VALUES
('assignment-1', 'rotation-1', 'user-1', DATETIME('now', '-7 days'), DATETIME('now', '-1 days'), FALSE),
('assignment-2', 'rotation-1', 'user-2', DATETIME('now'), DATETIME('now', '+7 days'), TRUE),
('assignment-3', 'rotation-2', 'user-3', DATETIME('now'), DATETIME('now', '+7 days'), TRUE);
18 changes: 13 additions & 5 deletions otto/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ go 1.24.1

require (
github.com/1password/onepassword-sdk-go v0.3.0
github.com/golang-migrate/migrate/v4 v4.18.3
github.com/google/go-github/v71 v71.0.0
github.com/google/uuid v1.6.0
github.com/jferrl/go-githubauth v1.2.0
github.com/mattn/go-sqlite3 v1.14.22
github.com/mattn/go-sqlite3 v1.14.28
github.com/stretchr/testify v1.10.0
go.opentelemetry.io/contrib/bridges/otelslog v0.10.0
go.opentelemetry.io/otel v1.35.0
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.11.0
Expand All @@ -24,6 +27,7 @@ require (

require (
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dylibso/observe-sdk/go v0.0.0-20240828172851-9145d8ad07e1 // indirect
github.com/extism/go-sdk v1.7.1 // indirect
github.com/go-logr/logr v1.4.2 // indirect
Expand All @@ -32,17 +36,21 @@ require (
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
github.com/google/go-github/v69 v69.2.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b // indirect
github.com/jmoiron/sqlx v1.4.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 // indirect
github.com/tetratelabs/wazero v1.9.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.23.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect
google.golang.org/grpc v1.71.0 // indirect
Expand Down
Loading