docs: improve godoc and documentation [skip ci]

This commit is contained in:
Julien Neuhart
2026-04-03 14:23:18 +02:00
parent 4811a00543
commit e4a43434dc
25 changed files with 157 additions and 131 deletions

View File

@@ -1,6 +1,6 @@
# Bruno API Collection
A [Bruno](https://www.usebruno.com/) collection lives in `.bruno/` and mirrors every Gotenberg route. When adding or updating a route, update the collection to match.
A [Bruno](https://www.usebruno.com/) collection in `.bruno/` mirrors every Gotenberg route. Update the collection when adding or updating a route.
## Structure
@@ -51,10 +51,10 @@ headers {
## Conventions
- **Mandatory fields** are listed without prefix; **optional fields** are prefixed with `~` (disabled by default in Bruno).
- **Mandatory fields** have no prefix; **optional fields** use the `~` prefix (disabled by default in Bruno).
- **File references** use relative paths to `test/integration/testdata/`.
- **Webhook and output filename headers** are included on every POST route as optional (`~`).
- **One `.bru` file per request**. For routes with read/write variants (e.g., bookmarks, metadata), create separate files in the same folder.
- **Webhook and output filename headers** appear on every POST route as optional (`~`).
- **One `.bru` file per request.** For routes with read/write variants (e.g., bookmarks, metadata), create separate files in the same folder.
## Checklist When Adding/Updating a Route
@@ -62,4 +62,4 @@ headers {
2. Include all form fields from the route handler. Check `FormData*` calls in the route function.
3. For file upload fields (`files`, `watermark`, `stamp`, `embeds`), use `@file(...)` with a suitable test file.
4. Verify the URL path matches the route's `Path` field exactly.
5. If you add a new module folder, keep the naming consistent (e.g., `PDF Engines/Rotate/`).
5. For new module folders, keep the naming consistent (e.g., `PDF Engines/Rotate/`).

View File

@@ -84,12 +84,12 @@ Stage only the files related to the change. Do not use `git add -A` or `git add
## Core Principles
- **Backward compatibility is law.** Never modify existing CLI flags, environment variables, or API form fields unless explicitly instructed to perform a breaking change. Flag any breaking change immediately.
- **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.
## Project Layout
## Project Layout and Navigation
```
cmd/gotenberg/ → Entry point only (wiring/startup). No business logic.
@@ -103,33 +103,20 @@ build/ → Dockerfile, fonts, Chromium config.
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.
## Codebase Navigation
- Start with `pkg/gotenberg/` for core interfaces and `pkg/modules/` for feature implementations.
- 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.
- Import ordering is enforced: standard library, third-party, then `github.com/gotenberg/gotenberg/v8`, separated by blank lines.
- 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/`.
---
## Makefile: the Only Build Interface
All build and verification tasks go through the Makefile. Do not run `go` commands directly unless debugging a specific package.
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:
| Command | Purpose | When to use |
| ----------------------- | ------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| `make build` | Build the Docker image | Before integration tests, or to verify compilation |
| ---------------- | --------------------------------------------- | ---------------------------------------------------------------------------- |
| `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 fmt` | Format Go code (`go fix`, `golangci-lint fmt`, `go mod tidy`) | Before every commit |
| `make lint` | Lint Go code (strict `.golangci.yml` config) | Before every commit. Zero errors permitted |
| `make lint-prettier` | Lint non-Go files (Markdown, YAML, etc.) with Prettier | Before every commit |
| `make prettify` | Format non-Go files (Markdown, YAML, etc.) with Prettier | Before every commit |
| `make test-unit` | Run unit tests (`go test -race ./...`) | After code changes to `pkg/` |
| `make test-integration` | Run integration tests (Gherkin/Godog, 40min timeout) | After any feature or route change |
| `make godoc` | Serve GoDoc at `localhost:6060` | To verify documentation |
## Module System
@@ -152,6 +139,49 @@ When adding a feature, first determine if it belongs in an existing module. Only
- **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
### 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...".
### Godoc
All exported types and functions require Godoc comments. Start with the identifier name:
```go
// Violation records a single rule violation with context.
type Violation struct { ... }
// ValidatePDFA audits the document against a PDF/A profile.
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:
```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].
```
This works for same-package identifiers (`[Violation]`), other packages (`[io.Reader]`), and methods (`[Reader.Open]`).
### Code Comments
- Explain _why_, not _what_. The code shows what; the comment explains the non-obvious reasoning.
- 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 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]`.
---
## Review Checklist
@@ -171,13 +201,7 @@ If any of these are violated, the change **must** be flagged as a breaking chang
The `.golangci.yml` enforces strict rules including: `gosec`, `govet`, `errcheck`, `staticcheck`, `dupl`, `bodyclose`, `exhaustive`, `errname`, `sloglint`, `gocritic`, and more. Zero linting errors are permitted.
Formatters enforce `gci`, `gofmt`, `gofumpt`, `goimports` with import ordering:
1. Standard library
2. Third-party packages
3. `github.com/gotenberg/gotenberg/v8`
Three groups separated by blank lines.
Formatters enforce `gci`, `gofmt`, `gofumpt`, `goimports` (see import ordering in [Coding Patterns](#coding-patterns)).
### Code Quality
@@ -189,16 +213,19 @@ Three groups separated by blank lines.
### Documentation
- Every exported function, type, constant, and variable has a GoDoc comment starting with its name.
- 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
Detailed guidelines for specific areas of the codebase:
Some areas of the codebase have their own README with detailed instructions:
- [`test/integration/README.md`](test/integration/README.md): Integration test framework, Gherkin step reference, available tags, and how to write new tests.
- [`.bruno/README.md`](.bruno/README.md): Bruno API collection structure, `.bru` file format, conventions, and route update checklist.
- [`pkg/modules/pdfengines/README.md`](pkg/modules/pdfengines/README.md): How to add new PDF engine features (Makefile variable and flag).
| 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) |

View File

@@ -2,7 +2,7 @@
## Supported Versions
Only the latest version of Gotenberg receives security updates and patches. Keep your environment up to date.
Only the latest version receives security updates and patches. Keep your environment up to date.
## Reporting a Vulnerability
@@ -12,18 +12,18 @@ Include:
- A detailed description of the vulnerability.
- Steps to reproduce the issue.
- Any potential impact on users or the system.
- Potential impact on users or the system.
This process is handled on a _'best-effort'_ basis. Response speed may vary depending on severity and available resources.
This process is handled on a best-effort basis. Response speed may vary depending on severity and available resources.
## Disclosure Policy
Once a vulnerability report is received and confirmed:
Once a report is received and confirmed:
- A fix and release timeline will be prepared.
- You will be notified when the fix is released.
- You will be credited for the discovery (unless you request anonymity).
- The reporter will be notified when the fix is released.
- The reporter will be credited for the discovery (unless anonymity is requested).
## Comments on this Policy
If you have suggestions on how this process could be improved, submit a pull request.
Submit a pull request with suggestions for improving this process.

View File

@@ -1,7 +1,7 @@
// Package gotenberg provides most of the logic of the module system.
// Package gotenberg implements the core module system.
//
// caddyserver/caddy, licensed under the Apache License 2.0, has significantly
// inspired this module system.
// Significantly inspired by caddyserver/caddy, licensed under the Apache
// License 2.0.
//
// More details are available on https://caddyserver.com/.
// See https://caddyserver.com/.
package gotenberg

View File

@@ -1,8 +1,7 @@
// Package otel gathers initialization utilities for OpenTelemetry
// instrumentation.
//
// This package has been significantly inspired by
// https://github.com/lucavallin/gotel.
// Significantly inspired by https://github.com/lucavallin/gotel.
//
// See: https://opentelemetry.io/.
// See https://opentelemetry.io/.
package otel

View File

@@ -1,7 +1,5 @@
// Package semconv is a copy/paste of utilities that are currently not exposed
// in the OpenTelemery Go SDK.
// Package semconv contains utilities not yet exposed in the OpenTelemetry Go
// SDK. Remove this package once an official API exists.
//
// This package MUST be removed once an "official" API is provided.
//
// See: https://github.com/open-telemetry/opentelemetry-go-contrib/issues/4580.
// See https://github.com/open-telemetry/opentelemetry-go-contrib/issues/4580.
package semconv

View File

@@ -1,3 +1,3 @@
// Package api provides a module, which is an HTTP server. Other modules may
// add multipart/form-data routes, middlewares, and health checks.
// Package api implements the HTTP server module. Other modules register
// multipart/form data routes, middlewares, and health checks through it.
package api

View File

@@ -1,4 +1,3 @@
// Package chromium provides a module which adds routes for converting HTML
// documents to PDF. Other modules may also retrieve the [Api] provided by this
// module.
// Package chromium adds routes for converting HTML documents to PDF. Exposes
// an [Api] for other modules.
package chromium

View File

@@ -1,11 +1,6 @@
// Package exiftool provides an implementation of the gotenberg.PdfEngine
// interface using the ExifTool command-line tool. This package allows for:
// Package exiftool implements gotenberg.PdfEngine using the ExifTool command-line tool. Reads and writes PDF metadata.
//
// 1. The reading of metadata.
// 2. The writing of metadata.
// Requires the EXIFTOOL_BIN_PATH environment variable.
//
// The path to the exiftool binary must be specified using the
// EXIFTOOL_BIN_PATH environment variable.
//
// See: https://exiftool.org.
// See https://exiftool.org.
package exiftool

View File

@@ -1,3 +1,2 @@
// Package api provides a module which manages a LibreOffice instance and
// interacts with it via the UNO (Universal Network Objects) API.
// Package api manages a LibreOffice instance via the UNO API.
package api

View File

@@ -1,3 +1,3 @@
// Package libreoffice provides a module which adds a route for converting
// documents to PDF with LibreOffice.
// Package libreoffice adds a route for converting documents to PDF with
// LibreOffice.
package libreoffice

View File

@@ -1,6 +1,3 @@
// Package pdfengine provides a module which interacts with LibreOffice via the
// UNO (Universal Network Objects) API and implements the gotenberg.PdfEngine
// interface. This package allows for:
//
// 1. The conversion to specific PDF formats.
// Package pdfengine implements gotenberg.PdfEngine using LibreOffice via the
// UNO API. Converts PDFs to specific PDF formats.
package pdfengine

View File

@@ -1,8 +1,5 @@
// Package pdfcpu provides an implementation of the gotenberg.PdfEngine
// interface using the pdfcpu command-line tool. This package allows for:
// Package pdfcpu implements gotenberg.PdfEngine using the pdfcpu command-line
// tool. Merges and splits PDF files.
//
// 1. The merging of PDF files.
// 2. The splitting of PDF files.
//
// See: https://github.com/pdfcpu/pdfcpu.
// See https://github.com/pdfcpu/pdfcpu.
package pdfcpu

View File

@@ -1,14 +1,32 @@
# Adding PDF Engine Features
When adding a new PDF engine capability (e.g., bookmarks, watermark, stamp, embed), you must update the Makefile to include the corresponding engine list variable and flag. Every `--pdfengines-*-engines` flag registered in `pkg/modules/pdfengines/pdfengines.go` must have a matching entry in the Makefile:
Each new PDF engine capability (e.g., bookmarks, watermark, stamp, embed) requires a matching Makefile entry. The Makefile variables control which engines are passed to Gotenberg at `make run` and `make test-integration` time (via `compose.yaml`). If you skip this step, the flag still works when set manually, but `make run` falls back to the default defined in `pdfengines.go`, which may not include the new engine.
1. **Add a variable** in the Makefile's variable block (around line 60-70):
Every `--pdfengines-*-engines` flag registered in `pkg/modules/pdfengines/pdfengines.go` must have a corresponding variable and flag in the Makefile:
1. **Add a variable** in the Makefile's variable block (around line 60 to 70):
```makefile
PDFENGINES_<FEATURE>_ENGINES=<default engines>
```
2. **Add the flag** in the Makefile's command args block (around line 140-155):
```makefile
--pdfengines-<feature>-engines=$(PDFENGINES_<FEATURE>_ENGINES) \
2. **Add the flag** in `compose.yaml`'s command args:
```yaml
- "--pdfengines-<feature>-engines=${PDFENGINES_<FEATURE>_ENGINES}"
```
The default value should match what is defined in `pdfengines.go`'s `fs.StringSlice(...)` call for that flag.
The default value must match the `fs.StringSlice(...)` call for that flag in `pdfengines.go`.
## Example: Rotate
The rotate feature was added with two engines (`pdfcpu` and `pdftk`). Here is what the additions look like:
**Makefile** (variable block):
```makefile
PDFENGINES_ROTATE_ENGINES=pdfcpu,pdftk
```
**compose.yaml** (command args):
```yaml
- "--pdfengines-rotate-engines=${PDFENGINES_ROTATE_ENGINES}"
```

View File

@@ -1,3 +1,3 @@
// Package pdfengines a way to gather and manage multiple modules that
// implement the gotenberg.PdfEngine interface.
// Package pdfengines gathers and manages modules that implement
// gotenberg.PdfEngine.
package pdfengines

View File

@@ -1,11 +1,7 @@
// Package pdftk provides an implementation of the gotenberg.PdfEngine
// interface using the PDFtk command-line tool. This package allows for:
// Package pdftk implements gotenberg.PdfEngine using the PDFtk command-line
// tool. Merges and splits PDF files.
//
// 1. The merging of PDF files.
// 2. The splitting of PDF files.
// Requires the PDFTK_BIN_PATH environment variable.
//
// The path to the PDFtk binary must be specified using the PDFTK_BIN_PATH
// environment variable.
//
// See: https://gitlab.com/pdftk-java/pdftk.
// See https://gitlab.com/pdftk-java/pdftk.
package pdftk

View File

@@ -1,5 +1,4 @@
// Package prometheus provides a module which collects metrics and exposes them
// via an HTTP route.
// Package prometheus collects metrics and exposes them via an HTTP route.
//
// See: https://prometheus.io/.
// See https://prometheus.io/.
package prometheus

View File

@@ -1,12 +1,7 @@
// Package qpdf provides an implementation of the gotenberg.PdfEngine
// interface using the QPDF command-line tool. This package allows for:
// Package qpdf implements gotenberg.PdfEngine using the QPDF command-line
// tool. Merges, splits, and flattens PDF files.
//
// 1. The merging of PDF files.
// 2. The splitting of PDF files.
// 3. Flattening of PDF files
// Requires the QPDF_BIN_PATH environment variable.
//
// The path to the QPDF binary must be specified using the QPDK_BIN_PATH
// environment variable.
//
// See: https://github.com/qpdf/qpdf.
// See https://github.com/qpdf/qpdf.
package qpdf

View File

@@ -1,3 +1,3 @@
// Package webhook provides a module which adds a middleware for uploading
// output files to any destination in an asynchronous fashion.
// Package webhook adds middleware for uploading output files to any destination
// asynchronously.
package webhook

View File

@@ -1,3 +1,3 @@
// Package chromium imports the application's modules for the Chromium-only
// variant (no LibreOffice).
// Package chromium imports modules for the Chromium-only variant (no
// LibreOffice).
package chromium

View File

@@ -1,2 +1,2 @@
// Package standard imports the application's default modules.
// Package standard imports the default modules.
package standard

View File

@@ -1,3 +1,3 @@
// Package libreoffice imports the application's modules for the
// LibreOffice-only variant (no Chromium).
// Package libreoffice imports modules for the LibreOffice-only variant (no
// Chromium).
package libreoffice

View File

@@ -2,15 +2,15 @@
- **Framework:** Gherkin (BDD) via [Godog](https://github.com/cucumber/godog), with `testcontainers-go` for Docker orchestration.
- **Feature files:** `test/integration/features/*.feature`, one file per endpoint or capability.
- **Test infrastructure:** `test/integration/scenario/`, Go step definitions, container management, HTTP helpers, PDF validation.
- **Test infrastructure:** `test/integration/scenario/` contains Go step definitions, container management, HTTP helpers, and PDF validation.
- **Entry point:** `test/integration/main_test.go` (build tag: `integration`).
- **Test data:** `test/integration/testdata/`
## How It Works
Each scenario spins up a fresh Gotenberg Docker container via testcontainers. The step definitions in `scenario/scenario.go` map Gherkin steps to Go functions. An additional `gotenberg/integration-tools` container provides PDF validation tools (`verapdf`, `pdfinfo`, `pdftotext`).
Each scenario spins up a fresh Gotenberg Docker container via testcontainers. Step definitions in `scenario/scenario.go` map Gherkin steps to Go functions. A separate `gotenberg/integration-tools` container provides PDF validation tools (`verapdf`, `pdfinfo`, `pdftotext`).
**Important:** Integration tests require a Docker image. Run `make build` before `make test-integration`.
**Important:** Run `make build` before `make test-integration`. Integration tests require a Docker image.
## Selective Test Runs
@@ -22,7 +22,14 @@ make test-integration TAGS=chromium-convert-html
make test-integration TAGS="merge,split"
```
Available tags: `chromium`, `chromium-concurrent`, `chromium-convert-html`, `chromium-convert-markdown`, `chromium-convert-url`, `chromium-screenshot-html`, `chromium-screenshot-markdown`, `chromium-screenshot-url`, `debug`, `health`, `libreoffice`, `libreoffice-convert`, `output-filename`, `pdfengines`, `pdfengines-convert`, `pdfengines-embed`, `embed`, `pdfengines-encrypt`, `encrypt`, `pdfengines-flatten`, `flatten`, `pdfengines-merge`, `merge`, `pdfengines-metadata`, `metadata`, `pdfengines-split`, `split`, `pdfengines-watermark`, `watermark`, `pdfengines-stamp`, `stamp`, `pdfengines-bookmarks`, `bookmarks`, `pdfengines-rotate`, `rotate`, `prometheus-metrics`, `root`, `version`, `webhook`, `download-from`.
Available tags:
| Group | Tags |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Chromium | `chromium`, `chromium-concurrent`, `chromium-convert-html`, `chromium-convert-markdown`, `chromium-convert-url`, `chromium-screenshot-html`, `chromium-screenshot-markdown`, `chromium-screenshot-url` |
| LibreOffice | `libreoffice`, `libreoffice-convert` |
| PDF Engines | `pdfengines`, `pdfengines-convert`, `pdfengines-merge`, `merge`, `pdfengines-split`, `split`, `pdfengines-flatten`, `flatten`, `pdfengines-rotate`, `rotate`, `pdfengines-embed`, `embed`, `pdfengines-encrypt`, `encrypt`, `pdfengines-watermark`, `watermark`, `pdfengines-stamp`, `stamp`, `pdfengines-metadata`, `metadata`, `pdfengines-bookmarks`, `bookmarks` |
| Infrastructure | `health`, `debug`, `root`, `version`, `output-filename`, `prometheus-metrics`, `webhook`, `download-from` |
Other useful flags:
@@ -35,8 +42,8 @@ make test-integration PLATFORM=linux/arm64 # Force a specific platform
1. Create or update a `.feature` file in `test/integration/features/`.
2. Tag it appropriately (e.g., `@chromium @chromium-convert-html`).
3. If the feature requires new tag(s), add them to both the `TAGS` comment block in the `Makefile` and the "Available tags" list above.
4. If you create a new step definition, add it to `scenario/scenario.go`, register it in `InitializeScenario`, and update the "Available Gherkin Steps" list below.
3. For new tags, add them to both the `TAGS` comment block in the `Makefile` and the "Available tags" list above.
4. For new step definitions, add the function to `scenario/scenario.go`, register it in `InitializeScenario`, and add the step pattern to the "Available Gherkin Steps" list below (follow the existing format: backtick-quoted pattern, then parenthetical notes on arguments).
5. Test data goes in `test/integration/testdata/`.
## Available Gherkin Steps

View File

@@ -1,2 +1,2 @@
// Package integration contains everything related to integration testing.
// Package integration contains the integration test suite.
package integration

View File

@@ -1,13 +1,13 @@
To generate a valid certificate and private key use the following command:
Generate a valid certificate and private key:
```bash
# In OpenSSL 1.1.1
# OpenSSL 1.1.1+
openssl req -x509 -newkey rsa:4096 -sha256 -days 9999 -nodes \
-keyout key.pem -out cert.pem -subj "/CN=localhost" \
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1,IP:::1"
```
To check a certificate use the following command:
Check a certificate:
```bash
openssl x509 -in cert.pem -text