mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-08-08 00:22:14 +01:00
docs: more succint [skip ci]
This commit is contained in:
247
CONTRIBUTING.md
247
CONTRIBUTING.md
@@ -1,39 +1,30 @@
|
||||
# Contributing to Gotenberg
|
||||
|
||||
**Gotenberg** is a Docker-based API for converting documents to PDF. It is a widely used production dependency. Stability and backward compatibility are paramount. When in doubt about whether a change is breaking, flag it rather than assuming it's safe.
|
||||
Gotenberg is a Docker-based API for converting documents to PDF. Two rules override everything else: **backward compatibility** (never rename or remove CLI flags, environment variables, API form fields, or HTTP endpoints without discussion) and **defensive programming** (assume input is malformed, handle errors explicitly, never panic).
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Go (see version in `go.mod`)
|
||||
- Module: `github.com/gotenberg/gotenberg/v8`
|
||||
- Go: see version in `go.mod`
|
||||
- Docker
|
||||
- Node.js (see version in `.node-version`), for Prettier linting
|
||||
- Node.js (see `.node-version`), for Prettier linting
|
||||
- [golangci-lint](https://golangci-lint.run/) v2+
|
||||
|
||||
### Build and Run
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
make build # Build the Docker image
|
||||
make run # Run a local Gotenberg container
|
||||
```
|
||||
|
||||
### Development Loop
|
||||
|
||||
```bash
|
||||
# Write your code, then:
|
||||
make build # Build the Docker image
|
||||
make run # Run a local Gotenberg container
|
||||
make fmt # Format Go code
|
||||
make prettify # Format non-Go files (Markdown, YAML, etc.)
|
||||
make lint # Lint Go code (zero errors permitted)
|
||||
make lint-prettier # Lint non-Go files
|
||||
make test-unit # Run unit tests
|
||||
make build # Build the Docker image (required before integration tests)
|
||||
make build # Required before integration tests
|
||||
make test-integration # Run all integration tests
|
||||
make telemetry # Start OpenTelemetry collector and OpenObserve
|
||||
make down # Stop all compose containers
|
||||
```
|
||||
|
||||
To run only the integration tests relevant to your change:
|
||||
Run only the integration tests relevant to your change:
|
||||
|
||||
```bash
|
||||
make test-integration TAGS=health
|
||||
@@ -41,117 +32,78 @@ make test-integration TAGS=chromium-convert-html
|
||||
make test-integration TAGS="merge,split"
|
||||
```
|
||||
|
||||
## Submitting a Pull Request
|
||||
All build and verification tasks go through the Makefile. Do not run `go` commands directly unless debugging a specific package.
|
||||
|
||||
For non-trivial changes, outline your approach before writing code. Open an issue or draft PR describing:
|
||||
| Command | Purpose | When to use |
|
||||
| ---------------- | --------------------------------------------- | ------------------------------------------------------------------------ |
|
||||
| `make run` | Run Gotenberg container via `docker compose` | Manual testing. Flags configured via Makefile variables and compose.yaml |
|
||||
| `make telemetry` | Start OpenTelemetry collector and OpenObserve | When testing telemetry locally |
|
||||
| `make down` | Stop all compose containers | After manual testing |
|
||||
| `make godoc` | Serve GoDoc at `localhost:6060` | To verify documentation |
|
||||
|
||||
- What needs to change and why.
|
||||
- The proposed solution, with enough detail to implement (files to modify, interface changes, form fields, etc.).
|
||||
- Which integration test tags will be affected and what new scenarios are needed.
|
||||
|
||||
Before opening (or marking ready) a PR, verify:
|
||||
|
||||
1. Code compiles: `make build`
|
||||
2. Code is formatted: `make fmt` and `make prettify`
|
||||
3. All linters pass: `make lint` and `make lint-prettier`
|
||||
4. Integration tests pass: `make test-integration` (at minimum, the relevant tags)
|
||||
5. Unit tests pass: `make test-unit`
|
||||
6. All exported symbols and new packages have GoDoc comments
|
||||
7. Bruno collection is updated (if routes were added or modified)
|
||||
|
||||
Review your changes against the [Review Checklist](#review-checklist) before submitting.
|
||||
|
||||
### Guidelines
|
||||
|
||||
- **One thing per PR.** Keep features, bug fixes, and refactoring in separate PRs.
|
||||
- **Backward compatibility matters.** Do not rename or remove existing CLI flags, environment variables, or API form fields without discussion.
|
||||
- **Integration tests first.** When adding a feature or route, start by writing the Gherkin scenario in `test/integration/features/`. See [`test/integration/README.md`](test/integration/README.md) for the full reference.
|
||||
- **Unit tests** when applicable: table-driven tests in `*_test.go` files using mocks from `pkg/gotenberg/mocks.go`.
|
||||
|
||||
### Commit Conventions
|
||||
|
||||
If committing, follow the [Conventional Commits](https://www.conventionalcommits.org/) specification:
|
||||
## Project layout
|
||||
|
||||
```
|
||||
<type>(<scope>): <description>
|
||||
cmd/gotenberg/ -> Entry point only (wiring/startup). No business logic.
|
||||
pkg/gotenberg/ -> Core module system, interfaces, utilities, mocks.
|
||||
pkg/modules/ -> Feature modules (api, chromium, libreoffice, pdfengines, etc.).
|
||||
pkg/standard/ -> Wires all standard modules together via imports.
|
||||
test/integration/ -> Gherkin feature files + Go test infrastructure.
|
||||
build/ -> Dockerfile, fonts, Chromium config.
|
||||
.bruno/ -> Bruno API collection (mirrors every route).
|
||||
```
|
||||
|
||||
Common types: `feat`, `fix`, `refactor`, `test`, `docs`, `chore`, `ci`, `build`. The scope should match the module or area of the change (e.g., `chromium`, `pdfengines`, `api`).
|
||||
Key interfaces live in `pkg/gotenberg/`: `Module`, `Provisioner`, `Validator`, `Debuggable`. Every module implements `Descriptor()` and self-registers.
|
||||
|
||||
Stage only the files related to the change. Do not use `git add -A` or `git add .`.
|
||||
## Coding rules
|
||||
|
||||
---
|
||||
### Module system
|
||||
|
||||
## Core Principles
|
||||
Gotenberg uses a self-registering module architecture inspired by CaddyServer. Each module lives in `pkg/modules/<name>/`, implements at minimum `gotenberg.Module` (`Descriptor()`), and self-registers via `init()`. Wiring happens through `pkg/standard/`.
|
||||
|
||||
- **Backward compatibility is law.** See the [Review Checklist](#review-checklist) for the full list of what must not change.
|
||||
- **Defensive programming.** Assume input is malformed. Handle errors explicitly. Never panic.
|
||||
- **Atomic commits.** One feature or fix per PR. Isolate refactoring from feature work.
|
||||
- **Idiomatic Go.** Follow "Effective Go" principles. All exported symbols must have GoDoc comments starting with their name.
|
||||
Determine if a feature belongs in an existing module before creating a new one. Only create a new module for a genuinely separate concern.
|
||||
|
||||
## Project Layout and Navigation
|
||||
### Error handling
|
||||
|
||||
```
|
||||
cmd/gotenberg/ → Entry point only (wiring/startup). No business logic.
|
||||
pkg/gotenberg/ → Core module system, interfaces, utilities, mocks.
|
||||
pkg/modules/ → Feature modules (api, chromium, libreoffice, pdfengines, etc.).
|
||||
pkg/standard/ → Wires all standard modules together via imports.
|
||||
test/integration/ → Gherkin feature files + Go test infrastructure.
|
||||
build/ → Dockerfile, fonts, Chromium config.
|
||||
.bruno/ → Bruno API collection (mirrors every route).
|
||||
```
|
||||
- Wrap every error with context: `fmt.Errorf("description: %w", err)`.
|
||||
- Never swallow errors silently.
|
||||
- Match errors with `errors.Is`, never `strings.Contains`.
|
||||
- No panics in production code paths.
|
||||
- Input is validated defensively.
|
||||
|
||||
Key interfaces live in `pkg/gotenberg/`: `Module`, `Provisioner`, `Validator`, `Debuggable`. Every module implements `Descriptor()` and self-registers. When adding features, determine if they belong in an existing module or require a new one.
|
||||
### Import ordering
|
||||
|
||||
- The integration test infrastructure in `test/integration/scenario/` is well-structured. Read `scenario.go` and `containers.go` to understand the Gherkin step definitions before writing new tests.
|
||||
- Mocks for all major interfaces are in `pkg/gotenberg/mocks.go`. Use them for unit tests rather than creating new ones.
|
||||
- When making changes, run only the relevant integration test tag rather than the full suite (40min timeout).
|
||||
- Telemetry infrastructure lives in `pkg/gotenberg/telemetry.go` (global Logger, Tracer, Meter) and `pkg/gotenberg/internal/` (log handlers, OTEL SDK init). HTTP semantic conventions are in `pkg/gotenberg/semconv/`.
|
||||
Enforced by `gci`: standard library, then third-party, then `github.com/gotenberg/gotenberg/v8`. Three groups separated by blank lines.
|
||||
|
||||
## Makefile: the Only Build Interface
|
||||
### Logging
|
||||
|
||||
All build and verification tasks go through the Makefile. Do not run `go` commands directly unless debugging a specific package. The [Development Loop](#development-loop) covers the commands used during daily work. Additional commands:
|
||||
Use `gotenberg.Logger(mod)` to get the module's slog logger during `Provision()`. All log calls must be context-aware: `logger.DebugContext(ctx, msg)`, `logger.InfoContext(ctx, msg)`, `logger.ErrorContext(ctx, msg)`. This propagates trace/span IDs into structured logs when OpenTelemetry is active.
|
||||
|
||||
| Command | Purpose | When to use |
|
||||
| ---------------- | --------------------------------------------- | ---------------------------------------------------------------------------- |
|
||||
| `make run` | Run Gotenberg container via `docker compose` | Manual testing. Flags are configured via Makefile variables and compose.yaml |
|
||||
| `make telemetry` | Start OpenTelemetry collector and OpenObserve | When testing telemetry locally |
|
||||
| `make down` | Stop all compose containers | After manual testing |
|
||||
| `make godoc` | Serve GoDoc at `localhost:6060` | To verify documentation |
|
||||
### Telemetry
|
||||
|
||||
## Module System
|
||||
External tool calls (Chromium, LibreOffice, PDF engines, webhooks, downloads) must create OTEL spans with `trace.SpanKindClient` and `semconv.ServerAddress("toolname")`. Use `gotenberg.Tracer()` and `gotenberg.Meter()` for traces and metrics.
|
||||
|
||||
Gotenberg uses a self-registering module architecture inspired by CaddyServer. Each module:
|
||||
### No business logic in `cmd/`
|
||||
|
||||
- Lives in `pkg/modules/<name>/`
|
||||
- Implements the `gotenberg.Module` interface (at minimum `Descriptor()`)
|
||||
- May also implement `gotenberg.Provisioner`, `gotenberg.Validator`, or `gotenberg.Debuggable`
|
||||
- Self-registers via `init()` and is wired through `pkg/standard/`
|
||||
The `cmd/gotenberg/` package is strictly for wiring and startup.
|
||||
|
||||
When adding a feature, first determine if it belongs in an existing module. Only create a new module if the feature represents a genuinely separate concern.
|
||||
### Mocks
|
||||
|
||||
## Coding Patterns
|
||||
Comprehensive mock implementations for all major interfaces live in `pkg/gotenberg/mocks.go`. Use these for unit tests rather than creating new ones.
|
||||
|
||||
- **Error handling:** Always wrap errors with context using `fmt.Errorf("description: %w", err)`. Never swallow errors silently.
|
||||
- **Import ordering:** Enforced by `gci`: standard library, then third-party, then `github.com/gotenberg/gotenberg/v8`. Three groups separated by blank lines.
|
||||
- **Mocks:** Comprehensive mock implementations for all major interfaces live in `pkg/gotenberg/mocks.go`. Use these for unit tests.
|
||||
- **Logging:** Use `gotenberg.Logger(mod)` to get the module's slog logger during `Provision()`. All log calls must be context-aware: `logger.DebugContext(ctx, msg)`, `logger.InfoContext(ctx, msg)`, `logger.ErrorContext(ctx, msg)`. This propagates trace/span IDs into structured logs when OpenTelemetry is active.
|
||||
- **Telemetry:** External tool calls (Chromium, LibreOffice, PDF engines, webhooks, downloads) must create OTEL spans with `trace.SpanKindClient` and `semconv.ServerAddress("toolname")`. Use `gotenberg.Tracer()` and `gotenberg.Meter()` for traces and metrics respectively.
|
||||
- **No business logic in `cmd/`:** The `cmd/gotenberg/` package is strictly for wiring and startup.
|
||||
## Documentation rules
|
||||
|
||||
## Documentation
|
||||
### Tone
|
||||
|
||||
### Writing Style
|
||||
|
||||
- **Short, declarative sentences.** Say what it does, then stop.
|
||||
- **Lead with the action.** "Validates font embedding" not "This function validates font embedding".
|
||||
- **Active voice.** "Gotenberg checks the profile" not "The profile is checked by Gotenberg".
|
||||
- **No em dashes.** Use a period, colon, or comma instead.
|
||||
- **No "we" hedging.** "Don't..." not "We do not recommend...".
|
||||
- Short, declarative sentences. Say what it does, then stop.
|
||||
- Lead with the action. "Validates font embedding", not "This function validates font embedding".
|
||||
- Active voice. "Gotenberg checks the profile", not "The profile is checked by Gotenberg".
|
||||
- No em dashes. Use a period, colon, or comma.
|
||||
- No "we" hedging. "Don't...", not "We do not recommend...".
|
||||
|
||||
### Godoc
|
||||
|
||||
All exported types and functions require Godoc comments. Start with the identifier name:
|
||||
Every exported type and function has a Godoc comment starting with its identifier name:
|
||||
|
||||
```go
|
||||
// Violation records a single rule violation with context.
|
||||
@@ -163,30 +115,52 @@ func ValidatePDFA(ctx context.Context, ...) ([]error, error)
|
||||
|
||||
Each package should have a `doc.go` with a `// Package foo ...` comment.
|
||||
|
||||
Reference other identifiers with square brackets so pkg.go.dev renders them as links:
|
||||
Reference identifiers with `[Name]` brackets for pkg.go.dev linking:
|
||||
|
||||
```go
|
||||
// ValidatePDFA returns violations as []error where each element is a
|
||||
// [Violation] value. See [Rule] for the structured rule fields.
|
||||
// The document must be opened via [pdf.Open] with an [io.ReaderAt].
|
||||
// ValidatePDFA returns violations as []error where each element
|
||||
// is a [Violation] value. See [Rule] for the structured fields.
|
||||
```
|
||||
|
||||
This works for same-package identifiers (`[Violation]`), other packages (`[io.Reader]`), and methods (`[Reader.Open]`).
|
||||
### Code comments
|
||||
|
||||
### Code Comments
|
||||
|
||||
- Explain _why_, not _what_. The code shows what; the comment explains the non-obvious reasoning.
|
||||
- Explain _why_, not _what_.
|
||||
- No numbered step comments (`// 1. Do X`, `// 2. Do Y`).
|
||||
- No section dividers with numbers (`// --- 8. Foo ---`). Plain dividers are fine for major boundaries (`// --- VeraPDF ---`).
|
||||
- No section dividers with numbers (`// --- 8. Foo ---`). Plain dividers are fine for major boundaries.
|
||||
- No noise comments that restate the code (`// Check if err is nil`, `// Return results`).
|
||||
- Reference spec clauses where relevant (`// Per ISO 32000-2, Table 116...`).
|
||||
- Mark technical debt with `// TODO: [context]`.
|
||||
- Mark debt with `// TODO: [context]`.
|
||||
|
||||
---
|
||||
### Formatting non-Go files
|
||||
|
||||
## Review Checklist
|
||||
Run `make prettify && make lint-prettier` for YAML, Markdown, and JSON.
|
||||
|
||||
### Backward Compatibility
|
||||
## Testing
|
||||
|
||||
Integration tests use Gherkin (BDD) via Godog with `testcontainers-go` for Docker orchestration. Feature files live in `test/integration/features/`. Step definitions live in `test/integration/scenario/`. Read `scenario.go` and `containers.go` to understand step definitions before writing new tests.
|
||||
|
||||
Unit tests: table-driven tests in `*_test.go` files using mocks from `pkg/gotenberg/mocks.go`.
|
||||
|
||||
Run only the relevant integration test tag rather than the full suite (40min timeout).
|
||||
|
||||
See:
|
||||
|
||||
- [`test/integration/README.md`](test/integration/README.md): Gherkin step reference, available tags, writing new tests.
|
||||
- [`.bruno/README.md`](.bruno/README.md): `.bru` file format, conventions, route update checklist.
|
||||
- [`pkg/modules/pdfengines/README.md`](pkg/modules/pdfengines/README.md): adding new engine features (Makefile variable and flag).
|
||||
|
||||
## Pull requests
|
||||
|
||||
Plan non-trivial changes before coding. Open an issue or draft PR describing what needs to change, the proposed solution (files to modify, interface changes, form fields), and which integration test tags are affected.
|
||||
|
||||
### Guidelines
|
||||
|
||||
- One thing per PR. Keep features, bug fixes, and refactoring separate.
|
||||
- Backward compatibility matters. Do not rename or remove existing CLI flags, environment variables, or API form fields without discussion.
|
||||
- Integration tests first. When adding a feature or route, start by writing the Gherkin scenario.
|
||||
- Bruno collection must be updated if routes were added or modified.
|
||||
|
||||
### Checklist
|
||||
|
||||
- [ ] No existing CLI flags renamed or removed
|
||||
- [ ] No existing environment variables renamed or removed
|
||||
@@ -194,38 +168,23 @@ This works for same-package identifiers (`[Violation]`), other packages (`[io.Re
|
||||
- [ ] No existing HTTP endpoints changed or removed
|
||||
- [ ] No changes to default values that alter existing behavior
|
||||
- [ ] Deprecated flags have both old and new names registered, with `fs.MarkDeprecated()`
|
||||
- [ ] Errors wrapped with context: `fmt.Errorf("description: %w", err)`
|
||||
- [ ] No business logic in `cmd/`
|
||||
- [ ] No panics in production code paths
|
||||
- [ ] New features belong in the correct module (or justify a new one)
|
||||
- [ ] Linting: `make fmt && make lint && make prettify && make lint-prettier` passes with zero warnings
|
||||
- [ ] Every exported function, type, constant, and variable has a Godoc comment starting with its name
|
||||
- [ ] New packages include a `doc.go` file
|
||||
- [ ] Integration tests pass: `make test-integration` (at minimum, the relevant tags)
|
||||
- [ ] Unit tests pass: `make test-unit`
|
||||
- [ ] Bruno collection updated (if routes were added or modified)
|
||||
|
||||
If any of these are violated, the change **must** be flagged as a breaking change.
|
||||
If any backward compatibility item is violated, the change **must** be flagged as a breaking change.
|
||||
|
||||
### Linting Standards
|
||||
### Commits
|
||||
|
||||
The `.golangci.yml` enforces strict rules including: `gosec`, `govet`, `errcheck`, `staticcheck`, `dupl`, `bodyclose`, `exhaustive`, `errname`, `sloglint`, `gocritic`, and more. Zero linting errors are permitted.
|
||||
[Conventional Commits](https://www.conventionalcommits.org/): `<type>(<scope>): <description>`.
|
||||
|
||||
Formatters enforce `gci`, `gofmt`, `gofumpt`, `goimports` (see import ordering in [Coding Patterns](#coding-patterns)).
|
||||
Common types: `feat`, `fix`, `refactor`, `test`, `docs`, `chore`, `ci`, `build`. The scope should match the module or area of the change (e.g., `chromium`, `pdfengines`, `api`).
|
||||
|
||||
### Code Quality
|
||||
|
||||
- Errors are wrapped with context: `fmt.Errorf("description: %w", err)`. No swallowed errors.
|
||||
- No business logic in `cmd/`.
|
||||
- No panics in production code paths.
|
||||
- Input is validated defensively.
|
||||
- New features belong in the correct module (or justify a new one).
|
||||
|
||||
### Documentation
|
||||
|
||||
- Every exported function, type, constant, and variable has a Godoc comment starting with its name (see [Godoc](#godoc)).
|
||||
- New packages include a `doc.go` file.
|
||||
- `README.md` is not modified unless explicitly requested.
|
||||
- All documentation follows the [Writing Style](#writing-style) and [Code Comments](#code-comments) guidelines.
|
||||
|
||||
---
|
||||
|
||||
## Scoped Guidelines
|
||||
|
||||
Some areas of the codebase have their own README with detailed instructions:
|
||||
|
||||
| Area | README | Covers |
|
||||
| ----------------- | ---------------------------------------------------------------------- | --------------------------------------------------------- |
|
||||
| Integration tests | [`test/integration/README.md`](test/integration/README.md) | Gherkin step reference, available tags, writing new tests |
|
||||
| Bruno collection | [`.bruno/README.md`](.bruno/README.md) | `.bru` file format, conventions, route update checklist |
|
||||
| PDF engines | [`pkg/modules/pdfengines/README.md`](pkg/modules/pdfengines/README.md) | Adding new engine features (Makefile variable and flag) |
|
||||
Stage specific files. Never `git add -A` or `git add .`. Do not push unless asked.
|
||||
|
||||
Reference in New Issue
Block a user