include .env .PHONY: help help: ## Show the help @grep -hE '^[A-Za-z0-9_ \-]*?:.*##.*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' .PHONY: build build: ## Build the Gotenberg's Docker image (use TARGET=gotenberg-chromium or TARGET=gotenberg-libreoffice for variants) docker build \ --target $(TARGET) \ -t $(DOCKER_REGISTRY)/$(DOCKER_REPOSITORY):$(GOTENBERG_VERSION) \ -f $(DOCKERFILE) $(DOCKER_BUILD_CONTEXT) TZ=UTC GOTENBERG_HIDE_BANNER=false GOTENBERG_GRACEFUL_SHUTDOWN_DURATION=30s GOTENBERG_BUILD_DEBUG_DATA=true API_PORT=3000 API_PORT_FROM_ENV= API_BIND_IP= API_TLS_CERT_FILE= API_TLS_KEY_FILE= API_START_TIMEOUT=30s API_TIMEOUT=30s API_BODY_LIMIT= API_ROOT_PATH=/ API_CORRELATION_ID_HEADER=Gotenberg-Trace API_ENABLE_BASIC_AUTH=false GOTENBERG_API_BASIC_AUTH_USERNAME= GOTENBERG_API_BASIC_AUTH_PASSWORD= API_ENABLE_OIDC_AUTH=false API_OIDC_ISSUER= API_OIDC_AUDIENCE= API_OIDC_JWKS_URL= API_DOWNLOAD_FROM_ALLOW_LIST= # Empty, like the flag default since 8.32.0. A textual deny-list cannot # enumerate every way to write a private address, so *_DENY_PRIVATE_IPS is the # control to reach for. Left false here so local testing can reach the host. API_DOWNLOAD_FROM_DENY_LIST= API_DOWNLOAD_FROM_DENY_PRIVATE_IPS=false API_DOWNLOAD_FROM_DENY_PUBLIC_IPS=false API_DOWNLOAD_FROM_ENABLE_ENVIRONMENT_PROXY=false API_DOWNLOAD_FROM_MAX_RETRY=4 API_DOWNLOAD_FROM_MAX_CONCURRENCY=10 API_DOWNLOAD_FROM_MAX_ENTRIES=0 API_DISABLE_DOWNLOAD_FROM=false API_DISABLE_HEALTH_CHECK_ROUTE_TELEMETRY=true API_DISABLE_ROOT_ROUTE_TELEMETRY=true API_DISABLE_DEBUG_ROUTE_TELEMETRY=true API_DISABLE_VERSION_ROUTE_TELEMETRY=true API_ENABLE_DEBUG_ROUTE=false CHROMIUM_RESTART_AFTER=100 CHROMIUM_MAX_QUEUE_SIZE=0 CHROMIUM_IDLE_SHUTDOWN_TIMEOUT=0 CHROMIUM_MAX_CONCURRENCY=6 CHROMIUM_AUTO_START=false CHROMIUM_START_TIMEOUT=20s CHROMIUM_ALLOW_INSECURE_LOCALHOST=false CHROMIUM_IGNORE_CERTIFICATE_ERRORS=false CHROMIUM_DISABLE_WEB_SECURITY=false CHROMIUM_ALLOW_FILE_ACCESS_FROM_FILES=false CHROMIUM_HOST_RESOLVER_RULES= CHROMIUM_PROXY_SERVER= CHROMIUM_ENABLE_ENVIRONMENT_PROXY=false CHROMIUM_ALLOW_LIST= CHROMIUM_DENY_LIST=^file:(?!//\/tmp/).* CHROMIUM_DENY_PRIVATE_IPS=false CHROMIUM_DENY_PUBLIC_IPS=false CHROMIUM_CLEAR_CACHE=false CHROMIUM_CLEAR_COOKIES=false CHROMIUM_DISABLE_JAVASCRIPT=false CHROMIUM_DISABLE_ROUTES=false LIBREOFFICE_RESTART_AFTER=10 LIBREOFFICE_MAX_QUEUE_SIZE=0 LIBREOFFICE_IDLE_SHUTDOWN_TIMEOUT=0 LIBREOFFICE_AUTO_START=false LIBREOFFICE_START_TIMEOUT=20s LIBREOFFICE_ALLOW_LIST= LIBREOFFICE_DENY_LIST= LIBREOFFICE_DENY_PRIVATE_IPS=false LIBREOFFICE_DENY_PUBLIC_IPS=false LIBREOFFICE_ENABLE_ENVIRONMENT_PROXY=false LIBREOFFICE_DISABLE_ROUTES=false LOG_LEVEL=info LOG_FIELDS_PREFIX= LOG_STD_FORMAT=auto LOG_STD_ENABLE_GCP_FIELDS=false LOG_STD_LEVEL_CASE=lower PDFENGINES_DISABLE_ROUTES=false PDFENGINES_MERGE_ENGINES=qpdf,pdfcpu,pdftk PDFENGINES_SPLIT_ENGINES=pdfcpu,qpdf,pdftk PDFENGINES_FLATTEN_ENGINES=qpdf PDFENGINES_CONVERT_ENGINES=libreoffice-pdfengine PDFENGINES_OPTIMIZE_IMAGES_ENGINES=pdfcpu PDFENGINES_READ_METADATA_ENGINES=exiftool PDFENGINES_WRITE_METADATA_ENGINES=exiftool PDFENGINES_READ_BOOKMARKS_ENGINES=pdfcpu PDFENGINES_WRITE_BOOKMARKS_ENGINES=pdfcpu PDFENGINES_WATERMARK_ENGINES=pdfcpu,pdftk PDFENGINES_STAMP_ENGINES=pdfcpu,pdftk PDFENGINES_ENCRYPT_ENGINES=qpdf,pdfcpu,pdftk PDFENGINES_ROTATE_ENGINES=pdfcpu,pdftk PDFENGINES_EMBED_ENGINES=pdfcpu PDFENGINES_EMBED_METADATA_ENGINES=qpdf PDFENGINES_FACTUR_X_ENGINES=qpdf PROMETHEUS_NAMESPACE=gotenberg PROMETHEUS_COLLECT_INTERVAL=1s PROMETHEUS_DISABLE_ROUTE_TELEMETRY=true PROMETHEUS_DISABLE_COLLECT=false PROMETHEUS_METRICS_PATH=/prometheus/metrics OTEL_SERVICE_NAME=gotenberg OTEL_TRACES_EXPORTER=none OTEL_METRICS_EXPORTER=none OTEL_LOGS_EXPORTER=none OTEL_EXPORTER_OTLP_PROTOCOL=grpc OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317 OTEL_EXPORTER_OTLP_INSECURE=true WEBHOOK_ENABLE_SYNC_MODE=false WEBHOOK_ALLOW_LIST= # See the note on API_DOWNLOAD_FROM_DENY_LIST. WEBHOOK_DENY_LIST= WEBHOOK_DENY_PRIVATE_IPS=false WEBHOOK_DENY_PUBLIC_IPS=false WEBHOOK_ENABLE_ENVIRONMENT_PROXY=false WEBHOOK_MAX_RETRY=4 WEBHOOK_RETRY_MIN_WAIT=1s WEBHOOK_RETRY_MAX_WAIT=30s WEBHOOK_CLIENT_TIMEOUT=30s WEBHOOK_DISABLE=false # Export all variables so they are available to Compose export .PHONY: run run: ## Start a Gotenberg container via Compose docker compose up gotenberg .PHONY: telemetry telemetry: ## Start an OpenTelemetry collector and OpenObserve containers via Compose docker compose up otel-collector openobserve .PHONY: down down: ## Stop all containers docker compose down -v .PHONY: test-unit test-unit: ## Run unit tests go test -race ./... PLATFORM= NO_CONCURRENCY=false # Available tags: # chromium # chromium-concurrent # chromium-convert-html # chromium-convert-markdown # chromium-convert-url # chromium-screenshot-html # chromium-screenshot-markdown # chromium-screenshot-url # chromium-ssrf # debug # health # libreoffice # libreoffice-convert # libreoffice-ssrf # output-filename # pdfengines # pdfengines-convert # pdfengines-embed # embed # pdfengines-encrypt # encrypt # pdfengines-flatten # flatten # pdfengines-optimize # optimize # pdfengines-merge # merge # pdfengines-metadata # metadata # pdfengines-split # split # pdfengines-watermark # watermark # pdfengines-stamp # stamp # pdfengines-rotate # rotate # factur-x # pdfengines-bookmarks # bookmarks # prometheus-metrics # root # version # webhook # download-from TAGS= .PHONY: test-integration test-integration: ## Run integration tests (automatically retries failed scenarios up to 3 times) go test -timeout 40m -tags=integration -v github.com/gotenberg/gotenberg/v8/test/integration -args \ --gotenberg-docker-repository=$(DOCKER_REPOSITORY) \ --gotenberg-version=$(GOTENBERG_VERSION) \ --gotenberg-container-platform=$(PLATFORM) \ --no-concurrency=$(NO_CONCURRENCY) \ --tags="$(TAGS)" .PHONY: lint lint: ## Lint Golang codebase golangci-lint run .PHONY: lint-prettier lint-prettier: ## Lint non-Golang codebase npx prettier --check . .PHONY: lint-todo lint-todo: ## Find TODOs in Golang codebase golangci-lint run --no-config --default=none --enable godox # TODO: restore a plain "go fix ./..." once both modernizers stop rewriting this # codebase into code that does not compile. Re-check by dropping a flag and # running "make fmt && make lint". Removing an analyzer upstream makes go fix # fail with "flag provided but not defined", so this cannot rot silently. # embedlit folds a post-literal field assignment into a literal that already # sets that key, producing a duplicate field name (cmd/gotenberg.go). Fixed in # Go 1.27.1, so drop that flag once the toolchain moves past 1.27.0. # See https://github.com/golang/go/issues/81101. # errorsastype rewrites errors.As to errors.AsType[T] without checking that T # satisfies error, which breaks on api.HttpError since it does not embed # error. Still broken as of Go 1.27.1. .PHONY: fmt fmt: ## Format Golang codebase and "optimize" the dependencies go fix -embedlit=false -errorsastype=false ./... golangci-lint fmt go mod tidy .PHONY: prettify prettify: ## Format non-Golang codebase npx prettier --write . # go install golang.org/x/tools/cmd/godoc@latest .PHONY: godoc godoc: ## Run a webserver with Gotenberg godoc $(info http://localhost:6060/pkg/github.com/gotenberg/gotenberg/v8) godoc -http=:6060