# 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](https://taskfile.dev) as its build tool. All commands are defined in `Taskfile.yml`. ### Building and Running ```bash # 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 ```bash # Run all tests with coverage and race detection task test # Drop test databases (PostgreSQL) task drop-test-db ``` ### Code Generation ```bash # 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 ```bash # 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: ```go 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**: ```go // 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**: ``` //.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**: ```go 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`): ```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: ```go user, err := database.Handle.Users().GetByUsername(ctx, username) ``` ### 2. Middleware Chain Pattern Routes composed with middleware functions: ```go 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: ```go 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`: ```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 ```go // 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**: ```go mode := database.Handle.Permissions().AccessMode(ctx, userID, repoID, options) if mode < database.AccessModeWrite { // Deny access } ``` **Database Transactions**: ```go return db.Transaction(func(tx *gorm.DB) error { // Operations return nil }) ``` **Redirecting**: ```go 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](https://docs.sourcegraph.com/dev/background-information/languages/go) ## Contributing Guidelines From `.github/CONTRIBUTING.md`: - **Talk, then code**: Post proposals for new features to [Discussions - Proposal](https://github.com/gogs/gogs/discussions/categories/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](https://crowdin.com/project/gogs) 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: ```bash 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)