9 Commits
Author SHA1 Message Date
Ruslan KonviserandClaude Opus 5.5 7b0e800893 ci(upgrade-test): the gated boot's ENCRYPTION_KEY has no '/', which would break the log scrub's sed (review)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 03:34:27 +02:00
Ruslan KonviserandClaude Opus 5.5 ac09018a2a ci(upgrade-test): also boot the after image with the gated plugins switched on, so their migrations run
The gated plugins (the Ever Works integration, the Inbox, Inbox email, the email
providers) load, and register their migrations, only when their switch is
exactly 'true'. Every boot of the upgrade test ran with the switches off, so a
new migration of theirs was never applied (it read as "missing" until develop's
image had it) — the Upgrade Test red on develop e0eff61f1d.

Additive: after each upgrade leg (SQLite, PostgreSQL), upgrade-test.sh boots
this branch's image once more on the SAME database with GATED_PLUGIN_FLAGS on
(+ a random ENCRYPTION_KEY): boot (+ demo seed on SQLite), the smoke checks plus
the plugins' routes answering 200 (SMOKE_GET_PATHS: EW status, inbox counts,
inbox providers), no migration error in the boot log, every migration this
branch adds recorded, earlier rows kept, none twice. Its checks are a new
"<db>-gated" column of the summary; every switches-off check is unchanged.
On by default; dispatch with gated_plugins=false to leave it out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 03:23:32 +02:00
Ruslan KonviserandClaude Opus 5.5 57195404ab ci(upgrade-test): develop's API on a fresh database, then this branch's on the same one — SQLite and PostgreSQL
Handover 2026-10-13 §4.T step 2, "upgrade test (demo-critical, never done yet)". A workflow_dispatch workflow (also
run on a pull request that changes it), GitHub-hosted:

- pulls develop's API image (default ghcr.io/ever-co/gauzy-api-demo:latest, the image the demo runs; its
  GAUZY_APP_COMMIT is compared with develop's tip) and builds this branch's .deploy/api/Dockerfile with push: false;
- per database (SQLite = the demo, PostgreSQL 16 = stage/prod): develop's image on a fresh database with the demo
  seed -> super admin login -> stop -> this branch's image on the SAME database (DEMO=true on SQLite, DEMO=false on
  PostgreSQL) -> health, login, GET /api/user/me (must be the admin), GraphQL __typename, a commerce GraphQL list,
  commerce-e2e, commerce-flow-e2e, return-receipt-e2e (GRAPHQL_INTROSPECTION=true for the schema checks);
- TypeORM's migrations table before and after: every migration file this branch adds over the before image's
  commit is recorded, develop's rows kept, none recorded twice; the boot log (up to "Listening at") holds no
  migration error (develop's documented pgvector degradation tolerated);
- optional `control`: this branch alone on a fresh database of each kind;
- one table in the job summary; scrubbed API logs and migration lists as an artifact.

Nothing is pushed; secrets are random per run and masked. Squashed from #10567's review history (Greptile 5/5).
Green on the integration of the fixes it found (#10578, #10586, #10591, #10593, #10599, #10601): run 38556657269 —
SQLite upgrade, PostgreSQL upgrade, fresh SQLite and fresh PostgreSQL all pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 14:19:31 +02:00
Ruslan KonviserandClaude Opus 5.5 a85bcea69e fix: what the AI review of the upgrade found
- Seed cleanup (Greptile): rimraf 3 took the failure callback for its options
  argument and called the success one either way, so a failed cleanup of
  generated files never stopped a seed. With rimraf 6's promise, the seed
  service's `.finally()` left the rejection unhandled (Node ends the process on
  that) and cleanAssets / the feature and report cleaners rejected. All four
  log the failure and go on; seed-cleanup.spec.ts fails on the old code.
- Tray icons: jimp 1's brightness() multiplies each channel by its argument,
  so 0.22's brightness(-0.1) made every *_gray tray icon black. 0.9 is the
  same 90%.
- CI static checks: the jest-config type check installed TypeScript 5.9.3,
  which refuses ignoreDeprecations "6.0" (TS5103); it now uses 6.0.3. Under
  6.0.3 the check found the 75 Node jest configs restating
  `require(jest.patterns).nodeTransformIgnorePatterns` (typed unknown by the
  check's shim). jest.preset.js already picks that variant, so they inherit it
  again, as core's config says they should.
- engines: the 114 workspace manifests still said node >=22; now >=26, as the
  root does.
- The desktop Dockerfiles (no workflow uses them): the Windows ones copy
  vendor/ before the install, the Linux ones build on node:26-trixie-slim
  (libayatana-appindicator3-1 replaces libappindicator3-1, gone after bullseye).
- jest-angular-projects.js declares the arrays it pushes to with `let`
  (repository convention, Greptile).
- jest.patterns.js: `yarn ng test <node project>` runs without the ES-module
  flag (the ng script already sets NODE_OPTIONS, and Nx never overrides a set
  variable with a .env value); documented, with `yarn nx test` as the way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 17:01:39 +02:00
Ruslan KonviserandClaude Opus 5.5 247ede1c98 build(deps): every package to its latest working version, Node 26, and the config the new majors need
Dependencies (root + 132 workspace package.json files, lockfile regenerated and deduplicated):
Node 24 -> 26 (.nvmrc, every Dockerfile stage, CI node-version, engines, @types/node), TypeScript 6.0.3,
Angular/CDK/Material 22.2.2, Nx 23.3.0, NestJS 12.1.3, Electron 44.7.0, Jest 30.5.2, and the rest to latest.

- Nebular has no npm release for Angular 22: vendor/nebular holds 18.0.0 built from the open upstream port
  (akveo/nebular#3319, commit 9f3a1f9; sha256 in the README), referenced as file: dependencies and pinned in
  resolutions. Remove it when Nebular publishes.
- resolutions: one copy each of @nestjs/* 12 (including under the plane-api integration, which still
  declares Nest 11), moment, graphql 16, ioredis 6 and @jridgewell/sourcemap-codec (the Angular schematics
  need encodeRangeMappings).
- TypeScript 6 changed defaults the code was written against; tsconfig.base.json and the Electron configs
  pin strict, esModuleInterop and noUncheckedSideEffectImports to false, and the Electron configs keep
  rootDir ./src (output layout). types: the configs that relied on the automatic @types sweep list theirs.
- Angular migrations rewrote the tsconfigs (strictTemplates off where it was implicit; extendedDiagnostics
  only where strictTemplates is on, NG4003).
- Jest: NestJS 12 is ES modules, so the Node projects run with --experimental-vm-modules and load ES
  modules natively (transforming one to CommonJS breaks it under the flag: uuid 14 and jose 6 threw
  "exports is not defined"); the Angular projects must run without the flag. jest.preset.js picks the
  transform rule from the mode the process runs in; the workspace .env.test sets the flag and each Angular
  project's .env.test clears it; test-unit.yml runs the two groups separately, with the list from
  .github/scripts/jest-angular-projects.js (which also fails when an Angular project lacks its .env.test).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 01:36:29 +02:00
Ruslan KonviserandClaude Opus 5 54ed5ebdb3 fix(ci): the install-once producer breaks on every cache HIT
Two correctness bugs from the AI review of #10010, both confirmed against the
merged code on develop.

1. build.yml - every warm run would fail.

   `Restore node_modules archive` used `lookup-only: true`, so on a cache HIT the
   archive is never written to disk. `Install`, `Run postinstall`, `Archive` and
   `Save` are all gated on `cache-hit != 'true'` and correctly skip - but
   `Upload node_modules archive` carries NO `if:` guard and sets
   `if-no-files-found: error`. On a hit it therefore uploads nothing and fails
   the job, and because the other four jobs `needs:` this one, it takes the whole
   gate with it.

   The cold run that first writes the key passes, so this is invisible until the
   SECOND run - the one the cache exists for. Dropping `lookup-only` makes the
   producer always materialise the archive: a ~2.9 GB download costs a few
   minutes against the ~90-minute install it replaces, and it is what guarantees
   the run-scoped artifact handoff actually has a file to hand off.

2. archive-node-modules.sh - a failed tar could publish a corrupt archive.

   The script set only `-o pipefail`, never `-e`, and tolerated tar exit 1 with
   `|| [ $? -eq 1 ]`. A tar exit status of 2 - a real error - left the `||`
   compound false and execution simply continued to the size check. A ~2.9 GB
   tree truncated early still clears the 100 MB floor, so the partial archive
   would be published and unpacked by four consumer jobs.

   Now `set -e`, with the tar status captured explicitly so exit 1 stays
   tolerated and anything else aborts before the archive can be published.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 23:24:53 +02:00
Ruslan KonviserandClaude Opus 5 06569082de fix(ci): close the review findings on the install-once restructure
Adversarial review of d7c56524d2 found one blocker I had walked straight into,
plus several things that would have made the gate quietly worse.

BLOCKER — `needs:` without `if:` erases four verdicts. A job whose dependency
fails is recorded `skipped`, not `failure`, and a skipped required check does
NOT block a merge. So any producer hiccup would have silently removed the
build-libs / build-api / build-web / build-desktop verdicts from the gate — the
exact "no failure just means never ran" hazard this file's own header warns
about, and the shape of the 2026-08-13 template-error incident it cites. All
four now carry `if: !cancelled()`: the ordering still holds, but a dead producer
yields a real red instead of four silent skips. build-desktop folds it into its
existing branch filter, because two `if:` keys is a duplicate mapping key and
the file will not parse.

Moving the archive shell into a script silently dropped errexit — an inline
`run:` gets `bash -eo pipefail` from GitHub, a script with its own shebang does
not. `tar ... || [ $? -eq 1 ]` therefore stopped aborting on a real tar failure,
so a truncated archive could be published under a good key. Cache entries are
immutable per key, so that poison would have persisted until the lockfile
changed, with every consumer falling back to a multi-hour install. tar's status
is now checked explicitly, and the size floor moves from 100 MB (~3% of reality)
to 1 GB.

The fallback called `yarn bootstrap`, which drops `--frozen-lockfile` and
`--network-timeout` — so the degraded path could build a tree that does not match
the committed lockfile and report green, while the Docker builds that DO install
frozen fail later on develop. Added `bootstrap:ci` carrying the CI flags, used by
both workflows' fallbacks so they cannot drift apart again.

Also from the review:
- `package.json` joins the cache key. Without it a PR that edits package.json
  without regenerating yarn.lock hashed identically to develop, hit the cache,
  and skipped the only `--frozen-lockfile` execution left in the workflow.
- `lookup-only: true` on the producer: on a hit it did nothing with the archive
  (every step is gated on the miss), so it was downloading ~2.9 GB purely to
  compute a boolean, in front of four jobs waiting on it.
- `fail-on-cache-miss: true` on the consumers: the producer's success means the
  entry exists, so a miss is an eviction, not a normal path. Fail in seconds
  rather than letting four jobs re-create the contention this change removes.
- the producer moves to the 8-core pool and 360 minutes. It is now the serial
  critical path, its work is CPU-bound, and a cold install here has no yarn
  tarball cache behind it — the equivalent deps job measured 88 min, 3h20m and
  3h46m. At 180 a cold miss would time out and take the whole gate with it.
- the header comment said the `needs:` edges were gone; it now describes why
  they are back and what they cost.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 15:58:34 +02:00
Ruslan KonviserandClaude Opus 5 d7c56524d2 perf(ci): build.yml installs the monorepo ONCE instead of five times in parallel
Five jobs each ran their own full `yarn install` of the same tree, concurrently,
on the same self-hosted pool — so they competed with each other for I/O. Same
commit, same run, measured on PR #10010 (run 32358690732):

  build-api             install 1h49m  -> passed
  build-monorepo-root   install 1h51m  -> passed
  build-libs            install 2h40m  -> KILLED at the 180-minute ceiling
  build-web             install 2h40m  -> KILLED at the 180-minute ceiling

The two that died never compiled a line: `Build packages` and `Build web` are
recorded as SKIPPED. Which two survive is a coin flip, and this gate has failed
that way four times during this work on code that builds fine.

`build-monorepo-root` already did install + postinstall and nothing else, so it
becomes the producer: it publishes the tree as one compressed archive, and the
other four `needs:` it and unpack in minutes instead of installing.

A CACHE, not an artifact — the opposite choice to test_playwright.yml, for two
reasons that only apply here. build.yml runs on every pull_request, so a ~2.9 GB
artifact per run would meter against the org's Actions storage (the quota that
just stopped Playwright reports uploading); caches are unbilled and live in a
separate 10 GB per-repo budget. And a cache persists ACROSS runs keyed on the
lockfile, so once develop populates it every PR restores rather than installing
— which an artifact, scoped to its own run, cannot do.

Net cache usage goes DOWN: `cache: yarn` is removed from all five jobs, which
frees the 4.66 GB setup-node yarn cache it kept per ref, and the ~2.9 GB tree
archive replaces it. The archive is only re-saved when yarn.lock, patches/ or
.scripts/postinstall.js change, so most PRs restore and never write.

The archive/restore shell is now shared with test_playwright.yml as
.github/scripts/archive-node-modules.sh and restore-node-modules.sh — one copy
behind eight call sites, rather than the drift that already bit the e2e workflow
once.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 15:42:18 +02:00
Ruslan KonviserandClaude Opus 5 b6268ead7e fix(ci,docs-ui): SonarCloud gate — a11y bug, optional chain, and my own duplication
The build gate went green but SonarCloud failed on two conditions.

new_reliability_rating 2 (needs 1). Adding `(click)` to the table container gave
the grid a pointer-only way to open a row: the rows are plain `<tr>`s with no
interactive role, so there was no keyboard path to the detail panel at all.
`onRowKeydown` opens the focused row on Enter/Space, reusing the same
control-guard so the kebab and Retry keep their own keys, and preventing Space
from scrolling the table out from under the panel it just opened. The two
helpers widen from MouseEvent to Event since they only ever read `event.target`.

new_duplicated_lines_density 4.8% (needs <= 3%), and it was mine: the unpack +
fallback blocks were copied verbatim into the build job and the shard job. They
had ALREADY drifted once — a fix landed in one copy and missed the other, which
is why the build job needed a second patch. Both now call
`.github/scripts/restore-node-modules.sh`.

Extracting it also removes the trap that fix was for. It is deliberately ONE
step rather than unpack-then-conditional-fallback: a step `if:` with no
status-check function is implicitly ANDed with `success()`, so a fallback gated
on a previous step's outcome cannot run after that step fails hard. A single
script cannot half-fire, so the hazard is gone by construction rather than by
remembering `continue-on-error`.

Also fixes the S6582 optional-chain smell in `resolveRowIndex`, and the header
comment that still described the (now removed) cache handoff.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 10:52:31 +02:00