Commit graph pcbjam/.github/workflows/release.yml
Author SHA1 Message Date
Gergő Törcsvári
473e1eb935
3D models from the registry: --models-source flag, staging + prod editor builds
The staging editor was built with neither --models-tag (no models on the
staging CDN) nor VITE_MODELS_SOURCE, so modelsSourceConfig() returned null,
no model3d handler was installed and every 3D ensure answered "(unserved)"
despite the chunked packages3D ingest being complete on the staging registry.

- build-editor.mjs: --models-source <cdn|registry> → VITE_MODELS_SOURCE
- deploy-staging.yml: editor builds with --models-source registry
- release.yml: prod editor builds with --models-source registry (libs/0016 §6
  step 4); MODELS_TAG now only feeds the demo build, which has no closed API.

Prod rollout order: split-lib-kinds + packages3D ingest on prod BEFORE the
next release tag — prod's registry currently has 0 model3d libs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H1iwtBFvQ8CFg966bqSZh2
2026-08-25 13:23:03 +02:00
Istvan Matejcsok
6dfa6ebd15 tests: move screenshot baselines from git to R2 (content-addressed manifest v2)
Baseline PNGs (492, 35 MB) leave git: they now live in the private R2 bucket
pcbjam-ci-screenshots as immutable sha256/<hex>.png objects, pinned by the
committed screenshot-manifest.json (v2: name, engine, sha256, bytes, dims).
tests/baseline-screenshots/ becomes a gitignored cache materialized by the new
`npm run screenshots:fetch` (r2-sync.ts + aws4fetch r2-store.ts).

- promote.ts: requires the RW keypair (shell env or gitignored tests/.env),
  syncs the cache, uploads new hashes BEFORE rewriting the manifest; the
  manifest diff is the only git-visible output. --prune only edits the
  manifest — R2 objects are never deleted, old commits still resolve.
- compare.ts: skips the gate (exit 0, no report.json) when the manifest
  expects baselines but the cache is empty (secretless callers).
- changelog.ts: diffs the manifest between revs and fetches bytes from R2;
  guards against the migration commit (base manifest not v2 → skip).
- gen-manifest --check: v2 schema + resurrection guard (fails if baseline
  PNGs are ever re-committed); credential-free so every caller can gate.
- wasm-build.yml: declares optional read-only S3 secrets, caches + fetches
  baselines before the lint gate; release.yml/deploy-staging.yml pass them.
- screenshot-changelog.yml: triggers on the manifest path instead of PNGs.

All 492 objects are seeded and hash-verified in the bucket; fetch/compare
degrade to a warn-and-skip without credentials.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 09:21:28 +02:00
Viktor Vaczi
e14faeca8b jspi: retire the asyncify pipeline — knob, post-link tail, binaryen hooks
Phase 8 in the parent repo. Deleted: asyncify-scheduler.js, apply-asyncify.sh,
apply-finalize.sh, inject-dyncall-shims.sh, asyncify-imports/removelist.txt,
the wasm-opt/finalize stub pair, scripts/binaryen-hoist-pass/ (the fork stays
a dormant submodule; removal is a follow-up), bench/wasm-opt-bench.sh (README
marked historical), wasm/shims/context_sleep.cpp, and the sched-context
harness app + Makefile targets.

PCBJAM_ASYNC_BACKEND is gone: build-wx-wasm.sh hardcodes the jspi stamp
(still force-cleans pre-migration trees), build-kicad-target.sh gives editors
the JSPI link surface and the CLIs nothing (they pin ASYNCIFY=0), the stub
dance is replaced by an unconditional .real-restore, build-wasm-test.sh lost
its whole post-link loop, docker/build.sh's postprocess is the ENV shim only,
and Makefile.wasm links every app JSPI with the scheduler shim as a tracked
prerequisite. pcbjam_async_policy.h keys on __EMSCRIPTEN__.

jspi-scheduler.js: wxWasmMainLoopPump dropped from the wrap census (the
export died with the D5 detach); inert [TRACE] instrumentation removed.

CI: wasm-build.yml rewritten for the single-cache pipeline (one output cache
keyed on compile inputs; post-processed bytes cached after the shim);
opt_level input removed from both callers. wasm-cache-hash.mjs inputs now
cover patch-env-shim.mjs + jspi-scheduler.js + jspi-exports.txt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NDeBaKKhQztd8KiVtHuyXr
2026-08-13 08:39:12 +02:00
Istvan Matejcsok
1b08a5eb06 feat(editor): report uncaught errors to Better Stack
The editor reported nothing when a session died. Evidence lived only in-tab —
an 800-line React array behind a "Show console" button — so diagnosis meant
asking a user to paste a screenshot.

Better Stack's Error Tracking ingests the Sentry wire protocol, so this runs
the stock @sentry/browser against a Better Stack DSN. Sentry.init installs its
own window error/unhandledrejection handlers, so uncaught main-thread errors
and the wasm traps that escape emscripten's DOM event handlers are captured
with no instrumentation at the throw sites. Not their JS tag: it has no
beforeSend or fingerprint hooks, its runtime spawns workers from cross-origin
CDN hosts (this page is COEP: require-corp), and it ships session replay on by
default — which on a CAD canvas records customers' board geometry.

@sentry/browser is imported in exactly one file so the vendor stays swappable,
mirroring how lib/analytics.ts isolates Plausible.

Also replaces the terminal-signature regex with a shared, unit-tested predicate
(wasm/terminal-error.ts) used by BOTH the fatal overlay and the reporter, so
they cannot disagree. The regex was a type check written as a string match and
had three live holes: `RuntimeError` was listed but never appears IN
`.message`; Chrome's bare "unreachable" and "null function" matched nothing
(the v0.1.20 prod log is exactly those); and narrowing "table index is out of
bounds" to `\bindex out of bounds` for Firefox in 197f317 silently stopped
matching Chrome's spelling. Checking the TYPE — every trap in this family is a
WebAssembly.RuntimeError — covers all engines and ends the spelling chase; the
message patterns remain as a fallback for paths that lose the Error object,
such as a worker ErrorEvent crossing the realm boundary with error: null.
197f317's pthread-worker tap, promote() and Firefox findings are kept as-is.

Notes:
- Off unless VITE_ERRORS_DSN is set AND VITE_ALLOW_USER_OVERRIDE !== "1" (dev
  servers and every Playwright harness set the latter, and production builds
  never do), so a production DSN in a local .env still cannot report. With no
  DSN the whole SDK is const-folded out: 1,193,080 vs 1,282,463 bytes of JS.
- browserApiErrors integration removed. It wraps setTimeout/rAF/addEventListener
  in try/catch, which is exactly how KiCad-on-Emscripten drives its main loop.
- Console breadcrumbs off (collab/debug.ts's clog fires per Yjs update and would
  evict the ring before any crash); dom/fetch/navigation breadcrumbs kept.
- beforeSend redacts token/apiKey/Bearer — collab/provider.ts puts the collab
  token in the y-partyserver URL, so a connection-failure string carries a live
  credential — and guards the cascade: one wedge produced 8 errors in prod, and
  after the first terminal event the rest are dropped into cascade_count.

Verified end to end against the real EU host from a cross-origin-isolated page:
POST /api/<id>/envelope/ -> 200, and 4 terminal throws produce 1 event
(control: 1 throw, same count).

Privacy policy 9, cookie policy 6 and the licenses page are updated: Better
Stack is disclosed as an EU processor, and the licenses page now describes the
browser app's own JS dependencies, which it never did.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 12:24:30 +02:00
Gergő Törcsvári
f8ae378645
perf(deploy): parallelize publish-wasm brotli (q11) across files
brotli q11 over the ~320MB wasm set ran serially on one core (~0.4MB/s),
dominating the release publish job. Compress every to-be-uploaded file
concurrently on the libuv threadpool (compressBytesAsync + UV_THREADPOOL_SIZE
sized to the machine), so wall time drops from sum-of-files to roughly the
largest single file (kicad_editor.wasm): 329s for the full set locally vs
~11.5min of CPU. Upload ordering invariants unchanged: meta.json still last
per tool, registry last overall; moved-tag guard and reuse path untouched.
Also adds BROTLI_PARAM_SIZE_HINT and moves the publish-wasm job to
ubicloud-standard-8 so there is a core per file.

Verified byte-identical CDN layout vs the old script (local driver, pinned
builtAt), blob roundtrip to source sha, reuse + --from-registry modes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQ9npuB6uLvkv6gzBV5GPh
2026-07-28 12:56:10 +02:00
Gergő Törcsvári
abb0923f70
standalone-hardening 0006: redirect non-editor routes to the mgmt app
VITE_APP_URL (build-editor --app-base, set to app.pcbjam.com in release CI)
turns on a redirect gate in App: home, project overview and mgmt-only paths
bounce to the mgmt app preserving path+search; tool boots, file deep-links,
lib editors and the @local pseudo-scope stay local. Unset (dev/demo) keeps
today's behavior. Policy is a pure helper (lib/redirect.ts) + vitest table.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A42xfPFNdfsUt9eowkC9eM
2026-07-16 17:24:37 +02:00
Istvan Matejcsok
40b53ad530 analytics: consolidate everything onto one shared Plausible pa- script
All pcbjam properties now load the same new-format Plausible script
(https://plausible.io/js/pa-KjNS9YmidydULZTstsjRg.js — one dashboard,
segment by hostname) instead of per-domain data-domain sites:

- site: replace Vercel Analytics with the pa- snippet in BaseLayout
  (is:inline, prod-only, crossorigin="anonymous" for the COEP
  require-corp routes in vercel.json); drop @vercel/analytics dep.
  Legal pages (cookies/privacy) rewritten Vercel -> Plausible.
- standalone: initAnalytics() rewritten for the new script format
  (queue shim + plausible.init(), async, no data-domain). The gate is
  now VITE_PLAUSIBLE_SRC (script URL, deploy-time opt-in) — unset means
  no tracking, so dev checkouts and third-party GPL builds stay clean.
  VITE_PLAUSIBLE_DOMAIN is gone.
- deploy: build-demo/build-editor --plausible now takes the script URL;
  release.yml/deploy-demo.yml hardcode the (public) pa- URL, so the
  repo Actions variable PLAUSIBLE_DOMAIN is no longer used.

Verified: standalone tsc + vite build (pa- URL present with env, absent
without, crossOrigin kept); astro build has the snippet on all layout
pages and no vercel/insights references; astro dev stays untracked.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 19:34:09 +02:00
Gergő Törcsvári
9697de7b26
deploy: pass --models-tag in release.yml demo + editor builds
The tag-triggered release pipeline built the demo (and backed editor)
without --models-tag, so modelsSourceConfig() returned null and every
kind=model3d ensure logged "(unserved)" — 3D component models never
loaded on demo.pcbjam.com. The manual deploy-demo.yml already passed
it; mirror its MODELS_TAG env (10.0.3) in release.yml for both builds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UQRBzfidkoTU5yc7HnF9x7
2026-07-10 09:22:52 +02:00
Istvan Matejcsok
d43f8ca719 deploy: Plausible analytics on editor.pcbjam.com (own site)
build-editor.mjs already threads --plausible → VITE_PLAUSIBLE_DOMAIN; the
deploy-editor job now passes editor.pcbjam.com so the backed editor tracks
as its own Plausible site (separate from the demo's vars.PLAUSIBLE_DOMAIN).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014dasZuqo6FStgT3rkC85im
2026-07-08 15:34:53 +02:00
Istvan Matejcsok
a0df92bfd1 deploy: backed editor at editor.pcbjam.com (remote mode, same CDN WASM)
New release.yml job `deploy-editor` (parallel to deploy-demo, same per-tag
WASM manifest): builds the standalone in REMOTE mode via the new
scripts/deploy/build-editor.mjs — projects/libs/auth from the closed API
(VITE_API_BASE_URL=https://api.pcbjam.com, VITE_LIBS_SOURCE=synced), Yjs
board rooms through the API host's path route (VITE_YJS_ENDPOINT=api origin,
doc source ydoc) — and ships it to the pcbjam-editor Pages project. The
demo deploy is unchanged. The closed stack itself (api./app.pcbjam.com)
deploys from pcbjam-private; when both sides change, that repo tags first.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014dasZuqo6FStgT3rkC85im
2026-07-08 11:42:38 +02:00
Gergő Törcsvári
b5ee3da92c
perf(cdn): publish demo WASM as brotli-q11 + no-transform
release.yml's publish-wasm job now uploads .wasm/.js with
`--compress br --quality 11` instead of gzip-6, and IMMUTABLE
content-addressed blobs carry `no-transform` so Cloudflare's edge
can't decompress + re-serve them at its on-the-fly br-4 (and the
original Content-Length passes through).

~15-20% smaller wasm on cdn.pcbjam.com. Forward-only: tool folders
already published stay gzip until their source changes (identity is
hashed over uncompressed bytes, so the compressor switch never
re-versions or re-uploads existing folders).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 12:21:54 +02:00
Gergő Törcsvári
e7640d0a97
build(wasm): standardize asyncify wasm-opt tail to -O1 everywhere
The Binaryen wasm-opt shrink after Asyncify was the only -O2 in the
pipeline: -O2 by default (local + tag release) but -O1 in main CI. The
C++/wx/deps compile is already -O1 everywhere (DEBUG_BUILD defaults to 1;
nothing passes --release in CI/release). Pin the tail to -O1 too:

- apply-asyncify.sh: BINARYEN_OPT_LEVEL default -O2 -> -O1
- release.yml: opt_level -O2 -> -O1 (demo now ships -O1)
- ci-ubicloud.yml / wasm-build.yml / docker/build.sh: refresh stale -O2 comments

Because both callers now build at -O1, the FINAL cache key converges, so a
tag release FINAL-cache-hits main's build and skips the asyncify-tail
rebuild entirely (previously the ~1-2h -O2 wasm-opt was rerun per release).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 12:18:57 +02:00
Gergő Törcsvári
39cb04b127
feat(standalone): demo follow-ups — waitlist, analytics, move-to-local, file tree, load progress
- Waitlist signup form on the home page (between Projects and Tools),
  cross-posting to the landing site's /api/waitlist (CORS + OPTIONS added there,
  gated by WAITLIST_ALLOWED_ORIGINS).
- Version badge gains a pcbjam.com landing-page link (VITE_LANDING_URL).
- Optional Plausible analytics from VITE_PLAUSIBLE_DOMAIN (off by default);
  wired into build-demo + both deploy workflows via vars.PLAUSIBLE_DOMAIN.
- Opening a read-only gallery project auto-"moves to local": it forks into a
  writable browser-local project at the same slug, shadowing the gallery row.
- Project + local-folder views use the same iconed tool launcher (ToolGrid)
  and a navigable directory FileTree instead of a flat file list.
- Boot overlay shows real wasm download progress (Module.instantiateWasm +
  streaming byte counter) and a "taking too long" state after 60s.
- Home Libraries section lists the active libs source (the read-only R2/CDN
  set in the demo) with a name filter; useLibs now reads libsSourceConfig.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPJYx4urFVhdMjEmYfMCMD
2026-06-27 07:59:09 +02:00
Gergő Törcsvári
913bc90fa0
ci: single build recipe (reusable) + ordered tag release pipeline; two-tier opt cache
Fixes two coupled bugs: (1) a tag deployed the demo by snapshotting the STALE registry with no build/publish, so v0.1.1 shipped v0.1.0-era wasm; (2) publish-wasm built -O1/3D-off while ci-ubicloud built -O1/3D-on — two diverging recipes.

wasm-build.yml (reusable workflow_call): THE single build+test recipe, parameterized by opt_level / build_3d_viewer / run_tests / no_cache / upload_output. Two-tier output cache around build.sh's new --compile-only/--postprocess-only split: a BASE cache keyed opt-INDEPENDENTLY (compile output + sysroot headers, shared across -O1/-O2) and a FINAL cache keyed opt-SPECIFICALLY. final-hit ⇒ skip all; final-miss+base-hit ⇒ restore base, run only the asyncify/-O tail; full-miss ⇒ compile then postprocess. Both keys now include the 3D flag, closing the cache-poisoning divergence.

ci-ubicloud.yml: thin caller (main/PR/dispatch) → wasm-build at -O1. release.yml (tag v*): meta → build (wasm-build at -O2 + e2e gate, uploads output, reuses main's base cache so only the -O2 tail rebuilds) → publish-wasm (content-addressed push + manifest-<tag>.json) → deploy-demo (build-demo pinned to that manifest + libs/kicad/$LIB_TAG → Pages). So the shipped build is exactly the tested build, and the demo can never point at stale wasm.

deploy-demo.yml: demoted to manual dispatch-only re-deploy (keeps the --from-registry snapshot for re-shipping an already-published tag). publish-wasm.yml: deleted (folded into release.yml + wasm-build.yml).

Validated: actionlint clean (bar the known ubicloud custom-label note), YAML + needs-graph + reusable-path checks pass. NOT runtime-tested — the build can't run locally; needs a CI smoke (a PR exercises ci-ubicloud→wasm-build; a throwaway tag exercises release.yml) before relying on it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 16:23:33 +02:00