Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
fcb5b30
Remove ComponentFactoryResolver (FWT-888)
norman-abramovitz Apr 1, 2026
1afa726
Replace first() with take(1) codebase-wide (FWT-907)
norman-abramovitz Apr 2, 2026
6910592
Add ApplicationDeploySourceTypes provider (FWT-888)
norman-abramovitz Apr 2, 2026
52f9ae7
Detect platform mismatch in dev backend (FWT-840)
norman-abramovitz Apr 2, 2026
a221cc8
Bump version to v5.0.0-dev.3
norman-abramovitz Apr 2, 2026
bcae4a4
Remove dead wrapper NgModules (FWT-872)
norman-abramovitz Apr 2, 2026
1e6928c
Guard against panic in K8s cert auth (FWT-872)
norman-abramovitz Apr 3, 2026
deeffbd
Fix K8s auth body consumption bug (FWT-872)
norman-abramovitz Apr 3, 2026
36e6755
Add editable path and clear to file input (FWT-872)
norman-abramovitz Apr 3, 2026
1ae5a9f
Convert K8s SCSS to Tailwind, fix chart colors (FWT-872)
norman-abramovitz Apr 3, 2026
b1b57f8
Fix list filter, icon font, and side panel bugs
norman-abramovitz Apr 3, 2026
6208328
Remove unused imports from AboutPageComponent
norman-abramovitz Apr 3, 2026
5dd28dc
Convert 28 small K8s SCSS files to Tailwind (FWT-872)
norman-abramovitz Apr 3, 2026
b4067bb
Fix resource viewer missing pipe and component imports
norman-abramovitz Apr 3, 2026
5551951
Fix RouterLink import and null ConfigMap data guard
norman-abramovitz Apr 3, 2026
701f763
Fix K8s home card counts not loading (OnPush)
norman-abramovitz Apr 3, 2026
c491199
Simplify service ports display, top-align table rows
norman-abramovitz Apr 3, 2026
274693d
Fix missing cellDefinition binding in table rows
norman-abramovitz Apr 3, 2026
4d828a8
Migrate e2e suite to Playwright
norman-abramovitz Apr 5, 2026
88360cb
Fix autoscaler specs missing HttpClient provider
norman-abramovitz Apr 5, 2026
7085906
Remove dead list helper, add type=text to filter input
norman-abramovitz Apr 5, 2026
01c5f17
Use Tailwind forms class strategy, remove input shadows
norman-abramovitz Apr 5, 2026
6cd4241
Migrate K8s/Helm package SCSS to Tailwind
norman-abramovitz Apr 5, 2026
1da7a6b
Fix E2E test timing for CF-backed list tests
norman-abramovitz Apr 5, 2026
bf791ae
Upgrade Angular 20.3.9→20.3.18, lodash-es, x/crypto
norman-abramovitz Apr 5, 2026
9f49e11
Fix Firefox E2E failures and proxy /api-keys routing bug
norman-abramovitz Apr 6, 2026
489bb10
ESLint config: vitest globals pattern + auto-fix (1826→788 warnings)
norman-abramovitz Apr 7, 2026
304fd07
Fix ESLint warnings: eqeqeq, case-declarations, negated-async, types
norman-abramovitz Apr 7, 2026
bec1473
Add check verb, FINAL and DRYRUN variables, remove bump release
norman-abramovitz Apr 7, 2026
514af54
Migrate remaining constructor injection to inject() function
norman-abramovitz Apr 7, 2026
f1d08c2
Fix accessibility and remaining template ESLint warnings
norman-abramovitz Apr 7, 2026
2a1f352
Remove dead imports and expand ESLint unused-vars ignore patterns
norman-abramovitz Apr 7, 2026
0085038
ESLint: ignore _-prefixed caught errors and spec vars
norman-abramovitz Apr 10, 2026
23cdbd2
Fix TS build errors from inject() migration
norman-abramovitz Apr 10, 2026
217f9e2
FWT-876: Reduce ESLint unused-vars warnings 532 to 5
norman-abramovitz Apr 10, 2026
698aa8d
Pin patched hono and vite via overrides
norman-abramovitz Apr 10, 2026
a0b3bfa
Fix Helm chart browsing broken by standalone migration
norman-abramovitz Apr 10, 2026
3c94ee4
FWT-872: Convert helm/monocular SCSS to Tailwind, remove dead code
norman-abramovitz Apr 10, 2026
51040a0
FWT-872: Remove 34 empty K8s placeholder SCSS files
norman-abramovitz Apr 10, 2026
2c15a76
Remove 125 empty placeholder SCSS files across packages
norman-abramovitz Apr 10, 2026
11504c1
WIP: ng update @angular/core@21 @angular/cli@21
norman-abramovitz Apr 10, 2026
a22ac6f
Angular 21 upgrade WIP
norman-abramovitz Apr 10, 2026
19ba374
WIP: vite 7 + vitest 4 migration, peer deps bumped
norman-abramovitz Apr 10, 2026
402f86f
Document Angular 21 upgrade WIP state
norman-abramovitz Apr 10, 2026
3df54d4
cf-stratos: fix NG0203 root cause + vitest 4 pool migration
norman-abramovitz Apr 10, 2026
f834007
cf-stratos: break circular @stratosui/core barrel imports in core/
norman-abramovitz Apr 10, 2026
b4c0fea
cf-stratos: initialize EntityCatalogHelper in 4 entity-touching specs
norman-abramovitz Apr 10, 2026
3ec854d
cf-stratos: use strict equality in autoscaler policy templates
norman-abramovitz Apr 10, 2026
e8b5139
cf-stratos: fix nil res deref in metrics/main.go (go vet)
norman-abramovitz Apr 10, 2026
7d33d0c
cf-stratos: bump version to v5.0.0-dev.4 for Angular 21 adepttech deploy
norman-abramovitz Apr 10, 2026
fb0230d
cf-stratos: widen endpoint register dialog inputs
norman-abramovitz Apr 10, 2026
89319d0
cf-stratos: restore previous pageHeader portal on component destroy
norman-abramovitz Apr 10, 2026
c8ecf60
cf-stratos: defense-in-depth for endpoint register modal lifecycle
norman-abramovitz Apr 10, 2026
c9d2faf
cf-stratos: e2e regression tests for register modal close paths
norman-abramovitz Apr 10, 2026
79d3982
cf-stratos: bump version to v5.0.0-dev.5 for adepttech redeploy
norman-abramovitz Apr 10, 2026
0d820e5
docs: update for shipped make bump lifecycle and check verb
norman-abramovitz Apr 10, 2026
234ffd8
rename STRATOS_E2E_* env vars to drop redundant prefix
norman-abramovitz Apr 11, 2026
d70d56b
add E2E_BROWSERS, E2E_TRACE, E2E_VIDEO, E2E_SCREENSHOTS
norman-abramovitz Apr 11, 2026
947f201
docs: cover E2E_* recipe variables and DRYRUN extension
norman-abramovitz Apr 11, 2026
046d68f
version-bump.sh: hide 'bump release' from user-facing usage text
norman-abramovitz Apr 11, 2026
d87b679
resolve display name when options load after value
norman-abramovitz Apr 12, 2026
a87347c
suppress duplicate label on app-select in form-field
norman-abramovitz Apr 12, 2026
b8eb760
add clearing, required marker, and color fixes to selects
norman-abramovitz Apr 12, 2026
57748c7
tighten form-field spacing and fix unlimited-input
norman-abramovitz Apr 12, 2026
801fc40
fix quota form labels, validation, and layout
norman-abramovitz Apr 12, 2026
68c6e0e
require dirty form on quota edit steps
norman-abramovitz Apr 12, 2026
443b257
convert custom-form-field label to tailwind, drop dead scss
norman-abramovitz Apr 12, 2026
610b9fb
wire floating labels for endpoint create/edit and connect auth forms
norman-abramovitz Apr 12, 2026
1ab865c
mirror quota form validity into a signal for cd propagation
norman-abramovitz Apr 13, 2026
f0b3072
wire floating labels and button state for org/space create/edit
norman-abramovitz Apr 13, 2026
e7a7cf6
convert service and cf-org-space step forms to floating-label pattern
norman-abramovitz Apr 13, 2026
8dab918
cascade-clear and suppress required error on None in cf-org-space step
norman-abramovitz Apr 13, 2026
212fbae
remove focus outline double-line and widen floating-label gap
norman-abramovitz Apr 13, 2026
f97c001
bump dev version to v5.0.0-dev.6
norman-abramovitz Apr 13, 2026
e231459
fix db URI parser to handle query parameters
norman-abramovitz Apr 13, 2026
9fae9ad
add optional background refresh for cnsi tokens
norman-abramovitz Apr 13, 2026
67358c5
fix list header multi-filter / right-side overlap
norman-abramovitz Apr 13, 2026
f086171
add copy UAA token button to page header
norman-abramovitz Apr 13, 2026
f71eeae
support GitHub Enterprise and private repo deploy via PAT
norman-abramovitz Apr 13, 2026
3b88706
support async service binding and show last-binding state
norman-abramovitz Apr 13, 2026
b769565
add Go unit tests for retrieveToken and vcs URL rewriting
norman-abramovitz Apr 13, 2026
2a670a9
FWT-917 instrument CfOrgSpaceDataService for diagnostic capture
norman-abramovitz Apr 13, 2026
f0288b3
FWT-917 extend instrumentation to cover quota and breadcrumb paths
norman-abramovitz Apr 14, 2026
8033da1
FWT-917 fix auto-selector race against orgs pagination load
norman-abramovitz Apr 14, 2026
96f9cc6
fix NG0203 injection-context error on add-service-instance Next click
norman-abramovitz Apr 14, 2026
89d9d3a
fix stuck Finish button spinner and JSON textarea UX
norman-abramovitz Apr 14, 2026
9cda1f9
fix stepper error snackbar auto-dismissing before user can read it
norman-abramovitz Apr 14, 2026
f77a33a
apply schema-form textarea treatment across peer multiline fields
norman-abramovitz Apr 14, 2026
cf3f8aa
fix stepper button states not updating under OnPush
norman-abramovitz Apr 15, 2026
aa857b8
fix NG0203 in deploy file scanner folder pick
norman-abramovitz Apr 15, 2026
39fc357
fix stuck upload progress on deploy source-upload step
norman-abramovitz Apr 15, 2026
52d8eaf
restore deploy step UI end-to-end under Angular 21
norman-abramovitz Apr 15, 2026
ab20834
bump version to dev.8
norman-abramovitz Apr 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
308 changes: 308 additions & 0 deletions ANGULAR-21-UPGRADE-STATUS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
# Angular 21 Upgrade — Work-in-Progress Status

**Branch:** `try/angular-21-upgrade` (worktree)
**Base:** `feature/Angular-21` at commit `2c15a7653a` (Remove 125 empty placeholder SCSS files)
**Last updated:** 2026-04-09

## TL;DR

Angular 21 is **fundamentally working**. Build succeeds, E2E passes (72/0), the
application runs correctly in a dev server against k3d. The blocker is the
**unit test infrastructure** — 234 vitest specs fail with `NG0203` because the
combination of Angular 21 + vitest 4 + zoneless change detection + NgRx Store
factory injection no longer works the way it did under Angular 20 + vitest 3.

This branch should be **kept as backup**. The actual Angular 21 upgrade is
deferred until the test infrastructure can be migrated (estimated 1–2 focused
days, or wait for `@analogjs/vite-plugin-angular` to ship full Angular 21 +
vitest 4 support).

---

## ✅ What Works

| Check | Result |
|---|---|
| `ng build stratos --configuration=development` | clean, 14.7s |
| `make check e2e` (Playwright core) | **72 passed / 41 skipped / 0 failed** |
| Dev server (vite 7 + ng serve) | runs on port 5540 |
| Manual smoke test (login → endpoints → K8s + Helm) | works end-to-end |
| Auto-migrations applied by `ng update` | 7 files migrated to control-flow syntax |

### Versions installed
| Package | Version |
|---|---|
| `@angular/core` | 21.2.8 |
| `@angular/cli` | 21.2.7 |
| `@angular/cdk` | 21.2.6 |
| `@angular/build` | 21.2.7 |
| `@angular-devkit/build-angular` | 21.2.7 |
| `@angular-builders/custom-webpack` | 21.0.3 |
| `@angular-eslint/*` | 21.0.0 |
| `@schematics/angular` | 21.2.7 |
| `@ngrx/store` (and effects, router-store, store-devtools) | 21.1.0 |
| `typescript` | 5.9.3 |
| `vite` | 7.3.2 (security CVEs all addressed) |
| `vitest` | 4.1.4 |
| `@analogjs/vitest-angular` | 2.4.4 |
| `@swimlane/ngx-graph` | 11.0.0 (peer wants CDK ≤20, works at runtime) |
| `ngrx-store-localstorage` | 20.1.0 (peer says `>=20`, works with NgRx 21) |

---

## ❌ The Blocker — `NG0203` in unit tests

### Symptom

`bun run test` produces:
```
Test Files 202 failed | 363 passed | 3 skipped (568)
Tests 234 failed | 866 passed | 9 skipped (1109)
```

Every component-rendering test that touches NgRx Store fails with:
```
Error: NG0203: The `StateObservable` token injection failed.
`inject()` function must be called from an injection context such as a
constructor, a factory function, a field initializer, or a function used
with `runInInjectionContext`.
Find more at https://v21.angular.dev/errors/NG0203

❯ injectInjectorOnly node_modules/@angular/core/fesm2022/_effect-chunk2.mjs:667
❯ ɵɵinject node_modules/@angular/core/fesm2022/_effect-chunk2.mjs:684
❯ Object.factory ng:/Store/ɵfac.js:5:49
❯ R3Injector.hydrate packages/core/src/di/r3_injector.ts:533
```

The **NgRx Store factory itself** (generated via `ɵɵngDeclareFactory`) is
running outside an active injection context. When it tries to `inject(StateObservable)`
to construct the `Store`, `getCurrentInjector()` returns `undefined` and
Angular 21 throws.

### What used to work (Angular 20 + vitest 3)

Pre-upgrade test files use this pattern:

```ts
TestBed.configureTestingModule({
imports: [SomeStandaloneComponent],
providers: [
importProvidersFrom(
CfAutoscalerTestingModule, // declares EntityCatalogFeatureModule
...generateBaseTestStoreModules(), // includes StoreModule.forRoot(appReducers)
CoreModule,
NoopAnimationsModule,
),
provideRouter([]),
provideZonelessChangeDetection(),
// ...
],
}).compileComponents();
```

Under Angular 20, `importProvidersFrom` flattened the modules' providers in
the right order: NgRx Store providers (including `StateObservable`) were in
place before any factory functions ran. Under Angular 21 the ordering breaks
— the `Store` factory is now invoked before the environment injector has
finished bootstrapping the providers it depends on.

### What was tried (and didn't fix it)

1. **Bump `@analogjs/vite-plugin-angular` and `@analogjs/vitest-angular` to 2.4.4**
— these are the latest, peer deps say they support `vite ^6 || ^7`,
`vitest ^1 || ^2 || ^3 || ^4`, no Angular version pinning. Installing them
did not change the failure mode.

2. **Migrate `vitest.workspace.ts` → `vitest.config.ts` with `projects:`**
— required because `defineWorkspace` was removed in vitest 4. The
migration succeeded mechanically (tests are now discovered) but didn't
change the failure mode.

3. **Replace `StoreModule.forRoot` with `provideStore` in `createBasicStoreModule`**
— converted the helper from `ModuleWithProviders<StoreRootModule>` to
`EnvironmentProviders` via `makeEnvironmentProviders([provideStore(...)])`.
This **shifted** the error from `Store` to `EntityCatalogFeatureModule`,
confirming the issue is with `importProvidersFrom` of NgModules whose
constructors call `inject(Store)`.

4. **Convert `EntityCatalogFeatureModule` to use `provideEnvironmentInitializer`**
— moved the `inject(Store)` / `inject(ReducerManager)` calls out of the
module constructor and into an environment initializer that runs after
all providers are bootstrapped. **This did not fix the test failure** —
the same `NG0203` reappears, this time inside the Store factory itself
when the test tries to inject Store directly.

5. **Inline `provideStore(appReducers, ...)` in a single test file's
providers array** — removing the `importProvidersFrom` wrapping
completely. Same `NG0203` failure. This rules out `importProvidersFrom`
as the cause and points at a deeper interaction between the vitest 4
test environment and Angular 21's injector-context tracking.

### Root cause hypothesis

The most likely root cause is that **`@analogjs/vite-plugin-angular`'s test
environment doesn't yet establish an injection context that survives
across NgRx 21's factory functions when running under vitest 4 + zoneless
mode**. NgRx 21's Store is now defined via `ɵɵngDeclareFactory` with
explicit `deps`, and Angular 21 enforces that factories run inside
`runInInjectorProfilerContext`. Under vitest 3 + Angular 20 the older
factory shape happened to bypass this requirement.

Notably, the **same code paths work fine in the production build and in
Playwright E2E** — the issue is specific to how vitest 4 spins up the
test environment.

---

## Files changed in this branch

### Required code changes for Angular 21 itself
| File | Change |
|---|---|
| `package.json` | Bumped Angular packages to 21, NgRx to 21.1.0, TypeScript override to ^5.9.3, vite override to ^7.3.2 |
| `tools/builders/prebuild-application/package.json` | Workspace builder bumped to 21.x |
| `vitest.config.ts` | Migrated from `defineWorkspace` to `projects:` (vitest 4 syntax) |
| `vitest.workspace.ts` | **Deleted** (removed in vitest 4) |
| `src/.../tile/tile.component.ts` | `@HostBinding('class.app-tile-1-3') private isOneThirdFixed` → `public` |
| `src/.../tile-group/tile-group.component.ts` | 7 `@HostBinding(...) private` fields → `public` |
| `src/.../tile-grid/tile-grid.component.ts` | `@Input() private fit` → `public` |

### Files updated by `ng update` automation
| File | Migration |
|---|---|
| `kubernetes-namespace-preview.component.html` | Block control flow syntax |
| `kubernetes-namespace-preview.component.ts` | Block control flow syntax |
| `stratos-title.component.ts` | Block control flow syntax |
| `custom-tabs.component.ts` | Block control flow syntax |
| `deploy-application-fs.component.ts` | Block control flow syntax |
| `cf-role-checkbox.component.ts` | Block control flow syntax |
| `add-api-key-dialog.component.ts` | Block control flow syntax |

---

## Pre-flight that confirmed the codebase was ready

Before attempting the upgrade, verified the following on `feature/Angular-21`:

| Risk area | Status |
|---|---|
| `provideZoneChangeDetection` already in `app.module.ts` | ✓ |
| No `NgModuleFactory` usage | ✓ |
| No `moduleId` on components | ✓ |
| No `UpgradeAdapter` (legacy upgrade) | ✓ |
| No custom interpolation delimiters | ✓ |
| No `lastSuccessfulNavigation` consumers | ✓ |
| 62 `[ngClass]` + 6 `[ngStyle]` bindings | left as-is — auto-migration available, low risk |
| TypeScript 5.8 → 5.9 | bumped via override |
| Gate check passed on base branch | 1104 tests, all green |
| E2E passed on base branch | 72 passed, 41 skipped, 0 failed |

---

## What surfaced as Angular 21 breaking changes that hit our code

| # | Breaking change | Files affected | Fix applied |
|---|---|---|---|
| 1 | Host binding type checking enabled by default — can't bind to `private` fields | `tile/`, `tile-group/`, `tile-grid/` | Made fields `public` |
| 2 | `@Input() private` similarly rejected | `tile-grid/` | Made `public` |
| 3 | TypeScript 5.9 minimum | `package.json` overrides | Bumped override |
| 4 | `@angular/build@^20` not compatible | resolved version | Forced override to `^21.2.7` |
| 5 | NgRx Store factory injection context (test env) | All component specs touching Store | **NOT FIXED** — see blocker section |

---

## How to resume this work

### Option A — wait for tooling

Watch these for vitest 4 + Angular 21 + zoneless support:
- `@analogjs/vite-plugin-angular` releases
- `@analogjs/vitest-angular` releases
- vitest 4 documentation on Angular testing

When the toolchain is ready, this branch can be rebased onto the latest
`feature/Angular-21` and the test failures should resolve without further
code changes.

### Option B — invest the focused day

The fix likely requires one of:

1. **Custom test bootstrap** that wraps every spec in `runInInjectionContext`
— would need a vitest plugin or a global beforeEach that initializes
TestBed's environment injector before any factory runs.

2. **Migrate every spec away from `importProvidersFrom`** of modules whose
constructors `inject()`, replacing them with explicit `provide*` calls
in the providers array. This is mechanical but touches ~200 spec files
and the test framework helpers (`generateBaseTestStoreModules`,
`BaseTestModules`, etc.).

3. **Stop using zoneless in tests** — switch back to `provideZoneChangeDetection`
for tests only. May or may not avoid the issue depending on how vitest 4
spins up the platform.

### Option C — abandon and revisit later

Drop this branch and stay on Angular 20 (`feature/Angular-21` branch — yes,
the name is misleading; it has been the long-running work branch for the
Angular 21 prep work but currently sits at Angular 20.3.18). All the
Angular 21 readiness work landed on `feature/Angular-21` already and the
codebase will continue to work fine on Angular 20 until the tooling
catches up.

---

## Resuming step-by-step

To resume work in this worktree:

```bash
cd /Users/norm/Projects/CloudFoundry/cf-stratos-ng21
git status # should be clean on try/angular-21-upgrade
git pull origin try/angular-21-upgrade

# Reinstall dependencies (worktree might have stale node_modules)
rm -rf node_modules bun.lock
bun install

# Verify build still works
npx ng build stratos --configuration=development

# Verify E2E still passes
make check e2e

# To work on the test infrastructure issue, start with one spec:
npx vitest run \
src/frontend/packages/cf-autoscaler/src/shared/list-types/app-autoscaler-metric-chart/app-autoscaler-metric-chart-card/combo-chart/combo-series-vertical.component.spec.ts \
--project=cf-autoscaler
```

### Files to look at first when resuming

| File | Why |
|---|---|
| `src/frontend/packages/store/testing/src/store-test-helper.ts` | `createBasicStoreModule` — currently uses `StoreModule.forRoot`, may need migration to `provideStore` |
| `src/frontend/packages/store/src/entity-catalog.module.ts` | `EntityCatalogFeatureModule` constructor injects `Store` — candidate for `provideEnvironmentInitializer` migration |
| `src/frontend/packages/core/test-framework/core-test.helper.ts` | `generateBaseTestStoreModules` — entry point for most test setups |
| `src/frontend/packages/*/src/test-setup.ts` | Per-package vitest test setup files — initialize TestBed platform |
| `vitest.config.ts` | Already migrated to vitest 4 `projects:` syntax |

---

## Pointers to the original Angular 20 baseline

If the upgrade is abandoned, the prep work is fully landed on
`feature/Angular-21` (still at Angular 20.3.18) and is in excellent shape.
Ship-ready commits since 2026-04-09:

| Commit | Description |
|---|---|
| `2c15a7653a` | Remove 125 empty placeholder SCSS files across packages |
| `51040a07e7` | FWT-872: Remove 34 empty K8s placeholder SCSS files |
| `3c94ee4e66` | FWT-872: Convert helm/monocular SCSS to Tailwind, remove dead code (-966 lines) |
| `a0b3bfa8ad` | Fix Helm chart browsing broken by standalone migration |
| `698aa8daaf` | Pin patched hono and vite via overrides (security CVEs) |
| `217f9e24ac` | FWT-876: Reduce ESLint unused-vars warnings 532 to 5 |
| `23cdbd2c97` | Fix TS build errors from inject() migration |
| `0085038667` | ESLint: ignore _-prefixed caught errors and spec vars |
Loading
Loading