CLAUDE.md 15 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

Gogs (Go Git Service) is a painless self-hosted Git service written in Go. It provides a GitHub-like web interface for managing Git repositories with minimal resource requirements.

  • Language: Go 1.20+
  • Web Framework: Macaron (v1)
  • Database: Dual ORM system - XORM (legacy) transitioning to GORM (new)
  • Supported Databases: PostgreSQL, MySQL, SQLite3, MSSQL, TiDB
  • Version: 0.14.0+dev

Common Development Commands

The project uses Task as its build tool. All commands are defined in Taskfile.yml.

Building and Running

# Build the binary
task build

# Build and start the web server
task web

# Build and start with auto-reload on file changes
task web --watch

# Run a release build (creates ZIP in release/ directory)
task release

Testing

# Run all tests with coverage and race detection
task test

# Drop test databases (PostgreSQL)
task drop-test-db

Code Generation

# Run all go:generate commands (including mocks)
task generate

# Generate database schema documentation
task generate-schemadoc

# Regenerate CSS from LESS files (IMPORTANT: Never edit .css files directly!)
task less

Code Quality

# Show all FIXME comments
task fixme

# Show all TODO comments
task todo

# Show legacy/deprecated code
task legacy

# Run linter
golangci-lint run

# Format code
goimports -w .

Development Setup

See docs/dev/local_development.md for complete setup instructions. Key steps:

  1. Install dependencies: Go 1.20+, Git, PostgreSQL/MySQL/SQLite, Task, Less.js, goimports, go-mockgen
  2. Create database and user
  3. Create custom/conf/app.ini with database configuration
  4. Run task web --watch to start development server

Important: Clone outside of $GOPATH (Go modules enabled).

High-Level Architecture

Application Structure

The application uses a single binary deployment model with embedded assets. Entry point is gogs.go using urfave/cli.

Main Commands

  • web: HTTP/HTTPS server (primary mode)
  • serv: Git operations over SSH (called by SSH server)
  • hook: Git hooks delegation (pre-receive, update, post-receive)
  • admin: CLI admin operations
  • backup/restore: Data backup/restore
  • import: Import repositories
  • cert: Generate TLS certificates

Routing Layer (Macaron-based)

Routes are organized by feature domain in internal/route/:

internal/route/
├── home.go              # Home page
├── install.go           # Installation wizard
├── admin/               # Admin panel routes
├── api/v1/              # REST API v1
├── org/                 # Organization management
├── repo/                # Repository routes (issues, PRs, settings, etc.)
├── user/                # User routes (login, settings, profile)
└── lfs/                 # Git LFS routes

Middleware Chain (applied globally):

Logger → Recovery → GZIP → Static → Renderer → i18n → Session → CSRF → Contexter

Authentication Middleware:

  • reqSignIn: Requires authenticated user
  • reqSignOut: Requires unauthenticated (for login/register pages)
  • reqAdmin: Requires site admin
  • reqRepoAdmin/reqRepoWriter: Repository-level permissions

Context System

Three main context types in internal/context/:

  1. context.Context: Main HTTP context

    • Wraps macaron.Context
    • User authentication state
    • Flash messages, localization
    • Template data management via c.Data map
  2. context.APIContext: API-specific context

    • JSON response helpers
    • API error handling
  3. Repository Context (context.Repository):

    • Git repository handle (GitRepo)
    • Access mode (read/write/admin/owner)
    • Current branch/commit/tag

Important: Macaron uses reflection to inject context into handlers:

func Handler(c *context.Context, repo *context.Repository) {
    // Dependencies automatically injected
}

Database Architecture

Dual ORM System (migration in progress):

  • XORM (internal/database/models.go): Legacy ORM, being phased out
  • GORM (internal/database/database.go): New ORM for modern features

Global Database Handle:

// Access via database.Handle
database.Handle.Users()          // *UsersStore
database.Handle.Repositories()   // *RepositoriesStore
database.Handle.Permissions()    // *PermissionsStore
database.Handle.AccessTokens()   // *AccessTokensStore
database.Handle.LoginSources()   // *LoginSourcesStore
database.Handle.TwoFactors()     // *TwoFactorsStore

Store Pattern: Each domain has a dedicated "Store" (repository pattern) in internal/database/:

  • Stores encapsulate database operations for a domain
  • Methods accept context.Context as first parameter
  • Example: UsersStore.GetByID(), UsersStore.Create(), UsersStore.Authenticate()

Models are defined alongside their stores in internal/database/:

  • users.go: User model + UsersStore
  • repositories.go: Repository model + RepositoriesStore
  • permissions.go: Access control logic
  • issue.go, pull.go: Issue and PR models
  • webhook.go: Webhook configuration

Migrations: Located in internal/database/migrations/. Schema versioning via Version table.

Authentication System

Multi-Source Authentication via Provider pattern (internal/auth/):

Supported authentication types:

  1. Plain: Local database (bcrypt hashed passwords)
  2. LDAP: LDAP via BindDN
  3. DLDAP: LDAP simple auth (direct bind)
  4. SMTP: SMTP server authentication
  5. PAM: Linux PAM authentication
  6. GitHub: GitHub OAuth

Authentication Flow:

  1. Check if local account (password hash validation)
  2. If external auth: Query login source from login_sources table
  3. Call Provider.Authenticate()
  4. Create/update user record
  5. Create session

Session Management:

  • Configurable backend: file, Redis, memcache
  • Library: github.com/go-macaron/session

Token Authentication:

  • Access tokens (SHA1 hashed) in internal/database/access_tokens.go
  • Priority: Session cookie → HTTP Basic Auth → Access token

Two-Factor Authentication:

  • TOTP-based (Time-based One-Time Password)
  • Implementation in internal/database/two_factors.go

Git Operations

Git Command Execution:

  • Wraps github.com/gogs/git-module library
  • Process management in internal/process/ (timeouts, cancellation)

Git over HTTP (internal/route/repo/http.go):

  • Proxies to git-http-backend
  • Operations: git-upload-pack (fetch/pull), git-receive-pack (push), git-upload-archive

Git over SSH (internal/cmd/serv.go):

  • Called by SSH server via forced commands in authorized_keys
  • Parses SSH_ORIGINAL_COMMAND
  • Authenticates via public key
  • Executes Git commands directly

Git Hooks (auto-generated per repository):

  • Scripts call gogs hook pre-receive|update|post-receive
  • Pre-receive: Branch protection, force push prevention
  • Post-receive: Trigger webhooks, update repository statistics

Repository Structure:

<REPO_ROOT>/<owner>/<repo>.git/
├── hooks/           # Git hooks (generated)
├── custom_hooks/    # User-defined hooks
├── objects/, refs/, etc.

Template & View Rendering

Template Engine: Go's html/template via Macaron renderer

Template Organization (templates/):

templates/
├── base/           # Layout and base components (head, header, footer)
├── user/           # User-related pages (auth, settings)
├── repo/           # Repository pages (home, issues, PRs, settings)
├── admin/          # Admin panel
├── org/            # Organization pages
├── install.tmpl    # Installation wizard
└── status/         # Error pages (404, 500)

Custom Template Functions (internal/template/template.go):

  • Config: AppName(), AppURL(), AppVer()
  • User: AvatarLink(), UserProfileLink()
  • Time: TimeSince(), DateFmtLong()
  • Git: ShortSHA1(), RenderCommitMessage()
  • String: Str2HTML(), EllipsisString()

Template Data Injection:

c.Data["Title"] = "Page Title"
c.Data["User"] = user
c.Success("user/page")  // renders templates/user/page.tmpl

Localization: Uses github.com/go-macaron/i18n with locale files in conf/locale/*.ini

Configuration System

INI-based Configuration:

  • Default: conf/app.ini (embedded in binary)
  • User overrides: custom/conf/app.ini (runtime)
  • Loaded via internal/conf/conf.go

Custom Directory: Everything under custom/ overrides defaults:

  • custom/conf/app.ini: Configuration
  • custom/templates/: Template overrides
  • custom/public/: Static file overrides

Development Config (custom/conf/app.ini):

RUN_MODE = dev

[server]
LOAD_ASSETS_FROM_DISK = true  # Load templates/public from disk (no rebuild needed)
OFFLINE_MODE = true            # Work without internet

[log.xorm]
MODE = console                 # Enable SQL query logging

Key Design Patterns

1. Repository/Store Pattern

Domain logic encapsulated in stores:

user, err := database.Handle.Users().GetByUsername(ctx, username)

2. Middleware Chain Pattern

Routes composed with middleware functions:

m.Group("/user/settings", func() {
    m.Get("", user.Settings)
}, reqSignIn)

3. Provider Pattern

Authentication sources implement common Provider interface

4. Form Binding

HTTP forms automatically bound to structs with validation:

m.Post("/login", binding.Bind(form.SignIn{}), user.LoginPost)

5. Context Injection

Macaron injects dependencies via reflection based on parameter types

6. Service Layer

Background services for long-running operations:

  • Cron jobs (internal/cron/): Repository statistics, cleanup
  • Mirror sync: Sync mirror repositories periodically
  • Webhook delivery: Async webhook delivery with retries

Important Development Notes

CSS Modifications

CRITICAL: Never edit .css files directly! They are generated from .less files.

  1. Edit files in public/less/
  2. Run task less to regenerate CSS
  3. The task less command uses lessc with clean-css plugin

Template and Static Asset Development

When actively developing templates/static files:

  1. Enable in custom/conf/app.ini:

    [server]
    LOAD_ASSETS_FROM_DISK = true
    
  2. No need to rebuild/restart after template changes

  3. Still run task generate if you modify conf/, template/, or public/ structure

Database Testing

  • Use internal/dbtest helpers for database tests
  • In-memory SQLite for fast unit tests
  • PostgreSQL for integration tests
  • Run task drop-test-db to clean up test databases

Error Handling Patterns

// Check specific error types
if database.IsErrUserNotExist(err) {
    c.NotFound()
    return
}

// Generic error handling
c.NotFoundOrError(err, "get user")

// Always wrap errors with context
return errors.Wrap(err, "description")

Common Operations

Checking Permissions:

mode := database.Handle.Permissions().AccessMode(ctx, userID, repoID, options)
if mode < database.AccessModeWrite {
    // Deny access
}

Database Transactions:

return db.Transaction(func(tx *gorm.DB) error {
    // Operations
    return nil
})

Redirecting:

c.Redirect("/user/login")
c.RedirectSubpath("/admin")  // Respects EXTERNAL_URL subpath

Code Style

  • Formatting: Use gofmt and goimports
  • Linting: Configured via .golangci.yml
  • Error Wrapping: Always wrap errors with context
  • Logging: Use log.Trace(), log.Info(), log.Error() (from unknwon.dev/clog/v2)
  • Comments: English only in code comments and docstrings
  • Style Guide: Follow Sourcegraph's Go style guide

Contributing Guidelines

From .github/CONTRIBUTING.md:

  • Talk, then code: Post proposals for new features to Discussions - Proposal
  • Self-review: Always self-review before requesting reviews
  • Incremental changes: Make self-contained changes, avoid huge diffs
  • No force push: Unless absolutely necessary (makes review harder)
  • English: Use English in code comments and docstrings
  • DO NOT update locale files except conf/locale_en-US.ini (use Crowdin instead)
  • DO NOT submit Docker compose files

Directory Structure Reference

/data/gogs/
├── gogs.go                   # Main entry point
├── gen.go                    # go:generate directives
├── Taskfile.yml              # Build tasks
├── conf/                     # Embedded default configuration and locales
├── internal/                 # Private application code
│   ├── cmd/                  # CLI commands (web, serv, hook, admin, etc.)
│   ├── route/                # HTTP route handlers (admin, api, org, repo, user)
│   ├── context/              # Request context types
│   ├── database/             # ORM models and stores
│   │   ├── migrations/       # Database migrations
│   │   └── schemadoc/        # Schema documentation generator
│   ├── auth/                 # Authentication providers (LDAP, SMTP, PAM, GitHub)
│   ├── conf/                 # Configuration loading and validation
│   ├── template/             # Template functions
│   ├── form/                 # Form definitions and validation
│   ├── gitutil/              # Git utilities
│   ├── markup/               # Markdown/markup rendering
│   ├── email/                # Email service
│   ├── cron/                 # Scheduled tasks
│   ├── ssh/                  # Built-in SSH server
│   └── ...util/              # Utility packages (cryptoutil, strutil, pathutil, etc.)
├── public/                   # Static assets
│   ├── css/                  # Generated CSS (DO NOT EDIT)
│   ├── less/                 # LESS source files (EDIT THESE)
│   ├── js/
│   └── img/
├── templates/                # Go templates
├── scripts/                  # Init scripts (systemd, supervisor, etc.)
└── docs/                     # Developer documentation
    └── dev/local_development.md

Architectural Decisions

Single Binary Deployment

  • All assets embedded (CSS, JS, templates, locales)
  • SQLite support for zero external dependencies
  • Prioritizes simplicity for self-hosting

Dual ORM Migration

  • Migrating from XORM to GORM gradually
  • GORM offers better maintenance, features (hooks, preloading), cleaner API
  • Both coexist during transition to avoid breaking changes

Macaron Framework

  • Lightweight, fast, excellent middleware support
  • Good i18n/session ecosystem
  • Familiar API (similar to Martini)

Testing

Run tests with:

task test

Tests use:

  • Standard Go testing framework
  • internal/dbtest for database test helpers
  • Mock generation via go-mockgen (see mockgen.yaml)
  • Race detection enabled by default

Debugging

  • Development Mode: Set RUN_MODE = dev in config
  • SQL Logging: Set [log.xorm] MODE = console in config
  • Template Debugging: Logs template name on render
  • Logging Levels: TRACE, INFO, WARN, ERROR, FATAL (configured in app.ini)