Commit Graph
21 Commits
Author SHA1 Message Date
dependabot[bot]andGitHub 647c642773 chore(deps): bump the actions group across 1 directory with 8 updates
Bumps the actions group with 8 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [actions/checkout](https://github.com/actions/checkout) | `7.0.0` | `7.0.1` |
| [actions/setup-python](https://github.com/actions/setup-python) | `6.3.0` | `7.0.0` |
| [docker/login-action](https://github.com/docker/login-action) | `4.4.0` | `4.5.2` |
| [github/codeql-action/init](https://github.com/github/codeql-action) | `4.37.0` | `4.37.3` |
| [github/codeql-action/autobuild](https://github.com/github/codeql-action) | `4.37.0` | `4.37.3` |
| [github/codeql-action/analyze](https://github.com/github/codeql-action) | `4.37.0` | `4.37.3` |
| [github/codeql-action/upload-sarif](https://github.com/github/codeql-action) | `4.37.0` | `4.37.3` |
| [actions/setup-node](https://github.com/actions/setup-node) | `4.4.0` | `7.0.0` |



Updates `actions/checkout` from 7.0.0 to 7.0.1
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0...3d3c42e5aac5ba805825da76410c181273ba90b1)

Updates `actions/setup-python` from 6.3.0 to 7.0.0
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/ece7cb06caefa5fff74198d8649806c4678c61a1...5fda3b95a4ea91299a34e894583c3862153e4b97)

Updates `docker/login-action` from 4.4.0 to 4.5.2
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/af1e73f918a031802d376d3c8bbc3fe56130a9b0...371161bbe7024a29a25c5e19bfcbc0804fe9ad2c)

Updates `github/codeql-action/init` from 4.37.0 to 4.37.3
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/99df26d4f13ea111d4ec1a7dddef6063f76b97e9...e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81)

Updates `github/codeql-action/autobuild` from 4.37.0 to 4.37.3
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/99df26d4f13ea111d4ec1a7dddef6063f76b97e9...e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81)

Updates `github/codeql-action/analyze` from 4.37.0 to 4.37.3
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/99df26d4f13ea111d4ec1a7dddef6063f76b97e9...e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81)

Updates `github/codeql-action/upload-sarif` from 4.37.0 to 4.37.3
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/99df26d4f13ea111d4ec1a7dddef6063f76b97e9...e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81)

Updates `actions/setup-node` from 4.4.0 to 7.0.0
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/49933ea5288caeca8642d1e84afbd3f7d6820020...820762786026740c76f36085b0efc47a31fe5020)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: actions/setup-node
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: actions/setup-python
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: docker/login-action
  dependency-version: 4.5.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.37.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: github/codeql-action/autobuild
  dependency-version: 4.37.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: github/codeql-action/init
  dependency-version: 4.37.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: github/codeql-action/upload-sarif
  dependency-version: 4.37.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-03 05:41:39 +00:00
SnapOtterandGitHub e815676505 ci(nightly): free disk before the Docker Container E2E build (#700)
The job builds a ~15GB image on three containers and has been terminated mid-run (exit 137, tests passing) on standard runners. Reclaim preinstalled toolchains before building to reduce the resource pressure correlated with the terminations.
2026-07-31 10:59:58 +08:00
SnapOtterandGitHub 734bccb674 ci(nightly): give Docker Container E2E a 90-minute budget (#698)
A from-scratch image build plus the full in-container suite runs ~65 minutes, so the 60-minute budget cancelled the job right before it finished while every test passed. Raise it to 90 minutes.
2026-07-31 09:40:23 +08:00
SnapOtterandGitHub 1b41da7615 ci(nightly): repair five job classes broken by the #649 QA hardening (#695)
Nightly has been red since 07-28; per-PR CI and main are green. Two investigations traced all five failing classes to #649: extended-matrix required AI bundles it never installs (removed), nightly SYSTEM_DEPS drifted from ci.yml (added libreoffice + a doc-binaries composite for pandoc/pdfcpu), the generated-case classifier only skipped ffmpeg (widened to pdfcpu/soffice/pandoc + excluded repo-audit specs from the lean docker image), a settings spec capped loginAttemptLimit and 429-cascaded the serial bucket (restore via API), and type-to-search refused keystrokes under a route announcer's programmatic focus (guard added). A nightly dispatch on the branch confirmed all five classes green.
2026-07-31 02:09:39 +08:00
SnapOtterandGitHub d10d0f544f fix: release QA hardening across processing, media, security, and CI gates (#649)
A release-readiness QA pass over the whole product. The commits split into
defects a user would hit and gates that were reporting green while measuring
nothing.

## Fixes that change behaviour

Rate limiting was bypassable on every install: TRUST_PROXY defaulted to true, so
request.ip came from a client-set header and a forged X-Forwarded-For got past
the login limiter. The default is now a private-network trust list.

A transient Postgres outage stranded in-flight jobs, leaving finished output on
disk with no row pointing at it. A reconciler now resolves those rows and adopts
the bytes rather than dropping the work.

A Redis connection that moved to a new address wedged every read-blocked
consumer, so completions stopped signalling while health still answered 200.
Socket timeouts plus subscriber pings recover it.

Installing more than one AI bundle left the shared venv multi-versioned and
silently broke three tools. The installer now reconciles distributions to one
version each.

Converting an image to JXL at quality 1 through 4 returned a 500, because
libjxl 0.7 rejects the distance those values compute. The quality is floored at
what the encoder honours. A missing ffmpeg was also reported to the user as a
corrupt upload; it now says the engine is unavailable.

RAW uploads reached an unpatched LibRaw on arm64, so it is built from source at
0.22.2, and the release scan was split so it can fail on an unfixed critical
instead of hiding it behind ignore-unfixed.

## Gates that could not fail

Two mutation lanes ran zero mutants because Stryker crawled the gitignored docs
build; coverage discarded its whole report on any failing test; the lint gate
skipped root tests, scripts, and two workspaces; and several generated matrices
counted a host missing ffmpeg as a passing tool. Each now measures what it
claims.

Full evidence and the outstanding release items are tracked locally and are not
part of this branch.
2026-07-27 15:37:30 +08:00
SnapOtterandGitHub 44f5aea326 fix(ci): repair the chronically-failing nightly workflow (#624)
The scheduled Nightly had been red for over a week across nearly every job. This
root-causes and fixes each one. All were pre-existing: missing CI provisioning,
specs that drifted as the app grew, a job too heavy for its timeout, and a fuzz
that was never configured for file-upload endpoints. None came from the recent
security merge.

- Coverage + Docker Container E2E: install tesseract and its language packs so
  the built-in Fast OCR tests stop throwing spawn ENOENT; gate two repo-file and
  release-workflow tests that cannot run inside the slimmed container image.
- E2E (Full, Serial, Cross-Browser, Device Matrix): refresh specs that drifted
  behind the app (tool renames, the now admin-only Tools tab, dropped About copy,
  locator collisions scoped to the right region). One real product fix rode
  along: /config/auth was refetched six times per tool-page load, so cache it
  behind a single shared fetch, dropping the tool page from 13 to 8 API calls.
- Extended Matrix + Fuzz: shard the integration suite four ways so the full
  format x tool matrix plus property fuzz fits its budget instead of overrunning
  the 90-minute ceiling every night.
- Schemathesis: exclude the tools with bespoke handlers that process
  synchronously in-request (they hang the fuzz on adversarial input) and suppress
  Hypothesis's data-generation health checks, which fire because file-upload
  endpoints reject the fuzzer's random bytes. not_a_server_error still runs on
  every generated case (5000+ per run).
- Stabilize two long-tail flakes: raise the avif matrix per-test cap from 240s to
  600s, and assert toHaveCount(0) on the deleted user row so a transient success
  toast no longer trips a strict-mode violation.

Verified end to end: the full Nightly workflow is green on this branch (all 14
jobs), and PR CI is green.
2026-07-24 03:54:50 +08:00
dependabot[bot]andGitHub 624795803a chore(deps): bump the actions group with 2 updates
Bumps the actions group with 2 updates: [actions/cache](https://github.com/actions/cache) and [actions/setup-python](https://github.com/actions/setup-python).


Updates `actions/cache` from 5.0.5 to 6.1.0
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/27d5ce7f107fe9357f9df03efb73ab90386fccae...55cc8345863c7cc4c66a329aec7e433d2d1c52a9)

Updates `actions/setup-python` from 6.2.0 to 6.3.0
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/a309ff8b426b58ec0e2a45f0f869d46889d02405...ece7cb06caefa5fff74198d8649806c4678c61a1)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: actions/setup-python
  dependency-version: 6.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-29 05:36:51 +00:00
SnapOtterandGitHub 63a03d26f2 feat: pipeline templates, analytics opt-out, 83 conversion presets, positioning + e2e modernization
Lands five integrated branches: pipeline templates (#355), analytics opt-out (#354), 83 conversion presets bringing the catalog to 240 tools (#356), self-hosted positioning (#353), and e2e modernization (#351).

Integration fixes: aligned stale web analytics tests with the opt-out/allow-list model, closed 3 CodeQL incomplete-sanitization alerts in the i18n generator, resolved settings/index/docs/format-matrix conflicts, and corrected tool counts to 240.
2026-06-28 18:57:53 +08:00
SnapOtterandGitHub bbad953e79 fix(nightly): de-flake matrix timeouts, exclude browser tool from fuzz (#349)
Systematic round - attack the failure classes, not one bug at a time:

- Flakiness (Coverage/Extended Matrix flipped green<->red): the generated
  format-matrix conversion tests hardcode a 30s/60s per-test timeout that the
  jobs' VITEST_TEST_TIMEOUT can't override. Heavy conversions under coverage
  instrumentation / full matrix intermittently exceed it. Raise to 120s.
- Schemathesis: html-to-image returns 503 when its headless browser isn't
  present (always, in the fuzz env) - same expected-unavailable class as the
  AI 501s. Exclude it from the fuzz (it can't be exercised without a browser).
- Baseline regen: bump timeout 60->120 min so it can finish now that the
  route sweep (#347) lets the visual specs pass instead of failing+retrying.
2026-06-25 00:14:45 +08:00
SnapOtterandGitHub 078743d6b2 fix(nightly): qpdf in test image, NUL-byte settings, AI sub-path fuzz exclude (#348)
Third round - the prior fixes unblocked these deeper failures on the nightly:

- Docker E2E: the patches/ fix (#346) let the build finish, so tests now
  run - and fail with 'spawnSync qpdf ENOENT'. Dockerfile.test installed
  imagemagick/ghostscript/exiftool but never qpdf, which the PDF tools and
  fixture-integrity checks need. Add it.
- NUL-byte 500 (real robustness bug Schemathesis found): a settings string
  containing U+0000 hits the jobs.settings jsonb insert and Postgres rejects
  it ('invalid byte sequence for encoding UTF8: 0x00'), 500ing tools like
  html-to-image. Strip NUL bytes from settings before the insert (NUL is
  never meaningful in tool settings). api typecheck passes.
- Schemathesis: the AI exclude (#346) only anchored on the tool id at the
  path end, so AI sub-endpoints like /passport-photo/analyze were still
  fuzzed and 501'd. Extend the regex to allow an optional sub-path.
2026-06-24 21:59:02 +08:00
SnapOtterandGitHub 3dd0ebb49d test(nightly): fix Schemathesis/Coverage/Docker/fuzz root causes (#346)
Second round of nightly fixes, each root-caused from the post-fix run:

- Docker E2E (real bug): Dockerfile.test never copied patches/, so pnpm
  install hit 'ENOENT patches/gray-matter@4.0.3.patch' and exited 254.
  Copy patches/ like the prod Dockerfile does. (My earlier network-retry
  guess was a misdiagnosis; reverted.)
- fuzz-settings (real bug): the graceful-skip regex matched 'precondition'
  but fast-check v4 says 'pre-condition' (hyphenated), so 3 constrained PDF
  tools (extract/remove/organize-pages) errored instead of skipping. Match
  the hyphen. Verified locally: 3 failed -> 3 passed.
- Schemathesis: AI tool endpoints return 501 FEATURE_NOT_INSTALLED when the
  ML bundle is absent (always, in CI). That is expected, not a server bug,
  and the endpoints cannot be fuzzed without the bundle, so exclude them.
  (The ASCII spec-load fix already landed in #344.)
- Extended Matrix: bump the per-test timeout to 600s; edit-metadata over
  every format still exceeded 300s even at 2 forks.
- Coverage: tests now pass (video-speed + timeout fixes); re-baseline the
  branches/functions thresholds to the measured floor with a written reason.
2026-06-24 20:08:40 +08:00
SnapOtterandGitHub 33dfcecd6a test(nightly): stabilize the exhaustive nightly suite (#345)
Triaged the nightly failures (all pre-existing, unrelated to the analytics
work) and fixed the ones with clear root causes:

- video-speed: a 1s tiny.mp4 sped up 2x rounds to ~0.75s, flaking the +/-25%
  duration assertion under heavy CI load. Use the 8s hero.mp4 (still 44.1kHz)
  so rounding is negligible. Verified locally.
- Extended Matrix + Coverage timeouts: full-matrix / coverage-instrumented runs
  starve the heavy media tests under 4 forks at the 30s default. Make maxForks
  env-overridable (VITEST_MAX_FORKS) and run those jobs with 2 forks + a 300s
  timeout so format-matrix conversions and qr-generate stop timing out.
- Device Matrix visual baselines: the update-visual-baselines workflow could
  not start the app ('failed to create database') because it never provisioned
  Postgres/Redis. Add the same services block the e2e jobs use.
- Docker E2E: a container pnpm install network blip exits 254. Add fetch
  retries + a longer network timeout (frozen-lockfile already passes locally).
- Cross-browser: the home page is the tool catalog now (no dropzone), and the
  tool routes moved to /<section>/<toolId>. Point the upload test at a real
  tool page and fix the stale single-segment routes (/resize -> /image/resize,
  etc.).

The flaky/timeout and cross-browser fixes can only be confirmed by the nightly
(they are load- and browser-specific); a fresh nightly run will verify.
2026-06-24 18:23:23 +08:00
SnapOtterandGitHub 0c8e2cfcde ci: harden testcontainers against Docker Hub registry flakes (#324)
Disable the redundant ryuk reaper (tests/global-setup.ts stops its containers
explicitly and CI runners are ephemeral) and pre-pull postgres/redis with retry,
so a transient Docker Hub 500 -- as hit pulling ryuk on #320's post-merge run --
cannot fail the suite. Covers ci.yml (unit + integration) and nightly.yml.
2026-06-22 22:26:47 +08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
8c2ddf8b3d chore(deps): bump actions/checkout in the actions group (#295)
Bumps the actions group with 1 update: [actions/checkout](https://github.com/actions/checkout).


Updates `actions/checkout` from 6.0.3 to 7.0.0
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/df4cb1c069e1874edd31b4311f1884172cec0e10...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-22 21:42:25 +08:00
SnapOtterandGitHub 3d9ff1e0d2 fix(api): decode RAW via LibRaw first so DNG processes at full resolution (#289) (#290)
RAW (DNG) processing crashed on ImageMagick's deprecated ufraw-batch
delegate, which fails on modern formats such as iPhone ProRAW DNG.

Root cause: the dcraw_emu (LibRaw) decode tier read the wrong output path.
dcraw_emu APPENDS the output extension (raw-in-X.dng -> raw-in-X.dng.tiff)
but the code looked for raw-in-X.tiff (replaced extension), so readFile threw
on every RAW, the tier silently fell through to ufraw, and the 24MB TIFF
leaked into the temp dir on each attempt.

- Repair the dcraw_emu output path; clean it up in finally (fixes the leak)
- Prefer LibRaw full decode over embedded-preview extraction so a
  full-resolution RAW is never silently returned as a reduced-size preview
  (sample DNG: was 1024x683 preview, now 3474x2314 full)
- Add RAW decode regression tests (DNG full-resolution + all 6 RAW formats);
  these were absent, which let the bug ship
- Install libraw-bin on CI test runners so dcraw_emu is actually exercised
2026-06-22 09:54:04 +08:00
SnapOtter e44be61053 test: add real device-emulated mobile and tablet testing (phase 3)
Replace the fake resized-desktop mobile specs with real Playwright device
projects (Pixel 7, iPhone 14, iPad gen 7, Galaxy Tab S9) that exercise
real touch, mobile UA, DPR, and WebKit engine.

Device projects in playwright.config.ts:
- mobile-chromium (Pixel 7, 412x839, Chromium)
- mobile-webkit (iPhone 14, 390x664, WebKit)
- tablet-webkit (iPad gen 7, 810x1080, WebKit)
- tablet-chromium (Galaxy Tab S9, 640x1024, Chromium)

Device specs (16 mobile, 10 tablet):
- Core flow: navigate to tool, upload, process, download
- Responsive chrome: bottom-nav, sidebar hidden, search, overflow
- Touch interactions: before-after slider, crop canvas
- Editor gate: phone asserts "Desktop Recommended" message
- Editor tablet: iPad (810px) renders canvas, Galaxy Tab (640px) shows gate
- SSE visibility-recovery regression guard
- RTL Arabic locale responsive check

Key finding: Galaxy Tab S9 viewport (640px) is below the 768px mobile
breakpoint, so useMobile() returns true and the editor shows the mobile
gate. Only iPad gen 7 (810px) is classified as non-mobile.

Component tests (17 tests, Vitest/jsdom):
- use-mobile hook: breakpoint behavior across all 4 device widths
- mobile-bottom-nav: render, navigation links, settings callback, icons

CI wiring:
- ci.yml: mobile-chromium smoke job (PR gate)
- nightly.yml: full device matrix with webkit
- update-visual-baselines.yml: webkit + device projects for goldens

Parity: 13218 passed, 0 dropped (PARITY OK)
2026-06-20 02:15:50 +08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
72ad63479c chore(deps): bump the actions group with 5 updates (#236)
Bumps the actions group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [actions/checkout](https://github.com/actions/checkout) | `6.0.2` | `6.0.3` |
| [actions/upload-artifact](https://github.com/actions/upload-artifact) | `4.6.2` | `7.0.1` |
| [actions/download-artifact](https://github.com/actions/download-artifact) | `4.3.0` | `8.0.1` |
| [actions/cache](https://github.com/actions/cache) | `4.2.3` | `5.0.5` |
| [github/codeql-action](https://github.com/github/codeql-action) | `4.36.0` | `4.36.2` |


Updates `actions/checkout` from 6.0.2 to 6.0.3
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/de0fac2e4500dabe0009e67214ff5f5447ce83dd...df4cb1c069e1874edd31b4311f1884172cec0e10)

Updates `actions/upload-artifact` from 4.6.2 to 7.0.1
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4.6.2...043fb46d1a93c77aae656e7c1c64a875d1fc6a0a)

Updates `actions/download-artifact` from 4.3.0 to 8.0.1
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v4.3.0...3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c)

Updates `actions/cache` from 4.2.3 to 5.0.5
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/5a3ec84eff668545956fd18022155c47e93e2684...27d5ce7f107fe9357f9df03efb73ab90386fccae)

Updates `github/codeql-action` from 4.36.0 to 4.36.2
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/7211b7c8077ea37d8641b6271f6a365a22a5fbfa...8aad20d150bbac5944a9f9d289da16a4b0d87c1e)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: actions/download-artifact
  dependency-version: 8.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: actions/cache
  dependency-version: 5.0.5
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: github/codeql-action
  dependency-version: 4.36.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 15:02:17 +08:00
SnapOtter ae1337901d feat(tools)!: SnapOtter 2.0 phase 4 wave 1: 45 core tools across all modalities (#219) 2026-06-13 10:18:49 +08:00
SnapOtter c451b939c7 feat(jobs)!: SnapOtter 2.0 phase 2 job spine: async queues, worker pools, object storage, admin dashboard (#217) 2026-06-13 10:17:13 +08:00
SnapOtterandGitHub 1c724d5d21 feat(db)!: SnapOtter 2.0 phase 1 foundation: postgres, migrator, compose stack (#216)
* feat(infra): add dev compose stack with postgres and redis

* fix(infra): comment dev env defaults until wired; harden dev compose restart and start_period

* chore(deps): add pg driver and testcontainers for postgres migration

* feat(db): translate schema to drizzle pg-core (timestamptz, boolean, pgEnum, jsonb)

Schema translation (apps/api/src/db/schema.ts):
- sqlite-core -> pg-core, all 10 tables preserved 1:1
- integer(mode:'timestamp') -> timestamp({ withTimezone: true })
- integer(mode:'boolean') -> boolean
- jobs.status text enum -> pgEnum('job_status') with same 4 values
- 7 columns changed from text to jsonb: jobs.inputFiles, jobs.settings,
  pipelines.steps, apiKeys.permissions, roles.permissions,
  auditLog.details, userFiles.toolChain
- settings.value stays text, jobs.error stays text, jobs.progress stays real

jsonb call-site sweep (removed JSON.stringify on writes, JSON.parse on reads):
- apps/api/src/routes/roles.ts: permissions read/write (3 sites)
- apps/api/src/routes/api-keys.ts: permissions write + read (2 sites)
- apps/api/src/routes/audit-log.ts: details read (1 site)
- apps/api/src/routes/pipeline.ts: steps write + read (2 sites)
- apps/api/src/routes/progress.ts: inputFiles write (2 sites)
- apps/api/src/routes/tool-factory.ts: toolChain read + write (2 sites)
- apps/api/src/routes/user-files.ts: toolChain read + write (4 sites)
- apps/api/src/permissions.ts: roles.permissions read (1 site)
- apps/api/src/lib/audit.ts: details write (1 site)
- apps/api/src/plugins/auth.ts: apiKeys.permissions read (1 site)

* refactor(db): type jsonb columns via $type and note raw CTE conversion requirements

* feat(db): archive sqlite migrations and generate postgres baseline

* chore(db): dockerignore legacy migrations, add archive breadcrumb, fix trailing newline

* feat(db): pg pool connection, advisory-locked boot migrations, DATABASE_URL config

* fix(db): friendly fatal on unreachable postgres, idempotent closeDb, lock-key convention note

* refactor(db): async drizzle calls in plugins, lib, permissions

* fix(api): analytics never throws, typed permission guard, single-query session invalidation

* refactor(db): async drizzle calls across all routes and bootstrap

Convert every route file and index.ts from sync SQLite drizzle
patterns to async node-postgres drizzle:

- .all() removed (bare await on select)
- .get() converted to destructured [row] = await ...
- .run() removed (bare await on insert/update/delete)
- .changes replaced with .rowCount (null-guarded) in progress.ts
- sqlite import removed from user-files.ts; raw CTEs converted to
  await db.execute(sql`...`) with postgres-dialect recursive CTEs
- ChainRow types updated: tool_chain is parsed jsonb (string[] | null),
  created_at is Date (timestamptz) with no * 1000 conversion
- All requirePermission() guard calls awaited (security: unawaited
  async guard returns truthy Promise, bypassing permission check)
- All hasEffectivePermission() and getPermissions() calls awaited
- All auditLog() calls awaited (preserves write-before-response order)
- trackEvent() and captureException() left un-awaited (fire-and-forget
  by design, guaranteed never-throw)
- ensureAnonymousUser(), startCleanupCron(), recoverStaleJobs() awaited
  in bootstrap sequence
- ensureInstanceId() and ensureDefaultSettings() made async

Files converted: 14 (index.ts + 12 route files + tools/index.ts)

* fix(db): await async checkStorageQuota in user-files upload/save routes

* fix(db): await checkStorageQuota in save-result route (missed second call site)

* feat(db): sqlite-to-postgres migrator with CLI and first-boot import

* fix(db): migrator error context, honest force semantics, boot-hook fatal, null-variance tests

* test: run suite against per-file postgres databases via testcontainers

- Add tests/global-setup.ts: spins up a Postgres testcontainer,
  creates a migrated template database once per vitest run.
- Rewrite tests/setup/per-fork-env.ts: each test file (forks pool)
  clones the template into its own database via CREATE DATABASE ...
  TEMPLATE, preserving the same per-file isolation granularity.
- Update vitest.config.ts: add globalSetup, pg alias, update comment.
- Fix tests/integration/test-server.ts: remove DB_PATH mkdir, async
  runMigrations, async db operations, remove SQLite WAL checkpoint.
- Fix 21 unit test db/index mocks: add pool and closeDb exports.
- Fix 8 unit test files: add async/await for now-async permission,
  audit, and analytics functions.
- Fix 18 integration test files: convert sync .run()/.all()/.get()
  to async drizzle patterns, add async to callbacks.
- Production change: apps/api/src/routes/teams.ts: cast COUNT(*)
  to ::int so Postgres returns a number instead of bigint string.

* fix(db): seed built-in roles, reject NUL bytes, cast COUNT, serialize job persists

- Seed built-in roles (admin, editor, user) at boot via ensureBuiltinRoles()
  with onConflictDoNothing, restoring data that legacy SQLite migration 0007
  provided via INSERT statements (the pg baseline is DDL-only).
- Reject NUL bytes in login credentials with 401 (postgres rejects \x00 in
  text columns; valid usernames never contain NUL, matching 1.x behavior).
- Cast COUNT(*)::int in user-files, audit-log, and roles listing queries so
  postgres returns a JS number instead of bigint-as-string.
- Serialize fire-and-forget job progress DB writes per jobId so the final
  "completed" status is never overwritten by a late-arriving "processing"
  write (race condition exposed by async postgres round-trips).

* test: fix teams race, seed roles in test server, poll for job status

- Add missing await to resetTeams() in teams PUT beforeEach (the async
  delete raced with the subsequent insert under postgres).
- Call ensureBuiltinRoles() in test server bootstrap so integration tests
  have the same built-in roles as production.
- Replace fixed 100ms flushPersist delay with a polling helper that waits
  for terminal job status, eliminating timing-dependent failures caused by
  postgres network round-trip latency.

* test: make heic temp-file cleanup assertion resilient to concurrent workers

Use a set-based diff instead of raw file count when checking that
decodeHeic cleans up temp files. Other concurrent test workers can
create heic-in-*/heic-out-* files in the shared tmpdir, inflating the
"after" count and causing spurious failures under full-suite load.

* fix(db): align builtin-role seed to post-0010 legacy state; test polish

* feat(docker): three-container compose (app, postgres, redis) with boot wait and migrations

* fix(docker): set TEST_DATABASE_URL so containerized tests skip testcontainers

* chore(docker): test compose project name, clearer 1.x upgrade comment, unref probe timer

* feat(enterprise): enforce D15 license boundary; move s3 storage into packages/enterprise

* fix(enterprise): restore lazy aws-sdk loading; community installs load no s3 code at boot

* fix(enterprise): boundary check catches dynamic imports; document getS3 concurrency

* feat(db)!: SnapOtter 2.0 phase 1 foundation: postgres, migrator, compose stack

BREAKING CHANGE: SQLite is no longer the runtime database. Deployments now
require Postgres (and Redis, used from phase 2). Existing installs migrate
with SQLITE_MIGRATE_PATH or 'pnpm --filter @snapotter/api migrate:sqlite'.

* fix(ci): postgres service + fresh e2e database per run; ignore unfixable torch CVE-2025-3000
2026-06-13 10:15:23 +08:00
SnapOtterandGitHub 4ec39c556f test: testing overhaul -- CI e2e gates, parallel suites, generated matrices, mutation testing (#215)
Closes the "e2e never runs in CI" hole. Adds per-PR e2e smoke gate,
nightly full-suite workflows, parallel vitest forks (per-fork DBs),
Playwright parallel/serial/visual projects against production builds,
metadata-generated test suites (drift guards, hostile inputs, format
matrix, pairwise settings, property-based fuzz), Stryker mutation
testing, Schemathesis API fuzz, coverage ratchet, and fixes for three
session-poisoning bugs that caused 200+ serial-bucket failures.

Bug fix included: favicon/split/bulk-rename could hang clients forever
when ZIP streaming failed after reply.hijack().
2026-06-10 22:01:13 +08:00