## About the changes
Closes #
This is a simpler version of
https://github.com/Unleash/unleash/pull/12609
### Important files
## Discussion points
## OSS PR checklist
- [x] I have read and agree to the [Unleash Contributor License
Agreement](https://github.com/Unleash/unleash/blob/main/CLA.md).
- [x] I have added tests or explained why tests are not needed.
- [x] I have updated documentation where relevant.
Small backend CI sharding comparison and would like your take on the
trade-off.
-
[Unsharded](https://github.com/bricks-software/unleash-enterprise/actions/runs/30802484505):
8m31s wall-clock / runner time.
- [4 backend
shards](https://github.com/bricks-software/unleash-enterprise/actions/runs/30832960347):
slowest shard finished in 4m06s, so the backend result arrived ~4m25s
earlier.
- The problem is the four shard jobs consumed 14m41s total runner time.
Almost 2× the unsharded run, due to duplicated PostgreSQL startup,
checkout, install, lint, and build.
The trade-off: roughly halve feedback time when capacity is available,
in exchange for 2x compute time.
There is also a minor benefit when rerunning failed tests (potentially
flaky): you can rerun one shared instead of a full ~8 minute full job.
## Summary
- Remove checkout, pnpm setup, and setup-node from
notify_enterprise.yaml.
- Keep the workflow focused on dispatching the enterprise sync workflow.
## Problem
The workflow configured actions/setup-node with cache: pnpm but never
ran pnpm install. On runs where setup-node attempted to save a missing
pnpm store during post-job cleanup, the job failed after the dispatch
had already succeeded with: Path Validation Error: Path(s) specified in
the action for caching do(es) not exist. Example:
https://github.com/Unleash/unleash/actions/runs/29926722488/job/88945679352
## Why
Profiling the CI wall time showed the test jobs spend most of their time
*not running tests*. On our 2-vCPU runners the vitest suites run
near-serially, and with per-file isolation every test file re-executes
its entire module graph:
| job (before) | wall | of which vitest | actually running tests |
|---|---|---|---|
| `backend / build` (OSS) | **17m45** | ~15m45 | ~3m (import: ~12m for
386 files) |
| `frontend / build` (OSS) | **14m34** | ~11m45 | ~2m (import 6m + jsdom
setup 2.4m) |
| `test-enterprise` (pro/enterprise/e2e) | 4–6.5m each | ~2.5–5m |
5s–1.6m (import dominates) |
Per-file isolation was also silently hiding real test bugs (leaked
timers, event listeners, singletons) — they existed, we just never saw
them.
## What
**OSS backend: pay the import cost once per worker instead of once per
file.**
- `oss/vitest.config.ts` splits into a `unit` project (306 files,
`isolate: false`) and an `e2e` project (80 full-app DB files, isolation
kept). The `pnpm run test` entrypoint is unchanged.
- A test-context-only harness keeps files independent without per-file
registries: `src/test/reset-cross-file-state.ts` resets process-global
state at every file boundary (shared event emitter listeners,
`ClientFeatureToggleDelta` / `ConfigurationRevisionService` singletons,
the impact-metrics registry, fake timers, mocks, `process.env`), and
`src/test/prom-last-wins.ts` makes prom-client metric registration
last-wins (installed in both workers and globalSetup — globalSetup runs
once per project in the shared main process). App modules are reached
via dynamic import at hook time; a static import would make vitest
re-execute the whole app graph per file.
- Fixes the test bugs this surfaced, found by instrumenting `expect` to
attribute cross-file assertions: a test in `scheduler-service.test.ts`
left a 50ms interval running forever, asserting into every later file;
test files were importing other test files as modules (arbitraries +
playground/sdk-context schema generators — now proper `*.arbitraries.ts`
helpers); the joi `htmlInput` extension could only be extended once per
process (now a factory); a same-millisecond timestamp flake.
**OSS frontend: shard across runners.**
- Frontend tests genuinely need per-file isolation (verified: a
no-isolation run failed 29/29 files and livelocked), and config-level
levers measured ~0–10%. `build_frontend_prs.yml` now splits lint/types
into their own job and runs the suite as a 4-way `vitest --shard` matrix
— four concurrent runners each own a quarter of the files.
**Enterprise/cloud: stop re-executing the OSS dist per test file.**
- `enterprise/` and `cloud/vitest.config.ts` externalize the linked
workspace packages so the dist loads through Node's module cache (once
per persistent fork) instead of being re-inlined per file. CI keeps
default fork parallelism — serializing to one fork protected the cache
but gave back the win on 2-vCPU runners (measured: no wall change), so
two persistent forks amortize the import instead.
- `--bail 1` in nodejs.yml never reached vitest (pnpm drops CLI args
forwarded through workspace script chains); bail now lives in the
configs, CI-conditional.
## Measured results
- `backend / build`: **17m45 → 9m12** (verified on this PR's runs).
Suite-level: import 1314s→4s aggregate, full unit suite 712s→40s serial.
- Order-independence verified with deterministic serial runs and an
`expect`-instrumentation pass showing zero cross-file assertions.
- `frontend / build`: unchanged until the sharding syncs upstream (see
below); expected **~14.5m → ~5m** (slowest shard + setup).
- `test-enterprise`/`test-cloud`: unchanged in wall time so far; the
fork-parallelism fix in the latest commit is expected to recover ~1–1.5m
per suite — pending re-measurement on this PR's next run.
## Deployment notes
- `oss/` changes must flow upstream via the subtree split; upstream CI
runs the same config and gets the same speedups.
- `oss-ci.yml` consumes `build_frontend_prs.yml` from
`Unleash/unleash@main`, so the frontend sharding activates only after
the upstream sync. Its check names change (`build` → `lint & types`,
`test (shard i/4)`) — upstream branch-protection lists need updating.
- Not done (possible follow-ups): the e2e project still pays per-file
imports (~200s aggregate); `globalSetup` runs once per project
(sequential, harmless); the `unleash-error` nested-value test fails on
Node 25 locally (passes on CI's Node 22) — pre-existing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
`biome check` Runs formatter, linter and import sorting to the requested
files.
https://biomejs.dev/reference/cli/#biome-check
`oss/frontend` wasn't using it.
Smallest change to simplify things is to align on how enterprise and oss
backend work:
* `pnpm lint` - checks
* `pnpm lint:fix` - formatting, fixing small lint issues, sorts imports
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
|
[cypress-io/github-action](https://redirect.github.com/cypress-io/github-action)
| action | patch | `v7.4.0` → `v7.4.1` |
---
> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/959) for more information.
---
### Release Notes
<details>
<summary>cypress-io/github-action (cypress-io/github-action)</summary>
###
[`v7.4.1`](https://redirect.github.com/cypress-io/github-action/releases/tag/v7.4.1)
[Compare
Source](https://redirect.github.com/cypress-io/github-action/compare/v7.4.0...v7.4.1)
##### Bug Fixes
- **deps:** update transient dependency to undici 6.27.0
([#​1807](https://redirect.github.com/cypress-io/github-action/issues/1807))
([fa4a118](https://redirect.github.com/cypress-io/github-action/commit/fa4a118725a8f001170d49631ea89e5d66fee626))
</details>
---
### Configuration
📅 **Schedule**: (in timezone Europe/Madrid)
- Branch creation
- "after 7pm every weekday,before 5am every weekday"
- Automerge
- At any time (no schedule defined)
🚦 **Automerge**: Enabled.
♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.
🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.
---
- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box
---
This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/Unleash/unleash).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNDIuMiIsInVwZGF0ZWRJblZlciI6IjQzLjI0Mi4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
## About the changes
Just notify enterprise without publishing static assets
## OSS PR checklist
- [x] I have read and agree to the [Unleash Contributor License
Agreement](https://github.com/Unleash/unleash/blob/main/CLA.md).
- [x] I have added tests or explained why tests are not needed.
- [x] I have updated documentation where relevant.
## About the changes
This introduces a lightweight Contributor License Agreement process for
the OSS repository after the move to AGPL.
The goal is to make contribution rights explicit without adding
unnecessary friction for community contributors. Contributors keep
ownership of their work, while Unleash receives the permissions needed
to continue developing, distributing, and, when needed, relicensing
accepted contributions.
The process is intentionally simple: contributors acknowledge the
agreement as part of the pull request flow, and automated dependency
updates can continue through the existing bot workflow. This gives the
project a clearer long-term governance trail while keeping day-to-day
contributions familiar.
## Discussion points
- Legal should review the final CLA wording before the signing flow is
treated as authoritative.
- Maintainers will still need to configure the external CLA check and
branch protection after this lands.
- The policy avoids adding individual copyright headers to source files;
attribution remains based on the project history and review trail.
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
After we moved to vitest, vitest already has a built in github-action
reporter, so we no longer need to use the extra workflow to get the test
summaries.
The Cypress e2e job had no timeout, allowing hung runs (e.g.
`groups.spec.ts`) to block for 1h20m+ before being manually cancelled.
## Changes
- **`.github/workflows/e2e.frontend.yaml`**: Add `timeout-minutes: 10`
to the `e2e` job — each matrix spec slot will now hard-fail at 10
minutes instead of running indefinitely. Our tests currently run in
under 3 minutes. 5 seems reasonable but 10 gives us a little bit of
room, just in case
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: gastonfournier <455064+gastonfournier@users.noreply.github.com>
Co-authored-by: Gastón Fournier <gaston@getunleash.io>
Things worth noticing (+14075, -20262 most of this is due to yarn.lock
being deleted, and a new package manager tool being checked in)
## Code changes (actual changes to ts files)
### Not interesting
- being explicit about test imports in frontend rather than using
tsconfig globals to resolve `describe`, `it`, `test`, `expect` et al.
### Interesting
- Type signatures resolutions have changed a slightly bit, so some of
our Knex queries needed to be extracted for tsc to manage to type
analyse and pass type checking. All tests are green, so I'm assuming I
managed to reproduce the behaviour, in particular
src/lib/features/project/project-read-model.ts has some extra variables
to pass typechecking.
### Other considerations
- Do we still build the way we did? (pnpm pack produces the same
files/artifact as yarn pack)
- Will this merge cleanly with enterprise (which runs prepack)?
#### Known unknowns
- I've changed our vite.config.mts in frontend to use vite's own built
in tsconfigpaths, but Thomas pointed out that he tried that already and
ran into some issue when enterprise used the dependency, so we'll need
to double check that it works, and be ready to rollback to using the
deprecated plugin (and accept that vite gives us a warning that this is
now native functionality).
### Build failures
- Expected is openapi validation on main, we've changed to using pnpm
action rather than yarn action so it won't recognize yarn as a
packageManager on main
- dependency scanner, due to how pnpm resolves dependencies, we now have
a more direct dependencies, which our scanner apparently is scoring too
low for it to be OK with them. These were already a dependency, just
transitively rather than direct, so I'm comfortable with this.
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
|
[akhileshns/heroku-deploy](https://redirect.github.com/akhileshns/heroku-deploy)
| action | minor | `v3.14.15` → `v3.15.15` |
---
> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/959) for more information.
---
### Release Notes
<details>
<summary>akhileshns/heroku-deploy (akhileshns/heroku-deploy)</summary>
###
[`v3.15.15`](https://redirect.github.com/AkhileshNS/heroku-deploy/releases/tag/v3.15.15)
[Compare
Source](https://redirect.github.com/akhileshns/heroku-deploy/compare/v3.14.15...v3.15.15)
- Bumped Node version to 24 (courtesy of
[@​papotte](https://redirect.github.com/papotte))
</details>
---
### Configuration
📅 **Schedule**: (in timezone Europe/Madrid)
- Branch creation
- "after 7pm every weekday,before 5am every weekday"
- Automerge
- At any time (no schedule defined)
🚦 **Automerge**: Enabled.
♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.
🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.
---
- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box
---
This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/Unleash/unleash).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNDEuMyIsInVwZGF0ZWRJblZlciI6IjQzLjE0MS4zIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
As the title says. Mostly updates text to point out we're running node
22, not 20, but also updates one workflow to use 22 rather than being
the only one using 20.
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
| [actions/cache](https://redirect.github.com/actions/cache) | action |
major | `v4` → `v5` |
---
### Release Notes
<details>
<summary>actions/cache (actions/cache)</summary>
### [`v5`](https://redirect.github.com/actions/cache/compare/v4...v5)
[Compare
Source](https://redirect.github.com/actions/cache/compare/v4...v5)
</details>
---
### Configuration
📅 **Schedule**: (in timezone Europe/Madrid)
- Branch creation
- "after 7pm every weekday,before 5am every weekday"
- Automerge
- At any time (no schedule defined)
🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.
♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.
🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.
---
- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box
---
This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/Unleash/unleash).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My42Ni40IiwidXBkYXRlZEluVmVyIjoiNDMuMTEwLjIiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbImRlcGVuZGVuY2llcyJdfQ==-->
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
|
[actions/create-github-app-token](https://redirect.github.com/actions/create-github-app-token)
| action | major | `v2` → `v3` |
---
### Release Notes
<details>
<summary>actions/create-github-app-token
(actions/create-github-app-token)</summary>
###
[`v3`](https://redirect.github.com/actions/create-github-app-token/compare/v2...v3)
[Compare
Source](https://redirect.github.com/actions/create-github-app-token/compare/v2...v3)
</details>
---
### Configuration
📅 **Schedule**: (in timezone Europe/Madrid)
- Branch creation
- "after 7pm every weekday,before 5am every weekday"
- Automerge
- At any time (no schedule defined)
🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.
♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.
🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.
---
- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box
---
This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/Unleash/unleash).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My42Ni40IiwidXBkYXRlZEluVmVyIjoiNDMuMTEwLjIiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbImRlcGVuZGVuY2llcyJdfQ==-->
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
## About the changes
- removing cypress e2e tests
- added similar unit tests
- fixing an issue where OSS version could got stuck on an invalid 404
page.
# Before
## Enterprise
yarn e2e:enterprise 12.06s user 4.31s system 27% cpu 59.897 total
yarn e2e:enterprise 13.33s user 4.64s system 30% cpu 59.711 total
yarn e2e:enterprise 12.82s user 4.61s system 30% cpu 58.065 total
# After
yarn e2e:enterprise 8.57s user 2.48s system 27% cpu 39.579 total
yarn e2e:enterprise 7.45s user 2.21s system 24% cpu 39.855 total
yarn e2e:enterprise 7.40s user 2.23s system 25% cpu 38.477 total
## About the changes
I wanted to add some tests but it turned out that things didn't work....
Split tests into:
* `oss`
* `enterprise`
Fixing some of the tests that failed (not all)
Skipping tests that do not work and we're not running them anyway.
### Important files
<img width="194" height="354" alt="Screenshot 2026-03-06 at 14 34 09"
src="https://github.com/user-attachments/assets/8d8c65eb-bdd5-406f-99ef-3c1b5fa3987f"
/>
## Discussion points
PR #11280 updated the Dockerfile base image to `node:22.22-alpine3.23`
to resolve CVE-2025-15467. However, the `docker_publish.yaml` workflow
explicitly passes `NODE_VERSION` as a build argument via the matrix
strategy:
```yaml
matrix:
version: [22.21-alpine3.23]
```
This `build-arg` overrides the Dockerfile default, meaning the published
Docker image was still being built with the vulnerable Node 22.21
despite the Dockerfile fix in #11280.
This PR updates the matrix version to `22.22-alpine3.23` so the
published image matches the intended base image.
Closes #
### Important files
- `.github/workflows/docker_publish.yaml` — the only change; updates
`matrix.version` from `22.21-alpine3.23` to `22.22-alpine3.23`
## Discussion points
This is a follow-up to #11280. The Dockerfile default was correctly
updated in that PR but the workflow's `build-args` was left behind,
silently overriding the fix for all published images.