Commit graph

109 commits

Author SHA1 Message Date
maziggy
8600b46d6c ci: upgrade setuptools in pip-audit jobs to clear PYSEC-2026-3447
The GitHub runner's Python toolcache ships setuptools 79.0.1, which
pip-audit flags for PYSEC-2026-3447 (fixed in 83.0.0), failing the
blocking Backend Security job. A fix version exists, so upgrade
setuptools in the install step rather than --ignore-vuln. Applied to
both ci.yml (blocking) and security.yml (scheduled scan).
2026-07-17 08:59:06 +02:00
maziggy
5d204b5498 fix(repo-stats): plot ghcr pulls on a linear axis so the trend is visible
The ghcr pulls chart used a symlog y axis with a hardcoded tick list, both
copied from github-repo-stats, which builds the rest of the report. Those
settings suit views and clones - small, spiky, frequently zero - but not
container pulls, which sit in a tight band far above zero.

Consequences on the published report: the series (7,946 to 16,160) lives
entirely inside the top decade of the log scale, so a 2x swing rendered as a
14px wobble on a 200px chart and read as a flat line. Of the nine fixed ticks,
six were squashed against the baseline and 50000 fell outside the domain and
never drew, leaving a single usable gridline.

Switch to a linear scale, drop the fixed ticks so Vega derives them from the
actual domain, and format labels with SI prefixes (5k / 10k / 15k). The zero
baseline and the 10% headroom are unchanged, so the axis stays honest; the same
swing now spans 92px and the growth from ~9k to ~15k pulls/day is legible.
2026-07-13 16:06:59 +02:00
maziggy
d29997fa24 Changed .github/workflows/ci.yml 2026-07-09 16:30:06 +02:00
maziggy
2d8b0005cb Housekeeping 2026-07-07 11:11:15 +02:00
maziggy
a48afbb6e7 ci(repo-stats): chart container pulls from ghcr.io alongside clones/stars
GHCR exposes total + 30-day daily-pull counts only in the package page
    HTML (no REST or GraphQL endpoint). jgehrcke/github-repo-stats has no
    notion of container metrics, so post-process the report after it runs.

    New: .github/scripts/ghcr_inject.py
    - Scrapes Total downloads (exact integer from title="N", not the K-rounded
      display) and the 30-day sparkline (rect data-merge-count, data-date).
    - Merges per-day rows into maziggy/bambuddy/ghcr-pulls.csv on gh-pages.
      Fresh window overwrites overlapping dates, so GitHub's late revisions
      to the last 30 days self-correct; days older than 30 stay frozen at
      whatever was captured while still in-window.
    - Patches latest-report/report.html: adds a TOC entry, a Container
      pulls (ghcr.io) section at the top, and a Vega-Lite line+point chart
      whose theme/config is cloned from the existing Total clones chart so
      it inherits the report's look-and-feel.
    - Bracketed by HTML-comment markers so re-runs replace rather than
      stack (jgehrcke regenerates report.html every tick; we re-inject).
    - Hard-fails if either scrape pattern stops matching — silent fallbacks
      would let the chart freeze without notice.

    Workflow: after run-ghrs, checkout source + gh-pages, run the injector,
    commit only if the diff is non-empty. Uses the existing contents: write
    permission; no new secrets.
2026-07-07 10:54:53 +02:00
maziggy
62f45c8c95 ci(repo-stats): clone the github-repo-stats data branch, not gh-pages
The opaque "exit code 1" from actions/checkout was actually masking a
"Remote branch gh-pages not found in upstream origin" — the bambuddy
repo doesn't have a gh-pages branch. jgehrcke/github-repo-stats writes
to a branch called `github-repo-stats` by default, and Pages is wired
to serve from that branch. The path under it (maziggy/bambuddy/latest-
report/report.html) is unchanged.

Switching the clone branch and renaming the local path from `gh-pages/`
to `data/` so the workflow reads correctly. The PAT-in-URL pattern and
the direct git clone (vs actions/checkout) stay — both still wanted so
the real git stderr reaches the log if anything goes wrong.

Verified by cloning `github-repo-stats` locally and running the
injector against its live report.html: clean patch, 30 days extracted,
all anchor markers (TOC, section, script) present.
2026-06-29 13:29:00 +02:00
maziggy
e092c1ea6e ci(repo-stats): swap actions/checkout for direct git clone on gh-pages
The PAT-token swap didn't fix the gh-pages fetch — same opaque "exit
code 1" from actions/checkout@v4 on both attempts of the retry loop,
with no underlying git stderr surfaced. Likely the wildcard-refspec
+ shallow fetch pattern v4 uses combined with something at the runner
side, but the action's swallowed errors make it untriagable from logs.

Replacing the gh-pages checkout with `git clone --branch gh-pages
--depth 1` using the same PAT, embedded in the URL. Direct, explicit,
and if anything goes wrong the real git error reaches the log instead
of "exit code 1". The recorded remote keeps the PAT, so the later
`git push` reuses it — no separate auth setup needed.

Identity config (user.name / user.email) moved into the clone step so
it lives on the freshly cloned repo; dropped the now-duplicate config
calls from commit-and-push.

Source checkout still uses actions/checkout@v4 since it never had a
problem (the default ref is the workflow's own commit, no wildcard
refspec required).
2026-06-29 13:25:23 +02:00
maziggy
9bb402b3bf ci(repo-stats): use jgehrcke's PAT for gh-pages checkout
The default GITHUB_TOKEN failed at `git fetch` for the gh-pages
checkout step with opaque "exit code 1" and no surfaced git stderr,
even with permissions: contents: write set at workflow level.
actions/checkout's retry loop didn't recover.

Switching the gh-pages checkout to secrets.GHRS_GITHUB_API_TOKEN —
the same PAT jgehrcke/github-repo-stats already writes the branch
with one step earlier — keeps the auth chain uniform and avoids the
mismatch. persist-credentials defaults to true, so the subsequent
commit-and-push step in the same gh-pages directory picks up the
PAT automatically; no separate change to the push step needed.

fetch-depth: 1 left explicit because checkout@v4 defaults to it but
the value's load-bearing for this workflow (we only need HEAD of
gh-pages, not history).
2026-06-29 13:21:32 +02:00
maziggy
00534679ae ci(repo-stats): chart container pulls from ghcr.io alongside clones/stars
GHCR exposes total + 30-day daily-pull counts only in the package page
HTML (no REST or GraphQL endpoint). jgehrcke/github-repo-stats has no
notion of container metrics, so post-process the report after it runs.

New: .github/scripts/ghcr_inject.py
- Scrapes Total downloads (exact integer from title="N", not the K-rounded
  display) and the 30-day sparkline (rect data-merge-count, data-date).
- Merges per-day rows into maziggy/bambuddy/ghcr-pulls.csv on gh-pages.
  Fresh window overwrites overlapping dates, so GitHub's late revisions
  to the last 30 days self-correct; days older than 30 stay frozen at
  whatever was captured while still in-window.
- Patches latest-report/report.html: adds a TOC entry, a Container
  pulls (ghcr.io) section at the top, and a Vega-Lite line+point chart
  whose theme/config is cloned from the existing Total clones chart so
  it inherits the report's look-and-feel.
- Bracketed by HTML-comment markers so re-runs replace rather than
  stack (jgehrcke regenerates report.html every tick; we re-inject).
- Hard-fails if either scrape pattern stops matching — silent fallbacks
  would let the chart freeze without notice.

Workflow: after run-ghrs, checkout source + gh-pages, run the injector,
commit only if the diff is non-empty. Uses the existing contents: write
permission; no new secrets.
2026-06-29 13:15:41 +02:00
maziggy
a5bb8a61a1 ci(repo-stats): chart container pulls from ghcr.io alongside clones/stars
GHCR exposes total + 30-day daily-pull counts only in the package page
HTML (no REST or GraphQL endpoint). jgehrcke/github-repo-stats has no
notion of container metrics, so post-process the report after it runs.

New: .github/scripts/ghcr_inject.py
- Scrapes Total downloads (exact integer from title="N", not the K-rounded
  display) and the 30-day sparkline (rect data-merge-count, data-date).
- Merges per-day rows into maziggy/bambuddy/ghcr-pulls.csv on gh-pages.
  Fresh window overwrites overlapping dates, so GitHub's late revisions
  to the last 30 days self-correct; days older than 30 stay frozen at
  whatever was captured while still in-window.
- Patches latest-report/report.html: adds a TOC entry, a Container
  pulls (ghcr.io) section at the top, and a Vega-Lite line+point chart
  whose theme/config is cloned from the existing Total clones chart so
  it inherits the report's look-and-feel.
- Bracketed by HTML-comment markers so re-runs replace rather than
  stack (jgehrcke regenerates report.html every tick; we re-inject).
- Hard-fails if either scrape pattern stops matching — silent fallbacks
  would let the chart freeze without notice.

Workflow: after run-ghrs, checkout source + gh-pages, run the injector,
commit only if the diff is non-empty. Uses the existing contents: write
permission; no new secrets.
2026-06-29 13:15:07 +02:00
maziggy
5a2a9f3578 Updated .github/workflows/cleanup-ghcr.yml 2026-06-21 10:06:16 +02:00
maziggy
f8cdaf5f0e Updated .github/workflows/cleanup-ghcr.yml 2026-06-21 10:05:37 +02:00
maziggy
f4dfe03a87 feat(windows-installer): native .exe installer pipeline
Brings the Windows installer work from dev to main without merging
  the rest of the 0.2.5b1 release content. Squashes 12 commits from
  dev (8711c54e..7bb11df2) into a single net-effect commit on main.

  Includes:
  - installers/windows/ — Inno Setup .iss script, build.py, vendored
    NSSM 2.24, bambuddy.ico (multi-resolution app icon), service
    install/uninstall .bat files, build pipeline README
  - backend/app/services/network_utils.py — Windows psutil branch so
    the VP bind-IP dropdown enumerates interfaces; Linux/macOS path
    unchanged
  - .github/workflows/windows-installer.yml — reconciles main's
    kludge-pushed copy with dev's accumulated changes (NSSM
    vendoring, version-from-tag, unversioned alias step, etc.)

  CHANGELOG and README entries for the Windows installer stay on
  dev — they reference unreleased 0.2.5b1 release notes that aren't
  on main yet.
2026-06-10 14:11:02 +02:00
maziggy
7bb11df268 feat(installer): unversioned alias on stable/beta tag releases
Adds bambuddy-windows-x64-setup.exe (unversioned) alongside the
  date-stamped bambuddy-<version>-windows-x64-setup.exe for stable
  and beta tag releases. Lets external surfaces (website, wiki,
  newsletters) link to a stable URL that survives version bumps:

    https://github.com/maziggy/bambuddy/releases/latest/download/
      bambuddy-windows-x64-setup.exe

  Daily prereleases are excluded — GitHub's `latest` redirect skips
  prereleases so the alias would add no value there, and an
  unversioned name next to a date-stamped versioned one on a daily
  release page is semantically confusing.
2026-06-10 13:41:00 +02:00
maziggy
ef1c7f578e ci(windows-installer): add explicit permissions block
Address CodeQL actions/missing-workflow-permissions finding. Least-
  privilege at workflow level: contents: write is required by
  softprops/action-gh-release to attach the installer .exe to a tag
  release; all other steps are read-only.
2026-06-10 12:36:44 +02:00
maziggy
28258ec76e ci(windows-installer): add explicit permissions block
Address CodeQL actions/missing-workflow-permissions finding. Least-
  privilege at workflow level: contents: write is required by
  softprops/action-gh-release to attach the installer .exe to a tag
  release; all other steps are read-only.
2026-06-10 12:36:36 +02:00
maziggy
96a5f953f0 ci(windows-installer): drop Inno Setup install step
windows-latest runners ship Inno Setup 6.7.1 pre-installed under the
  same path we already hardcode for ISCC.exe; the choco install was trying
  to downgrade to 6.2.2 and failing on the version mismatch.
2026-06-10 12:20:02 +02:00
maziggy
f327b7ffeb ci(windows-installer): drop Inno Setup install step
windows-latest runners ship Inno Setup 6.7.1 pre-installed under the
  same path we already hardcode for ISCC.exe; the choco install was trying
  to downgrade to 6.2.2 and failing on the version mismatch.
2026-06-10 12:18:01 +02:00
maziggy
45337cd540 ci: add Windows installer workflow 2026-06-10 12:16:31 +02:00
maziggy
8711c54ec3 feat(installer): scaffold Windows installer build pipeline
Lays down the Inno Setup + embedded Python pipeline for producing a
  self-contained Bambuddy Windows installer .exe. The installer ships
  an embedded Python 3.13, the pre-built React bundle, NSSM (service
  supervisor) and ffmpeg — no host Python or Node required on the
  target machine.

  Architecture:
  - Install: C:\Program Files\Bambuddy (admin install, one-time UAC)
  - Data:    C:\ProgramData\Bambuddy\data (preserved on uninstall)
  - Service: registered via NSSM, runs as LocalSystem, autostart on boot
  - UI:      browser at http://localhost:8000 (Start Menu shortcut)

  Files:
  - installers/windows/build.py            stages embedded Python + deps,
                                           frontend bundle, NSSM, ffmpeg
  - installers/windows/bambuddy.iss        Inno Setup compiler script
  - installers/windows/service/*.bat       NSSM register/deregister
  - .github/workflows/windows-installer.yml CI build on tag push + manual
                                           dispatch, uploads .exe artifact

  build.py hard-fails on non-Windows hosts; Wine cross-build is an
  unsupported escape hatch behind --allow-non-windows. v1 ships unsigned
  (SmartScreen warns on first run) — production signing will be wired up
  via SignPath OSS once the application is approved.

  See installers/windows/README.md for build prerequisites and the
  embedded-Python ._pth gotchas.
2026-06-10 12:13:53 +02:00
maziggy
c51a9772cb ci: bump actions to Node-24-compatible majors
GitHub forces Node-20 actions to run on Node 24 starting 2026-06-02 and
  removes Node 20 from the runner on 2026-09-16. Bumping each action to
  its first Node-24 major now gets us ahead of both deadlines and silences
  the deprecation warnings already firing in every CI run.

  Bumps (across ci.yml, security.yml, codeql.yml, auto-label-area.yml,
  issue-closed.yml, stale.yml):
  - actions/checkout         v4 -> v6
  - actions/setup-python     v5 -> v6
  - actions/setup-node       v4 -> v6
  - actions/cache            v4 -> v5
  - actions/upload-artifact  v4 -> v7
  - actions/github-script    v7 -> v9
  - actions/stale            v9 -> v10
  - docker/setup-buildx      v3 -> v4
  - docker/build-push        v5 -> v7

  Verified each major's breaking-change notes against our usage:
  - setup-node v6 limits auto-cache to npm only; we already pass
    cache: 'npm' explicitly, so nothing changes.
  - github-script v9 drops require('@actions/github'); none of our
    scripts use it (only require('fs') and the injected github/context
    globals).
  - setup-buildx v4 removes deprecated inputs; we call it with no
    inputs.
  - build-push v6 enables build summaries by default; informational,
    can disable via DOCKER_BUILD_SUMMARY=false env if it gets noisy.

  codeql-action stays on v4 (already runs on Node 24). Trivy and
  github-repo-stats are Docker actions and aren't affected by the
  Node-20 deprecation.
2026-05-26 12:45:33 +02:00
maziggy
158a3836a1 ci: bump actions to Node-24-compatible majors
GitHub forces Node-20 actions to run on Node 24 starting 2026-06-02 and
  removes Node 20 from the runner on 2026-09-16. Bumping each action to
  its first Node-24 major now gets us ahead of both deadlines and silences
  the deprecation warnings already firing in every CI run.

  Bumps (across ci.yml, security.yml, codeql.yml, auto-label-area.yml,
  issue-closed.yml, stale.yml):
  - actions/checkout         v4 -> v6
  - actions/setup-python     v5 -> v6
  - actions/setup-node       v4 -> v6
  - actions/cache            v4 -> v5
  - actions/upload-artifact  v4 -> v7
  - actions/github-script    v7 -> v9
  - actions/stale            v9 -> v10
  - docker/setup-buildx      v3 -> v4
  - docker/build-push        v5 -> v7

  Verified each major's breaking-change notes against our usage:
  - setup-node v6 limits auto-cache to npm only; we already pass
    cache: 'npm' explicitly, so nothing changes.
  - github-script v9 drops require('@actions/github'); none of our
    scripts use it (only require('fs') and the injected github/context
    globals).
  - setup-buildx v4 removes deprecated inputs; we call it with no
    inputs.
  - build-push v6 enables build summaries by default; informational,
    can disable via DOCKER_BUILD_SUMMARY=false env if it gets noisy.

  codeql-action stays on v4 (already runs on Node 24). Trivy and
  github-repo-stats are Docker actions and aren't affected by the
  Node-20 deprecation.
2026-05-26 12:45:04 +02:00
maziggy
5d248e16ba chore(triage): tighten bug-report template + add Area dropdown to cut invalid-issue load
170 issues have been closed with the `invalid` label (61 of them in
  the last 30 days alone — ~1 in 5 of all closed issues), almost always
  because the reporter hadn't run the in-app Connection Diagnostic or
  checked the documented troubleshooting page. The Connection Diagnostic
  shipped weeks ago but the bug-report form let people skip it: the
  "I ran it" checkbox was `required: false` and the Support Package
  field was optional. Tighten both.

  Form changes (.github/ISSUE_TEMPLATE/bug_report.yml):

  - Connection Diagnostic checkbox: required: false → true
  - Support Package field: required: false → true ("drag the .zip
    or explain why you cannot attach one")
  - New required textarea "Troubleshooting steps already taken" —
    forces the reporter to type WHAT they tried and WHICH wiki pages
    they checked before submitting. Empty answers can't submit.
  - Pre-form intro spells out the search → wiki → diagnostic →
    support package sequence and cites the 1-in-5 stat
  - Final-checks list grew from one to three required confirmations
    (searched issues + checked troubleshooting wiki + ran Connection
    Diagnostic for any connection/printing/camera issue)

  Bug categorization (the gap that motivated this):

  - Old `Component` dropdown was Bambuddy / SpoolBuddy / Both — no
    area triage signal
  - Replaced with two required dropdowns:
    - Product: Bambuddy / SpoolBuddy
    - Area: 15 options covering the actual feature surface +
      Other / not sure
  - Auto-label workflow (.github/workflows/auto-label-area.yml)
    reads the Area dropdown from the rendered issue body on
    open/edit and applies the matching area:* label. Tolerant of
    CRLF and the _No response_ placeholder, won't re-add on edit
    re-fires, warns on unknown Area values

  Maintainer hand-off — labels must exist BEFORE the workflow runs,
  since github-script's addLabels throws on missing labels. Create the
  16 labels (15 area:* + 1 area:unsorted) once via the `gh label create`
  commands captured in CHANGELOG / commit context.

  OS dropdown left untouched (Docker stays — per Martin).

  Printer Model dropdown verified against backend/app/utils/printer_models.py
  PRINTER_MODEL_MAP: all 13 current models present (X1 Carbon, X1, X1E,
  X2D, P1S, P1P, P2S, A1, A1 Mini, H2D, H2D Pro, H2C, H2S).
2026-05-24 16:20:47 +02:00
maziggy
e5ebab7ab8 chore(triage): tighten bug-report template + add Area dropdown to cut invalid-issue load
170 issues have been closed with the `invalid` label (61 of them in
  the last 30 days alone — ~1 in 5 of all closed issues), almost always
  because the reporter hadn't run the in-app Connection Diagnostic or
  checked the documented troubleshooting page. The Connection Diagnostic
  shipped weeks ago but the bug-report form let people skip it: the
  "I ran it" checkbox was `required: false` and the Support Package
  field was optional. Tighten both.

  Form changes (.github/ISSUE_TEMPLATE/bug_report.yml):

  - Connection Diagnostic checkbox: required: false → true
  - Support Package field: required: false → true ("drag the .zip
    or explain why you cannot attach one")
  - New required textarea "Troubleshooting steps already taken" —
    forces the reporter to type WHAT they tried and WHICH wiki pages
    they checked before submitting. Empty answers can't submit.
  - Pre-form intro spells out the search → wiki → diagnostic →
    support package sequence and cites the 1-in-5 stat
  - Final-checks list grew from one to three required confirmations
    (searched issues + checked troubleshooting wiki + ran Connection
    Diagnostic for any connection/printing/camera issue)

  Bug categorization (the gap that motivated this):

  - Old `Component` dropdown was Bambuddy / SpoolBuddy / Both — no
    area triage signal
  - Replaced with two required dropdowns:
    - Product: Bambuddy / SpoolBuddy
    - Area: 15 options covering the actual feature surface +
      Other / not sure
  - Auto-label workflow (.github/workflows/auto-label-area.yml)
    reads the Area dropdown from the rendered issue body on
    open/edit and applies the matching area:* label. Tolerant of
    CRLF and the _No response_ placeholder, won't re-add on edit
    re-fires, warns on unknown Area values

  Maintainer hand-off — labels must exist BEFORE the workflow runs,
  since github-script's addLabels throws on missing labels. Create the
  16 labels (15 area:* + 1 area:unsorted) once via the `gh label create`
  commands captured in CHANGELOG / commit context.

  OS dropdown left untouched (Docker stays — per Martin).

  Printer Model dropdown verified against backend/app/utils/printer_models.py
  PRINTER_MODEL_MAP: all 13 current models present (X1 Carbon, X1, X1E,
  X2D, P1S, P1P, P2S, A1, A1 Mini, H2D, H2D Pro, H2C, H2S).
2026-05-24 16:20:14 +02:00
maziggy
12d344cbfc ci(docker): full backend suite in Docker, 4-way matrix shard, GHA cache backend
Earlier patch trimmed the duplicate unit-test re-run from docker-test
  to drop a 5-10 min job that wasn't adding coverage. But "wasn't adding
  coverage" only holds for pure-logic tests — system-touching tests
  (ffmpeg version probes, ftp clients, subprocess shell-outs, locale/
  timezone-sensitive assertions, paths) genuinely can pass on the GHA
  host and fail in python:3.13-slim. Curation via a `docker_env`
  marker is fragile (new tests get forgotten); gating on `main` only
  defers the cost without removing it.

  Instead, run the full backend suite IN Docker on every PR but make
  it fast:

  - New docker-backend-tests job runs the same 4-way pytest-split
    matrix as the host backend-tests, just inside the test image.
  - docker/setup-buildx-action + docker/build-push-action@v5 with
    cache-from/cache-to: type=gha,scope=backend-test persist the
    BuildKit cache (pip-install layer included) across CI runs and
    across the 4 sibling shards. Cold build is ~150s/shard; warm
    build drops to ~10s/shard.
  - fail-fast: false so a single failing shard surfaces the rest's
    output too.

  Total CI wall-clock for a PR push is now gated by docker-test (the
  image-build + integration HTTP smoke + integration test suite job)
  at ~3 min, not by the unit-test re-run anymore.

  The earlier ci.yml step that ran `docker compose run --rm
  backend-test` synchronously in the docker-test job stays removed —
  the new docker-backend-tests matrix covers the same ground and is
  much faster.
2026-05-24 13:32:11 +02:00
maziggy
b3b37e8f08 ci(docker): full backend suite in Docker, 4-way matrix shard, GHA cache backend
Earlier patch trimmed the duplicate unit-test re-run from docker-test
  to drop a 5-10 min job that wasn't adding coverage. But "wasn't adding
  coverage" only holds for pure-logic tests — system-touching tests
  (ffmpeg version probes, ftp clients, subprocess shell-outs, locale/
  timezone-sensitive assertions, paths) genuinely can pass on the GHA
  host and fail in python:3.13-slim. Curation via a `docker_env`
  marker is fragile (new tests get forgotten); gating on `main` only
  defers the cost without removing it.

  Instead, run the full backend suite IN Docker on every PR but make
  it fast:

  - New docker-backend-tests job runs the same 4-way pytest-split
    matrix as the host backend-tests, just inside the test image.
  - docker/setup-buildx-action + docker/build-push-action@v5 with
    cache-from/cache-to: type=gha,scope=backend-test persist the
    BuildKit cache (pip-install layer included) across CI runs and
    across the 4 sibling shards. Cold build is ~150s/shard; warm
    build drops to ~10s/shard.
  - fail-fast: false so a single failing shard surfaces the rest's
    output too.

  Total CI wall-clock for a PR push is now gated by docker-test (the
  image-build + integration HTTP smoke + integration test suite job)
  at ~3 min, not by the unit-test re-run anymore.

  The earlier ci.yml step that ran `docker compose run --rm
  backend-test` synchronously in the docker-test job stays removed —
  the new docker-backend-tests matrix covers the same ground and is
  much faster.
2026-05-24 13:31:50 +02:00
maziggy
a4afc9c073 ci(docker): stop re-running unit tests inside the test image
The "Docker Build" job in ci.yml was running the same 5287 backend
  tests + 2022 frontend tests inside the bambuddy-backend-test /
  bambuddy-frontend-test images that the host-side backend-tests and
  frontend-tests jobs had already run. Same test code, same Python
  version (env.PYTHON_VERSION), same requirements.txt the test image
  installs. On 2-vCPU GHA runners that re-run added 5-10 min of
  wall-clock for zero new coverage — and "frontend tests in Docker"
  added another 2-3 min for the same reason.

  Drop both steps from the CI job. Keep everything that validates the
  Docker IMAGE specifically: production image build, backend module
  import verification, static-files-copied check, integration
  container bring-up + health/API/static HTTP smoke checks, and the
  integration test suite (which IS genuinely Docker-specific — it
  runs against the live container via BAMBUDDY_TEST_URL).

  test_docker.sh keeps the unit-test reruns because devs running it
  locally don't have a separate host-side pytest job to compare
  against.

  Combined with the earlier 4-way pytest-split shard on the host
  backend-tests job, expected PR-push wall-clock drops from
  ~10-12 min to ~3 min, gated on max(backend-tests shard, frontend
  tests, docker-image-build+integration).
2026-05-24 13:20:11 +02:00
maziggy
ed905d8406 ci(docker): stop re-running unit tests inside the test image
The "Docker Build" job in ci.yml was running the same 5287 backend
  tests + 2022 frontend tests inside the bambuddy-backend-test /
  bambuddy-frontend-test images that the host-side backend-tests and
  frontend-tests jobs had already run. Same test code, same Python
  version (env.PYTHON_VERSION), same requirements.txt the test image
  installs. On 2-vCPU GHA runners that re-run added 5-10 min of
  wall-clock for zero new coverage — and "frontend tests in Docker"
  added another 2-3 min for the same reason.

  Drop both steps from the CI job. Keep everything that validates the
  Docker IMAGE specifically: production image build, backend module
  import verification, static-files-copied check, integration
  container bring-up + health/API/static HTTP smoke checks, and the
  integration test suite (which IS genuinely Docker-specific — it
  runs against the live container via BAMBUDDY_TEST_URL).

  test_docker.sh keeps the unit-test reruns because devs running it
  locally don't have a separate host-side pytest job to compare
  against.

  Combined with the earlier 4-way pytest-split shard on the host
  backend-tests job, expected PR-push wall-clock drops from
  ~10-12 min to ~3 min, gated on max(backend-tests shard, frontend
  tests, docker-image-build+integration).
2026-05-24 13:19:55 +02:00
maziggy
ca08f1f340 fix(test): stop sys.modules-deleting backend.app.main in test_code_quality
+ ci: shard backend tests 4-way + drop -v for ~3.5x wall-clock speedup

  Root cause of the 4 CI failures on PR #1514 (all in
  test_print_start_assigns_printer_id_to_vp_archive.py +
  test_timelapse_baseline_restart_recovery.py): test_all_modules_importable
  in test_code_quality.py was deleting backend.app.main from sys.modules
  and re-importing it via importlib.import_module. That created NEW
  module-level dicts (_timelapse_baselines, _expected_prints,
  _active_prints, …) and re-ran root_logger.addHandler — hence the
  duplicate log lines at the same microsecond in captured stderr.

  Any sibling test that bound those names via "from backend.app.main
  import _timelapse_baselines" before the reimport now held a reference
  to the OLD dict; production code (reached via "from backend.app.main
  import on_print_start") resolved the symbol through the NEW module
  instance. Production mutated the new dict, the test read the old one,
  the assertion saw None / un-mutated mock_archive.

  Locally with -n 30, xdist load-balanced test_code_quality.py to a
  different worker process so the collision never happened (which is
  why the suite was green for me). CI's -n auto = -n 2 on ubuntu-latest
  made the collision deterministic.

  Fix: drop the "del sys.modules[name]" step. importlib.import_module
  already returns the cached module if cached, or runs the import
  machinery if not — either way, any import-time error surfaces. The
  "fresh import" framing was theatre; in practice every module in the
  list is already imported by other tests/fixtures before this test
  runs, so we were never actually getting a fresh import anyway — just
  destruction.

  CI workflow tightening (separate concern, same PR since both touch
  the test infrastructure):

  - Dropped -v from the pytest invocation. 5300+ "PASSED foo::bar"
    lines per worker were eating ~30-60s of stdout I/O on 2-vCPU
    runners. --tb=short is sufficient for failure context.
  - Sharded backend-tests into a 4-way matrix via pytest-split (new
    dev dep). Each shard runs ~1326 tests in ~95s on a 2-vCPU runner;
    all 4 run in parallel so wall-clock drops from 362s -> ~100s.
  - fail-fast: false on the matrix so a single failing shard doesn't
    hide failures in the other three — PRs see the complete failure
    picture in one push.
2026-05-24 12:51:24 +02:00
maziggy
4fac9ff12c fix(test): stop sys.modules-deleting backend.app.main in test_code_quality
+ ci: shard backend tests 4-way + drop -v for ~3.5x wall-clock speedup

  Root cause of the 4 CI failures on PR #1514 (all in
  test_print_start_assigns_printer_id_to_vp_archive.py +
  test_timelapse_baseline_restart_recovery.py): test_all_modules_importable
  in test_code_quality.py was deleting backend.app.main from sys.modules
  and re-importing it via importlib.import_module. That created NEW
  module-level dicts (_timelapse_baselines, _expected_prints,
  _active_prints, …) and re-ran root_logger.addHandler — hence the
  duplicate log lines at the same microsecond in captured stderr.

  Any sibling test that bound those names via "from backend.app.main
  import _timelapse_baselines" before the reimport now held a reference
  to the OLD dict; production code (reached via "from backend.app.main
  import on_print_start") resolved the symbol through the NEW module
  instance. Production mutated the new dict, the test read the old one,
  the assertion saw None / un-mutated mock_archive.

  Locally with -n 30, xdist load-balanced test_code_quality.py to a
  different worker process so the collision never happened (which is
  why the suite was green for me). CI's -n auto = -n 2 on ubuntu-latest
  made the collision deterministic.

  Fix: drop the "del sys.modules[name]" step. importlib.import_module
  already returns the cached module if cached, or runs the import
  machinery if not — either way, any import-time error surfaces. The
  "fresh import" framing was theatre; in practice every module in the
  list is already imported by other tests/fixtures before this test
  runs, so we were never actually getting a fresh import anyway — just
  destruction.

  CI workflow tightening (separate concern, same PR since both touch
  the test infrastructure):

  - Dropped -v from the pytest invocation. 5300+ "PASSED foo::bar"
    lines per worker were eating ~30-60s of stdout I/O on 2-vCPU
    runners. --tb=short is sufficient for failure context.
  - Sharded backend-tests into a 4-way matrix via pytest-split (new
    dev dep). Each shard runs ~1326 tests in ~95s on a 2-vCPU runner;
    all 4 run in parallel so wall-clock drops from 362s -> ~100s.
  - fail-fast: false on the matrix so a single failing shard doesn't
    hide failures in the other three — PRs see the complete failure
    picture in one push.
2026-05-24 12:50:35 +02:00
maziggy
be669a7aea Updated .github/ISSUE_TEMPLATE/bug_report.yml and .github/ISSUE_TEMPLATE/config.yml 2026-05-21 11:01:53 +02:00
maziggy
76e327f4a1 feat: connection diagnostic for "printer won't connect" triage
A triage review of the last 200 closed issues found ~1/3 were
  user-side setup errors — printer not in LAN developer mode, blocked
  ports, Docker bridge networking, wrong access code, cross-subnet —
  each costing a multi-round-trip support exchange.

  Add a Connection Diagnostic that runs those checks automatically:
  - backend/app/services/printer_diagnostic.py: TCP probes of MQTT
    8883 / FTPS 990 / RTSPS 322, LAN developer mode, Docker network
    mode, printer/host subnet match, MQTT credential class; each
    check returns pass/fail/warn/skip with a localized fix.
  - Routes: GET /printers/{id}/diagnostic (saved printer) and
    POST /printers/diagnostic (pre-save Add-Printer flow).
  - ConnectionDiagnostic.tsx: modal + shared checklist, surfaced from
    the printer card actions menu, an offline-printer quick button,
    the Add-Printer dialog, and a new System-page section.
  - The in-app bug reporter scans configured printers when the form
    opens and always shows the result inline — a healthy confirmation,
    or the detected problem and its fix.
  - config.yml troubleshooting link repointed to the rendered wiki
    page; bug_report.yml gains a diagnostic checkbox.

  Diagnostic strings translated across all 8 locales. Backend service
  unit tests (15) + frontend modal tests (3). Ruff clean, frontend
  build clean, i18n parity green.
2026-05-21 11:00:59 +02:00
maziggy
93118b9c3e chore(ci): also ignore disputed PyJWT CVE-2025-45768 in ci.yml
security.yml had this ignore added in 9d440beb but ci.yml runs its
  own pip-audit step with a separate ignore list. CI was still failing
  on main + dev. Reasoning identical to the security.yml comment —
  disputed by PyJWT maintainers, no fix exists, Bambuddy uses
  secrets.token_urlsafe(64) and rejects short secrets.
2026-05-20 13:22:20 +02:00
maziggy
2b8887c4d5 chore(ci): also ignore disputed PyJWT CVE-2025-45768 in ci.yml
security.yml had this ignore added in 9d440beb but ci.yml runs its
  own pip-audit step with a separate ignore list. CI was still failing
  on main + dev. Reasoning identical to the security.yml comment —
  disputed by PyJWT maintainers, no fix exists, Bambuddy uses
  secrets.token_urlsafe(64) and rejects short secrets.
2026-05-20 13:21:59 +02:00
maziggy
fdd20e49b3 chore(security): bump idna >=3.15 (CVE-2026-45409) + ignore disputed PyJWT advisory
- requirements.txt: pin idna>=3.15 to clear ReDoS in idna.encode() on
    crafted Unicode payloads. Transitive via anyio/httpx/requests/yarl,
    so the explicit floor stops a future downstream loosening from
    silently downgrading us.
  - security.yml: permanently --ignore-vuln CVE-2025-45768 (PyJWT). The
    advisory is disputed by the maintainers — "key length is chosen by
    the application" — and no fix version exists. Bambuddy is safe:
    auto-generates secrets via secrets.token_urlsafe(64) and rejects
    file-loaded secrets shorter than 32 chars (auth.py:177, :184).
  - security.yml: drop the stale Pygments --ignore-vuln CVE-2026-4539.
    Pygments has been patched upstream; the ignore no longer matches
    anything.
2026-05-20 12:56:22 +02:00
maziggy
9d440beb80 chore(security): bump idna >=3.15 (CVE-2026-45409) + ignore disputed PyJWT advisory
- requirements.txt: pin idna>=3.15 to clear ReDoS in idna.encode() on
    crafted Unicode payloads. Transitive via anyio/httpx/requests/yarl,
    so the explicit floor stops a future downstream loosening from
    silently downgrading us.
  - security.yml: permanently --ignore-vuln CVE-2025-45768 (PyJWT). The
    advisory is disputed by the maintainers — "key length is chosen by
    the application" — and no fix version exists. Bambuddy is safe:
    auto-generates secrets via secrets.token_urlsafe(64) and rejects
    file-loaded secrets shorter than 32 chars (auth.py:177, :184).
  - security.yml: drop the stale Pygments --ignore-vuln CVE-2026-4539.
    Pygments has been patched upstream; the ignore no longer matches
    anything.
2026-05-20 12:36:32 +02:00
maziggy
46468c9602 Updated .github/workflows/cleanup-ghcr.yml 2026-05-01 11:49:46 +02:00
maziggy
44e4b6b5d7 Workflow: .github/workflows/cleanup-ghcr.yml runs Sundays at 03:00 UTC across both packages, with a manual workflow_dispatch and a dry_run toggle. It builds the live-digest
set the same way we just did, so it won't ever delete a digest still referenced by a tagged manifest.
2026-05-01 09:41:01 +02:00
maziggy
f45f6a131d Updated Github issue template 2026-04-29 13:08:16 +02:00
maziggy
62f2e616a0 Changed CI 2026-04-23 08:57:15 +02:00
maziggy
a4c7d6d5cf Updated issue template 2026-04-20 15:10:59 +02:00
maziggy
a58ab8b8db Updated .github/workflows/ci.yml 2026-04-12 15:05:28 +02:00
maziggy
4c27ee680b docs: require companion docs PRs for user-visible changes 2026-04-10 15:20:02 +02:00
maziggy
d29688000f Updated CI 2026-04-07 17:16:48 +02:00
maziggy
0b0ab23052 Added stats CI 2026-04-07 16:19:42 +02:00
maziggy
240b6cb408 Added stats CI 2026-04-07 16:11:43 +02:00
maziggy
fd140e3c64 Added stats CI 2026-04-07 16:08:54 +02:00
maziggy
39a7898e52 Added Spoolbuddy options to Github issue template 2026-03-30 12:46:43 +02:00
maziggy
7841d2f997 Housekeeping 2026-03-26 14:20:11 +01:00
maziggy
ba991702e5 Housekeeping 2026-03-26 14:08:10 +01:00