Skip to content

IONOS(update): rebase IONOS delta onto Nextcloud v32.0.14 (NSW-944) - #322

Merged
printminion-co merged 19 commits into
ionos-dev-v32from
mk/dev/NSW-944-nc-v32
Aug 31, 2026
Merged

IONOS(update): rebase IONOS delta onto Nextcloud v32.0.14 (NSW-944)#322
printminion-co merged 19 commits into
ionos-dev-v32from
mk/dev/NSW-944-nc-v32

Conversation

@printminion-co

@printminion-co printminion-co commented Aug 20, 2026

Copy link
Copy Markdown

Note

Stacked PR 1 of 3 — base of the stack. This PR targets ionos-dev-v32.

ionos-dev-v32
 └─ #322  NSW-944  IONOS delta on vanilla v32.0.14
     └─ #323  NSW-945  vanilla app submodules
         └─ #324  NSW-946  IONOS-owned app submodules

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-dev is v31.0.8 plus 414 IONOS commits. A dry-run git merge v32.0.14
(git merge-tree --write-tree) produces 3576 conflicted files — the merge base is
be557caec03, a pre-31.0.0 translations commit, so every stable31 backport that was
independently 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-dev touches 153 files outside submodules; only 62 were also touched upstream
between 31.0.8 and 32.0.14.

Base branch is the new ionos-dev-v32. Nothing here targets ionos-dev or ionos-stable.

Commits

Commit Scope
IONOS(build) artifact CI workflows + ionos-dev-v32 build wiring
IONOS(build) build number as 5th version component
IONOS(submodules) .gitmodules + 32 gitlinks (IONOS pinned at ncw-config main, a092ab7)
IONOS(theming) IONOS theme
IONOS(theming) legal-URLs admin panel
IONOS(admin-delegation) core Settings / NavigationManager plumbing
IONOS(settings) IONOS(oauth2) IONOS(systemtags) per-app IDelegatedSettings conversions
IONOS(settings) allow hiding the server dev notice
IONOS(core) drop enterprise subscription notification
IONOS(user_ldap) user_limit mapping exception handling
IONOS(chore) regenerate theming composer autoloader
IONOS(chore) REUSE compliance
IONOS(ci) ×3 converge the build workflow onto per-major branch lanes — see below

Per-major branch lanes (added after the initial review round)

Three IONOS(ci) commits at the end of the series bring the build workflow onto the
ionos-dev-v<major> / ionos-stable-v<major> / rc/ncw-v<major>-<rc> convention, mirroring the
change tracked as HDNEXT-2144 (af090a71, 583b7e5a) and adding the two prerequisites that one
deferred.

Why they are in this PR rather than their own. They modify
.github/workflows/build-artifact.yml, which does not exist on ionos-dev-v32 — that branch is
bare vanilla v32.0.14 with only the 52 upstream workflows. The file is created by cb2f0936f46
in 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-v32 in quick succession, exactly
what the old concurrency key cancelled. Originally opened as #326, now closed.

Commit What
converge the trigger set on: push: becomes the union of every live lane; concurrency exception widened from an exact ref list to prefix matches
assert the branch major new prepare-matrix step: a lane whose name encodes a major must agree with version.php
derive branch routing stop enumerating literal branch names; pass NC_MAJOR to the GitLab pipeline

The live defect this fixes. The concurrency exception was an exact ref match, so
refs/heads/ionos-dev-v32 matched neither entry and fell through to the ref-keyed group with
cancel-in-progress: true — consecutive pushes to a per-major lane cancelled each other.

Two latent bugs also closed: on: push: named the literal ionos-dev-v32 (v33 would have
needed an edit) and ionos-stable-v* was absent; and the stage-prefix / BUILD_TYPE chains matched
ionos-stable exactly, so a future ionos-stable-v<N> would have classified as dev and
published into dev/.

Not changed: GITLAB_REF stays main and BUILD_TYPE keeps its existing values — both are
consumed on the GitLab side. NC_MAJOR is 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 assertion
was exercised across 22 cases (11 refs × version.php saying 31 and 32), since a guard only
proves itself by rejecting.

Nextcloud-major freeze

ionos-dev-v32 is added to on: push: branches: and to the upload-to-artifactory condition, but
deliberately not to trigger-remote-dev-workflow. v32 builds are therefore produced and
resolvable by check_release.sh, but no remote deploy can fire from them. Add the branch to the
trigger 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:

  • entire files_external IDelegatedSettings conversion
  • NewUserDialog sub-admin group work (availableSubAdminGroups, removeGroup, @option:deselected)
  • InstallationCompletedEvent + its Setup.php dispatch (v32 Setup.php:33,508)
  • CalDAV ProxyRead / ProxyWrite / User::getChild
  • duplicate-delegation guard: ConflictException, AuthorizedGroupService, AdminDelegation\Add/Show
  • developer-documentation link removal (upstream removed it too)
  • Console\Application, QueryBuilder, ShardedQueryBuilder, dav\Comments\EntityCollection

Where 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

  1. ConfigLexicon. v32's theming app introduced a config lexicon with Strictness::NOTICE.
    disable_admin_theming and the two legal-URL default keys are registered there and read via
    getAppValueBool/getAppValueString, instead of ionos-dev's raw getValueBool/IConfig reads,
    which would log a notice on every read.
  2. systemtags getAuthorizedAppConfig() is no longer empty. v32 gave that admin form a real
    restrict_creation_to_admin toggle, 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.
  3. Two test files rebased, not merged. v32 rewrote IconControllerTest and
    ThemingDefaultsTest (ConfigLexicon, typed appConfig getters, PHPUnit attributes). The IONOS
    intent was re-applied on top of v32's versions. In ThemingDefaultsTest the exact
    getAppValueString() call counts in the eight getShortFooter tests are dropped, because the
    legal-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.md

Guide Here Why
§4 run upgrade-do.sh skipped git checkout <tag> only detaches; its submodule loop moves apps to branch tips, contradicting upgrade-vanilla-app-submodule.md
§6 resolve merge conflicts no merge at all 3576-file conflict set, measured
§8 IONOS(update): upgrade core to <version> commit branch base is v32.0.14 no core-bump diff exists with a backport-onto-vanilla base
§8 IONOS pointer as last commit folded into the submodules commit that ordering exists to avoid staleness against an in-flight config PR; ncw-config needs no v32 change, so a trailing commit would only leave an inconsistent tree mid-series
base ionos-dev ionos-dev-v32 AC 2; consistent with .dev/docs/architecture/artifact-tree.md (the dev slot is fed by a per-NC-version integration branch)

Verified locally

  • php-cs-fixer clean on all 36 changed PHP files
  • eslint and CI-scoped stylelint clean
  • npm run build succeeds; emits the new theming-admin-legal-urls bundle
  • reuse lint compliant, 11222/11222 (it failed on 19 files this branch adds, so reuse.yml would have gone red)

Not yet verified: PHPUnit and check_release.sh — both need the dev container / a PR build.

Checklist

  • run .dev/container/check_release.sh pr-<number> ncw — validate pr locally
  • update submodule sha (IONOS/)
  • verify appinfo/info.xml shows max-version >= 32 for every updated app

Related

AC 1 of NSW-944 (remote-trigger job guard freezing the deployed Nextcloud major) is not
implemented here — dropped by request. What stands in for it is the freeze-by-omission described
above, which is weaker: it prevents accidental deploys from the v32 branch but does not guard
ionos-dev / ionos-stable.

Jira: NSW-944

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]>
@printminion-co
printminion-co requested a lite review from Copilot August 20, 2026 20:58

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread version.php
Comment on lines +75 to +85
$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;
}
}
}
Comment thread version.php
Comment on lines +66 to +69
// Accessed via:
// - ServerVersion::getBuildId() -> 12345678901
// - ServerVersion::getHumanVersion() -> "31.0.8 (12345678901)"
// - ServerVersion::getVersion() -> [31, 0, 8, 1, 12345678901]
Comment on lines 92 to 103
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;
}
Comment on lines 128 to 139
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;
}
Comment on lines +163 to +171
// 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);
}
Comment on lines 54 to +62
$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(),
Comment on lines +54 to +58
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)"
Comment on lines +77 to +80
promote-artifact:
# Copy specified artifact to the release repo -> No rebuild
# (ionos-productivity-ncwserver-release)
needs: promote-git
Comment on lines +5 to +10
.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]>
@printminion-co
printminion-co marked this pull request as ready for review August 21, 2026 13:22
@printminion-co

Copy link
Copy Markdown
Author

PR map, review order and merge order

Six PRs make up the v32 upgrade. Review order and merge order are different — per
review-pr.md, all PRs are reviewed before any are merged.

# Ticket What Base
#322 NSW-944 IONOS delta rebased onto vanilla v32.0.14 + the CI lane convention ionos-dev-v32
#323 NSW-945 vanilla app submodules ionos-dev-v32
#324 NSW-946 IONOS-owned app submodules ionos-dev-v32
IONOS-Productivity/nc-activity#2 NSW-946 delegated-settings delta rebased onto v32.0.14 fork
IONOS-Productivity/nc-bruteforcesettings#2 NSW-946 same fork
IONOS-Productivity/nc-password_policy#2 NSW-946 same fork

Plus #325, which is independent — it targets ionos-dev (the v31 lane), not this stack.

Review in this order

  1. IONOS(update): rebase IONOS delta onto Nextcloud v32.0.14 (NSW-944) #322 — the foundation. Vanilla v32.0.14 base, the IONOS delta, the submodules, the build
    workflow. Nothing downstream makes sense without it.
  2. IONOS(update): update vanilla app submodules for Nextcloud v32 (NSW-945) #323 — vanilla app bumps on top.
  3. IONOS(update): update IONOS-owned app submodules for Nextcloud v32 (NSW-946) #324 together with the three fork PRs — the fork PRs hold the actual code; IONOS(update): update IONOS-owned app submodules for Nextcloud v32 (NSW-946) #324 only moves
    gitlinks to it.
  4. IONOS(ci): converge the build workflow onto per-major branch lanes #325 — any time.

Reading the diffs. All three target ionos-dev-v32 while being stacked on each other, so
GitHub shows #323 as 28 commits and #324 as 34. Each PR's own contribution is the top commits:
19 for #322, the top 12 for #323, the top 6 for #324.

Merge in this order

The gitlink dependency inverts the sequence:

  1. Merge nc-activity#2, nc-bruteforcesettings#2, nc-password_policy#2
  2. Cut v32.0.14-<sha7> prerelease tags on each
  3. Repoint IONOS(update): update IONOS-owned app submodules for Nextcloud v32 (NSW-946) #324's three provisional gitlinks to those tags (force-push)
  4. IONOS(update): rebase IONOS delta onto Nextcloud v32.0.14 (NSW-944) #322IONOS(update): update vanilla app submodules for Nextcloud v32 (NSW-945) #323IONOS(update): update IONOS-owned app submodules for Nextcloud v32 (NSW-946) #324 into ionos-dev-v32
  5. IONOS(ci): converge the build workflow onto per-major branch lanes #325 into ionos-dev, independently and at any point

Known-red checks that are not defects in these PRs

  • The three fork PRs cannot go all-green. Their inherited upstream workflows pin
    runs-on: ubuntu-latest-low, a runner label this org does not provide (0 self-hosted runners), so
    ~two-thirds of their jobs queue forever. Pre-existing: nc-bruteforcesettings has never had a
    successful PHPUnit run. Judge them on the checks that do run — Psalm among them.
  • nc-activity#2's Cypress failures are base failures, proven with a control PR containing bare
    v32.0.14 and zero file changes, which failed identically.
  • nc-password_policy#2's Psalm failure is a base failure too — it dies during dependency install
    on vanilla's nextcloud/ocp: dev-master vs config.platform.php: 8.1, before analysing any code.

Not yet done

check_release.sh pr-324 ncw has not been run — one boot validates the whole stack, since #324
sits on top. Both artifacts resolve in Artifactory. The QA that matters most is
occ admin-delegation:show plus a delegated non-admin: info.xml going green proves
compatibility, not that the three IDelegatedSettings patches still work.

@printminion-co
printminion-co merged commit 0fd8d3e into ionos-dev-v32 Aug 31, 2026
27 of 38 checks passed
@printminion-co
printminion-co deleted the mk/dev/NSW-944-nc-v32 branch August 31, 2026 15:42
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.

2 participants