Compare commits

...

17 Commits

Author SHA1 Message Date
Julien Neuhart
8252142e65 chore(deps): update Go dependencies 2025-03-17 14:01:07 +01:00
Julien Neuhart
6e2270632e chore(logging): remove unit tests - integration tests are inc anyway 2025-03-17 13:56:48 +01:00
Julien Neuhart
7d14546d3b feat(logging): add GCP severity mapping 2025-03-17 13:56:48 +01:00
Julien Neuhart
b31024c362 feat(libreoffice): add updateIndexes form field 2025-03-17 13:32:26 +01:00
Julien Neuhart
85eaef05ad fix(deps): update Go dependencies - chromedp ERROR: unhandled page event *page.EventFrameStartedNavigating 2025-03-06 08:55:57 +01:00
Julien Neuhart
16e4c10862 chore(deps): update Go dependencies 2025-03-05 08:57:48 +01:00
Julien Neuhart
904e0f61a8 fix(pdfengines): split and read metadata pdf engines calls 2025-03-04 18:08:57 +01:00
Zdravko
70953aa66e fix(qpdf): allow warnings (#1135)
* Optionally allow warnings in QPDF operations

* warnings are not errors by default

* Apply suggestions from code review

Co-authored-by: Julien Neuhart <neuhart.julien@gmail.com>

* fix some merge artifacts

* follow the args convention

---------

Co-authored-by: Julien Neuhart <neuhart.julien@gmail.com>
2025-02-28 10:43:38 +01:00
Julien Neuhart
d41de5d270 docs(README): switch to new short description [skip ci] 2025-02-21 11:55:12 +01:00
Julien Neuhart
d8603f4f34 feat: update short description 2025-02-21 11:22:22 +01:00
Julien Neuhart
b13a8094ea ci(continous-delivery): remove wrong outputs 2025-02-19 19:42:30 +01:00
Kyohei Fukuda
28e07bad54 docs(README): add pdfme sponsor (#1131) 2025-02-14 14:28:53 +01:00
Julien Neuhart
81cc483166 test: fix issues with go 1.24 2025-02-13 09:21:38 +01:00
Julien Neuhart
8d7cc55c28 chore(go): upgrade to 1.24 2025-02-13 08:44:58 +01:00
Julien Neuhart
083e0e307a chore(deps): update Go dependencies 2025-02-12 16:24:16 +01:00
Julien Neuhart
80f3f89a89 fix(pdfcpu): correct sorting for the output paths of the split method 2025-02-12 11:47:45 +01:00
Julien Neuhart
3dc8c18849 fix(fs): get file by ext sorted by mod time 2025-02-11 17:00:18 +01:00
34 changed files with 396 additions and 649 deletions

4
.env
View File

@@ -1,4 +1,4 @@
GOLANG_VERSION=1.23 GOLANG_VERSION=1.24
DOCKER_REGISTRY=gotenberg DOCKER_REGISTRY=gotenberg
DOCKER_REPOSITORY=gotenberg DOCKER_REPOSITORY=gotenberg
GOTENBERG_VERSION=snapshot GOTENBERG_VERSION=snapshot
@@ -7,7 +7,7 @@ GOTENBERG_USER_UID=1001
NOTO_COLOR_EMOJI_VERSION=v2.047 # See https://github.com/googlefonts/noto-emoji/releases. NOTO_COLOR_EMOJI_VERSION=v2.047 # See https://github.com/googlefonts/noto-emoji/releases.
PDFTK_VERSION=v3.3.3 # See https://gitlab.com/pdftk-java/pdftk/-/releases - Binary package. PDFTK_VERSION=v3.3.3 # See https://gitlab.com/pdftk-java/pdftk/-/releases - Binary package.
PDFCPU_VERSION=v0.8.1 # See https://github.com/pdfcpu/pdfcpu/releases. PDFCPU_VERSION=v0.8.1 # See https://github.com/pdfcpu/pdfcpu/releases.
GOLANGCI_LINT_VERSION=v1.63.4 # See https://github.com/golangci/golangci-lint/releases. GOLANGCI_LINT_VERSION=v1.64.2 # See https://github.com/golangci/golangci-lint/releases.
GOTENBERG_VERSION=snapshot GOTENBERG_VERSION=snapshot
DOCKERFILE=build/Dockerfile DOCKERFILE=build/Dockerfile
DOCKERFILE_CLOUDRUN=build/Dockerfile.cloudrun DOCKERFILE_CLOUDRUN=build/Dockerfile.cloudrun

View File

@@ -50,8 +50,6 @@ jobs:
name: Release linux/arm64 name: Release linux/arm64
runs-on: ubuntu-24.04-arm runs-on: ubuntu-24.04-arm
outputs: outputs:
docker_hub_username: ${{ secrets.DOCKERHUB_USERNAME }}
docker_hub_password: ${{ secrets.DOCKERHUB_TOKEN }}
tags: ${{ steps.build_push.outputs.tags }} tags: ${{ steps.build_push.outputs.tags }}
tags_cloud_run: ${{ steps.build_push.outputs.tags_cloud_run }} tags_cloud_run: ${{ steps.build_push.outputs.tags_cloud_run }}
steps: steps:

View File

@@ -32,7 +32,7 @@ jobs:
- name: Run linters - name: Run linters
uses: golangci/golangci-lint-action@v6 uses: golangci/golangci-lint-action@v6
with: with:
version: v1.63.4 version: v1.64.2
tests: tests:
needs: needs:

View File

@@ -37,12 +37,12 @@ linters:
- promlinter - promlinter
#- sloglint #- sloglint
- staticcheck - staticcheck
- tenv
- testableexamples - testableexamples
- tparallel - tparallel
- typecheck - typecheck
- unconvert - unconvert
- unused - unused
- usetesting
- wastedassign - wastedassign
- whitespace - whitespace

View File

@@ -63,6 +63,7 @@ LIBREOFFICE_DISABLE_ROUTES=false
LOG_LEVEL=info LOG_LEVEL=info
LOG_FORMAT=auto LOG_FORMAT=auto
LOG_FIELDS_PREFIX= LOG_FIELDS_PREFIX=
LOG_ENABLE_GCP_SEVERITY=false
PDFENGINES_ENGINES= PDFENGINES_ENGINES=
PDFENGINES_MERGE_ENGINES=qpdf,pdfcpu,pdftk PDFENGINES_MERGE_ENGINES=qpdf,pdfcpu,pdftk
PDFENGINES_SPLIT_ENGINES=pdfcpu,qpdf,pdftk PDFENGINES_SPLIT_ENGINES=pdfcpu,qpdf,pdftk
@@ -134,6 +135,7 @@ run: ## Start a Gotenberg container
--log-level=$(LOG_LEVEL) \ --log-level=$(LOG_LEVEL) \
--log-format=$(LOG_FORMAT) \ --log-format=$(LOG_FORMAT) \
--log-fields-prefix=$(LOG_FIELDS_PREFIX) \ --log-fields-prefix=$(LOG_FIELDS_PREFIX) \
--log-enable-gcp-severity=$(LOG_ENABLE_GCP_SEVERITY) \
--pdfengines-engines=$(PDFENGINES_ENGINES) \ --pdfengines-engines=$(PDFENGINES_ENGINES) \
--pdfengines-merge-engines=$(PDFENGINES_MERGE_ENGINES) \ --pdfengines-merge-engines=$(PDFENGINES_MERGE_ENGINES) \
--pdfengines-split-engines=$(PDFENGINES_SPLIT_ENGINES) \ --pdfengines-split-engines=$(PDFENGINES_SPLIT_ENGINES) \

View File

@@ -1,7 +1,7 @@
<p align="center"> <p align="center">
<img src="https://user-images.githubusercontent.com/8983173/130322857-185831e2-f041-46eb-a17f-0a69d066c4e5.png" alt="Gotenberg Logo" width="150" height="150" /> <img src="https://user-images.githubusercontent.com/8983173/130322857-185831e2-f041-46eb-a17f-0a69d066c4e5.png" alt="Gotenberg Logo" width="150" height="150" />
<h3 align="center">Gotenberg</h3> <h3 align="center">Gotenberg</h3>
<p align="center">A Docker-powered stateless API for PDF files</p> <p align="center">A containerized API for seamless PDF conversion</p>
<p align="center"> <p align="center">
<a href="https://hub.docker.com/r/gotenberg/gotenberg"><img alt="Total downloads (gotenberg/gotenberg)" src="https://img.shields.io/docker/pulls/gotenberg/gotenberg"></a> <a href="https://hub.docker.com/r/gotenberg/gotenberg"><img alt="Total downloads (gotenberg/gotenberg)" src="https://img.shields.io/docker/pulls/gotenberg/gotenberg"></a>
<a href="https://hub.docker.com/r/thecodingmachine/gotenberg"><img alt="Total downloads (thecodingmachine/gotenberg)" src="https://img.shields.io/docker/pulls/thecodingmachine/gotenberg"></a> <a href="https://hub.docker.com/r/thecodingmachine/gotenberg"><img alt="Total downloads (thecodingmachine/gotenberg)" src="https://img.shields.io/docker/pulls/thecodingmachine/gotenberg"></a>
@@ -45,6 +45,9 @@ Head to the [documentation](https://gotenberg.dev/docs/getting-started/introduct
<a href="https://zolsec.com?utm_source=gotenberg_github&utm_medium=website" target="_blank"> <a href="https://zolsec.com?utm_source=gotenberg_github&utm_medium=website" target="_blank">
<img src="https://github.com/gotenberg/gotenberg/assets/8983173/707ccc97-a79b-4dcb-8fc8-6827366e5be3" alt="Zolsec Logo" width="333" height="163" /> <img src="https://github.com/gotenberg/gotenberg/assets/8983173/707ccc97-a79b-4dcb-8fc8-6827366e5be3" alt="Zolsec Logo" width="333" height="163" />
</a> </a>
<a href="https://pdfme.com?utm_source=gotenberg_github&utm_medium=website" target="_blank">
<img src="https://github.com/user-attachments/assets/2a75dd40-ca18-4d34-acd5-5dd474595168" alt="pdfme Logo" width="333" height="163" />
</a>
</p> </p>
Sponsorships help maintaining and improving Gotenberg - [become a sponsor](https://github.com/sponsors/gulien) ❤️ Sponsorships help maintaining and improving Gotenberg - [become a sponsor](https://github.com/sponsors/gulien) ❤️

View File

@@ -63,7 +63,7 @@ ARG NOTO_COLOR_EMOJI_VERSION
ARG PDFTK_VERSION ARG PDFTK_VERSION
LABEL org.opencontainers.image.title="Gotenberg" \ LABEL org.opencontainers.image.title="Gotenberg" \
org.opencontainers.image.description="A Docker-powered stateless API for PDF files." \ org.opencontainers.image.description="A containerized API for seamless PDF conversion." \
org.opencontainers.image.version="$GOTENBERG_VERSION" \ org.opencontainers.image.version="$GOTENBERG_VERSION" \
org.opencontainers.image.authors="Julien Neuhart <neuhart.julien@gmail.com>" \ org.opencontainers.image.authors="Julien Neuhart <neuhart.julien@gmail.com>" \
org.opencontainers.image.documentation="https://gotenberg.dev" \ org.opencontainers.image.documentation="https://gotenberg.dev" \
@@ -171,7 +171,7 @@ RUN \
apt-get update -qq &&\ apt-get update -qq &&\
apt-get upgrade -yqq &&\ apt-get upgrade -yqq &&\
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq --no-install-recommends -t bookworm-backports libreoffice &&\ DEBIAN_FRONTEND=noninteractive apt-get install -y -qq --no-install-recommends -t bookworm-backports libreoffice &&\
curl -Ls https://raw.githubusercontent.com/gotenberg/unoconverter/v0.0.1/unoconv -o /usr/bin/unoconverter &&\ curl -Ls https://raw.githubusercontent.com/gotenberg/unoconverter/v0.1.1/unoconv -o /usr/bin/unoconverter &&\
chmod +x /usr/bin/unoconverter &&\ chmod +x /usr/bin/unoconverter &&\
# unoconverter will look for the Python binary, which has to be at version 3. # unoconverter will look for the Python binary, which has to be at version 3.
ln -s /usr/bin/python3 /usr/bin/python &&\ ln -s /usr/bin/python3 /usr/bin/python &&\

View File

@@ -24,7 +24,7 @@ const banner = `
\___/\___/\__/\__/_//_/_.__/\__/_/ \_, / \___/\___/\__/\__/_//_/_.__/\__/_/ \_, /
/___/ /___/
A Docker-powered stateless API for PDF files. A containerized API for seamless PDF conversion.
Version: %s Version: %s
------------------------------------------------------- -------------------------------------------------------
` `

43
go.mod
View File

@@ -1,45 +1,45 @@
module github.com/gotenberg/gotenberg/v8 module github.com/gotenberg/gotenberg/v8
go 1.23.0 go 1.24.0
require ( require (
github.com/alexliesenfeld/health v0.8.0 github.com/alexliesenfeld/health v0.8.0
github.com/andybalholm/brotli v1.1.1 // indirect github.com/andybalholm/brotli v1.1.1 // indirect
github.com/barasher/go-exiftool v1.10.0 github.com/barasher/go-exiftool v1.10.0
github.com/chromedp/cdproto v0.0.0-20250203011601-a3c71a042730 github.com/chromedp/cdproto v0.0.0-20250311215558-29dfcc2791de
github.com/chromedp/chromedp v0.12.1 github.com/chromedp/chromedp v0.13.1
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 github.com/hashicorp/go-retryablehttp v0.7.7
github.com/klauspost/compress v1.17.11 // indirect github.com/klauspost/compress v1.18.0 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect github.com/klauspost/pgzip v1.2.6 // indirect
github.com/labstack/echo/v4 v4.13.3 github.com/labstack/echo/v4 v4.13.3
github.com/labstack/gommon v0.4.2 github.com/labstack/gommon v0.4.2
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/microcosm-cc/bluemonday v1.0.27 github.com/microcosm-cc/bluemonday v1.0.27
github.com/pierrec/lz4/v4 v4.1.22 // indirect github.com/pierrec/lz4/v4 v4.1.22 // indirect
github.com/prometheus/client_golang v1.20.5 github.com/prometheus/client_golang v1.21.1
github.com/russross/blackfriday/v2 v2.1.0 github.com/russross/blackfriday/v2 v2.1.0
github.com/spf13/pflag v1.0.6 github.com/spf13/pflag v1.0.6
github.com/ulikunitz/xz v0.5.12 // indirect github.com/ulikunitz/xz v0.5.12 // indirect
go.uber.org/multierr v1.11.0 go.uber.org/multierr v1.11.0
go.uber.org/zap v1.27.0 go.uber.org/zap v1.27.0
golang.org/x/crypto v0.32.0 // indirect golang.org/x/crypto v0.36.0 // indirect
golang.org/x/net v0.34.0 golang.org/x/net v0.37.0
golang.org/x/sync v0.11.0 golang.org/x/sync v0.12.0
golang.org/x/sys v0.30.0 // indirect golang.org/x/sys v0.31.0 // indirect
golang.org/x/term v0.29.0 golang.org/x/term v0.30.0
golang.org/x/text v0.22.0 golang.org/x/text v0.23.0
) )
require ( require (
github.com/dlclark/regexp2 v1.11.4 github.com/dlclark/regexp2 v1.11.5
github.com/mholt/archives v0.1.0 github.com/mholt/archives v0.1.0
github.com/shirou/gopsutil/v4 v4.25.1 github.com/shirou/gopsutil/v4 v4.25.2
) )
require ( require (
github.com/STARRY-S/zip v0.2.1 // indirect github.com/STARRY-S/zip v0.2.2 // indirect
github.com/aymerick/douceur v0.2.0 // indirect github.com/aymerick/douceur v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/bodgit/plumbing v1.3.0 // indirect github.com/bodgit/plumbing v1.3.0 // indirect
@@ -49,6 +49,7 @@ require (
github.com/chromedp/sysutil v1.1.0 // indirect github.com/chromedp/sysutil v1.1.0 // indirect
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
github.com/ebitengine/purego v0.8.2 // indirect github.com/ebitengine/purego v0.8.2 // indirect
github.com/go-json-experiment/json v0.0.0-20250223041408-d3c622f1b874 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect
github.com/gobwas/httphead v0.1.0 // indirect github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect github.com/gobwas/pool v0.2.1 // indirect
@@ -57,24 +58,22 @@ require (
github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/josharian/intern v1.0.0 // indirect github.com/lufia/plan9stats v0.0.0-20250303091104-876f3ea5145d // indirect
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-colorable v0.1.14 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nwaples/rardecode/v2 v2.1.0 // indirect github.com/nwaples/rardecode/v2 v2.1.1 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/common v0.63.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect github.com/prometheus/procfs v0.15.1 // indirect
github.com/sorairolake/lzip-go v0.3.5 // indirect github.com/sorairolake/lzip-go v0.3.5 // indirect
github.com/therootcompany/xz v1.0.1 // indirect github.com/therootcompany/xz v1.0.1 // indirect
github.com/tklauser/go-sysconf v0.3.14 // indirect github.com/tklauser/go-sysconf v0.3.15 // indirect
github.com/tklauser/numcpus v0.9.0 // indirect github.com/tklauser/numcpus v0.10.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect
go4.org v0.0.0-20230225012048-214862532bf5 // indirect go4.org v0.0.0-20230225012048-214862532bf5 // indirect
golang.org/x/time v0.10.0 // indirect golang.org/x/time v0.11.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect google.golang.org/protobuf v1.36.5 // indirect
) )

86
go.sum
View File

@@ -17,8 +17,8 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/STARRY-S/zip v0.2.1 h1:pWBd4tuSGm3wtpoqRZZ2EAwOmcHK6XFf7bU9qcJXyFg= github.com/STARRY-S/zip v0.2.2 h1:8QeCbIi1Z9U5MgoDARJR1ClbBo9RD46SmVy+dl0woCk=
github.com/STARRY-S/zip v0.2.1/go.mod h1:xNvshLODWtC4EJ702g7cTYn13G53o1+X9BWnPFpcWV4= github.com/STARRY-S/zip v0.2.2/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk=
github.com/alexliesenfeld/health v0.8.0 h1:lCV0i+ZJPTbqP7LfKG7p3qZBl5VhelwUFCIVWl77fgk= github.com/alexliesenfeld/health v0.8.0 h1:lCV0i+ZJPTbqP7LfKG7p3qZBl5VhelwUFCIVWl77fgk=
github.com/alexliesenfeld/health v0.8.0/go.mod h1:TfNP0f+9WQVWMQRzvMUjlws4ceXKEL3WR+6Hp95HUFc= github.com/alexliesenfeld/health v0.8.0/go.mod h1:TfNP0f+9WQVWMQRzvMUjlws4ceXKEL3WR+6Hp95HUFc=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
@@ -38,10 +38,10 @@ github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chromedp/cdproto v0.0.0-20250203011601-a3c71a042730 h1:IEa+Va47x06CJQaLKFoce5iPTRRR5uI/GbeZbxdnYdc= github.com/chromedp/cdproto v0.0.0-20250311215558-29dfcc2791de h1:tOKSCbB420VENW1Wz10EnSXr4jLnWoq25vnBW4ScmF0=
github.com/chromedp/cdproto v0.0.0-20250203011601-a3c71a042730/go.mod h1:RTGuBeCeabAJGi3OZf71a6cGa7oYBfBP75VJZFLv6SU= github.com/chromedp/cdproto v0.0.0-20250311215558-29dfcc2791de/go.mod h1:NItd7aLkcfOA/dcMXvl8p1u+lQqioRMq/SqDp71Pb/k=
github.com/chromedp/chromedp v0.12.1 h1:kBMblXk7xH5/6j3K9uk8d7/c+fzXWiUsCsPte0VMwOA= github.com/chromedp/chromedp v0.13.1 h1:FDh9CfaAt0w70gl69Hb69M/xgZrWuppH9AW22aGa+iU=
github.com/chromedp/chromedp v0.12.1/go.mod h1:F6+wdq9LKFDMoyxhq46ZLz4VLXrsrCAR3sFqJz4Nqc0= github.com/chromedp/chromedp v0.13.1/go.mod h1:O3nO4Lno7iLoVX+7GdqQkehhKG7DtLf/zFRyJo0AhXY=
github.com/chromedp/sysutil v1.1.0 h1:PUFNv5EcprjqXZD9nJb9b/c9ibAbxiYo4exNWZyipwM= github.com/chromedp/sysutil v1.1.0 h1:PUFNv5EcprjqXZD9nJb9b/c9ibAbxiYo4exNWZyipwM=
github.com/chromedp/sysutil v1.1.0/go.mod h1:WiThHUdltqCNKGc4gaU50XgYjwjYIhKWoHGPTUfWTJ8= github.com/chromedp/sysutil v1.1.0/go.mod h1:WiThHUdltqCNKGc4gaU50XgYjwjYIhKWoHGPTUfWTJ8=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
@@ -51,8 +51,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4= github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4=
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
@@ -64,6 +64,8 @@ github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-json-experiment/json v0.0.0-20250223041408-d3c622f1b874 h1:F8d1AJ6M9UQCavhwmO6ZsrYLfG8zVFWfEfMS2MXPkSY=
github.com/go-json-experiment/json v0.0.0-20250223041408-d3c622f1b874/go.mod h1:TiCD2a1pcmjd7YnhGH0f/zKNcCD06B029pHhzV23c2M=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
@@ -92,8 +94,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@@ -121,14 +123,12 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
@@ -143,10 +143,8 @@ github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0= github.com/lufia/plan9stats v0.0.0-20250303091104-876f3ea5145d h1:fjMbDVUGsMQiVZnSQsmouYJvMdwsGiDipOZoN66v844=
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= github.com/lufia/plan9stats v0.0.0-20250303091104-876f3ea5145d/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
@@ -157,8 +155,8 @@ github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwX
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/nwaples/rardecode/v2 v2.1.0 h1:JQl9ZoBPDy+nIZGb1mx8+anfHp/LV3NE2MjMiv0ct/U= github.com/nwaples/rardecode/v2 v2.1.1 h1:OJaYalXdliBUXPmC8CZGQ7oZDxzX1/5mQmgn0/GASew=
github.com/nwaples/rardecode/v2 v2.1.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw= github.com/nwaples/rardecode/v2 v2.1.1/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
@@ -167,21 +165,21 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs= github.com/shirou/gopsutil/v4 v4.25.2 h1:NMscG3l2CqtWFS86kj3vP7soOczqrQYIEhO/pMvvQkk=
github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI= github.com/shirou/gopsutil/v4 v4.25.2/go.mod h1:34gBYJzyqCDT11b6bMHP0XCvWeU3J61XRT7a2EmCRTA=
github.com/sorairolake/lzip-go v0.3.5 h1:ms5Xri9o1JBIWvOFAorYtUNik6HI3HgBTkISiqu0Cwg= github.com/sorairolake/lzip-go v0.3.5 h1:ms5Xri9o1JBIWvOFAorYtUNik6HI3HgBTkISiqu0Cwg=
github.com/sorairolake/lzip-go v0.3.5/go.mod h1:N0KYq5iWrMXI0ZEXKXaS9hCyOjZUQdBDEIbXfoUwbdk= github.com/sorairolake/lzip-go v0.3.5/go.mod h1:N0KYq5iWrMXI0ZEXKXaS9hCyOjZUQdBDEIbXfoUwbdk=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
@@ -199,10 +197,10 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY= github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU= github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY= github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
github.com/tklauser/numcpus v0.9.0 h1:lmyCHtANi8aRUgkckBgoDk1nHCux3n2cgkJLXdQGPDo= github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
github.com/tklauser/numcpus v0.9.0/go.mod h1:SN6Nq1O3VychhC1npsWostA+oW+VOQTxZrS604NSRyI= github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
@@ -232,8 +230,8 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -276,8 +274,8 @@ golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -290,8 +288,8 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -313,13 +311,13 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -327,12 +325,12 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=

View File

@@ -2,6 +2,7 @@ package gotenberg
import ( import (
"runtime" "runtime"
"sort"
"sync" "sync"
flag "github.com/spf13/pflag" flag "github.com/spf13/pflag"
@@ -42,6 +43,8 @@ func BuildDebug(ctx *Context) {
debug.ModulesAdditionalData[ID] = debuggable.Debug() debug.ModulesAdditionalData[ID] = debuggable.Debug()
} }
sort.Sort(AlphanumericSort(debug.Modules))
ctx.ParsedFlags().VisitAll(func(f *flag.Flag) { ctx.ParsedFlags().VisitAll(func(f *flag.Flag) {
debug.Flags[f.Name] = f.Value.String() debug.Flags[f.Name] = f.Value.String()
}) })

View File

@@ -53,8 +53,8 @@ func TestBuildDebug(t *testing.T) {
Version: Version, Version: Version,
Architecture: runtime.GOARCH, Architecture: runtime.GOARCH,
Modules: []string{ Modules: []string{
"foo",
"bar", "bar",
"foo",
}, },
ModulesAdditionalData: map[string]map[string]interface{}{ ModulesAdditionalData: map[string]map[string]interface{}{
"bar": { "bar": {

View File

@@ -3,8 +3,6 @@ package gotenberg
import ( import (
"fmt" "fmt"
"os" "os"
"path/filepath"
"strings"
"github.com/google/uuid" "github.com/google/uuid"
) )
@@ -86,25 +84,6 @@ func (fs *FileSystem) MkdirAll() (string, error) {
return path, nil return path, nil
} }
// WalkDir walks through the root level of a directory and returns a list of
// files paths that match the specified file extension.
func WalkDir(dir, ext string) ([]string, error) {
var files []string
err := filepath.Walk(dir, func(path string, info os.FileInfo, pathErr error) error {
if pathErr != nil {
return pathErr
}
if info.IsDir() {
return nil
}
if strings.EqualFold(filepath.Ext(info.Name()), ext) {
files = append(files, path)
}
return nil
})
return files, err
}
// Interface guards. // Interface guards.
var ( var (
_ MkdirAll = (*OsMkdirAll)(nil) _ MkdirAll = (*OsMkdirAll)(nil)

View File

@@ -6,7 +6,6 @@ import (
"io" "io"
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"strings" "strings"
"testing" "testing"
@@ -145,78 +144,3 @@ func TestFileSystem_MkdirAll(t *testing.T) {
}) })
} }
} }
func TestWalkDir(t *testing.T) {
for _, tc := range []struct {
scenario string
dir string
ext string
expectError bool
expectFiles []string
}{
{
scenario: "directory does not exist",
dir: uuid.NewString(),
ext: ".pdf",
expectError: true,
},
{
scenario: "find PDF files",
dir: func() string {
path := fmt.Sprintf("%s/a_directory", os.TempDir())
err := os.MkdirAll(path, 0o755)
if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err))
}
err = os.WriteFile(fmt.Sprintf("%s/a_foo_file.pdf", path), []byte{1}, 0o755)
if err != nil {
t.Fatalf("expected no error but got: %v", err)
}
err = os.WriteFile(fmt.Sprintf("%s/a_bar_file.PDF", path), []byte{1}, 0o755)
if err != nil {
t.Fatalf("expected no error but got: %v", err)
}
err = os.WriteFile(fmt.Sprintf("%s/a_baz_file.txt", path), []byte{1}, 0o755)
if err != nil {
t.Fatalf("expected no error but got: %v", err)
}
return path
}(),
ext: ".pdf",
expectError: false,
expectFiles: []string{"/tmp/a_directory/a_bar_file.PDF", "/tmp/a_directory/a_foo_file.pdf"},
},
} {
t.Run(tc.scenario, func(t *testing.T) {
defer func() {
err := os.RemoveAll(tc.dir)
if err != nil {
t.Fatalf("expected no error while cleaning up but got: %v", err)
}
}()
files, err := WalkDir(tc.dir, tc.ext)
if !tc.expectError && err != nil {
t.Fatalf("expected no error but got: %v", err)
}
if tc.expectError && err == nil {
t.Fatal("expected error but got none")
}
if tc.expectError && err != nil {
return
}
if !reflect.DeepEqual(files, tc.expectFiles) {
t.Errorf("expected files %+v, but got %+v", tc.expectFiles, files)
}
})
}
}

View File

@@ -31,7 +31,7 @@ func TestGarbageCollect(t *testing.T) {
err := os.MkdirAll(path, 0o755) err := os.MkdirAll(path, 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/a_foo_file", path), []byte{1}, 0o755) err = os.WriteFile(fmt.Sprintf("%s/a_foo_file", path), []byte{1}, 0o755)

View File

@@ -7,7 +7,8 @@ import (
) )
// AlphanumericSort implements sort.Interface and helps to sort strings // AlphanumericSort implements sort.Interface and helps to sort strings
// alphanumerically. // alphanumerically by either a numeric prefix or, if missing, a numeric
// suffix.
// //
// See: https://github.com/gotenberg/gotenberg/issues/805. // See: https://github.com/gotenberg/gotenberg/issues/805.
type AlphanumericSort []string type AlphanumericSort []string
@@ -21,20 +22,20 @@ func (s AlphanumericSort) Swap(i, j int) {
} }
func (s AlphanumericSort) Less(i, j int) bool { func (s AlphanumericSort) Less(i, j int) bool {
numI, restI := extractPrefix(s[i]) numI, restI := extractNumber(s[i])
numJ, restJ := extractPrefix(s[j]) numJ, restJ := extractNumber(s[j])
// Compares numerical prefixes if they exist. // If both strings contain a number, compare them numerically.
if numI != -1 && numJ != -1 { if numI != -1 && numJ != -1 {
if numI != numJ { if numI != numJ {
return numI < numJ return numI < numJ
} }
// If numbers are equal, falls back to string comparison of the rest. // If the numbers are equal, compare the "rest" strings.
return restI < restJ return restI < restJ
} }
// If one has a numerical prefix and the other doesn't, the one with the // If one contains a number and the other doesn't, the one with the number
// number comes first. // comes first.
if numI != -1 { if numI != -1 {
return true return true
} }
@@ -42,28 +43,52 @@ func (s AlphanumericSort) Less(i, j int) bool {
return false return false
} }
// If neither has a numerical prefix, compare as strings // Neither has a number; fall back to lexicographical order.
return s[i] < s[j] return s[i] < s[j]
} }
// extractPrefix attempts to extract a numerical prefix and the rest of the filename // extractNumber attempts to extract a numeric portion from the filename.
func extractPrefix(filename string) (int, string) { // It first checks for a numeric prefix (digits at the beginning).
matches := numPrefixRegexp.FindStringSubmatch(filename) // If none is found, it next attempts to match a number immediately before the
if len(matches) > 2 { // extension (for filenames such as "sample1_1.pdf").
prefix, err := strconv.Atoi(matches[1]) // If that fails, it then attempts a trailing numeric pattern.
if err == nil { // If no number is found, it returns -1 and the original string.
return prefix, matches[2] func extractNumber(str string) (int, string) {
// Check for a numeric prefix.
if matches := prefixRegexp.FindStringSubmatch(str); len(matches) > 2 {
if num, err := strconv.Atoi(matches[1]); err == nil {
return num, matches[2]
} }
} }
// Returns -1 if no numerical prefix is found, indicating to just compare // Check for a number immediately before an extension.
// as strings. if matches := extensionSuffixRegexp.FindStringSubmatch(str); len(matches) > 3 {
return -1, filename if num, err := strconv.Atoi(matches[2]); err == nil {
// Remove the numeric block but keep the extension.
return num, matches[1] + matches[3]
}
}
// Check for a trailing number (with no extension following).
if matches := suffixRegexp.FindStringSubmatch(str); len(matches) > 2 {
if num, err := strconv.Atoi(matches[2]); err == nil {
return num, matches[1]
}
}
// No numeric portion found.
return -1, str
} }
var numPrefixRegexp = regexp.MustCompile(`^(\d+)(.*)$`) // Regular expressions used by extractNumber.
var (
// Matches a numeric prefix: one or more digits at the start.
prefixRegexp = regexp.MustCompile(`^(\d+)(.*)$`)
// Matches a numeric block immediately before a file extension.
extensionSuffixRegexp = regexp.MustCompile(`^(.*?)(\d+)(\.[^.]+)$`)
// Matches a trailing numeric sequence when there is no extension.
suffixRegexp = regexp.MustCompile(`^(.*?)(\d+)$`)
)
// Interface guard. // Interface guard.
var ( var _ sort.Interface = (*AlphanumericSort)(nil)
_ sort.Interface = (*AlphanumericSort)(nil)
)

View File

@@ -17,6 +17,16 @@ func TestAlphanumericSort(t *testing.T) {
values: []string{"10qux.pdf", "2_baz.txt", "2_aza.txt", "1bar.pdf", "Afoo.txt", "Bbar.docx", "25zeta.txt", "3.pdf", "4_foo.pdf"}, values: []string{"10qux.pdf", "2_baz.txt", "2_aza.txt", "1bar.pdf", "Afoo.txt", "Bbar.docx", "25zeta.txt", "3.pdf", "4_foo.pdf"},
expectedSort: []string{"1bar.pdf", "2_aza.txt", "2_baz.txt", "3.pdf", "4_foo.pdf", "10qux.pdf", "25zeta.txt", "Afoo.txt", "Bbar.docx"}, expectedSort: []string{"1bar.pdf", "2_aza.txt", "2_baz.txt", "3.pdf", "4_foo.pdf", "10qux.pdf", "25zeta.txt", "Afoo.txt", "Bbar.docx"},
}, },
{
scenario: "numeric suffixes with extensions",
values: []string{"sample1_10.pdf", "sample1_11.pdf", "sample1_4.pdf", "sample1_3.pdf", "sample1_1.pdf", "sample1_2.pdf"},
expectedSort: []string{"sample1_1.pdf", "sample1_2.pdf", "sample1_3.pdf", "sample1_4.pdf", "sample1_10.pdf", "sample1_11.pdf"},
},
{
scenario: "numeric suffixes",
values: []string{"sample1_10", "sample1_11", "sample1_4", "sample1_3", "sample1_1", "sample1_2"},
expectedSort: []string{"sample1_1", "sample1_2", "sample1_3", "sample1_4", "sample1_10", "sample1_11"},
},
{ {
scenario: "hrtime (PHP library)", scenario: "hrtime (PHP library)",
values: []string{"245654773395259", "245654773395039", "245654773395149", "245654773394919", "245654773394369"}, values: []string{"245654773395259", "245654773395039", "245654773395149", "245654773394919", "245654773394369"},

View File

@@ -329,7 +329,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<iframe src='file:///etc/passwd'></iframe>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<iframe src='file:///etc/passwd'></iframe>"), 0o755)
@@ -361,7 +361,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<iframe src='file:///etc/passwd'></iframe>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<iframe src='file:///etc/passwd'></iframe>"), 0o755)
@@ -393,7 +393,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Skip networkIdle event</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Skip networkIdle event</h1>"), 0o755)
@@ -428,7 +428,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>ErrInvalidHttpStatusCode</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>ErrInvalidHttpStatusCode</h1>"), 0o755)
@@ -461,7 +461,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/style.css", fs.WorkingDirPath()), []byte("body{font-family: Arial, Helvetica, sans-serif;}"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/style.css", fs.WorkingDirPath()), []byte("body{font-family: Arial, Helvetica, sans-serif;}"), 0o755)
@@ -499,7 +499,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<script type=\"application/javascript\">throw new Error(\"Exception\")</script>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<script type=\"application/javascript\">throw new Error(\"Exception\")</script>"), 0o755)
@@ -532,7 +532,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
return fs return fs
@@ -558,7 +558,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"http://localhost:100/style.css\"></head><body><h1>ErrResourceLoadingFailed</h1></body></html>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"http://localhost:100/style.css\"></head><body><h1>ErrResourceLoadingFailed</h1></body></html>"), 0o755)
@@ -592,7 +592,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Clear cache</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Clear cache</h1>"), 0o755)
@@ -625,7 +625,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Clear cookies</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Clear cookies</h1>"), 0o755)
@@ -658,7 +658,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<script type=\"application/javascript\">throw new Error(\"Exception\")</script>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<script type=\"application/javascript\">throw new Error(\"Exception\")</script>"), 0o755)
@@ -692,7 +692,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Set cookies</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Set cookies</h1>"), 0o755)
@@ -727,7 +727,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>User-Agent override</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>User-Agent override</h1>"), 0o755)
@@ -762,7 +762,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Extra HTTP headers</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Extra HTTP headers</h1>"), 0o755)
@@ -816,7 +816,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>ErrOmitBackgroundWithoutPrintBackground</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>ErrOmitBackgroundWithoutPrintBackground</h1>"), 0o755)
@@ -849,7 +849,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Hide default white background</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Hide default white background</h1>"), 0o755)
@@ -885,7 +885,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>ErrInvalidEmulatedMediaType</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>ErrInvalidEmulatedMediaType</h1>"), 0o755)
@@ -918,7 +918,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<style>@media print { #screen { display: none } }</style><p id=\"screen\">Screen media type</p>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<style>@media print { #screen { display: none } }</style><p id=\"screen\">Screen media type</p>"), 0o755)
@@ -953,7 +953,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<script type=\"application/javascript\">await new Promise(r => setTimeout(r, 10000));</script>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<script type=\"application/javascript\">await new Promise(r => setTimeout(r, 10000));</script>"), 0o755)
@@ -988,7 +988,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<script type=\"application/javascript\">await new Promise(r => setTimeout(r, 10000));</script>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<script type=\"application/javascript\">await new Promise(r => setTimeout(r, 10000));</script>"), 0o755)
@@ -1023,7 +1023,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
html := ` html := `
@@ -1069,7 +1069,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>ErrInvalidEvaluationExpression</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>ErrInvalidEvaluationExpression</h1>"), 0o755)
@@ -1104,7 +1104,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
html := ` html := `
@@ -1150,7 +1150,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Custom header and footer</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Custom header and footer</h1>"), 0o755)
@@ -1185,7 +1185,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Custom header and footer</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Custom header and footer</h1>"), 0o755)
@@ -1221,7 +1221,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>ErrInvalidPrinterSettings</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>ErrInvalidPrinterSettings</h1>"), 0o755)
@@ -1259,7 +1259,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>ErrPageRangesSyntaxError</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>ErrPageRangesSyntaxError</h1>"), 0o755)
@@ -1292,7 +1292,7 @@ func TestChromiumBrowser_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Default options</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Default options</h1>"), 0o755)
@@ -1491,7 +1491,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<iframe src='file:///etc/passwd'></iframe>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<iframe src='file:///etc/passwd'></iframe>"), 0o755)
@@ -1523,7 +1523,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<iframe src='file:///etc/passwd'></iframe>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<iframe src='file:///etc/passwd'></iframe>"), 0o755)
@@ -1555,7 +1555,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Skip networkIdle event</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Skip networkIdle event</h1>"), 0o755)
@@ -1590,7 +1590,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>ErrInvalidHttpStatusCode</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>ErrInvalidHttpStatusCode</h1>"), 0o755)
@@ -1623,7 +1623,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/style.css", fs.WorkingDirPath()), []byte("body{font-family: Arial, Helvetica, sans-serif;}"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/style.css", fs.WorkingDirPath()), []byte("body{font-family: Arial, Helvetica, sans-serif;}"), 0o755)
@@ -1661,7 +1661,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<script type=\"application/javascript\">throw new Error(\"Exception\")</script>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<script type=\"application/javascript\">throw new Error(\"Exception\")</script>"), 0o755)
@@ -1694,7 +1694,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
return fs return fs
@@ -1720,7 +1720,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"http://localhost:100/style.css\"></head><body><h1>ErrResourceLoadingFailed</h1></body></html>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"http://localhost:100/style.css\"></head><body><h1>ErrResourceLoadingFailed</h1></body></html>"), 0o755)
@@ -1754,7 +1754,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Clear cache</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Clear cache</h1>"), 0o755)
@@ -1787,7 +1787,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Clear cookies</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Clear cookies</h1>"), 0o755)
@@ -1820,7 +1820,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<script type=\"application/javascript\">throw new Error(\"Exception\")</script>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<script type=\"application/javascript\">throw new Error(\"Exception\")</script>"), 0o755)
@@ -1854,7 +1854,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Set cookies</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Set cookies</h1>"), 0o755)
@@ -1889,7 +1889,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>User-Agent override</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>User-Agent override</h1>"), 0o755)
@@ -1924,7 +1924,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Extra HTTP headers</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Extra HTTP headers</h1>"), 0o755)
@@ -1978,7 +1978,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>ErrOmitBackgroundWithoutPrintBackground</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>ErrOmitBackgroundWithoutPrintBackground</h1>"), 0o755)
@@ -2013,7 +2013,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>ErrInvalidEmulatedMediaType</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>ErrInvalidEmulatedMediaType</h1>"), 0o755)
@@ -2046,7 +2046,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<style>@media print { #screen { display: none } }</style><p id=\"screen\">Screen media type</p>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<style>@media print { #screen { display: none } }</style><p id=\"screen\">Screen media type</p>"), 0o755)
@@ -2081,7 +2081,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<script type=\"application/javascript\">await new Promise(r => setTimeout(r, 10000));</script>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<script type=\"application/javascript\">await new Promise(r => setTimeout(r, 10000));</script>"), 0o755)
@@ -2116,7 +2116,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<script type=\"application/javascript\">await new Promise(r => setTimeout(r, 10000));</script>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<script type=\"application/javascript\">await new Promise(r => setTimeout(r, 10000));</script>"), 0o755)
@@ -2151,7 +2151,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
html := ` html := `
@@ -2197,7 +2197,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>ErrInvalidEvaluationExpression</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>ErrInvalidEvaluationExpression</h1>"), 0o755)
@@ -2232,7 +2232,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
html := ` html := `
@@ -2278,7 +2278,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Default options</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Default options</h1>"), 0o755)
@@ -2321,7 +2321,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Default options</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Default options</h1>"), 0o755)
@@ -2369,7 +2369,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Default options</h1>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("<h1>Default options</h1>"), 0o755)

View File

@@ -1052,7 +1052,7 @@ func TestConvertMarkdownRoute(t *testing.T) {
err := os.MkdirAll(dirPath, 0o755) err := os.MkdirAll(dirPath, 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", dirPath), []byte("<div>{{ toHTML \"markdown.md\" }}</div>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", dirPath), []byte("<div>{{ toHTML \"markdown.md\" }}</div>"), 0o755)
@@ -1080,7 +1080,7 @@ func TestConvertMarkdownRoute(t *testing.T) {
err := os.MkdirAll(dirPath, 0o755) err := os.MkdirAll(dirPath, 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", dirPath), []byte("<div>{{ toHTML \"markdown.md\" }}</div>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", dirPath), []byte("<div>{{ toHTML \"markdown.md\" }}</div>"), 0o755)
@@ -1107,7 +1107,7 @@ func TestConvertMarkdownRoute(t *testing.T) {
err := os.MkdirAll(dirPath, 0o755) err := os.MkdirAll(dirPath, 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", dirPath), []byte("<div>{{ toHTML \"markdown.md\" }}</div>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", dirPath), []byte("<div>{{ toHTML \"markdown.md\" }}</div>"), 0o755)
@@ -1142,7 +1142,7 @@ func TestConvertMarkdownRoute(t *testing.T) {
err := os.MkdirAll(dirPath, 0o755) err := os.MkdirAll(dirPath, 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", dirPath), []byte("<div>{{ toHTML \"markdown.md\" }}</div>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", dirPath), []byte("<div>{{ toHTML \"markdown.md\" }}</div>"), 0o755)
@@ -1259,7 +1259,7 @@ func TestScreenshotMarkdownRoute(t *testing.T) {
err := os.MkdirAll(dirPath, 0o755) err := os.MkdirAll(dirPath, 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", dirPath), []byte("<div>{{ toHTML \"markdown.md\" }}</div>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", dirPath), []byte("<div>{{ toHTML \"markdown.md\" }}</div>"), 0o755)
@@ -1287,7 +1287,7 @@ func TestScreenshotMarkdownRoute(t *testing.T) {
err := os.MkdirAll(dirPath, 0o755) err := os.MkdirAll(dirPath, 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", dirPath), []byte("<div>{{ toHTML \"markdown.md\" }}</div>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", dirPath), []byte("<div>{{ toHTML \"markdown.md\" }}</div>"), 0o755)
@@ -1314,7 +1314,7 @@ func TestScreenshotMarkdownRoute(t *testing.T) {
err := os.MkdirAll(dirPath, 0o755) err := os.MkdirAll(dirPath, 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", dirPath), []byte("<div>{{ toHTML \"markdown.md\" }}</div>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", dirPath), []byte("<div>{{ toHTML \"markdown.md\" }}</div>"), 0o755)
@@ -1349,7 +1349,7 @@ func TestScreenshotMarkdownRoute(t *testing.T) {
err := os.MkdirAll(dirPath, 0o755) err := os.MkdirAll(dirPath, 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/index.html", dirPath), []byte("<div>{{ toHTML \"markdown.md\" }}</div>"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/index.html", dirPath), []byte("<div>{{ toHTML \"markdown.md\" }}</div>"), 0o755)

View File

@@ -62,6 +62,10 @@ type Options struct {
// PageRanges allows to select the pages to convert. // PageRanges allows to select the pages to convert.
PageRanges string PageRanges string
// UpdateIndexes specifies whether to update the indexes before conversion
// or not, with the risk of disabling links in documents.
UpdateIndexes bool
// ExportFormFields specifies whether form fields are exported as widgets // ExportFormFields specifies whether form fields are exported as widgets
// or only their fixed print representation is exported. // or only their fixed print representation is exported.
ExportFormFields bool ExportFormFields bool
@@ -152,6 +156,7 @@ func DefaultOptions() Options {
Password: "", Password: "",
Landscape: false, Landscape: false,
PageRanges: "", PageRanges: "",
UpdateIndexes: true,
ExportFormFields: true, ExportFormFields: true,
AllowDuplicateFieldNames: false, AllowDuplicateFieldNames: false,
ExportBookmarks: true, ExportBookmarks: true,

View File

@@ -279,6 +279,10 @@ func (p *libreOfficeProcess) pdf(ctx context.Context, logger *zap.Logger, inputP
args = append(args, "--export", fmt.Sprintf("PageRange=%s", options.PageRanges)) args = append(args, "--export", fmt.Sprintf("PageRange=%s", options.PageRanges))
} }
if !options.UpdateIndexes {
args = append(args, "--disable-update-indexes")
}
args = append(args, "--export", fmt.Sprintf("ExportFormFields=%t", options.ExportFormFields)) args = append(args, "--export", fmt.Sprintf("ExportFormFields=%t", options.ExportFormFields))
args = append(args, "--export", fmt.Sprintf("AllowDuplicateFieldNames=%t", options.AllowDuplicateFieldNames)) args = append(args, "--export", fmt.Sprintf("AllowDuplicateFieldNames=%t", options.AllowDuplicateFieldNames))
args = append(args, "--export", fmt.Sprintf("ExportBookmarks=%t", options.ExportBookmarks)) args = append(args, "--export", fmt.Sprintf("ExportBookmarks=%t", options.ExportBookmarks))

View File

@@ -265,7 +265,7 @@ func TestLibreOfficeProcess_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/document.txt", fs.WorkingDirPath()), []byte("Context done"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/document.txt", fs.WorkingDirPath()), []byte("Context done"), 0o755)
@@ -295,36 +295,36 @@ func TestLibreOfficeProcess_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
in, err := os.Open("/tests/test/testdata/libreoffice/protected.docx") in, err := os.Open("/tests/test/testdata/libreoffice/protected.docx")
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
defer func() { defer func() {
err := in.Close() err := in.Close()
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
}() }()
out, err := os.Create(fmt.Sprintf("%s/protected.docx", fs.WorkingDirPath())) out, err := os.Create(fmt.Sprintf("%s/protected.docx", fs.WorkingDirPath()))
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
defer func() { defer func() {
err := out.Close() err := out.Close()
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
}() }()
_, err = io.Copy(out, in) _, err = io.Copy(out, in)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
return fs return fs
@@ -348,7 +348,7 @@ func TestLibreOfficeProcess_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/document.txt", fs.WorkingDirPath()), []byte("Context done"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/document.txt", fs.WorkingDirPath()), []byte("Context done"), 0o755)
@@ -376,7 +376,7 @@ func TestLibreOfficeProcess_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/document.txt", fs.WorkingDirPath()), []byte("Success"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/document.txt", fs.WorkingDirPath()), []byte("Success"), 0o755)
@@ -404,7 +404,7 @@ func TestLibreOfficeProcess_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/document.txt", fs.WorkingDirPath()), []byte("Success"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/document.txt", fs.WorkingDirPath()), []byte("Success"), 0o755)
@@ -456,7 +456,7 @@ func TestLibreOfficeProcess_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/document.txt", fs.WorkingDirPath()), []byte("Landscape"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/document.txt", fs.WorkingDirPath()), []byte("Landscape"), 0o755)
@@ -485,7 +485,7 @@ func TestLibreOfficeProcess_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/document.txt", fs.WorkingDirPath()), []byte("Landscape"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/document.txt", fs.WorkingDirPath()), []byte("Landscape"), 0o755)
@@ -514,7 +514,7 @@ func TestLibreOfficeProcess_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/document.txt", fs.WorkingDirPath()), []byte("Landscape"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/document.txt", fs.WorkingDirPath()), []byte("Landscape"), 0o755)
@@ -543,7 +543,7 @@ func TestLibreOfficeProcess_pdf(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/document.txt", fs.WorkingDirPath()), []byte("Landscape"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/document.txt", fs.WorkingDirPath()), []byte("Landscape"), 0o755)
@@ -629,7 +629,7 @@ func TestNonBasicLatinCharactersGuard(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/document.txt", fs.WorkingDirPath()), []byte("Basic latin characters"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/document.txt", fs.WorkingDirPath()), []byte("Basic latin characters"), 0o755)
@@ -650,7 +650,7 @@ func TestNonBasicLatinCharactersGuard(t *testing.T) {
err := os.MkdirAll(fs.WorkingDirPath(), 0o755) err := os.MkdirAll(fs.WorkingDirPath(), 0o755)
if err != nil { if err != nil {
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) t.Fatalf("expected no error but got: %v", err)
} }
err = os.WriteFile(fmt.Sprintf("%s/éèßàùä.txt", fs.WorkingDirPath()), []byte("Non-basic latin characters"), 0o755) err = os.WriteFile(fmt.Sprintf("%s/éèßàùä.txt", fs.WorkingDirPath()), []byte("Non-basic latin characters"), 0o755)

View File

@@ -39,6 +39,7 @@ func convertRoute(libreOffice libreofficeapi.Uno, engine gotenberg.PdfEngine) ap
password string password string
landscape bool landscape bool
nativePageRanges string nativePageRanges string
updateIndexes bool
exportFormFields bool exportFormFields bool
allowDuplicateFieldNames bool allowDuplicateFieldNames bool
exportBookmarks bool exportBookmarks bool
@@ -68,6 +69,7 @@ func convertRoute(libreOffice libreofficeapi.Uno, engine gotenberg.PdfEngine) ap
String("password", &password, defaultOptions.Password). String("password", &password, defaultOptions.Password).
Bool("landscape", &landscape, defaultOptions.Landscape). Bool("landscape", &landscape, defaultOptions.Landscape).
String("nativePageRanges", &nativePageRanges, defaultOptions.PageRanges). String("nativePageRanges", &nativePageRanges, defaultOptions.PageRanges).
Bool("updateIndexes", &updateIndexes, defaultOptions.UpdateIndexes).
Bool("exportFormFields", &exportFormFields, defaultOptions.ExportFormFields). Bool("exportFormFields", &exportFormFields, defaultOptions.ExportFormFields).
Bool("allowDuplicateFieldNames", &allowDuplicateFieldNames, defaultOptions.AllowDuplicateFieldNames). Bool("allowDuplicateFieldNames", &allowDuplicateFieldNames, defaultOptions.AllowDuplicateFieldNames).
Bool("exportBookmarks", &exportBookmarks, defaultOptions.ExportBookmarks). Bool("exportBookmarks", &exportBookmarks, defaultOptions.ExportBookmarks).
@@ -149,6 +151,7 @@ func convertRoute(libreOffice libreofficeapi.Uno, engine gotenberg.PdfEngine) ap
Password: password, Password: password,
Landscape: landscape, Landscape: landscape,
PageRanges: nativePageRanges, PageRanges: nativePageRanges,
UpdateIndexes: updateIndexes,
ExportFormFields: exportFormFields, ExportFormFields: exportFormFields,
AllowDuplicateFieldNames: allowDuplicateFieldNames, AllowDuplicateFieldNames: allowDuplicateFieldNames,
ExportBookmarks: exportBookmarks, ExportBookmarks: exportBookmarks,

View File

@@ -0,0 +1,49 @@
package logging
import (
"fmt"
"go.uber.org/zap/zapcore"
)
// Foreground colors.
// Copy pasted from go.uber.org/zap/internal/color/color.go
const (
black color = iota + 30
red
green
yellow
blue
magenta
cyan
white
)
type color uint8
func (c color) Add(s string) string {
return fmt.Sprintf("\x1b[%dm%s\x1b[0m", uint8(c), s)
}
func levelToColor(l zapcore.Level) color {
switch l {
case zapcore.DebugLevel:
return cyan
case zapcore.InfoLevel:
return blue
case zapcore.WarnLevel:
return yellow
case zapcore.ErrorLevel:
return red
case zapcore.DPanicLevel:
return red
case zapcore.PanicLevel:
return red
case zapcore.FatalLevel:
return red
case zapcore.InvalidLevel:
return red
default:
return red
}
}

View File

@@ -0,0 +1,36 @@
package logging
import "go.uber.org/zap/zapcore"
func gcpSeverity(l zapcore.Level) string {
switch l {
case zapcore.DebugLevel:
return "DEBUG"
case zapcore.InfoLevel:
return "INFO"
case zapcore.WarnLevel:
return "WARNING"
case zapcore.ErrorLevel:
return "ERROR"
case zapcore.DPanicLevel:
return "CRITICAL"
case zapcore.PanicLevel:
return "ALERT"
case zapcore.FatalLevel:
return "EMERGENCY"
case zapcore.InvalidLevel:
return "DEFAULT"
default:
return "DEFAULT"
}
}
func gcpSeverityEncoder(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString(gcpSeverity(l))
}
func gcpSeverityColorEncoder(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
severity := gcpSeverity(l)
c := levelToColor(l)
enc.AppendString(c.Add(severity))
}

View File

@@ -34,9 +34,10 @@ const (
// Logging is a module which implements the [gotenberg.LoggerProvider] // Logging is a module which implements the [gotenberg.LoggerProvider]
// interface. // interface.
type Logging struct { type Logging struct {
level string level string
format string format string
fieldsPrefix string fieldsPrefix string
enableGcpSeverity bool
} }
// Descriptor returns a [Logging]'s module descriptor. // Descriptor returns a [Logging]'s module descriptor.
@@ -48,6 +49,7 @@ func (log *Logging) Descriptor() gotenberg.ModuleDescriptor {
fs.String("log-level", infoLoggingLevel, fmt.Sprintf("Choose the level of logging detail. Options include %s, %s, %s, or %s", errorLoggingLevel, warnLoggingLevel, infoLoggingLevel, debugLoggingLevel)) fs.String("log-level", infoLoggingLevel, fmt.Sprintf("Choose the level of logging detail. Options include %s, %s, %s, or %s", errorLoggingLevel, warnLoggingLevel, infoLoggingLevel, debugLoggingLevel))
fs.String("log-format", autoLoggingFormat, fmt.Sprintf("Specify the format of logging. Options include %s, %s, or %s", autoLoggingFormat, jsonLoggingFormat, textLoggingFormat)) fs.String("log-format", autoLoggingFormat, fmt.Sprintf("Specify the format of logging. Options include %s, %s, or %s", autoLoggingFormat, jsonLoggingFormat, textLoggingFormat))
fs.String("log-fields-prefix", "", "Prepend a specified prefix to each field in the logs") fs.String("log-fields-prefix", "", "Prepend a specified prefix to each field in the logs")
fs.Bool("log-enable-gcp-severity", false, "Enable Google Cloud Platform severity mapping")
return fs return fs
}(), }(),
@@ -62,6 +64,7 @@ func (log *Logging) Provision(ctx *gotenberg.Context) error {
log.level = flags.MustString("log-level") log.level = flags.MustString("log-level")
log.format = flags.MustString("log-format") log.format = flags.MustString("log-format")
log.fieldsPrefix = flags.MustString("log-fields-prefix") log.fieldsPrefix = flags.MustString("log-fields-prefix")
log.enableGcpSeverity = flags.MustBool("log-enable-gcp-severity")
return nil return nil
} }
@@ -101,7 +104,7 @@ func (log *Logging) Logger(mod gotenberg.Module) (*zap.Logger, error) {
return nil, fmt.Errorf("get log level: %w", err) return nil, fmt.Errorf("get log level: %w", err)
} }
encoder, err := newLogEncoder(log.format) encoder, err := newLogEncoder(log.format, log.enableGcpSeverity)
if err != nil { if err != nil {
return nil, fmt.Errorf("get log encoder: %w", err) return nil, fmt.Errorf("get log encoder: %w", err)
} }
@@ -166,7 +169,7 @@ func newLogLevel(level string) (zapcore.Level, error) {
return lvl, nil return lvl, nil
} }
func newLogEncoder(format string) (zapcore.Encoder, error) { func newLogEncoder(format string, gcpSeverity bool) (zapcore.Encoder, error) {
isTerminal := term.IsTerminal(int(os.Stdout.Fd())) isTerminal := term.IsTerminal(int(os.Stdout.Fd()))
encCfg := zap.NewProductionEncoderConfig() encCfg := zap.NewProductionEncoderConfig()
@@ -177,15 +180,25 @@ func newLogEncoder(format string) (zapcore.Encoder, error) {
encoder.AppendString(ts.Local().Format("2006/01/02 15:04:05.000")) encoder.AppendString(ts.Local().Format("2006/01/02 15:04:05.000"))
} }
if format == textLoggingFormat || format == autoLoggingFormat { if format == autoLoggingFormat {
encCfg.EncodeLevel = zapcore.CapitalColorLevelEncoder format = textLoggingFormat
} }
}
if format == autoLoggingFormat && isTerminal { if format == textLoggingFormat && gcpSeverity {
format = textLoggingFormat encCfg.EncodeLevel = gcpSeverityColorEncoder
} else if format == autoLoggingFormat { } else if format == textLoggingFormat {
format = jsonLoggingFormat encCfg.EncodeLevel = zapcore.CapitalColorLevelEncoder
} else if gcpSeverity {
encCfg.EncodeLevel = gcpSeverityEncoder
}
} else {
if format == autoLoggingFormat {
format = jsonLoggingFormat
}
if gcpSeverity {
encCfg.EncodeLevel = gcpSeverityEncoder
}
} }
switch format { switch format {

View File

@@ -1,347 +0,0 @@
package logging
import (
"fmt"
"reflect"
"testing"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"go.uber.org/zap/zaptest/observer"
"github.com/gotenberg/gotenberg/v8/pkg/gotenberg"
)
func TestLogging_Descriptor(t *testing.T) {
descriptor := new(Logging).Descriptor()
actual := reflect.TypeOf(descriptor.New())
expect := reflect.TypeOf(new(Logging))
if actual != expect {
t.Errorf("expected '%s' but got '%s'", expect, actual)
}
}
func TestLogging_Provision(t *testing.T) {
for _, tc := range []struct {
scenario string
level string
format string
fieldsPrefix string
expectLevel string
expectFormat string
expectFieldsPrefix string
}{
{
scenario: "default values",
expectLevel: infoLoggingLevel,
expectFormat: autoLoggingFormat,
expectFieldsPrefix: "",
},
{
scenario: "explicit values",
level: "debug",
format: "json",
fieldsPrefix: "gotenberg",
expectLevel: debugLoggingLevel,
expectFormat: jsonLoggingFormat,
expectFieldsPrefix: "gotenberg",
},
{
scenario: "wrong values", // no validation at this point.
level: "foo",
format: "foo",
expectLevel: "foo",
expectFormat: "foo",
expectFieldsPrefix: "",
},
} {
t.Run(tc.scenario, func(t *testing.T) {
var flags []string
if tc.level != "" {
flags = append(flags, "--log-level", tc.level)
}
if tc.format != "" {
flags = append(flags, "--log-format", tc.format)
}
if tc.fieldsPrefix != "" {
flags = append(flags, "--log-fields-prefix", tc.fieldsPrefix)
}
logging := new(Logging)
fs := logging.Descriptor().FlagSet
err := fs.Parse(flags)
if err != nil {
t.Fatalf("expected no error while parsing flags but got: %v", err)
}
ctx := gotenberg.NewContext(gotenberg.ParsedFlags{FlagSet: fs}, nil)
err = logging.Provision(ctx)
if err != nil {
t.Fatalf("expected no error while provisioning but got: %v", err)
}
if logging.level != tc.expectLevel {
t.Errorf("expected logging level '%s' but got '%s'", tc.expectLevel, logging.level)
}
if logging.format != tc.expectFormat {
t.Errorf("expected logging format '%s' but got '%s'", tc.expectFormat, logging.format)
}
if logging.fieldsPrefix != tc.expectFieldsPrefix {
t.Errorf("expected logging fields prefix '%s' but got '%s'", tc.expectFieldsPrefix, logging.fieldsPrefix)
}
})
}
}
func TestLogging_Validate(t *testing.T) {
for _, tc := range []struct {
scenario string
level string
format string
expectError bool
}{
{
scenario: "invalid level",
level: "foo",
expectError: true,
},
{
scenario: "invalid format",
level: debugLoggingLevel,
format: "foo",
expectError: true,
},
{
scenario: "valid level and format",
level: debugLoggingLevel,
format: autoLoggingFormat,
},
} {
logging := new(Logging)
logging.level = tc.level
logging.format = tc.format
err := logging.Validate()
if tc.expectError && err == nil {
t.Errorf("%s: expected error but got: %v", tc.scenario, err)
}
if !tc.expectError && err != nil {
t.Errorf("%s: expected no error but got: %v", tc.scenario, err)
}
}
}
func TestLogging_Logger(t *testing.T) {
for _, tc := range []struct {
scenario string
level string
format string
fieldsPrefix string
expectError bool
}{
{
scenario: "invalid level",
level: "foo",
expectError: true,
},
{
scenario: "invalid format",
level: debugLoggingLevel,
format: "foo",
expectError: true,
},
{
scenario: "valid level and format",
level: debugLoggingLevel,
format: autoLoggingFormat,
},
} {
t.Run(tc.scenario, func(t *testing.T) {
logging := new(Logging)
logging.level = tc.level
logging.format = tc.format
logging.fieldsPrefix = tc.fieldsPrefix
_, err := logging.Logger(&gotenberg.ModuleMock{
DescriptorMock: func() gotenberg.ModuleDescriptor {
return gotenberg.ModuleDescriptor{ID: "mock", New: nil}
},
})
if tc.expectError && err == nil {
t.Fatal("expected error but got none")
}
if !tc.expectError && err != nil {
t.Fatalf("expected no error but got: %v", err)
}
})
}
}
func TestCustomCore(t *testing.T) {
for _, tc := range []struct {
scenario string
level zapcore.Level
fieldsPrefix string
expectEntry bool
}{
{
scenario: "level enabled",
level: zapcore.DebugLevel,
fieldsPrefix: "gotenberg",
expectEntry: true,
},
{
scenario: "no fields prefix",
level: zapcore.DebugLevel,
expectEntry: true,
},
{
scenario: "level disabled",
level: zapcore.ErrorLevel,
},
} {
t.Run(tc.scenario, func(t *testing.T) {
core, obsvr := observer.New(tc.level)
lgr := zap.New(customCore{
Core: core,
fieldsPrefix: tc.fieldsPrefix,
}).With(zap.String("a_field", "a value"))
lgr.Debug("a debug message", zap.String("another_field", "another value"))
entries := obsvr.TakeAll()
if tc.expectEntry && len(entries) == 0 {
t.Fatal("expected an entry")
}
if !tc.expectEntry && len(entries) != 0 {
t.Fatal("expected no entry")
}
var prefix string
if tc.fieldsPrefix != "" {
prefix = tc.fieldsPrefix + "_"
}
for _, entry := range entries {
fields := entry.Context
if len(fields) != 2 {
t.Fatalf("expected 2 fields but got %d", len(fields))
}
if fields[0].Key != fmt.Sprintf("%sa_field", prefix) {
t.Errorf("expected 'gotenberg_a_field' but got '%s'", fields[0].Key)
}
if fields[1].Key != fmt.Sprintf("%sanother_field", prefix) {
t.Errorf("expected 'gotenberg_another_field' but got '%s'", fields[1].Key)
}
}
})
}
}
func Test_newLogLevel(t *testing.T) {
for _, tc := range []struct {
scenario string
level string
expectZapLevel zapcore.Level
expectError bool
}{
{
scenario: "error level",
level: errorLoggingLevel,
expectZapLevel: zapcore.ErrorLevel,
},
{
scenario: "warning level",
level: warnLoggingLevel,
expectZapLevel: zapcore.WarnLevel,
},
{
scenario: "info level",
level: infoLoggingLevel,
expectZapLevel: zapcore.InfoLevel,
},
{
scenario: "debug level",
level: debugLoggingLevel,
expectZapLevel: zapcore.DebugLevel,
},
{
scenario: "invalid level",
level: "foo",
expectZapLevel: zapcore.InvalidLevel,
expectError: true,
},
} {
t.Run(tc.scenario, func(t *testing.T) {
actual, err := newLogLevel(tc.level)
if tc.expectError && err == nil {
t.Fatal("expected error but got none")
}
if !tc.expectError && err != nil {
t.Fatalf("expected no error but got: %v", err)
}
if tc.expectZapLevel != actual {
t.Errorf("expected %d level but got %d", tc.expectZapLevel, actual)
}
})
}
}
func Test_newLogEncoder(t *testing.T) {
for _, tc := range []struct {
scenario string
format string
expectError bool
}{
{
scenario: "auto format",
format: autoLoggingFormat,
},
{
scenario: "text format",
format: textLoggingFormat,
},
{
scenario: "json format",
format: jsonLoggingFormat,
},
{
scenario: "invalid format",
format: "foo",
expectError: true,
},
} {
t.Run(tc.scenario, func(t *testing.T) {
_, err := newLogEncoder(tc.format)
if tc.expectError && err == nil {
t.Fatal("expected error but got none")
}
if !tc.expectError && err != nil {
t.Errorf("expected no error but got: %v", err)
}
})
}
}

View File

@@ -7,6 +7,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"sort"
"strings" "strings"
"syscall" "syscall"
@@ -128,11 +129,25 @@ func (engine *PdfCpu) Split(ctx context.Context, logger *zap.Logger, mode gotenb
return nil, fmt.Errorf("split PDFs with pdfcpu: %w", err) return nil, fmt.Errorf("split PDFs with pdfcpu: %w", err)
} }
outputPaths, err := gotenberg.WalkDir(outputDirPath, ".pdf") var outputPaths []string
err = filepath.Walk(outputDirPath, func(path string, info os.FileInfo, pathErr error) error {
if pathErr != nil {
return pathErr
}
if info.IsDir() {
return nil
}
if strings.EqualFold(filepath.Ext(info.Name()), ".pdf") {
outputPaths = append(outputPaths, path)
}
return nil
})
if err != nil { if err != nil {
return nil, fmt.Errorf("walk directory to find resulting PDFs from split with pdfcpu: %w", err) return nil, fmt.Errorf("walk directory to find resulting PDFs from split with pdfcpu: %w", err)
} }
sort.Sort(gotenberg.AlphanumericSort(outputPaths))
return outputPaths, nil return outputPaths, nil
} }

View File

@@ -230,15 +230,15 @@ func TestPdfCpu_Split(t *testing.T) {
scenario: "success (intervals)", scenario: "success (intervals)",
ctx: context.TODO(), ctx: context.TODO(),
mode: gotenberg.SplitMode{Mode: gotenberg.SplitModeIntervals, Span: "1"}, mode: gotenberg.SplitMode{Mode: gotenberg.SplitModeIntervals, Span: "1"},
inputPath: "/tests/test/testdata/pdfengines/sample1.pdf", inputPath: "/tests/test/testdata/pdfengines/sample4.pdf",
expectError: false, expectError: false,
expectOutputPathsCount: 3, expectOutputPathsCount: 20,
}, },
{ {
scenario: "success (pages)", scenario: "success (pages)",
ctx: context.TODO(), ctx: context.TODO(),
mode: gotenberg.SplitMode{Mode: gotenberg.SplitModePages, Span: "1"}, mode: gotenberg.SplitMode{Mode: gotenberg.SplitModePages, Span: "1"},
inputPath: "/tests/test/testdata/pdfengines/sample1.pdf", inputPath: "/tests/test/testdata/pdfengines/sample4.pdf",
expectError: false, expectError: false,
expectOutputPathsCount: 1, expectOutputPathsCount: 1,
}, },
@@ -246,7 +246,7 @@ func TestPdfCpu_Split(t *testing.T) {
scenario: "success (pages & unify)", scenario: "success (pages & unify)",
ctx: context.TODO(), ctx: context.TODO(),
mode: gotenberg.SplitMode{Mode: gotenberg.SplitModePages, Span: "1-2", Unify: true}, mode: gotenberg.SplitMode{Mode: gotenberg.SplitModePages, Span: "1-2", Unify: true},
inputPath: "/tests/test/testdata/pdfengines/sample1.pdf", inputPath: "/tests/test/testdata/pdfengines/sample4.pdf",
expectError: false, expectError: false,
expectOutputPathsCount: 1, expectOutputPathsCount: 1,
}, },

View File

@@ -74,16 +74,14 @@ func (multi *multiPdfEngines) Split(ctx context.Context, logger *zap.Logger, mod
var err error var err error
var mu sync.Mutex // to safely append errors. var mu sync.Mutex // to safely append errors.
resultChan := make(chan splitResult, len(multi.splitEngines))
for _, engine := range multi.splitEngines { for _, engine := range multi.splitEngines {
resultChan := make(chan splitResult, 1)
go func(engine gotenberg.PdfEngine) { go func(engine gotenberg.PdfEngine) {
outputPaths, err := engine.Split(ctx, logger, mode, inputPath, outputDirPath) outputPaths, err := engine.Split(ctx, logger, mode, inputPath, outputDirPath)
resultChan <- splitResult{outputPaths: outputPaths, err: err} resultChan <- splitResult{outputPaths: outputPaths, err: err}
}(engine) }(engine)
}
for range multi.splitEngines {
select { select {
case result := <-resultChan: case result := <-resultChan:
if result.err != nil { if result.err != nil {
@@ -160,16 +158,14 @@ func (multi *multiPdfEngines) ReadMetadata(ctx context.Context, logger *zap.Logg
var err error var err error
var mu sync.Mutex // to safely append errors. var mu sync.Mutex // to safely append errors.
resultChan := make(chan readMetadataResult, len(multi.readMetadataEngines))
for _, engine := range multi.readMetadataEngines { for _, engine := range multi.readMetadataEngines {
resultChan := make(chan readMetadataResult, 1)
go func(engine gotenberg.PdfEngine) { go func(engine gotenberg.PdfEngine) {
metadata, err := engine.ReadMetadata(ctx, logger, inputPath) metadata, err := engine.ReadMetadata(ctx, logger, inputPath)
resultChan <- readMetadataResult{metadata: metadata, err: err} resultChan <- readMetadataResult{metadata: metadata, err: err}
}(engine) }(engine)
}
for range multi.readMetadataEngines {
select { select {
case result := <-resultChan: case result := <-resultChan:
if result.err != nil { if result.err != nil {

View File

@@ -22,7 +22,8 @@ func init() {
// QPdf abstracts the CLI tool QPDF and implements the [gotenberg.PdfEngine] // QPdf abstracts the CLI tool QPDF and implements the [gotenberg.PdfEngine]
// interface. // interface.
type QPdf struct { type QPdf struct {
binPath string binPath string
globalArgs []string
} }
// Descriptor returns a [QPdf]'s module descriptor. // Descriptor returns a [QPdf]'s module descriptor.
@@ -41,6 +42,8 @@ func (engine *QPdf) Provision(ctx *gotenberg.Context) error {
} }
engine.binPath = binPath engine.binPath = binPath
// Warnings should not cause errors.
engine.globalArgs = []string{"--warning-exit-0"}
return nil return nil
} }
@@ -88,7 +91,10 @@ func (engine *QPdf) Split(ctx context.Context, logger *zap.Logger, mode gotenber
if !mode.Unify { if !mode.Unify {
return nil, fmt.Errorf("split PDFs using mode '%s' without unify with QPDF: %w", mode.Mode, gotenberg.ErrPdfSplitModeNotSupported) return nil, fmt.Errorf("split PDFs using mode '%s' without unify with QPDF: %w", mode.Mode, gotenberg.ErrPdfSplitModeNotSupported)
} }
args = append(args, inputPath, "--pages", ".", mode.Span, "--", outputPath) args = append(args, inputPath)
args = append(args, engine.globalArgs...)
args = append(args, "--pages", ".", mode.Span)
args = append(args, "--", outputPath)
default: default:
return nil, fmt.Errorf("split PDFs using mode '%s' with QPDF: %w", mode.Mode, gotenberg.ErrPdfSplitModeNotSupported) return nil, fmt.Errorf("split PDFs using mode '%s' with QPDF: %w", mode.Mode, gotenberg.ErrPdfSplitModeNotSupported)
} }
@@ -110,6 +116,7 @@ func (engine *QPdf) Split(ctx context.Context, logger *zap.Logger, mode gotenber
func (engine *QPdf) Merge(ctx context.Context, logger *zap.Logger, inputPaths []string, outputPath string) error { func (engine *QPdf) Merge(ctx context.Context, logger *zap.Logger, inputPaths []string, outputPath string) error {
var args []string var args []string
args = append(args, "--empty") args = append(args, "--empty")
args = append(args, engine.globalArgs...)
args = append(args, "--pages") args = append(args, "--pages")
args = append(args, inputPaths...) args = append(args, inputPaths...)
args = append(args, "--", outputPath) args = append(args, "--", outputPath)
@@ -131,10 +138,11 @@ func (engine *QPdf) Merge(ctx context.Context, logger *zap.Logger, inputPaths []
// original annotations. // original annotations.
func (engine *QPdf) Flatten(ctx context.Context, logger *zap.Logger, inputPath string) error { func (engine *QPdf) Flatten(ctx context.Context, logger *zap.Logger, inputPath string) error {
var args []string var args []string
args = append(args, inputPath)
args = append(args, "--generate-appearances") args = append(args, "--generate-appearances")
args = append(args, "--flatten-annotations=all") args = append(args, "--flatten-annotations=all")
args = append(args, "--replace-input") args = append(args, "--replace-input")
args = append(args, inputPath) args = append(args, engine.globalArgs...)
cmd, err := gotenberg.CommandContext(ctx, logger, engine.binPath, args...) cmd, err := gotenberg.CommandContext(ctx, logger, engine.binPath, args...)
if err != nil { if err != nil {

View File

@@ -154,6 +154,15 @@ func TestQPdf_Merge(t *testing.T) {
}, },
expectError: false, expectError: false,
}, },
{
scenario: "success even with warnings",
ctx: context.TODO(),
inputPaths: []string{
"/tests/test/testdata/pdfengines/sample1.pdf",
"/tests/test/testdata/pdfengines/sample5.pdf",
},
expectError: false,
},
} { } {
t.Run(tc.scenario, func(t *testing.T) { t.Run(tc.scenario, func(t *testing.T) {
engine := new(QPdf) engine := new(QPdf)
@@ -236,6 +245,14 @@ func TestQPdf_Split(t *testing.T) {
expectError: false, expectError: false,
expectOutputPathsCount: 1, expectOutputPathsCount: 1,
}, },
{
scenario: "success even with warnings",
ctx: context.TODO(),
mode: gotenberg.SplitMode{Mode: gotenberg.SplitModePages, Span: "1-2", Unify: true},
inputPath: "/tests/test/testdata/pdfengines/sample5.pdf",
expectError: false,
expectOutputPathsCount: 1,
},
} { } {
t.Run(tc.scenario, func(t *testing.T) { t.Run(tc.scenario, func(t *testing.T) {
engine := new(QPdf) engine := new(QPdf)
@@ -304,6 +321,13 @@ func TestQPdf_Flatten(t *testing.T) {
createCopy: true, createCopy: true,
expectError: false, expectError: false,
}, },
{
scenario: "success even with warnings",
ctx: context.TODO(),
inputPath: "/tests/test/testdata/pdfengines/sample5.pdf",
createCopy: true,
expectError: false,
},
} { } {
t.Run(tc.scenario, func(t *testing.T) { t.Run(tc.scenario, func(t *testing.T) {
engine := new(QPdf) engine := new(QPdf)

BIN
test/testdata/pdfengines/sample4.pdf vendored Normal file

Binary file not shown.

BIN
test/testdata/pdfengines/sample5.pdf vendored Normal file

Binary file not shown.