diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 14fc600..818eb89 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -5,13 +5,20 @@ name: Publish # per Docker convention (git tag `v1.0.0` -> docker tag `1.0.0`); `:latest` # auto-attaches via the default `latest=auto` flavor of `type=semver`. # +# Every release is built twice from the same Dockerfile: the default image, and a +# `-fonts` flavor built with `CAMOUFOX_KEEP_SPOOFED_OS_FONTS=1` so the spoofed +# Windows/macOS font bundles stay in the image (+~891MB). The pool spoofs those +# OSes per browser, so rendered output (screenshots, MHTML) from the default image +# shows tofu for any non-Linux fingerprint; the `-fonts` flavor is for callers who +# consume rendered output rather than HTML. +# # Lives separately from publish-nightly.yml (main branch pushes) and # publish-baseline.yml (baseline runtime pushes). `v*-baseline` is # intentionally excluded so a baseline-only release doesn't also push # `:X.Y.Z`/`:latest` — publish-baseline.yml owns that path. # # Tag contract: -# - push `v*` -> :X.Y.Z + :latest +# - push `v*` -> :X.Y.Z + :latest, and :X.Y.Z-fonts + :latest-fonts # - push `v*-baseline` -> nothing here (excluded) # - push `main` -> nothing here (handled by publish-nightly.yml) @@ -37,6 +44,8 @@ jobs: needs: verify strategy: matrix: + platform: [linux/amd64, linux/arm64] + flavor: [default, fonts] include: - platform: linux/amd64 runner: ubuntu-latest @@ -82,8 +91,10 @@ jobs: push: true labels: ${{ steps.meta.outputs.labels }} outputs: type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true - cache-from: type=gha,scope=trawl-release-${{ matrix.platform }} - cache-to: type=gha,scope=trawl-release-${{ matrix.platform }},mode=max + cache-from: type=gha,scope=trawl-release-${{ matrix.flavor }}-${{ matrix.platform }} + cache-to: type=gha,scope=trawl-release-${{ matrix.flavor }}-${{ matrix.platform }},mode=max + build-args: | + CAMOUFOX_KEEP_SPOOFED_OS_FONTS=${{ matrix.flavor == 'fonts' && '1' || '0' }} secrets: | GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} @@ -95,7 +106,7 @@ jobs: - uses: actions/upload-artifact@v7 with: - name: digests-release-${{ env.PLATFORM_PAIR }} + name: digests-release-${{ matrix.flavor }}-${{ env.PLATFORM_PAIR }} path: /tmp/digests/* if-no-files-found: error retention-days: 1 @@ -103,6 +114,9 @@ jobs: merge: runs-on: ubuntu-latest needs: build + strategy: + matrix: + flavor: [default, fonts] permissions: contents: read packages: write @@ -115,7 +129,7 @@ jobs: - uses: actions/download-artifact@v8 with: path: /tmp/digests - pattern: digests-release-* + pattern: digests-release-${{ matrix.flavor }}-* merge-multiple: true - uses: docker/login-action@v4 @@ -130,6 +144,9 @@ jobs: id: meta with: images: ${{ env.IMAGE }} + # The fonts build carries a `-fonts` suffix on every tag, `:latest` included + # (`onlatest=true`), so it never shadows the default image's tags. + flavor: ${{ matrix.flavor == 'fonts' && 'suffix=-fonts,onlatest=true' || '' }} tags: | # Versioned release tag: git tag v1.0.0 → docker tag 1.0.0 (strips the v prefix, # per Docker convention; type=ref would mirror the v through). type=semver also diff --git a/CHANGELOG.md b/CHANGELOG.md index 64456af..847b3e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- Publish a `-fonts` flavor of every release image (`:X.Y.Z-fonts`, `:latest-fonts`) built with `CAMOUFOX_KEEP_SPOOFED_OS_FONTS=1`, so callers who consume `screenshot` output can pull an image whose spoofed Windows/macOS font bundles are present instead of building one. + ## [1.5.0] - 2026-09-04 ### Changed diff --git a/README.md b/README.md index 6055a47..5761d03 100644 --- a/README.md +++ b/README.md @@ -330,24 +330,29 @@ Tier 4: Residential proxy ──── success ──→ cache + return (15–45 | `docker-compose.minimal.yml` | Scraper only, no Redis | | `docker-compose.prod.yml` | Production: `restart: always`, memory limit, healthcheck | -## Docker images (one GHCR package, two tags) +## Docker images (one GHCR package, three tags) -| Image tag | Built from | Runtime | Use case | -| ---------------------------------- | ------------------------------ | ----------------------------- | ---------------------------------------------------------- | -| `ghcr.io/germondai/trawl:latest` | `apps/api/Dockerfile` | Bun 1.4.0 (modern, AVX2) | Default — modern Linux amd64/arm64 | -| `ghcr.io/germondai/trawl:baseline` | `apps/api/Dockerfile.baseline` | Bun 1.4.0 baseline (no AVX2) | Older CPUs / older kernels (Synology NAS, J4125, Atom-era) | +| Image tag | Built from | Runtime | Use case | +| -------------------------------------- | ------------------------------ | ----------------------------- | ---------------------------------------------------------- | +| `ghcr.io/germondai/trawl:latest` | `apps/api/Dockerfile` | Bun 1.4.0 (modern, AVX2) | Default — modern Linux amd64/arm64 | +| `ghcr.io/germondai/trawl:latest-fonts` | `apps/api/Dockerfile` | Bun 1.4.0 (modern, AVX2) | Same image built with `CAMOUFOX_KEEP_SPOOFED_OS_FONTS=1` — for `screenshot` consumers (+~891 MB) | +| `ghcr.io/germondai/trawl:baseline` | `apps/api/Dockerfile.baseline` | Bun 1.4.0 baseline (no AVX2) | Older CPUs / older kernels (Synology NAS, J4125, Atom-era) | -Both tags live on the same `ghcr.io/germondai/trawl` package — they share the registry but use different Dockerfile sources. Pick whichever tag fits your hardware: +All tags live on the same `ghcr.io/germondai/trawl` package — they share the registry but differ in Dockerfile source or build arguments. Pick whichever tag fits your hardware and output: ```yaml # Modern hardware (most users) image: ghcr.io/germondai/trawl:latest +# Rendered output: the pool spoofs Windows/macOS per browser, and the default image +# drops those font bundles, so screenshots of a non-Linux fingerprint render as tofu. +image: ghcr.io/germondai/trawl:latest-fonts + # Older CPUs without AVX2 / Synology / older kernels image: ghcr.io/germondai/trawl:baseline ``` -Synology note: many Synology NAS units (DSM 7.x on J4125 / older hardware) ship kernel 4.4.x, which Bun's modern runtime can't fully handle. Standard Bun requires kernel 5.1+ (5.6+ recommended); the baseline build degrades gracefully down to kernel 3.10. The `:baseline` tag is published for that case — **confirmed working** on a Synology DS920+ (Celeron J4125, DSM 7.3.2, kernel 4.4.302): the container starts cleanly, `/health` reports healthy, and it solves live Cloudflare challenges via `/v1` (see [#1](https://github.com/germondai/trawl/issues/1)). Published by independent GitHub Actions workflows: pushing `v1.5.0` creates `:1.5.0`, `:latest`, `:1.5.0-baseline`, and `:baseline`; pushing `main` creates `:nightly` and `:nightly-`. +Synology note: many Synology NAS units (DSM 7.x on J4125 / older hardware) ship kernel 4.4.x, which Bun's modern runtime can't fully handle. Standard Bun requires kernel 5.1+ (5.6+ recommended); the baseline build degrades gracefully down to kernel 3.10. The `:baseline` tag is published for that case — **confirmed working** on a Synology DS920+ (Celeron J4125, DSM 7.3.2, kernel 4.4.302): the container starts cleanly, `/health` reports healthy, and it solves live Cloudflare challenges via `/v1` (see [#1](https://github.com/germondai/trawl/issues/1)). Published by independent GitHub Actions workflows: pushing `v1.5.0` creates `:1.5.0`, `:latest`, `:1.5.0-fonts`, `:latest-fonts`, `:1.5.0-baseline`, and `:baseline`; pushing `main` creates `:nightly` and `:nightly-`. ## Releases & versioning diff --git a/apps/docs/deployment/standalone.md b/apps/docs/deployment/standalone.md index 6faec6c..b1fa1db 100644 --- a/apps/docs/deployment/standalone.md +++ b/apps/docs/deployment/standalone.md @@ -79,7 +79,9 @@ docker build \ -t trawl . ``` -This adds approximately 891 MB to the image. Other build arguments pin or validate bundled +This adds approximately 891 MB to the image. Every release is also published prebuilt this way as +`ghcr.io/germondai/trawl:-fonts` / `:latest-fonts`, so a `screenshot` consumer does not +need to build locally. Other build arguments pin or validate bundled dependencies and normally should not be overridden: `UBO_VERSION`, `UBO_AMO_FILE_ID`, `UBO_SHA256`, `GEOLITE_CITY_MIN_BYTES`, and (for the baseline image) `BUN_VERSION`.