Skip to content

Release: merge beta into main (1.0.3-unstable.4) — manual conflict resolution#93

Merged
rubenvdlinde merged 174 commits intomainfrom
chore/beta-to-main-merge
May 1, 2026
Merged

Release: merge beta into main (1.0.3-unstable.4) — manual conflict resolution#93
rubenvdlinde merged 174 commits intomainfrom
chore/beta-to-main-merge

Conversation

@rubenvdlinde
Copy link
Copy Markdown
Contributor

Replaces #92

#92 (auto-opened release) had merge conflicts and couldn't be admin-merged. This PR is the same release with the 3 conflicts resolved manually:

File Resolution
`appinfo/info.xml` Take beta — version 1.0.3-unstable.4, licence EUPL-1.2 (main was still 1.0.2 + agpl from before the EUPL transition that happened weeks ago on the development line)
`package.json` Take beta wholesale (vitest + @vue/test-utils + @vitejs/plugin-vue2 + @nextcloud/webpack-vue-config@^5.5.0). Main's webpack-vue-config@^7.0.2 (from dependabot #69) had peer conflicts with beta's locked webpack-cli — kept beta's known-good build chain; webpack-vue-config can be re-bumped to v7 in a follow-up PR with proper peer-dep upgrades
`package-lock.json` Take beta + npm install to regenerate against the merged package.json. 1521 packages installed cleanly (1 deprecation notice for eslint@8.57.1, same as on beta)

What this brings to main

173 commits, including:

Known check failure (acceptable)

`quality / Vue Quality (eslint)` will fail with the Nc* `import/named` errors — blocked on nextcloud-vue cutting a release from development (PR #102 is on ncvue/development; semantic-release publishes from ncvue/main). Will go green automatically once mydash picks up the next `@conduction/nextcloud-vue` beta.

Test plan

  • CI green (modulo the known eslint block)
  • After merge: GitHub's "X vulnerabilities on default branch" badge drops the 6 high-severity count to 0
  • Spot-check a dashboard / widget loads on a fresh install from main

actions-user and others added 30 commits February 6, 2026 10:36
- Create header-override.css for white header with blue logo bar
- Override nldesign theme CSS with maximum specificity selectors
- Add Nederland logo in blue bar (matching login page design)
- Load override CSS in Application.php boot() method
- Header is now white with dark icons/text for better contrast
- Blue bar now extends 50px above header (top: -50px)
- Bar height increased to 100px for proper visibility
- Nederland logo copied to mydash/img/ folder for accessibility
- Logo positioned at bottom of blue bar (bottom: 5px)
- Logo uses relative path from CSS folder (../img/nederland-logo.svg)
- Body overflow set to visible to allow bar to extend above page
- Header overflow set to visible for pseudo-elements
- Remove filter: invert(1) from icons (was making them white)
- Set all header text to dark gray (#333333)
- Set app name and header links to Rijksblauw (#154273)
- App menu active/hover states use blue (#154273) with bold text
- Unified search icon and SVGs set to dark (#333333)
- User menu, notifications, contacts icons set to dark (opacity: 0.8)
- Settings menu icon filter removed for proper dark display
- All icons and text now properly visible on white header background
BREAKING CHANGE: Switch from CSS ::before/::after to real DOM element positioning

- Use position: fixed on header-appname-container/dashboard link
- Blue bar now targets actual logo element instead of pseudo-elements
- Logo displays at bottom of bar with flexbox (align-items: flex-end)
- Z-index: 9999 ensures bar floats above all content
- Logo made white via filter: brightness(0) invert(1)
- Works with both <img> and <svg> logo elements
- Bar dimensions: 60px wide × 100px tall, positioned at top: 0, left: 5px
- Remove old ::before and ::after pseudo-element code
- Logo now properly visible at bottom of blue bar across all pages
- Target actual Nextcloud logo link: a#nextcloud and a[href="/"]
- Also target .header-start wrapper (in addition to .header-left)
- Logo div (.logo) background replaced with nederland-logo.svg
- Remove incorrect selectors (.header-appname-container, dashboard link)
- Add legacy support for img/svg elements inside logo link
- Box-sizing: border-box for proper padding calculation
- Logo positioned at bottom via flexbox (align-items: flex-end)
- Fixed positioning ensures logo bar floats above all content
- Remove margin-top and padding-top from html, body, and #body-user
- Eliminates white space above blue bar
- Change flexbox to flex-direction: column with justify-content: flex-end
- Logo now correctly positioned at bottom of blue bar
- Adjust padding: remove all padding except padding-bottom: 10px
- Logo displays at bottom with proper spacing from edge
- Height reduced from 100px to 60px for more compact look

- Left position changed from 5px to 15px

- margin-top: 0px !important to override Nextcloud header.scss

- margin-left: 0px !important for precise positioning

- Logo height adjusted to 30px to fit 60px bar
- Height changed from 60px to 80px for better visual balance

- Logo height adjusted to 32px to fit proportionally
- visibility: visible !important (was hidden)

- padding: 0 !important (remove 70px left padding)

- text-indent: 0 !important (was -9999px)

- min-height and max-height: 32px (override 50px)

- text-align: center for proper centering

- Logo now properly visible and centered in blue bar
- Add eslint.config.js with Nextcloud ESLint config
- Add required ESLint dependencies
- Fix indentation and trailing spaces
- All linting checks now pass
- Merge duplicate pinia imports into single import statement
- Remove unused NcDashboardWidgetItem component registration
- Fix indentation of widgetItems computed property (1 tab too few)
- Remove trailing whitespace

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Build output should not be committed to the repository.
Simplified gitignore to ignore the entire /js/ directory.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace existing license (Apache-2.0/AGPL) with EUPL-1.2 across all
metadata files: LICENSE, appinfo/info.xml, composer.json, package.json.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tylelint)

Add Conduction coding standard with custom sniffs, PHPMD mess detection,
phpmetrics code metrics, and stylelint for CSS linting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add PR checks for PHP quality (PHPCS, PHPMD, phpmetrics) and frontend
quality (ESLint, stylelint) that run on pull requests to main branches.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Apply Conduction PHPCS standard to all Db entities and mappers. Add
concrete utility classes and interfaces to Db namespace to fix SAP
violation. Split monolithic migration into dedicated table builders.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…mplexity

Extract WidgetFormatter, WidgetItemLoader, PlacementService, PlacementUpdater,
TileUpdater, VisibilityChecker, UserAttributeResolver, DashboardFactory,
DashboardResolver, TemplateService, AdminSettingsService, AdminTemplateService,
RuleEvaluatorService, ResponseHelper, and RequestDataExtractor. Add dedicated
RuleApiController. Reduces phpmetrics violations to zero.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Only allow PRs from 'development' or 'hotfix/*' branches to merge into
main. Other source branches will be blocked by a required status check.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Enforce branching strategy: main only from beta, beta only from
development, development only from feature/*. Hotfix/* bypasses all
restrictions. Add beta to quality check triggers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Document the promotion-based branching model (feature→development→beta→main),
hotfix policy, required quality checks, and local development workflow
with a mermaid flow diagram.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…rary

Replace NcSettingsSection with CnSettingsSection from @conduction/nextcloud-vue.
Add webpack alias for shared library with dedup aliases (vue$, pinia$,
@nextcloud/vue$) to prevent dual-instance bugs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…webpack alias

Makes the shared library work in both monorepo dev (local source alias)
and CI/production builds (npm package from git). The alias only activates
when ../nextcloud-vue/src exists.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces github:ConductionNL/nextcloud-vue#main with ^0.1.0-beta.1
from npm registry. Faster installs, no git clone + build step.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
rubenvdlinde and others added 25 commits April 30, 2026 22:55
Release: merge development into beta
…#39)

Walkthrough of all 9 archived OpenSpec features uncovered three critical
runtime bugs in features marked "implemented". This commit restores them
and clears the supporting quality stack.

Critical fixes
--------------
- ConditionalRule.createdAt: switch ?DateTime to ?string ('c' format) to
  match sibling entities. Doctrine DBAL cannot bind a DateTime without
  an explicit addType registration; INSERT now succeeds. Same change in
  AdminSetting.updatedAt.
- AdminTemplateService + TemplateService: drop the ramsey/uuid require
  and inline the random_bytes(16) UUID generator already used by
  DashboardFactory. The ramsey class was never autoloaded at runtime
  (Application.php does not require vendor/autoload.php), so admin
  template creation and template-based dashboard provisioning both
  threw 500.
- DashboardService: stop hardcoding PERMISSION_FULL when auto-creating
  user dashboards. Read defaultPermissionLevel and defaultGridColumns
  from AdminSettingMapper and pass them to DashboardFactory.

Quality
-------
- NamedParametersSniff: skip Entity-magic accessors (set*/get*/is*) on
  classes extending OCP\AppFramework\Db\Entity. Entity::__call uses
  args[0], so named arguments break those calls. Eliminates 4 false
  positives.
- DashboardService: add the missing WidgetPlacement import (PHPMD).
- PageController: replace \OC::server->get(IManager::class) with proper
  DI (Psalm).
- UserAttributeResolver: drop the non-existent IUser::getLanguage() call
  and read 'core/lang' from IConfig instead (Psalm).

Frontend warnings
-----------------
- TileEditor + WidgetStyleEditor: NcSelect was passing 'Icon' as the
  vue-select label key (option.Icon does not exist). Use input-label
  for the visible label and label="label" for the option key.
- AdminSettings: replace HTML <label> + bare NcSelect with input-label
  prop, satisfying NcSelect's accessibility requirement.
- DashboardSwitcher / WidgetPicker / WidgetWrapper / TileEditor /
  WidgetStyleEditor: add aria-label or input-label to icon-only NcButton
  / unlabeled NcTextField / inline color-pick NcButton instances.

Tests: 106 / 106 passing (293 assertions). composer phpcs / phpmd /
psalm clean. End-to-end API verified for conditional rules, admin
templates, prometheus metrics.
* fix(security): Stop leaking exception messages to API clients (ADR-005)

`ResponseHelper::error()` was returning `\$exception->getMessage()` in
the response body, which surfaces internal error detail (stack traces,
DB errors, file paths) to any authenticated caller that triggered a
thrown exception. ADR-005 Rule 5 forbids this: "No stack traces in API
responses; generic messages."

Fix closes the leak immediately by returning a generic
\`"Operation failed"\` message to the client regardless of the
underlying exception.

The helper now accepts two optional parameters for callers that want
to preserve visibility + customise the client message:

- \`?LoggerInterface \$logger\` — when provided, the real exception is
  recorded at ERROR level with full context (`['exception' => \$exception]`)
  before the response is returned.
- \`string \$message='Operation failed'\` — generic client-facing text.

Signature stays backwards-compatible: all 20 existing
\`ResponseHelper::error(exception: \$e)\` call sites across
DashboardApiController, TileApiController, WidgetApiController,
RuleApiController, AdminController continue to work unchanged, and
they now return generic text instead of leaking.

Follow-up (not in this commit): thread \`\$this->logger\` through the
6 controllers that currently don't inject a LoggerInterface so the
server log regains exception visibility. Tracked in docs/adr-audit.md.

* fix(licence): Remove contradictory AGPL SPDX block from 72 PHP files (ADR-014)

Every PHP file in lib/ and tests/Unit/ carried TWO licence declarations
that disagreed: the PHPDoc `@license EUPL-1.2` (correct, Conduction
standard) plus a separate SPDX block declaring
`SPDX-License-Identifier: AGPL-3.0-or-later` with
`SPDX-FileCopyrightText: 2024 MyDash Contributors`. Any REUSE-based
compliance tooling would reject this — two licences on one file is
ambiguous.

Removed the SPDX block from all 72 files. The PHPDoc `@license
EUPL-1.2` + `@copyright 2024 Conduction b.v.` tags are now the single
source of truth. REUSE compliance will be restored by the REUSE.toml
addition in the next commit — path-based annotations map **/*.php to
EUPL-1.2 + Conduction copyright.

Verified: `grep -rl "SPDX-License-Identifier: AGPL" lib/ tests/` returns
zero. Same for `SPDX-FileCopyrightText: 2024 MyDash Contributors`.

The PHPDoc blocks now terminate cleanly after `@link` / `@license`,
no trailing orphan SPDX tags.

* refactor(frontend): @nextcloud/vue → @conduction/nextcloud-vue (ADR-004)

ADR-004 requires Conduction apps to route all Nextcloud Vue components
through the @conduction/nextcloud-vue wrapper instead of importing from
@nextcloud/vue directly. Nine Vue files had direct imports:

- src/components/WidgetWrapper.vue       (NcButton)
- src/components/TileEditor.vue          (NcModal, NcButton, NcTextField, NcSelect, NcColorPicker)
- src/components/WidgetPicker.vue        (NcButton, NcTextField, NcEmptyContent)
- src/views/Views.vue                    (NcButton, NcEmptyContent)
- src/components/TileCard.vue            (NcButton)
- src/components/DashboardSwitcher.vue   (NcSelect)
- src/components/WidgetRenderer.vue      (NcDashboardWidget, NcEmptyContent, NcLoadingIcon)
- src/components/WidgetStyleEditor.vue   (NcModal, NcButton, NcTextField, NcSelect, NcColorPicker, NcCheckboxRadioSwitch)
- src/components/admin/AdminSettings.vue (NcButton, NcSelect, NcSelectTags, NcTextField, NcCheckboxRadioSwitch, NcEmptyContent, NcModal)

All 9 now import from @conduction/nextcloud-vue. The package was already
in package.json dependencies, so no dep bump needed.

Verified: `grep -rn "from '@nextcloud/vue'" src/` returns zero matches.

* chore: Add REUSE.toml + bump Nextcloud max-version to 34

**REUSE.toml**: closes the REUSE-compliance hole from the previous
commit. The SPDX block was removed from 72 PHP files; this declaration
restores machine-readable licence + copyright data via path-based
annotations:

- `**/*.php` → EUPL-1.2 + "2024 Conduction B.V." (matches every PHP
  file's PHPDoc @license/@copyright tags — reuse lint accepts the
  PHPDoc form when the REUSE.toml declares it explicitly)
- Vue / JS / TS / CSS / SCSS / shell → EUPL-1.2 + Conduction
- Config / data / markdown / JSON / YAML / XML / TOML / img / l10n /
  lockfiles → CC0-1.0 (standard Nextcloud-app treatment)

Template-matching shape — same structure as nextcloud-app-template's
REUSE.toml, year bumped to 2024 (MyDash inception) instead of 2026,
and `**/*.toml` added to the CC0 block so REUSE.toml itself is
covered.

**info.xml**: Nextcloud max-version 33 → 34. Template supports 34; no
reason MyDash shouldn't.

Skipped the Makefile dev-link symlink helper from the template — the
template needs it because the repo is cloned as
`nextcloud-app-template` but the <id> is `app-template`. MyDash's
<id> is `mydash` and the repo is cloned as `mydash`, so the symlink
trick is a no-op here.

* docs: Add architecture, ADR index, ADR compliance audit

Three new markdown docs wire into the Docusaurus site (autogenerated
sidebar picks them up automatically):

- `docs/architecture.md` — component map (Controller/Service/Mapper
  layers with concrete class counts), request flow for dashboard
  update, auth posture, DI posture, capability table mapping to
  openspec/specs/, and an explicit "does NOT do" list covering the
  ADRs that are legitimately N/A for MyDash.
- `docs/adr/README.md` — names the canonical ADR location
  (ConductionNL/hydra/openspec/architecture/) and explains why app
  repos no longer carry stale copies (decidesk #71 false-positive
  drift lesson). Quick index of the 23 ADRs.
- `docs/adr-audit.md` — per-ADR compliance matrix for MyDash as of
  2026-04-24. 25 compliant / 4 partial / 2 gaps / 14 N/A. Each row
  carries a status + evidence + link to fix when applicable.

Captures the verified findings from the two-agent audit run at the
start of this PR:

- 0 service-locator escapes in lib/ (fixed in commit 5712903)
- `ResponseHelper::error` no longer leaks (commit 9be1255)
- 72 PHP files have clean EUPL-1.2 headers (commit ab9fc70)
- 9 @nextcloud/vue imports swapped (commit 222cf3f)
- REUSE.toml + max-version bump (commit b5ca3a4)

The two remaining `❌` gaps — @SPEC annotation pass + Newman
collection — are flagged for OpenSpec changes + Hydra pickup in the
next commit.

Corrects the earlier audit's false-alarm IDOR claim (agent 2 stopped
at controller null-check, didn't trace through to the service-layer
ownership verification). ADR-005 per-object auth is actually PASS
once you follow DashboardApiController::update → PermissionService →
DashboardService's ownership guard.

* spec: Two OpenSpec changes for the remaining ADR gaps

Converts the two ❌ items from docs/adr-audit.md into formal OpenSpec
changes so Hydra's pipeline can pick them up once this PR merges.

**spec-annotation-pass-2026-04-24/** — Close the ADR-003 `@spec` tag
gap. Runs `/opsx-annotate mydash` against the 66 methods already
classified by `openspec/coverage-report.md` (61 Bucket 1 + 5 Bucket
2b from the legacy-widget-bridge retrofit). Design.md resolves the
3 NEEDS-REVIEW flags from the coverage scan:

- `DashboardResolver::getEffectivePermissionLevel` vs
  `PermissionService::getEffectivePermissionLevel` — both tag with
  `@spec permissions/spec.md#requirement-effective-permission-level`;
  PermissionService is the authoritative impl, DashboardResolver is
  a thin delegator.
- `MyDashAdmin::getForm` + `MyDashAdminSection::getID` — treat as
  plumbing (Nextcloud admin-UI boilerplate, no domain behaviour),
  skip `@spec` annotation with an explanatory docblock note.

**newman-integration-suite-2026-04-24/** — Close the ADR-008 Newman
gap. Adds `tests/integration/mydash.postman_collection.json` covering
all 17 OCS endpoints across 6 folders (Health+Metrics, Dashboards,
Tiles, Widgets, Rules, Admin). Fixture setup/teardown pattern avoids
test pollution; admin-vs-member auth branch tests verify
authorization posture. `.github/workflows/code-quality.yml` already
has `enable-newman: true` — the collection is all that's missing.

Each change has proposal.md + design.md + tasks.md + specs/README.md
explaining that they're annotation-only / test-only (no Requirement
deltas needed). Format mirrors the archived retrofits in
openspec/changes/archive/.

GitHub issues + Hydra dispatch handled in a follow-up — spec files
land on development first so Hydra can locate them.

* chore: update SBOM

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Release: merge development into beta
…/nextcloud-vue (ADR-004) (#71)

Two stragglers added by widget-add-edit-modal (#65/#68 runtime-shell) and
widget-context-menu (#60) before ADR-004 landed via #34. Swap is mechanical:
NcButton + NcEmptyContent are already exported by @conduction/nextcloud-vue
and used elsewhere in the codebase (TileCard, WidgetRenderer, AdminSettings).
…ADR-005) (#80)

12 sites in 3 controllers were forwarding `$e->getMessage()` from
DoesNotExistException / InvalidArgumentException / generic Exception
straight into the response body. Replaced each with a fixed safe string
matching the HTTP status semantics:

- 4 × `DoesNotExistException` → 404 → `'Dashboard not found'`
- 4 × generic `Exception` → 403 → `'Forbidden'`
- 3 × `InvalidArgumentException` → 400 → `'Invalid request'`
- 1 × `InvalidArgumentException` (groups payload) → 400 → `'Invalid groups payload'`

Kept 2 sites unchanged: `PersonalDashboardsDisabledException` returns its
own `getErrorCode()` + a safe translated message designed for clients.

Also kept logger->warning/error sites with `$e->getMessage()` — those are
server-side log context, never reach the client.

Affected files:
- lib/Controller/AdminController.php
- lib/Controller/DashboardApiController.php
- lib/Controller/DashboardShareApiController.php
The Commit SBOM step ran a direct `git push` on every push trigger.
On protected branches (main / development / beta) the org ruleset
rejects unbypassed pushes, so the workflow failed with `GH013:
Repository rule violations` — visible as the red `SBOM Generation &
Validation` check on every release PR.

Split the post-build step in two:

- Unprotected branches (feature/**, bugfix/**, hotfix/**) keep the
  fast direct-commit path — no protection, no friction.
- Protected branches use peter-evans/create-pull-request to open
  (or update) `chore/sbom-update-<branch>` with the regenerated
  `sbom.cdx.json`, base'd on the same branch. A reviewer merges as
  with any other PR, so the org ruleset's "1 review" requirement is
  honoured.
- pull_request events skip the commit step entirely. The SBOM is
  already validated by the preceding scan steps; once the PR merges,
  this same workflow re-runs against the target branch and goes down
  one of the two paths above.

Adds explicit job-level `permissions: contents: write,
pull-requests: write` so the bot can write commits / open PRs without
relying on whatever the repo's default permissions happen to be.
… openspec (#74)

* docs(openspec): scrub sendent name from image-widget + link-button proposals

Replaces "Sendent-era code" / "Sendent UX bug" / "(Sendent, Grafana, ...)"
phrasing with neutral wording ("an earlier prototype", "deliberate UX
choice", "Grafana, Microsoft Power BI tiles, and similar"). The
technical content is unchanged.

* docs: add walkthrough findings + admin/customize panel screenshots

Captures notes from a working session through the dashboard flows
(mydash-walkthrough/findings.md) plus screenshots of the admin
settings, customize-panel (dashboards + widgets), and template-create
modal for inclusion in the docs site.

* chore: update SBOM

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
…eHelper (#75)

- Apply phpcbf autofix for parameter-name + type spacing alignment
  in the docblocks (17 violations).
- Replace the inline ternary on PageController:101 with an explicit
  if-block; inline IF statements are blocked by the project sniffs and
  the line was 136 chars (over the 125 limit).

Unblocks the release PR (#72) which was failing the
`quality / PHP Quality (phpcs)` check.
…reate (#77)

DashboardFactoryTest::testCreateRespectsExplicitPermissionLevel was
failing with "Unknown named parameter \$permissionLevel" because the
factory hard-coded \$dashboard->setPermissionLevel(PERMISSION_FULL) and
exposed no way for callers (or share / fork flows) to override it.

Add the param, default to PERMISSION_FULL (preserving existing
behaviour for the 9 other tests + every production caller, which are
all positional), and pass it through to setPermissionLevel(). Tests:
354 / 354 passing.
…ame (#78)

PHPStan was silently passing on CI (the composer phpstan script swallows
its exit code via `|| echo 'PHPStan not installed, skipping...'`), so two
real bugs in Notifier.php had been sitting unflagged:

1. The L10N type-hints / docblocks referenced `\OCP\L10N\IL10N`, but
   that class doesn't exist — the actual interface lives at `\OCP\IL10N`
   (lib/public/IL10N.php in the server). Wrong type-hint meant DI would
   have failed at runtime as soon as a `dashboard_shared` notification
   was rendered. 6 occurrences fixed (2 sites × {docblock, signature}
   plus permissionLabel).

2. Two `setParsedMessage(subject: $x)` calls used the wrong named
   argument — INotification::setParsedMessage's parameter is
   `$message`, not `$subject`. Same wrong-named-arg pattern PHP's
   strict mode rejects with "Unknown named parameter".

After the fix, `phpstan analyse lib/Notification/Notifier.php` is clean
(was 4 errors), and the full-repo phpstan count drops 47 → 26 because
the IL10N typo was poisoning every `$l->t(...)` callsite downstream.

phpcbf-autofixed the docblock alignment that shifted as a side effect
of shortening `\OCP\L10N\IL10N` → `\OCP\IL10N` (5 chars narrower).
PR #78 dropped phpstan from 47 → 26 by fixing the IL10N namespace
typo. This batch clears the rest in four contained groups and
removes the `|| echo` swallow from the composer script so future
regressions actually fail CI.

DashboardShareApiController (21): swap `DataResponse` →
`JSONResponse` throughout the controller, matching the pattern used
by every other passing controller in the app (DashboardApiController,
ResponseHelper). The two classes are wire-compatible (both extend
Response, same constructor signature, both serialise to JSON), but
phpstan can resolve the JSONResponse generics from the existing
docblocks while the bare DataResponse type was forcing it to
infer T = `array<string, string>` and reject the call.

AdminSettingMapper (2): the entity's setUpdatedAt() expects
`?string`, but setSetting() was passing a `DateTime` object.
Format to `Y-m-d H:i:s` to match the convention from
DashboardFactory::create().

AdminTemplateService (1): replace `Ramsey\Uuid\Uuid::uuid4()` with
the same `random_bytes`-based generator used by DashboardFactory.
Ramsey UUID is not a declared composer dep, so the previous code
would have crashed at runtime — this surfaced because phpstan
correctly couldn't resolve the class.

FileService (2): two `Node::nodeExists()` / `Node::newFile()` /
`Node::putContent()` calls were made on results of `Folder::get()`,
which returns the `Node` interface. Type-narrow with explicit
`instanceof Folder` / `instanceof File` checks (throwing
`RuntimeException` on the impossible-but-typed branch) so phpstan
can verify the methods exist. Side benefit: removes a stale
`@phpstan-ignore-next-line` that was silencing a real downstream
error.

composer.json: drop the `|| echo 'PHPStan not installed, skipping...'`
from the `phpstan` script. The `||` made every phpstan run exit 0,
so the CI quality job was reporting green even when phpstan failed
— exactly how the IL10N typo and the Ramsey-UUID call slipped through
review. Now `composer phpstan` propagates the real exit code.

Verified locally: phpstan `[OK] No errors`, phpcs clean across
49 files, phpunit 354/354 (the one pre-existing
DashboardFactoryTest::testCreateRespectsExplicitPermissionLevel
failure is fixed by PR #77 and out of scope here).
Release: merge development into beta
Co-authored-by: rubenvdlinde <4021899+rubenvdlinde@users.noreply.github.com>
#81)

Lockfile-only update via `npm audit fix` (no --force, no
package.json changes, no major bumps). Resolves every high-severity
npm vulnerability flagged on the development branch:

- fast-xml-parser CVE chain (incl. Dependabot alerts #52, #70):
  4.5.4 → 4.5.6 (entity-expansion bypass + comment/CDATA injection)
- lodash prototype pollution
- node-forge prototype pollution
- path-to-regexp ReDoS
- picomatch ReDoS

As a side effect npm chose @conduction/nextcloud-vue 0.1.0-beta.15
(was beta.3), picking up the 12 intervening upstream releases
including all the recent CnAppNav/CnAppRoot work. The Nc* re-export
issue blocking eslint is unaffected — that lands when nextcloud-vue
PR #102 ships and a new beta is published.

Remaining: 12 moderate + 32 low. All require major-version bumps
(@nextcloud/webpack-vue-config v7, vue-loader v17, @vue/test-utils v2
which is Vue-3-only and would break the app, etc.) — out of scope
for an audit-fix sweep, deserve dedicated PRs with build/test
verification.

Verified locally:
- `npm run build` succeeds (35 webpack warnings, same baseline as dev)
- `npm run lint` no new errors (the 32 Nc* `import/named` errors are
  pre-existing, fixed by nextcloud-vue PR #102)
- `npm audit` reports 0 high-severity vulnerabilities (was 6)
Co-authored-by: rubenvdlinde <4021899+rubenvdlinde@users.noreply.github.com>
Release: merge development into beta
…asset only) (#86)

The central Quality workflow (ConductionNL/.github#34) now publishes SBOMs
exclusively as release assets — see SECURITY.md "Software Bill of Materials".

This PR cleans up the per-app remnants:
- delete .github/workflows/sbom.yml (the central job replaces it)
- delete the checked-in sbom.cdx.json (release asset is the source of truth)
- gitignore SBOM files so future generations don't accidentally land in repo

Stable URL for clients:
  https://github.com/ConductionNL/mydash/releases/latest/download/sbom.cdx.json

Co-authored-by: SBOM Cleanup <ops@conduction.nl>
…cloud sections

Cleans up the .gitignore around three problems:

- Duplicates: /node_modules/, /node_modules/*, node_modules/ all
  appearing — and likewise for /vendor/. Reduce to the canonical
  /node_modules/ + /vendor/ form.
- Missing entries that have actually bitten the repo:
  - /*.iml + *.Identifier (IntelliJ-derived editors)
  - /docusaurus/{node_modules,build,.docusaurus}/ (the docs build
    artifacts were only partially ignored)
  - /coverage-frontend/, /quality-reports/, /tests/.phpunit.cache,
    /.php-cs-fixer.cache (test/quality artifacts)
  - /custom_apps/, /config/ (when checked out inside a Nextcloud
    server tree — applies to the dev-docker setup)
  - .claude/worktrees/ (matches the workflow we converged on for
    parallel-agent work in this repo)
- Reorganise into clearly labelled sections (IDE, OS, Editor swap,
  Claude Code, Dependencies, Build artifacts, Documentation,
  Testing & Quality, PHP, Environment, Nextcloud, SBOM) so it's
  easier to scan and add to.

Preserves every entry the previous .gitignore actually used —
notably the SBOM ignores added by #86, the .env / .env.local
entries, and the editor swap files.

Builds on / supersedes the abandoned chore/19/harmonize-gitignore
branch which was based on a much older dev tip and would have
dropped the SBOM + env entries on merge.
chore: harmonize .gitignore — dedupe + add IDE/docs/Nextcloud sections
Three small fixes to clear the failing checks on PR #87 (release
development → beta).

DashboardShareApiController — re-apply the DataResponse →
JSONResponse swap from #79. PR #80 (security) was opened from a
base before #79 landed; when #80 merged, its diff base contained
the old DataResponse code, silently reverting the swap. Brings the
21 phpstan errors in this controller back to zero, same as the
already-merged #79 fix.

AdminTemplateService:328 — collapse a stray double blank line
after pickFirstMatch() into a single blank line. Single phpcs
error introduced by #79's generateUuid() addition (added \n\n
between methods instead of \n).

.license-overrides.json — add apexcharts@5.10.6. license-checker
flags it as `Custom: https://apexcharts.com/media/apexcharts-logo.png`
because it picks up a stray HTTP URL from the package's README;
the actual project is MIT-licensed (LICENSE file in the repo). It
arrives as a transitive dep through @conduction/nextcloud-vue, so
this is a generic fix not tied to any one feature.

NOT included: the eslint Nc* import/named errors. Those are blocked
on nextcloud-vue cutting a release from development (PR #102 merged
into ncvue/development, not yet propagated to ncvue/main where
semantic-release publishes from).

Verified locally:
- composer phpstan → [OK] No errors (was 21)
- composer phpcs   → clean (was 1)
- composer test:unit → 354/354
fix: re-apply JSONResponse swap (reverted by #80) + phpcs nit + apexcharts license
Release: merge development into beta
…ile)

173 commits from beta. Three conflicts resolved:

- appinfo/info.xml: take beta's version + license
  (1.0.3-unstable.4 + EUPL-1.2 — main was 1.0.2 + agpl,
  the EUPL-1.2 transition happened on the development line
  weeks ago).
- package.json: take beta's full devDependency set
  (vitest + @vue/test-utils + @vitejs/plugin-vue2 +
  @nextcloud/webpack-vue-config@^5.5.0). Main's
  webpack-vue-config@^7.0.2 (from dependabot #69) had peer
  conflicts with beta's locked webpack-cli, so a clean
  install couldn't satisfy both. Conservative choice:
  keep beta's known-good build chain; re-bump
  webpack-vue-config to v7 in a follow-up PR with proper
  peer-dep upgrades.
- package-lock.json: take beta's lockfile then
  `npm install` to regenerate against the merged
  package.json. Result: 1521 packages installed
  cleanly (1 deprecation warning for eslint@8.57.1,
  same as on beta).

Closes the 173-commit gap that built up while beta was
moving forward — most notably brings the npm audit fix
(#81 — clears all 6 high-severity vulnerabilities), the
phpcs/phpstan/eslint fixes (#75/#79/#90), the dashboard
sharing + widget feature batch (#44#68), and the
@nextcloud/vue → @conduction/nextcloud-vue swap (#71)
into the stable branch.
@rubenvdlinde rubenvdlinde merged commit b64a9bf into main May 1, 2026
14 of 18 checks passed
@rubenvdlinde rubenvdlinde deleted the chore/beta-to-main-merge branch May 1, 2026 13:00

jobs:
check:
uses: ConductionNL/.github/.github/workflows/branch-protection.yml@main
Comment on lines +12 to +20
uses: ConductionNL/.github/.github/workflows/quality.yml@main
with:
app-name: mydash
php-version: "8.3"
enable-psalm: true
enable-phpstan: true
enable-phpmetrics: true
enable-frontend: true
enable-eslint: true
Comment on lines +11 to +13
uses: ConductionNL/.github/.github/workflows/documentation.yml@main
with:
cname: mydash.app
Comment on lines +15 to +20
uses: ConductionNL/.github/.github/workflows/issue-triage.yml@feature/openspec-project-sync
with:
app-name: mydash
backlog-existing: ${{ github.event_name == 'workflow_dispatch' && inputs.backlog-existing || false }}
secrets:
PROJECT_TOKEN: ${{ secrets.PROJECT_TOKEN }}
Comment on lines +11 to +15
uses: ConductionNL/.github/.github/workflows/openspec-sync.yml@feature/openspec-project-sync
with:
app-name: mydash
secrets:
PROJECT_TOKEN: ${{ secrets.PROJECT_TOKEN }}
Comment on lines +10 to +13
uses: ConductionNL/.github/.github/workflows/release-beta.yml@main
with:
app-name: mydash
secrets: inherit
Comment on lines +10 to +13
uses: ConductionNL/.github/.github/workflows/release-stable.yml@main
with:
app-name: mydash
secrets: inherit

jobs:
sync:
uses: ConductionNL/.github/.github/workflows/sync-to-beta.yml@main
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 1, 2026

Quality Report — ConductionNL/mydash @ b3ac301

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 100/100
npm ✅ 498/498
PHPUnit ⏭️
Newman ⏭️
Playwright ⏭️

Quality workflow — 2026-05-01 13:01 UTC

Download the full PDF report from the workflow artifacts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants