Release: merge development into beta - #67
Conversation
release: Promote external-source installs + PAT + discovery to main
…pdates Bumps the npm_and_yarn group with 10 updates in the / directory: | Package | From | To | | --- | --- | --- | | [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `7.3.1` | `7.3.3` | | [brace-expansion](https://github.com/juliangruber/brace-expansion) | `1.1.12` | `1.1.14` | | [picomatch](https://github.com/micromatch/picomatch) | `4.0.3` | `4.0.4` | | [picomatch](https://github.com/micromatch/picomatch) | `2.3.1` | `2.3.2` | | [picomatch](https://github.com/micromatch/picomatch) | `4.0.2` | `4.0.4` | | [axios](https://github.com/axios/axios) | `1.13.5` | `1.16.0` | | [dompurify](https://github.com/cure53/DOMPurify) | `3.3.1` | `3.4.2` | | [flatted](https://github.com/WebReflection/flatted) | `3.3.3` | `3.4.2` | | [immutable](https://github.com/immutable-js/immutable-js) | `4.3.5` | `4.3.8` | | [lodash](https://github.com/lodash/lodash) | `4.17.23` | `4.18.1` | | [postcss](https://github.com/postcss/postcss) | `8.5.6` | `8.5.14` | | [yaml](https://github.com/eemeli/yaml) | `2.8.2` | `2.8.4` | Updates `vite` from 7.3.1 to 7.3.3 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v7.3.3/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v7.3.3/packages/vite) Updates `brace-expansion` from 1.1.12 to 1.1.14 - [Release notes](https://github.com/juliangruber/brace-expansion/releases) - [Commits](juliangruber/brace-expansion@v1.1.12...v1.1.14) Updates `picomatch` from 4.0.3 to 4.0.4 - [Release notes](https://github.com/micromatch/picomatch/releases) - [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md) - [Commits](micromatch/picomatch@4.0.3...4.0.4) Updates `picomatch` from 2.3.1 to 2.3.2 - [Release notes](https://github.com/micromatch/picomatch/releases) - [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md) - [Commits](micromatch/picomatch@4.0.3...4.0.4) Updates `picomatch` from 4.0.2 to 4.0.4 - [Release notes](https://github.com/micromatch/picomatch/releases) - [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md) - [Commits](micromatch/picomatch@4.0.3...4.0.4) Updates `axios` from 1.13.5 to 1.16.0 - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](axios/axios@v1.13.5...v1.16.0) Updates `dompurify` from 3.3.1 to 3.4.2 - [Release notes](https://github.com/cure53/DOMPurify/releases) - [Commits](cure53/DOMPurify@3.3.1...3.4.2) Updates `flatted` from 3.3.3 to 3.4.2 - [Commits](WebReflection/flatted@v3.3.3...v3.4.2) Updates `follow-redirects` from 1.15.11 to 1.16.0 - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](follow-redirects/follow-redirects@v1.15.11...v1.16.0) Updates `immutable` from 4.3.5 to 4.3.8 - [Release notes](https://github.com/immutable-js/immutable-js/releases) - [Changelog](https://github.com/immutable-js/immutable-js/blob/main/CHANGELOG.md) - [Commits](immutable-js/immutable-js@v4.3.5...v4.3.8) Updates `lodash` from 4.17.23 to 4.18.1 - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](lodash/lodash@4.17.23...4.18.1) Updates `postcss` from 8.5.6 to 8.5.14 - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](postcss/postcss@8.5.6...8.5.14) Updates `yaml` from 2.8.2 to 2.8.4 - [Release notes](https://github.com/eemeli/yaml/releases) - [Commits](eemeli/yaml@v2.8.2...v2.8.4) --- updated-dependencies: - dependency-name: vite dependency-version: 7.3.3 dependency-type: direct:development dependency-group: npm_and_yarn - dependency-name: brace-expansion dependency-version: 1.1.14 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: picomatch dependency-version: 4.0.4 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: picomatch dependency-version: 2.3.2 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: picomatch dependency-version: 4.0.4 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: axios dependency-version: 1.16.0 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: dompurify dependency-version: 3.4.2 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: flatted dependency-version: 3.4.2 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: follow-redirects dependency-version: 1.16.0 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: immutable dependency-version: 4.3.8 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: lodash dependency-version: 4.18.1 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: postcss dependency-version: 8.5.14 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: yaml dependency-version: 2.8.4 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com>
Defense against supply-chain attacks (e.g. shai-hulud, nx-style compromised publishes) by blocking install of any package version published less than 24h ago. - .npmrc: new; `min-release-age=1` (npm 11.5+ native; older npm ignores it) - .github/dependabot.yml: add `cooldown.default-days: 1` to the existing npm entry, with @conduction/* excluded so first-party releases reach this repo immediately For release-day consumption of fresh @conduction/* deps, use `npm install --min-release-age=0 @conduction/pkg@x.y.z`.
Quality Report — ConductionNL/app-versions @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ❌ | ||||
| phpcs | ❌ | ||||
| phpmd | ❌ | ||||
| psalm | ❌ | ||||
| phpstan | ❌ | ||||
| phpmetrics | ❌ | ||||
| eslint | ❌ | ||||
| stylelint | ❌ | ||||
| composer | ❌ | ❌ | |||
| npm | ✅ | ✅ 282/282 | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-05-25 20:11 UTC
Download the full PDF report from the workflow artifacts.
Quality Report — ConductionNL/app-versions @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ❌ | ||||
| psalm | ❌ | ||||
| phpstan | ❌ | ||||
| phpmetrics | ❌ | ||||
| eslint | ❌ | ||||
| stylelint | ❌ | ||||
| composer | ✅ | ✅ 7/7 | |||
| npm | ✅ | ✅ 282/282 | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-05-25 23:49 UTC
Download the full PDF report from the workflow artifacts.
…from Conduction/.github
….yml from Conduction/.github
…yml from Conduction/.github
…from Conduction/.github
Generated from Conduction/nextcloud-app-template's docs/ structure with per-app config (title, tagline, url, projectName, editUrl, Codeberg link) substituted in docusaurus.config.js. Live at https://app-versions.conduction.nl. Scaffolded by autonomous fleet docs rollout to give every Conduction app a working Docusaurus site that the central documentation workflow can keep deploying via the existing wrapper at .forgejo/workflows/documentation.yml.
… directory with 11 updates' (#1) from dependabot/npm_and_yarn/npm_and_yarn-359624454c into main automerge: app-versions#1
Production-readiness hardening. Psalm now reports 0 errors across lib/ while the 91-test unit suite, php-cs-fixer, and php -l stay green. - Add tests/stubs/server-internals.php (wired via psalm.xml <stubs>) declaring the runtime-only Nextcloud/transitive classes the installer reuses (OC, OC_App, OC\Archive\TAR/ZIP, OC\Files\FilenameValidator, OC\AppFramework\Bootstrap\ Coordinator, OC\DB\Connection/MigrationService, phpseclib\File\X509, PEAR_Error, Doctrine\DBAL\Schema\Table). These exist at runtime but are not in nextcloud/ocp. - Migrate deprecated APIs: IConfig::get/set/deleteAppValue -> IAppConfig, Util::getVersion() -> ServerVersion::getVersionString(), IAppManager::getInstalledApps() -> getEnabledApps(), OCP\Files::rmdirr() -> private recursive-delete helpers. - Tighten boundary types from parsed info.xml / json_decode (is_* guards, casts, precise array shapes) to clear the Mixed* cascade. - Mark DI/route/event entry points with @psalm-api to remove findUnusedCode false positives; delete genuinely-dead members (findOwnedBy, two toArray()). - Fix latent bug in InstallerService: the 3-arg version_compare() returns bool, so the `=== 0/1/-1` comparisons were always false (dead already-installed early-return + inverted upgrade/downgrade labels). Now correct. - exclude tests/stubs from php-cs-fixer. Update Source/Discovery unit-test mocks for the IAppConfig constructor changes.
Complete the GitHub->Codeberg migration in the docs site (the app code +
metadata were already retargeted). Swaps github.com/ConductionNL/app-versions
-> codeberg.org/Conduction/app-versions and the browse-path scheme
/tree/<branch>/ -> Codeberg's /src/branch/<branch>/, plus the navbar/CTA
labels (GitHub -> Codeberg) and the org contact link.
Genuine GitHub-source feature references (GitHub releases as an install
source, private-repo PATs, owner/repo examples) are intentionally kept.
Files: docusaurus.config.js, src/pages/index.js, intro.md, static/llms.txt,
tutorials/admin/{01-admin-settings,02-rollback,03-github-source}.md
…lize recovery Improve how App Versions reports and handles install failures (issue #10): - Structured failure payloads: every failed install returns stage, category and an actionable hint regardless of the debug toggle, with category-driven HTTP status (409/422/502) instead of a blanket 500 (FailureClassifier). - Pre-flight environment checks: getInstalledApps() flags non-writable / dev-checkout app folders (manageable + warning), and installAppVersion() fails fast with preflight_permission before downloading (EnvironmentCheck). - Finalize-phase recovery: both installers retain the .appversion-backup until finalize() succeeds and restore on failure; new installStatus taxonomy installed / reverted / installed-but-broken (InstallFailure), surfacing honestly that DB migrations cannot be auto-rolled back. - Frontend: prefer the structured backend message/hint over the OCS meta message, render stage/category/hint, and show a warning badge on non-manageable app cards. Adds unit tests for the classifier, environment check and orchestrator outcomes. Refs #10.
…, surface-external-sources-ui Planning artifacts (proposal/design/specs/tasks/plan.json) for the three-change program that moves App Versions into admin settings, adds Codeberg as a forge (generic forge abstraction, access-token auth), and surfaces GitHub+Codeberg source/token/trusted-allowlist management in the admin-settings UI. Tracked on Codeberg as issues #11, #12, #13. Discovery UI and OAuth auth are deferred. Refs #11 #12 #13.
…2 June 2026) + conduction.nl link
Relocate the app from the top-nav navigation entry to a Nextcloud admin Settings section (issue #11): - Add Sections\AdminSection (IIconSection) + Settings\Admin (ISettings), registered via appinfo/info.xml <settings>; reuses the existing Vue SPA template as the settings form body (empty renderAs). - Remove the <navigations> block from info.xml. - Remove PageController + its FrontpageRoute (GET /): the UI is now served only via the admin settings form, so non-admins have no page shell to load. - Unit tests for both classes. Refs #11.
Generalize the GitHub-specific release/auth code into a forge abstraction so GitHub and Codeberg (Forgejo) share one driver, validator and trust model (issue #12). Auth is via access tokens. - Forge value object + ForgeRegistry (github, codeberg configs: api base, auth scheme Bearer|token, scope-header presence, token-create URL). - GithubReleaseSource → generic ForgeReleaseSource(forge); GitHub behaviour unchanged, Codeberg lists/resolves via the Forgejo API. - SourceBinding gains a `forge` field (default github); id is now {forge}:owner/repo; legacy {kind:github-release} rows load as github. Adds SourceBinding::codeberg(). - SourceRegistry resolves both forges to one driver; parseSourceId/listAvailable handle codeberg. - TrustedSourceList: forge-qualified patterns; legacy bare patterns normalize to github:; default now github:ConductionNL/* + codeberg:Conduction/*. - PatValidator is forge-aware (Codeberg via Forgejo /user, accepted with an unverifiable-scope warning). Pat gains a `forge` column (migration Version1001Date20260609120000, default github) + KIND_FORGE_TOKEN. - PatResolver matches a token's forge to the binding's forge. - PatDeeplinkBuilder gains a Codeberg token-create deeplink. Full backward compatibility for persisted GitHub bindings, source-ids and PAT rows. Discovery and OAuth remain out of scope. Refs #12.
…ternal-sources-ui
Backend for surface-external-sources-ui (issue #13), part 1 of 2: - InstallerService::addTrustedPattern()/removeTrustedPattern() with curated, forge-qualified pattern construction and over-broad-glob rejection (no whole-forge or match-everything globs; concrete owner required). - ApiController: POST /api/trusted-sources and DELETE /api/trusted-sources/{pattern} (admin-only, password-confirmed); listing reuses GET /api/sources. - Forge-aware bind/token endpoints: bindSource, createPat (forge → validate + KIND_FORGE_TOKEN) and patDeeplink now accept a `forge` param; PatManager::create stores the forge. Backward compatible (defaults to github). - Unit tests: curated add/remove, dangerous-glob rejection set, non-admin 403. Frontend (tabbed admin UI + Sources/Tokens/TrustedSources panels + shell swap) is the remaining part. Refs #13.
…d sources Frontend for surface-external-sources-ui (issue #13), part 2 of 2: - Swap the NcContent/NcAppContent app-shell for a settings-section container and add an in-component tab bar (Apps / Sources / Tokens / Trusted sources); the existing apps→versions→install view becomes the default Apps tab. - SourcesPanel: bind an installed app to a github/codeberg owner/repo source. - TokensPanel: list/add/share/delete access tokens with per-forge create deeplink; targetPattern derived from forge + owner. - TrustedSourcesPanel: list/curated-add (with explicit trust confirmation)/remove forge-qualified allowlist patterns; surfaces backend rejection messages. - Shared src/ocs.ts request helpers (GET/write + password confirmation). Verified with `vite build` (compiles cleanly). Follow-ups for live verification: richer NC form components (NcTextField/NcSelect) over native inputs, app-wide t() i18n, eslint (blocked by a pre-existing server-root config issue), and the manual settings-panel walkthrough. Refs #13.
Browser/HTTP verification found DELETE /api/trusted-sources/{pattern} returning
404 on Apache: with AllowEncodedSlashes Off (the default), the %2F in an
encoded forge-qualified pattern is rejected before the request reaches
Nextcloud, so removing a trusted source always failed.
Switch the pattern to a URL-encoded query parameter
(DELETE /api/trusted-sources?pattern=…), which carries %2F without any server
config change. Re-verified live: add then delete round-trips to 200 and the
pattern is removed. Updates the controller, TrustedSourcesPanel, the non-admin
test, and the spec/design/tasks. Refs #13.
Quality Report — ConductionNL/app-versions @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ❌ | ||||
| psalm | ❌ | ||||
| phpstan | ❌ | ||||
| phpmetrics | ❌ | ||||
| eslint | ✅ | ||||
| stylelint | ❌ | ||||
| composer | ✅ | ✅ 7/7 | |||
| npm | ✅ | ✅ 282/282 | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-07-24 00:26 UTC
Download the full PDF report from the workflow artifacts.
- Lkg::toArray() now declares the precise return shape so InstallerService::getInstalledApps()'s docblock matches what psalm infers for the enriched app-card array (was MoreSpecificReturnType / LessSpecificReturnStatement). - Drop the always-false ($includeDebug) debug-branch ternary in the downgrade-guard early return — the guard only fires when !$includeDebug, so the branch was dead (TypeDoesNotContainType / RedundantCondition). - Explicitly type the orphanedMigrations value read off the (loosely typed) signed-installer result before assigning it to the payload (MixedAssignment). Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Quality Report — ConductionNL/app-versions @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ❌ | ||||
| psalm | ❌ | ||||
| phpstan | ❌ | ||||
| phpmetrics | ❌ | ||||
| eslint | ✅ | ||||
| stylelint | ❌ | ||||
| composer | ✅ | ✅ 7/7 | |||
| npm | ✅ | ✅ 282/282 | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-07-24 00:35 UTC
Download the full PDF report from the workflow artifacts.
Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Quality Report — ConductionNL/app-versions @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ❌ | ||||
| psalm | ❌ | ||||
| phpstan | ❌ | ||||
| phpmetrics | ❌ | ||||
| eslint | ✅ | ||||
| stylelint | ❌ | ||||
| composer | ✅ | ✅ 7/7 | |||
| npm | ✅ | ✅ 282/282 | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-07-24 00:37 UTC
Download the full PDF report from the workflow artifacts.
…from debug (#101) Gives App Versions a first-class occ surface so provisioning scripts, CI pipelines, and Docker image builds can reproduce an exact app version — the capability core's occ app:install lacks (nextcloud/server#36940). - lib/Command/ListVersions.php — `occ app_versions:versions <appId> [--source=] [--json]`, table or JSON output with a per-version compatibility marker relative to the installed version. - lib/Command/InstallVersion.php — `occ app_versions:install <appId> <version> [--source=] [--dry-run] [--allow-downgrade] [--json]`, delegating entirely to InstallerService::installAppVersion (no duplicated install logic) with the documented 0-9 exit-code map and a self/core-app trust-context guard. Neither command password-confirms (CLI trust context matches core occ semantics). - InstallerService::isManageableApp() — the self/core-app guard extracted from the duplicated inline check in getAppVersions()/installAppVersion() into one shared, publicly reusable predicate. - InstallerService::installAppVersion() gains an explicit optional $dryRun parameter, independent of $includeDebug (verbosity only); the server-side downgrade guard now checks !$dryRun instead of !$includeDebug — fixes a latent bug where debug=1 alone would have silently bypassed the guard on a real downgrade once dryRun became independent. Omitting $dryRun preserves every existing caller's legacy behavior (falls back to $includeDebug). - ApiController::installVersion reads an independent `dryRun` request parameter; `debug=1` without an explicit `dryRun` still implies a dry run (deprecated) and the response carries a deprecationNotice. - src/App.vue splits the single conflated "install dry-run / debug" checkbox into two independent toggles (Dry run, Show install debug output), each sent as its own explicit request parameter. - docs/cli.md documents both commands, flags, the exit-code table, and a reproducible Docker-build provisioning example. openapi.json is unchanged — vendor/bin/generate-spec (openapi-extractor v1.8.7) hard-fails on this app's EUPL-1.2 license unrelated to this change; the /install endpoint was already undocumented before this PR. Tests: 396 unit tests green (was 166 on development), including new coverage for the exit-code map, both commands' guards, the dryRun/debug resolution matrix, and a regression test for the downgrade-guard fix. Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Quality Report — ConductionNL/app-versions @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ❌ | ||||
| psalm | ❌ | ||||
| phpstan | ❌ | ||||
| phpmetrics | ❌ | ||||
| eslint | ✅ | ||||
| stylelint | ❌ | ||||
| composer | ✅ | ✅ 15/15 | |||
| npm | ✅ | ✅ 282/282 | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-07-24 01:05 UTC
Download the full PDF report from the workflow artifacts.
Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Quality Report — ConductionNL/app-versions @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ❌ | ||||
| psalm | ❌ | ||||
| phpstan | ❌ | ||||
| phpmetrics | ❌ | ||||
| eslint | ✅ | ||||
| stylelint | ❌ | ||||
| composer | ✅ | ✅ 15/15 | |||
| npm | ✅ | ✅ 282/282 | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-07-24 01:07 UTC
Download the full PDF report from the workflow artifacts.
Per-app semver-level auto-update policies (none/patch/minor/all) executed
nightly through the standard installer path, honoring pins, bounded to a
configurable maintenance window, defaulting off, and reporting every
outcome as an admin notification.
- lib/Service/Policy/{Policy,PolicyStore} — policy.{appId} app-config JSON,
mirroring PinStore/SourceBindingStore's malformed-JSON-is-safe pattern.
- lib/Service/AutoUpdate/CandidateSelector — pure semver-level candidate
selection (patch/minor/all; pre-release and non-semver versions never
qualify for patch/minor).
- lib/Service/AutoUpdate/AttemptLedger — auto_attempt.{appId} never-retry
ledger, pruned to the last 10 entries per app.
- lib/Service/AutoUpdate/AutoUpdateWindow — HH:MM-HH:MM window parsing and
containment, including midnight-crossing windows.
- lib/Service/AutoUpdate/AutoUpdateSettingsStore — global kill switch
(default off) + window (default 01:00-05:00) app-config values.
- lib/Service/AutoUpdate/AutoUpdateNotifier + Notifier subjects
auto_update_success/auto_update_failure.
- lib/BackgroundJob/AutoUpdateJob — daily TimedJob; no-ops when disabled or
outside the window, skips pinned apps without a source query, installs
through InstallerService::installAppVersion only (which already writes
the audit trail), per-app try/catch isolation.
- ApiController: GET /api/policies, PUT/DELETE /api/app/{appId}/policy,
PUT /api/auto-update/settings (password-confirmed writes, 400 on an
invalid level or malformed window, 403 non-admin).
- Frontend: PolicySelector.vue (NcSelect per-app policy control with an
active badge and an "automation disabled" hint), global kill-switch +
window settings row in the Apps tab, autoUpdateWindow.ts client-side
format validation.
Unit tests for every new class plus the four new API endpoints and the two
new notification subjects; Vitest coverage for PolicySelector and the
window-validation utility.
Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Quality Report — ConductionNL/app-versions @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ❌ | ||||
| psalm | ❌ | ||||
| phpstan | ❌ | ||||
| phpmetrics | ❌ | ||||
| eslint | ✅ | ||||
| stylelint | ❌ | ||||
| composer | ✅ | ✅ 15/15 | |||
| npm | ✅ | ✅ 282/282 | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-07-24 01:39 UTC
Download the full PDF report from the workflow artifacts.
Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Quality Report — ConductionNL/app-versions @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ❌ | ||||
| psalm | ❌ | ||||
| phpstan | ❌ | ||||
| phpmetrics | ❌ | ||||
| eslint | ✅ | ||||
| stylelint | ❌ | ||||
| composer | ✅ | ✅ 15/15 | |||
| npm | ✅ | ✅ 282/282 | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-07-24 01:41 UTC
Download the full PDF report from the workflow artifacts.
…ollback (#105) Adds a release-artifact cache so a rollback still works when the upstream download URL has rotted: - ArtifactCache service persists the verified archive + verification metadata (sha256 always; App Store: + signature/certificate) to app data on every successful install, pruning beyond artifact_cache_keep (default 3; 0 disables caching). Best-effort — a caching failure never fails the install. - Both installers fall back to the cached artifact when the source download fails, re-running the full verification chain (signed: certificate + signature re-verify with the stored materials; external: sha256 + standard archive validation; allowlist unaffected — it already runs before the download step). A tampered cache entry is discarded and the original download error surfaces. Install outcomes carry servedFromCache. - Version listings stamp cachedOffline per entry (one cache query per listing). - GET /api/cache (summary) + DELETE /api/cache?appId= (password-confirmed clear, all apps or one) admin-only endpoints. - UI: offline badge on version rows, new "Artifact cache" settings tab (CachePanel) with per-app and clear-all actions. Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Quality Report — ConductionNL/app-versions @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ❌ | ||||
| psalm | ❌ | ||||
| phpstan | ❌ | ||||
| phpmetrics | ❌ | ||||
| eslint | ✅ | ||||
| stylelint | ❌ | ||||
| composer | ✅ | ✅ 15/15 | |||
| npm | ✅ | ✅ 282/282 | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-07-24 02:15 UTC
Download the full PDF report from the workflow artifacts.
Quality Report — ConductionNL/app-versions @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ❌ | ||||
| psalm | ❌ | ||||
| phpstan | ❌ | ||||
| phpmetrics | ❌ | ||||
| eslint | ✅ | ||||
| stylelint | ❌ | ||||
| composer | ✅ | ✅ 15/15 | |||
| npm | ✅ | ✅ 282/282 | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-07-24 02:16 UTC
Download the full PDF report from the workflow artifacts.
The occ CLI change (#101) declared symfony/console in the production require block. Nextcloud server already provides symfony/console (v6.4.32 on NC 34) — occ IS a Symfony Console application — so every app that ships commands links against core's copy. Declaring it as a production dependency means a --no-dev package build bundles a second Symfony Console (plus string/service-contracts and four polyfills) into the app's own vendor/, which Nextcloud autoloads for enabled apps. A duplicate console library in an app vendor tree is the same shadowing hazard that has bricked instances before via bundled sabre/xml and OCP stubs; there is no upside since core's copy is always present at runtime. Moved to require-dev so CommandTester stays available to the unit suite while a production build ships nothing extra. Verified: composer install --no-dev leaves vendor/ with no symfony directory, and the full unit suite (485 tests) stays green with the dev install.
Quality Report — ConductionNL/app-versions @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ❌ | ||||
| psalm | ❌ | ||||
| phpstan | ❌ | ||||
| phpmetrics | ❌ | ||||
| eslint | ✅ | ||||
| stylelint | ❌ | ||||
| composer | ✅ | ✅ 17/17 | |||
| npm | ✅ | ✅ 282/282 | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-07-24 02:19 UTC
Download the full PDF report from the workflow artifacts.
downgradeGuardHint() passed a string carrying %1$s/%2$s placeholders to IL10N::t() with no parameter array, then sprintf()'d the result itself. L10NString vsprintf()s the translated text against exactly the parameters given, so casting it threw ValueError: The arguments array must contain 2 items, 0 given on EVERY downgrade refusal — the core safety feature of the migration safety guard. Live symptom: 'occ app_versions:install <app> <older>' aborted with an unhandled exception instead of the specified refusal, and the HTTP path would 500 rather than return 409. The whole unit suite stayed green because five IL10N fakes were written as 'fn (string $text) => $text' — they ignored the parameter array and never vsprintf'd, so neither the missing parameters nor the unsubstituted placeholders were observable. Test-fake drift hid a production crash. - Pass the versions through t()'s parameter array (what it exists for) - Make all five IL10N fakes faithful to L10NString (vsprintf) - Add a regression test asserting no %n$s survives in the hint Live-verified on an isolated nextcloud:34 instance: the refusal now prints both versions and exits 3 (EXIT_DOWNGRADE_REFUSED) as specified.
Quality Report — ConductionNL/app-versions @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ❌ | ||||
| psalm | ❌ | ||||
| phpstan | ❌ | ||||
| phpmetrics | ❌ | ||||
| eslint | ✅ | ||||
| stylelint | ❌ | ||||
| composer | ❌ | ✅ 17/17 | |||
| npm | ✅ | ✅ 282/282 | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-07-24 02:28 UTC
Download the full PDF report from the workflow artifacts.
The app shipped ten capabilities with unit and component tests but no end-to-end coverage: no Playwright dependency, no config, and only four `@e2e` tags across 219 scenarios — all of them exclusions on pre-existing specs. Driving the real UI immediately surfaced product defects that the green suites could not see. ## Suite - `playwright.config.ts` — base URL and credentials from the environment so the same specs run against a disposable container or CI; serial execution because the specs share one settings page and mutate server state. - `tests/e2e/auth.setup.ts` — logs in once, persists the session, dismisses the first-run wizard, and warms the App Store caches so a cold catalogue download cannot masquerade as a product failure. - 27 specs over the settings shell (incl. keyboard navigation and a WCAG 4.1.2 accessible-name sweep), version listing, release notes, safe mode, the downgrade guard, pinning with its audit entries, auto-update policies, discovery, and the history/cache/tokens/trusted-source panels. - `docs/e2e.md` documents bootstrapping an instance and the conventions. - `data-testid` hooks added to HistoryPanel and CachePanel. ## Defects found and fixed 1. **Discovery returned nothing on any ordinary connection.** The catalogue download is ~12.4 MB against a 30 s timeout; it aborted mid-body (`cURL error 28`), so every search silently produced zero App Store results. Timeout raised to 180 s — the result is cached for an hour. 2. **A 30 MB row in `oc_appconfig`.** The catalogue was cached whole, and Nextcloud loads every config value of an app at once, so one discovery search degraded *every* request this app makes — a version listing went from 1.7 s to over three minutes. Only the seven fields discovery reads are cached now (30,722,360 → 74,576 bytes), with a size cap that also discards the oversized entry written by older builds. 3. **No caching of App Store lookups at all.** `apps.json` ignores its `filter` parameter and returns the entire store, so every version listing, advisory correlation and install pre-check refetched it. Resolved payloads are now cached per app for an hour, with an explicit request timeout: repeat listings went from ~33 s to ~1.7 s. ## Traceability 16 `@e2e` tags across the specs, each naming a spec file that exists. Only scenarios the suite genuinely drives are annotated — the remaining scenarios are left untagged rather than papered over with exclusions. Verified: 488 PHP tests, 58 vitest tests, 27 Playwright tests, eslint clean (0 errors; the 25 warnings match the development baseline).
Quality Report — ConductionNL/app-versions @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ❌ | ||||
| psalm | ❌ | ||||
| phpstan | ❌ | ||||
| phpmetrics | ❌ | ||||
| eslint | ✅ | ||||
| stylelint | ❌ | ||||
| composer | ✅ | ✅ 17/17 | |||
| npm | ✅ | ⏭️ | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-07-24 16:26 UTC
Download the full PDF report from the workflow artifacts.
… bugs it caught (#111) Building a fixture forge (a Forgejo-shaped HTTP double the app can bind to and install from) unlocked end-to-end coverage of the whole forge surface that could not be tested against real GitHub/Codeberg. Driving real installs through it immediately caught three defects that unit tests and prior UI verification had all missed — because nothing before had ever driven a full install to completion on Nextcloud 34. ## Product seams (both default to the public host — production unchanged) - ForgeRegistry reads each forge's api/web base URL from app config (`forge.{id}.{api_base,web_base}`), enabling self-hosted Forgejo / GitHub Enterprise — and the fixture. +unit tests. - The four forge/PAT HTTP call sites now derive `allow_local_address` from Nextcloud's own `allow_local_remote_servers` system switch (default false) instead of a hardcoded false, so an operator pointing a forge at a private network flips one standard switch. Exercised end-to-end by the fixture. ## Bugs the fixture caught and this fixes 1. **Every install crashed on NC 34.** InstallFinalizer called `OC_App::setAppTypes()`, removed from core — a fatal in the finalize phase shared by *both* the App Store (signed) and forge install paths. No install completed. Replaced with a public-API replication of the `types` app-config write that AppManager still reads. 2. **A finalize-phase Error bypassed restore.** The finalize call was caught as `Exception`; the setAppTypes fatal was an `Error`, so it escaped the restore/installed-but-broken handling and left the app half-swapped. Both installers now catch `\Throwable` around finalize. 3. **TOFU digest enforcement was bypassed by an explicit source override.** Installing with `--source` / the API `source` param built a fresh binding with no recorded digests, so a rewritten release reinstalled through a one-off override silently skipped trust-on-first-use. resolveBinding now carries the stored binding's recorded digests when the override names the same source; only a genuine rebind discards them. +unit tests. ## Also - `occ app_versions:install --accept-new-sha` (CLI parity with the API's acceptNewSha override). - `tests/e2e/forge.spec.ts` (10 specs): version-specific install, TOFU record + fail-closed + acknowledged-accept, downgrade guard, missing-sibling warning, rate-limit, offline-cache rollback + badge. Installs run through `occ` (opcache-safe); the fixture control plane rewrites/deletes assets and forces statuses. `retries: 1` for the state-heavy serial forge specs. - 8 new `@e2e` tags on the scenarios these cover. Verified: 493 PHP tests, 58 vitest, 37 Playwright (27 existing + 10 forge), eslint 0 errors.
|
|
||
| res.writeHead(404); res.end('not found') | ||
| } catch (err) { | ||
| res.writeHead(500); res.end(String(err)) |
Quality Report — ConductionNL/app-versions @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ❌ | ||||
| psalm | ❌ | ||||
| phpstan | ❌ | ||||
| phpmetrics | ❌ | ||||
| eslint | ✅ | ||||
| stylelint | ❌ | ||||
| composer | ✅ | ✅ 17/17 | |||
| npm | ✅ | ✅ 282/282 | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-07-25 10:53 UTC
Download the full PDF report from the workflow artifacts.
… bug fixed (#112) * test(e2e): occ CLI + version-management coverage (47 scenarios) - tests/e2e/cli.spec.ts (8): human/JSON listing, unknown app, reproducible install, downgrade-flag, dry-run, integrity exit code, self-management refusal. - tests/e2e/version-management.spec.ts (13): bound-source query, forge binding + re-bind overwrite, one-off query, dry-run, clean install, rollback, failure category → HTTP status, manageable/core card flags, settings placement. - @e2e traceability: cli-commands 8/8; version-management 39/39 (26 driven + 13 reason-bearing excludes for paths not reachable in a browser — non-writable dest, dev-checkout, finalize-fault injection, password-confirm, real-install opcache-503, unreachable App Store, out-of-scope init-swallow). * test(e2e): app-discovery + audit-trail coverage (40 scenarios) - discovery.spec.ts: +no-match empty state, +installed-only (API contract). - audit.spec.ts (new): install recorded (who/what/when), filter by app, immutable (no mutation endpoints), non-admin blocked, History tab render. - @e2e: app-discovery 22/22, audit-trail 18/18 — driven where reachable, reason-bearing excludes for provider/cache/PAT-private/retention-job paths. Running total: 110/219 scenarios annotated. * test(e2e): external-sources coverage (42/42) external-sources.spec.ts (13): forge listing, repo-not-found, appId/version mismatch rejection, digest-without-sibling, failed-install-records-nothing, acceptNewSha-harmless-on-first-install, rebind-discards-digests, digests-in- version-list, untrusted-bind-403, curated allowlist add/remove/owner-wildcard/ over-broad-rejection. Remaining scenarios tagged to existing specs or excluded (TrustedSourceList/ForgeRegistry/driver unit tests, PAT-auth-scheme). Running total: 145/219 annotated. * test(e2e): pin enforcement coverage (version-pinning 20/20) pinning-guards.spec.ts (3): install-over-pin rejected, reinstall-pinned-needs- no-override, list-pins-with-live-status. Drift/notification/override scenarios excluded with reasons (monitored-not-enforced NC-core drift, daily reconcile job, occ lacks --pin/--override-pin, web-install opcache-503). Running total: 161/219 annotated. * test(e2e): install-effects coverage (migration-safety/cache/changelog/auto-update) install-effects.spec.ts (7): last-known-good updated-on-success / preserved-on- failure, artifact cache populate + clear, forge release body -> changelog, invalid policy level rejected. Fixture releases now carry a body. Remaining scenarios tagged or excluded (migration-diff needs an app with migrations, retention/tamper/write-failure cache injection, changelog range + truncation vitest/unit, nightly auto-update job, slow all-apps advisory endpoint — advisory correlation confirmed live: pinned-to-vulnerable). Running total: 188/219 annotated — only pat-management remains. * fix(pat): PAT list/resolution 500'd on a NULL shared_with_admins; + e2e coverage The fixture-backed PAT e2e caught a fourth latent crash. `shared_with_admins` was created `notnull => false` with no default, and QBMapper omits the field on insert when it equals the entity default (false) — so every stored PAT persisted a NULL there. Reading such a row then fatals (`Pat::$sharedWithAdmins` is a non-nullable `bool`), which 500s `GET /api/pats` AND PatResolver during forge fetches the moment any token exists — breaking the whole PAT feature on every database, latent because no test had created and then read a PAT on a live instance. Migration Version1004 backfills existing NULLs to false and gives the column a `false` default + NOT NULL, so an omitted insert stores false. App version → 1.4.1. pat-management.spec.ts (8): codeberg token accepted with unverifiable-scope warning, revoked token rejected, no-plaintext-in-API, edit + delete, per-forge deeplinks (classic/fine-grained/codeberg), Tokens panel redaction, non-admin blocked. Fixture /user now rejects a "revoked" token. @e2e: pat-management 31/31 — 12 driven, 19 reason-bearing excludes (github scope-header validation, private-repo auth, expiry-warning TimedJob, multi-admin ownership, internal invariants). TOTAL: 219/219 scenarios annotated. * test(e2e): tidy discovery spec (lint)
Quality Report — ConductionNL/app-versions @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ❌ | ||||
| psalm | ❌ | ||||
| phpstan | ❌ | ||||
| phpmetrics | ❌ | ||||
| eslint | ✅ | ||||
| stylelint | ❌ | ||||
| composer | ✅ | ✅ 17/17 | |||
| npm | ✅ | ✅ 282/282 | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-07-25 19:41 UTC
Download the full PDF report from the workflow artifacts.
…tore stale-if-error cache bug (#113) Converts 13 gate-19 scenarios from "@e2e exclude … unit-tested" to executed Playwright/occ tests, driving what the harness could not before: - background jobs (jobs.spec): auto-update apply+notify, pinned-skip, kill-switch no-op, failed-not-retried+notify, PAT expiry warn-once / no-expiry, audit prune, pin-drift reconcile (drift + no-drift). Driven via `occ background-job:execute --force-execute`. - PAT validation (pat-validation.spec): classic-PAT scope accept/reject, expiry capture, encryption-at-rest, private-repo auth (with/without token), expired-PAT skip, cross-owner delete refused, user-deletion sweep. The fixture forge now serves X-OAuth-Scopes / token-expiry headers and can gate a repo behind auth. - faults & cache integrity (faults.spec): migration-diff on downgrade, finalize- phase failure reverts, cache retention, untrusted-source cache not served, tampered-cache discarded, unreachable App Store surfaces an error. Bug found by running the full suite together and fixed here: - AppStoreSource cached the store payload TTL-gated only. When the 1-hour TTL lapsed and the store had an outage (observed live: garm3 answering 200 with an empty body), every App Store listing blanked out despite a good cached copy on disk. Add stale-if-error: on a failed live re-fetch, serve the last cached payload regardless of age. Unit-tested (testStaleCacheServedWhenRefetchFails) and proven live (72 versions served from stale cache against a dead upstream). Test-harness robustness: - auth.setup installs the App Store subject app (`notes`) idempotently, so the suite is self-provisioning instead of depending on a manual step. - the downgrade-guard spec targets the oldest release the store still lists instead of a hard-coded 1.0.0 that has since aged out of the catalogue. Also fixes three pre-existing php-cs-fixer import-order nits (ApiController and two unit tests) encountered along the way. gate-19 stays at 219/219; 130 scenarios now backed by executed tests (was ~118). Full suite: 97 passed, 1 flaky-passed-on-retry, 0 failures. Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Quality Report — ConductionNL/app-versions @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ❌ | ||||
| psalm | ❌ | ||||
| phpstan | ❌ | ||||
| phpmetrics | ❌ | ||||
| eslint | ✅ | ||||
| stylelint | ❌ | ||||
| composer | ✅ | ✅ 17/17 | |||
| npm | ✅ | ✅ 282/282 | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-07-26 09:14 UTC
Download the full PDF report from the workflow artifacts.
Automated PR to sync development changes to beta for beta release.
Merging this PR will trigger the beta release workflow.