IONOS(update): rebase IONOS delta onto Nextcloud v32.0.14 (NSW-944) - #322
Conversation
Port the IONOS-only CI from ionos-dev onto the vanilla v32.0.14 base: build-artifact.yml, promote-artifact.yml, the get-job-data composite action and the app-cache detection script. Two deliberate changes on top of the ionos-dev version: - Add ionos-dev-v32 to on:push:branches and to the upload-to-artifactory condition, but NOT to the trigger-remote-dev-workflow condition. The v32 integration branch therefore produces artifacts that check_release.sh can resolve, while no remote deploy can ever be triggered from it. That combination is the Nextcloud-major freeze for the v32 line. Artifactory paths stay collision-free because they are namespaced by ncw-<ncVersion>. - Fix needs.trigger-remote-dev_workflow.result -> ...-dev-workflow... in the summary job, which was silently reading an undefined value. Jira: NSW-944 Signed-off-by: Misha M.-Kupriyanov <[email protected]>
version.php appends the CI/CD run ID from .buildnumber as a 5th element of $OC_Version so a production instance can be traced back to the exact workflow run that produced it. The block is purely additive and applies unchanged on the v32.0.14 base; only the illustrative example values in the comment were refreshed from 31.0.8 to 32.0.14. .gitignore ignores the generated .buildnumber file. Its other differences against ionos-dev (/build/jsdocs/, /tests/.phpunit.cache) are upstream v32 changes, not IONOS ones, so v32's version is kept. Jira: NSW-944 Signed-off-by: Misha M.-Kupriyanov <[email protected]>
Port .gitmodules and all 32 gitlinks (31 apps-external apps plus the IONOS config submodule) from ionos-dev onto the v32.0.14 base. Pointers are the current ionos-dev SHAs; the v32 compatibility bumps for the vanilla apps land separately under NSW-945, and the four IONOS fork apps under NSW-946. IONOS is pinned at ncw-config main tip a092ab7, which is what ionos-dev already pins - ncw-config is version-agnostic and needs no v32 change, so there is no separate config PR to wait on. Deviating from update-nextcloud.md here on purpose: the guide keeps the IONOS pointer as the last commit because it goes stale against an in-flight config PR. With no such PR, folding it in keeps .gitmodules and the gitlinks consistent in every commit. The pointer is re-verified against origin/main before review. Jira: NSW-944 Signed-off-by: Misha M.-Kupriyanov <[email protected]>
Port the IONOS theme from ionos-dev onto v32.0.14: IonosTheme (registered in ThemesService), the per-app css/ionos/* stylesheets, Open Sans webfonts, IONOS favicons and logo, and the admin-theming lock. Reconciled against v32's theming refactor rather than applied verbatim: - ThemingDefaults::isAdminThemingDisabled() now reads through the config lexicon (getAppValueBool) like v32's isUserThemingDisabled(), instead of the raw getValueBool(APP_ID, ...) used on ionos-dev. v32 introduced ConfigLexicon with Strictness::NOTICE, so disable_admin_theming is registered there as ADMIN_THEMING_DISABLED; an unregistered key would log a notice on every read. - UpdateConfig::SUPPORTED_KEYS is the union of both sides: v32 added background_color, IONOS added disable_admin_theming. - IconController::getFavicon()/getTouchIcon() keep the IONOS rewrite (always serve the static theming icon, fall back to core) and therefore drop v32's dynamic per-app icon generation. ThemingDefaults::replaceImagePath() routes every favicon variant accordingly. - IconControllerTest and ThemingDefaultsTest were rebased rather than merged: v32 rewrote both (ConfigLexicon, typed appConfig getters, PHPUnit attributes), so the IONOS intent was re-applied on top of v32's versions - the dynamic-generation favicon tests are replaced by static-icon tests, name expectations follow OC_Defaults' 'Nextcloud Workspace', the replaceImagePath provider covers the new favicon routes, and isAdminThemingDisabled gained coverage. php-cs-fixer clean. Jira: NSW-944 Signed-off-by: Misha M.-Kupriyanov <[email protected]>
Adds the "Advanced options" admin section (AdminLegalUrls, delegated via IDelegatedSettings) that lets an admin override the legal-notice and privacy-policy URLs, plus the deployment-provided defaults behind them. ThemingDefaults::getImprintUrl()/getPrivacyUrl() now fall back to imprintUrlDefault/privacyUrlDefault when the admin set no URL. On ionos-dev those getters read IConfig directly; on the v32 base they go through ConfigLexicon like the rest of the class, so the two default keys are registered as lexicon entries - v32's Strictness::NOTICE would otherwise log a notice on every read. ThemingDefaultsTest: the fallback adds a config read whenever the admin left a URL empty, which invalidated the exact getAppValueString() call counts in the eight getShortFooter tests. Those counts are dropped - the rendered footer assertion in each test is what actually verifies the behaviour - and every willReturnMap that stubs imprintUrl/privacyUrl now also stubs the *Default key, since an unmapped call would return null through a string-typed mock. getImprintUrl/getPrivacyUrl gained explicit coverage for the fallback path. Jira: NSW-944 Signed-off-by: Misha M.-Kupriyanov <[email protected]>
…nd NavigationManager Core plumbing for the IONOS delegated-settings model, gated on the 'settings.only-delegated-settings' system config: - Settings\Manager::getAllowedAdminSettings() stops treating a member of the admin group as an admin, so even admins see only the sections delegated to their groups. - DeclarativeManager skips admin-section declarative forms entirely. - NavigationManager collapses the separate "Personal settings" and "Administration settings" entries into a single "Settings" entry, since there is no longer an admin overview to link to. Applied cleanly onto v32.0.14 - none of these three files was touched upstream between 31.0.8 and 32.0.14. Every per-app IDelegatedSettings conversion in the following commits depends on this. Jira: NSW-944 Signed-off-by: Misha M.-Kupriyanov <[email protected]>
Delegation and Security become IDelegatedSettings (IL10N injected,
getName()/getAuthorizedAppConfig() implemented) so both can be handed to a
group. Delegation clones the settings manager to keep the already-filtered
delegated state, which fixes rendering of delegated sections when browsing
straight to /settings/admin/admindelegation. Security now passes the
encryption module list to its template, which only renders the encryption
admin block when a module is installed.
CommonSettingsTrait loads declarative admin forms only for real admins - a
delegated user has no business seeing them.
Dropped as already upstreamed in v32.0.14 (verified: the merged result is
byte-identical to v32):
- AuthorizedGroupService, AdminDelegation\Add, AdminDelegation\Show and
ConflictException - the duplicate-delegation guard and the JSON/priority
output for admin-delegation:show. Add.php keeps upstream's wording
("Administration of X is already delegated") rather than re-diverging.
- AppSettingsController: IONOS removed the appstoreDeveloperDocs initial
state; upstream removed it too, so there is nothing left to port.
SecurityTest keeps v32's typed property declarations rather than ionos-dev's
older @var docblocks, with the IONOS $l10n mock added on top.
Jira: NSW-944
Signed-off-by: Misha M.-Kupriyanov <[email protected]>
Admin becomes IDelegatedSettings so the OAuth 2.0 clients section can be delegated to a group: IL10N injected, getName() returns 'OAuth 2.0 clients', getAuthorizedAppConfig() is empty because the section writes no app config. Applied cleanly onto v32.0.14. Jira: NSW-944 Signed-off-by: Misha M.-Kupriyanov <[email protected]>
Admin becomes IDelegatedSettings so the collaborative-tags section can be delegated to a group. Not a verbatim port: v32.0.14 gave this form a real control. Upstream added the restrict_creation_to_admin toggle, injecting IAppConfig and IInitialState into the constructor, whereas ionos-dev's v31-era version took IL10N only and had an empty form. The merged class keeps upstream's constructor and getForm() and layers the delegation on top. Consequently getAuthorizedAppConfig() no longer returns an empty array as it did on ionos-dev. The toggle is persisted through the provisioning_api appconfig endpoint, which authorizes non-admin writes from these patterns, so without systemtags/restrict_creation_to_admin a delegated group would see the toggle and fail to save it. Jira: NSW-944 Signed-off-by: Misha M.-Kupriyanov <[email protected]>
ServerDevNotice returns no section when 'settings.hide-dev-notice' is set, so the "you are using a development version" notice can be suppressed on Workspace instances. Narrower than the ionos-dev original, which also removed the developer documentation link from the app store navigation. Upstream removed that link itself in v32.0.14 - both AppStoreNavigation.vue and AppSettingsController's appstoreDeveloperDocs initial state are already gone - so the merged result is identical to v32 and there is nothing left to port. That also settles the apply/revert/re-apply churn this change went through on ionos-dev. Jira: NSW-944 Signed-off-by: Misha M.-Kupriyanov <[email protected]>
The user_limit_reached notification keeps its subject and icon but loses the "enter your subscription key in the support app" message and the "Learn more" action linking to nextcloud.com/enterprise - neither applies to a Workspace instance. php-cs-fixer dropped the now-unused IAction import. The promoted IConfig constructor property is left in place: it is now unused, but removing it changes a core class's constructor signature for no functional gain. Jira: NSW-944 Signed-off-by: Misha M.-Kupriyanov <[email protected]>
Access::dnExists() mapping path now catches HintException from mapAndAnnounceIfApplicable(), logs a warning and returns false instead of letting the exception escape. Without this, hitting the account limit during an LDAP lookup surfaces as a hard error rather than the user simply not being mapped. Applied cleanly onto v32.0.14. Jira: NSW-944 Signed-off-by: Misha M.-Kupriyanov <[email protected]>
Registers OCA\Theming\Themes\IonosTheme and OCA\Theming\Settings\AdminLegalUrls, generated with `composer dump-autoload` in apps/theming/composer rather than hand-porting ionos-dev's maps, so v32's own entries are preserved. Only the theming app needs this. The other autoload entries in the ionos-dev delta cover classes v32.0.14 already ships and already registers: OCA\DAV\CalDAV\Principal\ProxyRead/ProxyWrite, OCA\Settings\Service\ConflictException and OCP\Install\Events\InstallationCompletedEvent. The local composer also rewrote whitespace in composer/composer/LICENSE; that unrelated churn is reverted. Jira: NSW-944 Signed-off-by: Misha M.-Kupriyanov <[email protected]>
`reuse lint` failed on 19 files this branch adds, so .github/workflows/reuse.yml (which runs on every pull_request) would have gone red: - SPDX headers added to the 16 css/ionos/* stylesheets, the get-job-data composite action and promote-artifact.yml, plus a .license sidecar for copilot-instructions.md, which has no comment syntax. - REUSE.toml annotations for the Open Sans webfont kit (Apache-2.0) and the IONOS favicons/ionos.jpg. - REUSE.toml override for core/img/logo/logo.png and logo.svg: upstream annotates them as LicenseRef-NextcloudTrademarks, which is wrong once the files carry the IONOS logo. None of this existed on ionos-dev - v31 used .reuse/dep5 and these assets were never registered there either - but the files are new in this branch, so the headers belong here. `reuse lint` now reports 11222/11222 files covered. Jira: NSW-944 Signed-off-by: Misha M.-Kupriyanov <[email protected]>
CI's static-code-analysis job reported six errors, all introduced by this branch: - IconController::getFavicon()/getTouchIcon() still declared the upstream return union (FileDisplayResponse|NotFoundResponse). The IONOS rewrite only ever returns a DataDisplayResponse, so the declared type was wider than the implementation and Psalm flagged MoreSpecificReturnType plus InvalidReturnStatement. Narrowed to what the methods actually return, and dropped the now-unreachable 404 from the OpenAPI annotations. - AdminLegalUrls read its four values through the deprecated IConfig::getAppValue(). Switched to IAppConfig::getAppValueString() with the ConfigLexicon constants, matching how ThemingDefaults reads the same keys. Verified with vendor-bin/psalm: no remaining Psalm errors in any file this branch touches. Jira: NSW-944 Signed-off-by: Misha M.-Kupriyanov <[email protected]>
getFavicon() and getTouchIcon() no longer have a 404 path since the IONOS rewrite always serves either the static theming icon or the core fallback. Removing the unreachable NotFoundResponse from their annotations left the committed spec stale, which the openapi CI job checks. Regenerated with build/openapi-checker.sh; the only change is dropping the two dead 404 responses. Jira: NSW-944 Signed-off-by: Misha M.-Kupriyanov <[email protected]>
There was a problem hiding this comment.
Pull request overview
Rebases the IONOS Nextcloud Workspace delta onto vanilla Nextcloud v32.0.14, reintroducing IONOS-specific build/artifact wiring, delegated-settings behavior, and theming (including IONOS theme assets and admin legal-URL settings) on top of the new upstream baseline.
Changes:
- Adds CI/CD build metadata handling and new CI automation (artifact promotion + app-build caching helper).
- Extends settings/delegation behavior (admin-settings visibility, navigation entries, delegated-settings implementations) and updates related PHPUnit coverage.
- Introduces/updates IONOS theming: new theme provider + extensive CSS overrides, legal-URL admin panel, and favicon routing behavior.
Reviewed changes
Copilot reviewed 103 out of 127 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| webpack.modules.js | Adds bundling entrypoint for theming admin legal-URLs UI. |
| version.php | Injects CI build number into version metadata via .buildnumber. |
| tests/lib/Settings/ManagerTest.php | Adds coverage for delegated-settings behavior in Settings Manager. |
| tests/lib/NavigationManagerTest.php | Adds coverage for navigation behavior when delegated settings are enabled/disabled. |
| REUSE.toml | Adds/overrides REUSE annotations for IONOS theme assets and logos. |
| openapi.json | Updates OpenAPI responses for favicon/touch-icon endpoints. |
| lib/private/Settings/Manager.php | Treats admins as non-admin when settings.only-delegated-settings is enabled. |
| lib/private/Settings/DeclarativeManager.php | Skips admin declarative forms when only-delegated-settings is enabled. |
| lib/private/NavigationManager.php | Collapses settings navigation when only-delegated-settings is enabled. |
| lib/private/legacy/OC_Defaults.php | Rebrands defaults to “Nextcloud Workspace” and adds slogan. |
| core/Notification/CoreNotifier.php | Removes enterprise subscription upsell message/action for user-limit notification. |
| apps/user_ldap/lib/Access.php | Adds exception handling for user-limit mapping and logs warning. |
| apps/theming/tests/ThemingDefaultsTest.php | Updates theming defaults expectations; adds coverage for legal-URL defaults + admin-theming disabled flag. |
| apps/theming/tests/Settings/AdminLegalUrlsTest.php | Adds tests for new AdminLegalUrls delegated settings form. |
| apps/theming/tests/Service/ThemesServiceTest.php | Adds IONOS theme expectations + adjusts enforced-theme behavior tests. |
| apps/theming/tests/Controller/IconControllerTest.php | Updates icon-controller tests for static favicon/touch-icon behavior. |
| apps/theming/templates/settings-admin-legal.php | Adds admin legal-URLs settings mount point. |
| apps/theming/src/mixins/admin/FieldMixin.js | Refreshes styles for disable_admin_theming changes. |
| apps/theming/src/AdminTheming.vue | Adds admin-theming-disabled behavior and uses server-provided defaults for name/url/slogan. |
| apps/theming/src/AdminLegalUrls.vue | Adds Vue UI for editing legal notice / privacy policy URLs (with defaults). |
| apps/theming/src/admin-legal-urls.js | Adds webpack entry that mounts AdminLegalUrls Vue app. |
| apps/theming/openapi.json | Aligns theming app OpenAPI with favicon/touch-icon endpoint behavior. |
| apps/theming/lib/ThemingDefaults.php | Adds legal-URL fallback-to-default behavior; routes more favicon variants via theming. |
| apps/theming/lib/Themes/IonosTheme.php | Adds IONOS theme provider (custom CSS + font faces + CSS variables). |
| apps/theming/lib/Settings/AdminLegalUrls.php | Adds delegated settings section for legal URLs and authorizes appconfig keys. |
| apps/theming/lib/Settings/Admin.php | Extends admin theming initial state (defaults + adminThemingDisabled). |
| apps/theming/lib/Service/ThemesService.php | Registers IONOS theme and keeps dyslexia font available in enforced-theme mode. |
| apps/theming/lib/Controller/ThemingController.php | Blocks app-menu updates when admin theming is disabled. |
| apps/theming/lib/Controller/IconController.php | Serves static theming favicon/touch-icon for all apps (with core fallback). |
| apps/theming/lib/ConfigLexicon.php | Registers new theming keys: disable_admin_theming, legal-URL default keys. |
| apps/theming/lib/Command/UpdateConfig.php | Allows disable_admin_theming in theming config CLI updates. |
| apps/theming/img/favicon.svg | Adds IONOS favicon SVG asset. |
| apps/theming/img/favicon-touch.svg | Adds IONOS touch-icon SVG asset. |
| apps/theming/css/ionos/variables.css | Adds IONOS theme variables. |
| apps/theming/css/ionos/user-saml.css | Adds IONOS override for user_saml styling issue. |
| apps/theming/css/ionos/tasks.css | Adds IONOS styling overrides for Tasks. |
| apps/theming/css/ionos/talk.css | Adds IONOS styling overrides for Talk. |
| apps/theming/css/ionos/tables.css | Adds IONOS styling overrides for Tables. |
| apps/theming/css/ionos/sidebar.css | Adds IONOS styling overrides for sidebar/navigation. |
| apps/theming/css/ionos/settings.css | Adds IONOS styling overrides for Settings app pages. |
| apps/theming/css/ionos/profile.css | Adds (currently empty) IONOS profile styling file. |
| apps/theming/css/ionos/notes.css | Adds IONOS styling overrides for Notes. |
| apps/theming/css/ionos/guest.css | Adds IONOS styling overrides for guest/login footer. |
| apps/theming/css/ionos/files.css | Adds extensive IONOS styling overrides for Files and shared UI elements. |
| apps/theming/css/ionos/collabora.css | Adds IONOS override for Collabora viewer positioning. |
| apps/theming/css/ionos/calendar.css | Adds IONOS styling overrides for Calendar (+ guest tweaks). |
| apps/theming/css/ionos/buttons.css | Adds extensive IONOS button styling overrides across contexts. |
| apps/theming/css/ionos/assistant.css | Adds IONOS styling overrides for Assistant modal. |
| apps/theming/css/ionos/apps.css | Adds IONOS styling overrides for Apps listing. |
| apps/theming/css/ionos/_layout.css | Adds IONOS layout overrides (header, content sizing, login box, etc.). |
| apps/theming/css/default.css | Adds missing --color-text variable. |
| apps/theming/composer/composer/autoload_static.php | Registers new theming classes in composer autoload. |
| apps/theming/composer/composer/autoload_classmap.php | Registers new theming classes in composer classmap. |
| apps/theming/appinfo/info.xml | Registers AdminLegalUrls settings page. |
| apps/systemtags/tests/Settings/AdminTest.php | Updates Admin settings tests for delegation + name + authorized appconfig. |
| apps/systemtags/lib/Settings/Admin.php | Converts to IDelegatedSettings and authorizes restrict_creation_to_admin. |
| apps/settings/tests/Settings/Admin/SecurityTest.php | Updates Security settings tests for delegation + name + template params. |
| apps/settings/tests/Settings/Admin/DelegationTest.php | Adds tests for new admin delegation settings section. |
| apps/settings/templates/settings/admin/security.php | Conditionally renders encryption admin Vue mount. |
| apps/settings/lib/Settings/Personal/ServerDevNotice.php | Adds system config to hide server dev notice. |
| apps/settings/lib/Settings/Admin/Security.php | Converts to IDelegatedSettings and adds name + template params. |
| apps/settings/lib/Settings/Admin/Delegation.php | Converts to IDelegatedSettings; clones manager to preserve filtered state. |
| apps/settings/lib/Controller/CommonSettingsTrait.php | Loads declarative admin settings only for admins. |
| apps/oauth2/tests/Settings/AdminTest.php | Updates OAuth2 admin settings tests for delegation + name + IL10N. |
| apps/oauth2/lib/Settings/Admin.php | Converts to IDelegatedSettings and adds name + IL10N injection. |
| apps/dashboard/src/DashboardApp.vue | Removes !important from a hover background style. |
| .gitmodules | Rewrites/expands submodule definitions (incl. IONOS + apps-external). |
| .gitignore | Ignores CI-generated .buildnumber. |
| .github/workflows/promote-artifact.yml | Adds workflow to fast-forward ionos-stable and promote artifacts in Artifactory. |
| .github/scripts/detect-app-cache.sh | Adds helper script to decide build vs restore from cache (GitHub cache + JFrog). |
| .github/copilot-instructions.md.license | Adds SPDX license sidecar for Copilot instructions doc. |
| .github/copilot-instructions.md | Adds repository Copilot instructions documentation. |
| .github/actions/get-job-data/action.yml | Adds composite action to fetch a job URL from GitHub Actions run data. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| $buildNumberFile = __DIR__ . '/.buildnumber'; | ||
| if (file_exists($buildNumberFile)) { | ||
| $buildNumberContent = @file_get_contents($buildNumberFile); | ||
| if ($buildNumberContent !== false) { | ||
| $buildId = (int)trim($buildNumberContent); | ||
| if ($buildId > 0) { | ||
| // Append build ID as 5th element in version array | ||
| $OC_Version[] = $buildId; | ||
| } | ||
| } | ||
| } |
| // Accessed via: | ||
| // - ServerVersion::getBuildId() -> 12345678901 | ||
| // - ServerVersion::getHumanVersion() -> "31.0.8 (12345678901)" | ||
| // - ServerVersion::getVersion() -> [31, 0, 8, 1, 12345678901] |
| public function getFavicon(string $app = 'core'): Response { | ||
| if ($app !== 'core' && !$this->appManager->isEnabledForUser($app)) { | ||
| $app = 'core'; | ||
| // Always serve the static favicon.ico from theming app for all apps | ||
| $staticFavicon = \OC::$SERVERROOT . '/apps/theming/img/favicon.ico'; | ||
| if (file_exists($staticFavicon)) { | ||
| $response = new DataDisplayResponse( | ||
| $this->fileAccessHelper->file_get_contents($staticFavicon), | ||
| Http::STATUS_OK, | ||
| ['Content-Type' => 'image/x-icon'] | ||
| ); | ||
| $response->cacheFor(86400); | ||
| return $response; | ||
| } |
| public function getTouchIcon(string $app = 'core'): Response { | ||
| if ($app !== 'core' && !$this->appManager->isEnabledForUser($app)) { | ||
| $app = 'core'; | ||
| // Always serve the static favicon-touch.png from theming app for all apps | ||
| $staticTouchIcon = \OC::$SERVERROOT . '/apps/theming/img/favicon-touch.png'; | ||
| if (file_exists($staticTouchIcon)) { | ||
| $response = new DataDisplayResponse( | ||
| $this->fileAccessHelper->file_get_contents($staticTouchIcon), | ||
| Http::STATUS_OK, | ||
| ['Content-Type' => 'image/png'] | ||
| ); | ||
| $response->cacheFor(86400); | ||
| return $response; | ||
| } |
| // Check if admin theming is disabled | ||
| if ($this->themingDefaults->isAdminThemingDisabled()) { | ||
| return new DataResponse([ | ||
| 'data' => [ | ||
| 'message' => $this->l10n->t('Admin theming is disabled'), | ||
| ], | ||
| 'status' => 'error' | ||
| ], Http::STATUS_FORBIDDEN); | ||
| } |
| $this->initialState->provideInitialState('adminThemingParameters', [ | ||
| 'isThemable' => $themable, | ||
| 'notThemableErrorMessage' => $errorMessage, | ||
| 'name' => $this->themingDefaults->getEntity(), | ||
| 'url' => $this->themingDefaults->getBaseUrl(), | ||
| 'slogan' => $this->themingDefaults->getSlogan(), | ||
| 'defaultName' => (new \OC_Defaults())->getEntity(), | ||
| 'defaultUrl' => (new \OC_Defaults())->getBaseUrl(), | ||
| 'defaultSlogan' => (new \OC_Defaults())->getSlogan(), |
| echo "Installing JFrog CLI..." | ||
| # Install JFrog CLI | ||
| curl -fL https://install-cli.jfrog.io | sh | ||
| export PATH=$PATH:$PWD | ||
| echo "JFrog CLI version: $(jf --version)" |
| promote-artifact: | ||
| # Copy specified artifact to the release repo -> No rebuild | ||
| # (ionos-productivity-ncwserver-release) | ||
| needs: promote-git |
| .app-profile.content { | ||
| .user-actions__other.action-items img { | ||
|
|
||
|
|
||
| } | ||
| } |
GitHub evaluates `on:` from the pushed branch's own copy of the file, so the trigger list has to name every live lane or a branch goes dark on its own push. Replace the enumerated entries with the converged union: the per-major globs, the legacy unsuffixed lanes, the trains in both naming eras, and user dev branches. Keeping the unsuffixed entries is load-bearing. `ionos-dev` does not match `ionos-dev-v*`, so dropping them would leave the v31 lane dark — the same defect the globs are here to prevent. Also widen the concurrency exception from an exact ref list to prefix matches. `refs/heads/ionos-dev-v32` matched neither entry of the old `contains(fromJson(...))` list, so it fell through to the ref-keyed group with `cancel-in-progress: true` — consecutive pushes to a per-major lane cancelled each other instead of each taking a unique run_id key. No behaviour change on any existing lane: a branch's `on:` block only evaluates for pushes to that branch, and every branch still matches its own name. Mirrors the equivalent change tracked as HDNEXT-2144 (af090a7, 583b7e5). Signed-off-by: Misha M.-Kupriyanov <[email protected]>
A lane whose name encodes a Nextcloud major can disagree with the major the tree actually builds — a branch cut from the wrong base, or a version.php that was never bumped. Nothing caught that, and the failure mode is a build published or deployed under the name of a major it is not. Check it in prepare-matrix, right after checkout, so a mismatch fails in seconds rather than after a full build. Only lanes that encode a major are checked: ionos-dev-v<N>, ionos-stable-v<N> and rc/ncw-v<N>-<rc>. The unsuffixed lanes and */dev/* carry nothing to compare against and are skipped with a notice. The RC product prefix is 'ncw' — rc/ncw-v32-1, tracked in Jira as NCW-v32-1. Historic RC branches are rc/ncw-3…rc/ncw-7 and the older rc/nsw-2/3, so the retired single-number form is recognised explicitly and skipped with a notice. Any other rc/* ref warns rather than passing quietly. A guard whose pattern silently matches nothing is worse than no guard, because it still reads as green. The plausible near-misses — a missing 'v' (rc/ncw-32-1), or a prefix from elsewhere — warn. version.php is parsed with sed rather than `php -r` so the step does not depend on a PHP setup step having run. This is the guard HDNEXT-2144 named as a prerequisite for widening the deploy gate to the per-major lanes. It changes no gate on its own. Signed-off-by: Misha M.-Kupriyanov <[email protected]>
Every routing decision in this workflow matched literal branch names, so each new Nextcloud major needed an edit in several places and a missed one failed silently. Derive them instead: - upload-to-artifactory matched the literal 'ionos-dev-v32' introduced earlier in this series; match 'ionos-dev-v'/'ionos-stable-v' by prefix so a new major needs no edit. - The Artifactory stage prefix matched 'ionos-stable' exactly, so a future ionos-stable-v<N> would have classified as dev and published into dev/. Match the suffixed form there. That job's gate admits the per-major lanes, so the arm is reachable. - VALID_BRANCH_PATTERN in the trigger diagnostics described the unsuffixed set only. Widen it, and derive both operator-facing messages from a single VALID_BRANCH_DESC so the text cannot drift from the pattern again. Diagnostic output only; it gates nothing. - Pass NC_MAJOR to the GitLab pipeline, taken from NC_VERSION rather than the branch name: version.php is authoritative and the unsuffixed lanes encode no major at all. The BUILD_TYPE stable arm in trigger-remote-dev-workflow is deliberately NOT widened, unlike the stage-prefix arm above. That job's gate matches the unsuffixed names by exact equality, so a suffixed arm there would be unreachable — an arm that reads as support which does not exist. The two jobs differ because their gates differ; both sites now say so, and the freeze note carries the consequence: widening the gate at a cutover must widen that arm in the same change, or a newly-admitted ionos-stable-v<N> would deploy as BUILD_TYPE=dev. GITLAB_REF stays 'main' and BUILD_TYPE keeps its existing values. Both are consumed on the GitLab side, so changing either needs a coordinated change there first; NC_MAJOR is additive and cannot break an existing consumer. Deriving the ref per major is what the 2026-07-29 deploy incident was, and it stays out of scope here. The freeze note now names both per-major lanes, 'ionos-dev-v*' and 'ionos-stable-v*'; it previously named only the dev one while the gate froze both. Signed-off-by: Misha M.-Kupriyanov <[email protected]>
0573a26 to
9ac0655
Compare
PR map, review order and merge orderSix PRs make up the v32 upgrade. Review order and merge order are different — per
Plus #325, which is independent — it targets Review in this order
Merge in this orderThe gitlink dependency inverts the sequence:
Known-red checks that are not defects in these PRs
Not yet done
|
Note
Stacked PR 1 of 3 — base of the stack. This PR targets
ionos-dev-v32.Merge bottom-up: #322 → #323 → #324. GitHub retargets each child PR automatically as its parent merges.
Summary
Rebases the IONOS Nextcloud Workspace delta onto vanilla v32.0.14 (NSW-944).
ionos-devisv31.0.8plus 414 IONOS commits. A dry-rungit merge v32.0.14(
git merge-tree --write-tree) produces 3576 conflicted files — the merge base isbe557caec03, a pre-31.0.0 translations commit, so every stable31 backport that wasindependently cherry-picked into stable32 collides. That is unreviewable, so this branch instead
starts at the vanilla tag and re-applies the IONOS delta as grouped commits.
git diff v31.0.8..ionos-devtouches 153 files outside submodules; only 62 were also touched upstreambetween 31.0.8 and 32.0.14.
Base branch is the new
ionos-dev-v32. Nothing here targetsionos-devorionos-stable.Commits
IONOS(build)ionos-dev-v32build wiringIONOS(build)IONOS(submodules).gitmodules+ 32 gitlinks (IONOS pinned at ncw-configmain, a092ab7)IONOS(theming)IONOS(theming)IONOS(admin-delegation)IONOS(settings)IONOS(oauth2)IONOS(systemtags)IONOS(settings)IONOS(core)IONOS(user_ldap)IONOS(chore)IONOS(chore)IONOS(ci)×3Per-major branch lanes (added after the initial review round)
Three
IONOS(ci)commits at the end of the series bring the build workflow onto theionos-dev-v<major>/ionos-stable-v<major>/rc/ncw-v<major>-<rc>convention, mirroring thechange tracked as HDNEXT-2144 (
af090a71,583b7e5a) and adding the two prerequisites that onedeferred.
Why they are in this PR rather than their own. They modify
.github/workflows/build-artifact.yml, which does not exist onionos-dev-v32— that branch isbare vanilla v32.0.14 with only the 52 upstream workflows. The file is created by
cb2f0936f46in this PR, so there is nowhere earlier for them to go. Keeping the backport and its correction
together also means the concurrency fix is live from the first merge of this stack, which
matters: merging #322 → #323 → #324 is three pushes to
ionos-dev-v32in quick succession, exactlywhat the old concurrency key cancelled. Originally opened as #326, now closed.
on: push:becomes the union of every live lane; concurrency exception widened from an exact ref list to prefix matchesprepare-matrixstep: a lane whose name encodes a major must agree withversion.phpNC_MAJORto the GitLab pipelineThe live defect this fixes. The concurrency exception was an exact ref match, so
refs/heads/ionos-dev-v32matched neither entry and fell through to the ref-keyed group withcancel-in-progress: true— consecutive pushes to a per-major lane cancelled each other.Two latent bugs also closed:
on: push:named the literalionos-dev-v32(v33 would haveneeded an edit) and
ionos-stable-v*was absent; and the stage-prefix /BUILD_TYPEchains matchedionos-stableexactly, so a futureionos-stable-v<N>would have classified as dev andpublished into
dev/.Not changed:
GITLAB_REFstaysmainandBUILD_TYPEkeeps its existing values — both areconsumed on the GitLab side.
NC_MAJORis additive and cannot break an existing consumer.The freeze below is preserved and generalised. The deploy gate's arms are exact equality, so no
ionos-dev-v*lane can ever match — for every future major, with no further edits.Verified: the workflow file here is byte-identical to the copy on #325 (
ionos-dev); the assertionwas exercised across 22 cases (11 refs ×
version.phpsaying 31 and 32), since a guard onlyproves itself by rejecting.
Nextcloud-major freeze
ionos-dev-v32is added toon: push: branches:and to theupload-to-artifactorycondition, butdeliberately not to
trigger-remote-dev-workflow. v32 builds are therefore produced andresolvable by
check_release.sh, but no remote deploy can fire from them. Add the branch to thetrigger condition only at the v32 cutover. This is noted inline in
build-artifact.yml.Dropped as already upstreamed in v32.0.14
31 of the 121 delta files. Each was verified mechanically — applying that file's IONOS delta onto
v32 is a no-op — not by inspection:
files_externalIDelegatedSettings conversionNewUserDialogsub-admin group work (availableSubAdminGroups,removeGroup,@option:deselected)InstallationCompletedEvent+ itsSetup.phpdispatch (v32Setup.php:33,508)ProxyRead/ProxyWrite/User::getChildConflictException,AuthorizedGroupService,AdminDelegation\Add/ShowConsole\Application,QueryBuilder,ShardedQueryBuilder,dav\Comments\EntityCollectionWhere wording differed, upstream's wins rather than re-diverging (e.g. Add.php's
"Administration of X is already delegated").
Reconciled rather than ported — worth reviewer attention
Strictness::NOTICE.disable_admin_themingand the two legal-URL default keys are registered there and read viagetAppValueBool/getAppValueString, instead of ionos-dev's rawgetValueBool/IConfigreads,which would log a notice on every read.
getAuthorizedAppConfig()is no longer empty. v32 gave that admin form a realrestrict_creation_to_admintoggle, persisted through the provisioning_api appconfig endpoint.With ionos-dev's empty array a delegated group would see the toggle and silently fail to save it.
IconControllerTestandThemingDefaultsTest(ConfigLexicon, typed appConfig getters, PHPUnit attributes). The IONOSintent was re-applied on top of v32's versions. In
ThemingDefaultsTestthe exactgetAppValueString()call counts in the eightgetShortFootertests are dropped, because thelegal-URL fallback adds a read whenever the admin left a URL empty; the rendered-footer
assertions still carry the coverage.
Deviations from
.dev/docs/how-to/update-nextcloud.mdupgrade-do.shgit checkout <tag>only detaches; its submodule loop moves apps to branch tips, contradictingupgrade-vanilla-app-submodule.mdIONOS(update): upgrade core to <version>commitv32.0.14ionos-devionos-dev-v32.dev/docs/architecture/artifact-tree.md(the dev slot is fed by a per-NC-version integration branch)Verified locally
php-cs-fixerclean on all 36 changed PHP fileseslintand CI-scopedstylelintcleannpm run buildsucceeds; emits the newtheming-admin-legal-urlsbundlereuse lintcompliant, 11222/11222 (it failed on 19 files this branch adds, soreuse.ymlwould have gone red)Not yet verified: PHPUnit and
check_release.sh— both need the dev container / a PR build.Checklist
.dev/container/check_release.sh pr-<number> ncw— validate pr locallyappinfo/info.xmlshowsmax-version >= 32for every updated appRelated
ncw-*custom apps): NSW-946 — IONOS(update): update IONOS-owned app submodules for Nextcloud v32 (NSW-946) #324Jira: NSW-944