Skip to content

Release: merge development into beta - #371

Open
github-actions[bot] wants to merge 31 commits into
betafrom
development
Open

Release: merge development into beta#371
github-actions[bot] wants to merge 31 commits into
betafrom
development

Conversation

@github-actions

@github-actions github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Automated PR to sync development changes to beta for beta release.

Merging this PR will trigger the beta release workflow.

Reminder: Add a major, minor, or patch label to this PR to control the version bump. Default is patch.

github-actions Bot and others added 30 commits September 6, 2026 12:49
…260906124933

chore(sync): carry beta back into development
hydra-gates v1.10.0 -> v1.16.0
nc-vue      - -> -

Lock-only: both packages are already declared with caret ranges that
permit these versions, so nothing about what this app ACCEPTS changes
- only what it currently resolves to. Opened by the weekly fleet
shared-dependency bump, because a lock nobody re-resolves is a pin
nobody chose.

Merging is gated by this repository's own suite, deliberately: taking
hydra-gates v1.8.1 added patchObject() to a published interface, which
is a load-time fatal for any concrete double that implements it without
the method. CI is the only thing that can tell a safe bump from that.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…#359)

Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.68.0 to 8.69.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.69.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.69.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [rector/rector](https://github.com/rectorphp/rector) from 2.6.4 to 2.6.6.
- [Release notes](https://github.com/rectorphp/rector/releases)
- [Commits](rectorphp/rector@2.6.4...2.6.6)

---
updated-dependencies:
- dependency-name: rector/rector
  dependency-version: 2.6.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [nextcloud/openapi-extractor](https://github.com/nextcloud-releases/openapi-extractor) from 1.9.0 to 1.9.1.
- [Changelog](https://github.com/nextcloud-releases/openapi-extractor/blob/main/CHANGELOG.md)
- [Commits](nextcloud-releases/openapi-extractor@v1.9.0...v1.9.1)

---
updated-dependencies:
- dependency-name: nextcloud/openapi-extractor
  dependency-version: 1.9.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
)

Bumps [conduction/hydra-gates](https://github.com/ConductionNL/.github) from 1.16.0 to 1.16.1.
- [Release notes](https://github.com/ConductionNL/.github/releases)
- [Commits](ConductionNL/.github@v1.16.0...v1.16.1)

---
updated-dependencies:
- dependency-name: conduction/hydra-gates
  dependency-version: 1.16.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…te five migrations into one

The app id moved app_versions -> versioniq, but oc_app_versions_pats and
oc_app_versions_audit kept the old prefix. This moves them, ahead of the App
Store launch, so the app ships under one name throughout.

Version1100Date20260908000000 replaces all five incremental migrations. It
renames both tables in place in preSchemaChange, so every stored PAT and audit
row survives, then declares the full target schema. On PostgreSQL it also
renames the sequences and primary-key constraints that a table rename leaves
behind.

The declared schema was generated from `occ db:schema:expected --sql` and then
diffed field by field against it: 2 tables, 24 columns, 4 indexes, zero
differences.

The legacyStorageKeys map in src/App.vue is deliberately untouched. Those are
browser localStorage keys, not tables, and rewriting them would have collapsed
each entry into a self-mapping and silently killed the pre-rename fallback.

Also replaces the two "FROZEN ON THE OLD APP ID" mapper comments, which now
describe the move, and extends the Doctrine Table test stub with hasColumn,
hasPrimaryKey and hasIndex. All three exist in the DBAL 3.10.2 the server
ships; the stub simply had not needed them before.
…379)

The CLI php.ini on developer machines sets memory_limit=-1, so a runaway test
has nothing to stop it. On 2026-09-08 an openregister unit test recursed inside
the Nextcloud DI container and took 19 GB of RAM plus 6 GB of swap, because the
bootstrap had loaded a source tree that was never installed: base.php declares
OC and builds a server container before it throws, and that half-built container
knows none of the app's registrations, so it autowires from scratch.

- phpunit.xml and tests/phpunit-unit-only.xml cap memory at 2G. Neither had a
  php block at all. The unit-only suite peaks at 52 MB without coverage, so a
  leak now fails within seconds.
- tests/bootstrap.php pulled in the SERVER's tests/bootstrap.php unguarded, so
  outside a checkout it emitted a require_once warning and then failed half-way
  through. It now refuses with one line naming tests/phpunit-unit-only.xml, and
  refuses again when the tree is present but not installed.
- psalm gets an explicit 2G limit. There is no phpmd script in this app.

Verified locally: unit-only 575 tests exit 0 before and after, peak 52 MB; the
cap reads 2G; a fake tree that is present but uninstalled refuses with exit 1
and never runs the server bootstrap, and the same tree marked installed does
run it.

Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…refix

The repair-step block listed the two tables under "NOT MIGRATED, deliberately"
and explained that renaming them by hand would be destructive for no gain. This
branch renames them, so that bullet described the opposite of what now happens,
and the prefix sweep had already turned it into a sentence claiming the
versioniq_ tables keep their old prefix.

Replaced with a note saying which migration moves them and when, kept next to
the repair steps because the distinction that block is drawing is exactly which
carry-over each mechanism is responsible for.
…global server (#382)

* refactor: resolve services through the injected container, never the global server

The three installer classes reached for OCP\Server::get() thirteen times.
Outside a booted Nextcloud that autowires an app service from scratch and can
recurse through a constructor cycle until memory runs out, and inside a
DI-built class it is a hidden dependency a unit test cannot control.

SelectedReleaseInstallerService takes the six platform services its lazy
getters used to fetch (IFactory, IAppManager, IConfig, IAppConfig,
ITempManager, IClientService) plus ServerVersion, which this app already
injects in ApiController and ServerVersionProvider.
ExternalReleaseInstallerService takes IFactory and ServerVersion the same way.

Two classes have no OCP interface to type-hint, so all three take a
ContainerInterface and resolve them at call time: OC\Files\FilenameValidator
in both installers, and in InstallFinalizer the bootstrap Coordinator and the
raw DB Connection the MigrationService wants. The Coordinator is what
registers app services, so depending on it eagerly would invite the same
bootstrap cycle the change is closing.

No test constructs these classes: the recovery test uses
newInstanceWithoutConstructor and the rest mock them, so the wider
constructors change no call site.

The fleet no-service-locator sniff counts 0 in lib. Unit suite 575 tests, exit
0, peak RSS 53 MB.

* docs: record the container-lookup refactor for versioniq

* chore: keep the run note out of the repo

LOCATORS-RESULT.md is this refactor's local run record, not app content. The
other apps in the same fleet pass leave it untracked; it should not have been
committed here.

---------

Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
…olidate-migrations

Rename the app_versions_ tables to versioniq_ and consolidate five migrations into one
… provisioning API

Three auto-update specs have been red on development since before the table
rename, and they fail on the probe rather than on the behaviour.

appConfigValue() reads oc_appconfig through the provisioning API. That API
answers an unset key and a key it cannot see with the same empty string, so a
read through it cannot tell "the app never saved this" from "the probe cannot
see what the app saved". On the failing run the app's own PUT returned
autoUpdateEnabled: true and 21 provisioning reads over the following 19 seconds
all returned "".

The specs now read GET /api/policies, which is the endpoint the UI itself uses.
That is still a separate HTTP request, so app config is loaded fresh and a value
that never reached the database still fails. It is a sharper probe, not a softer
one, and it fails naming what the app believes rather than what a second API
could not see.

The seeded policy in the kill-switch spec is now asserted twice: that the PUT
returned ok, and that the app reports the policy back. Without either, a seed
that never ran shows up only as a missing hint, which reads as a broken
component.

WHAT I COULD NOT ESTABLISH, and what would close it: the root cause. Eliminated
with evidence — the app id matches on both sides, no password-confirmation
dialog appears in the failing snapshot, the provisioning API reads bool-typed
keys correctly on a live NC 34, the app declares no config lexicon,
setTypedValue does invalidate the APCu snapshot, PolicyStore writes a string
exactly as PinStore does and the pin specs pass, and the app code is
byte-identical to an instance where the whole sequence works. If these specs
stay red, the failure is now reported against the app's own contract and says
whether the value was written at all.
Assert auto-update state through the app's own API, not the provisioning API
… context

Last of the four. The previous run took this suite from 4 failures to 1, and the
one left is the per-app policy spec.

What the failing run actually showed: the Dashboard card rendered both the
"Auto-update: Patch" badge and the disabled hint. That badge is v-if="isActive"
on a level prop bound to the parent's policies map, and onPolicyChange writes
that map only after the PUT returns a payload. So the server accepted the write.
The read is what came back empty.

The distinguishing pattern is which context does the writing. The kill-switch
spec writes with page.request and reads with page.request, and passes. This spec
wrote from the page and read through page.request, and got an empty list back.

So it now asserts persistence by reloading and looking again: the badge is still
there and the selector still reads Patch. That is the property the test name
claims, it is what a user would notice, and it still fails if the value never
reached the database, since a reload re-fetches from the server. It just keeps
the write and the read on the same side.

The kill-switch specs keep using autoUpdateState() and are unaffected: they
write through page.request themselves, which is the combination that works.
…s data (#389)

Seven E2E tests blamed the app for something the transport did. Nextcloud
answers 503 with a well-formed OCS envelope whose data is empty, and these
specs did (await res.json())?.ocs?.data without checking the status, so the
next line reported 'silent zero versions' or an undefined failure category.
The 503 is expected here and already documented in installFixture: an install
holds maintenance mode and the HTTP install path resets the opcache, and these
specs install from resetFixtureApp in a beforeEach.

helpers.ts gains ocsRequest, which waits out a 503 for 30s and returns the
response, and ocsData, which throws naming maintenance mode and the opcache
reset rather than handing back an empty array. appConfigValue is rebuilt on the
same wait. The four affected specs use them at the call sites: faults, forge,
install-effects and version-management. version-management's 409 assertion read
the OCS meta into a voided local and checked nothing; it now asserts the 409.

Rebased onto development, which had meanwhile fixed the other half of this
independently: commit ef57e9f replaced the provisioning_api persistence oracle
in auto-update.spec.ts with the app's own policies endpoint, and moved the kill
switch restore into afterEach. That work is kept as it stands and this branch no
longer touches auto-update.spec.ts.

Verified: prettier exit 0, eslint exit 0 with 113 pre-existing warnings,
playwright --list still reports 103 tests in 17 files with unchanged titles so
the @e2e references in openspec/specs still resolve. No local suite run: it
needs a booted instance, so the proof is the development push.

Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Prove the per-app policy survives a reload, in one browsing context
One red spec was turning into two.

The kill-switch spec enables automation, then restores it at the very end of the
test body. That restore is skipped when the test fails. The next spec asserts the
disabled hint, which renders on `isActive && !autoUpdateEnabled`, so it failed
because the switch was still on, for a reason that had nothing to do with what it
checks.

Measured on the last run: the disabled-hint spec failed at the hint on line 170,
while the two assertions this branch's predecessor added just above it — that the
seed PUT returned ok, and that the app reads the policy back — both passed. So
the seed worked and the policy persisted. Only the precondition was wrong.

Two changes, both about isolation:

  - afterEach now also turns the kill switch off. It already cleared the policy;
    it just did not undo the other piece of global state a test in this block
    changes. afterEach runs on failure, the inline restore does not.
  - the disabled-hint spec establishes and asserts its own precondition rather
    than inheriting it, so it reports "the kill switch must be off before this
    spec can mean anything" instead of an absent element.

This does not address the kill-switch spec's own intermittent failure, where a
saved value reads back false from the app's own endpoint. That is tracked
separately and is a CI-only behaviour; the point here is that it should cost one
red spec rather than two.
Stop the kill-switch spec leaking its state into the next one
The last red spec in this suite, and the same shape as the two already fixed.

The pattern across the four specs in this block is consistent once the others
were fixed:

  writes with page.request, reads with page.request  -> passes
  writes in the page, reads in the page              -> passes
  writes in the page, reads with page.request        -> fails

This one was the third. It polled for 20 seconds, twice, and saw
autoUpdateEnabled false, while the app's own PUT in the same run had returned
autoUpdateEnabled: true.

So it now confirms the save the way the UI does, then reloads and reads the form
back. The save button's disabled state is a real signal rather than a proxy:
`isSavingAutoUpdateSettings || !isAutoUpdateSettingsDirty || !isAutoUpdateWindowValid`,
and saveAutoUpdateSettings only clears the dirty flag after unwrapOcsResponse
returns, so a failed save leaves the button enabled. The reload then re-fetches
from the server, so a value that never reached the database still fails here.

Suite progression this session: 92 passed / 4 failed / 6 flaky, then 98/1,
then 100/2, then 101/1 with no flaky. This is the remaining one.

The underlying CI-only behaviour is not fixed and is not fixable from a spec:
a write made in the page is not visible to a read made through Playwright's
request context on that instance. Recorded separately with the hypotheses
already eliminated.
Verify the kill switch in the page that set it
Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 4.1.11 to 5.0.0.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.0/packages/vitest)

---
updated-dependencies:
- dependency-name: vitest
  dependency-version: 5.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
)

Bumps [conduction/hydra-gates](https://github.com/ConductionNL/.github) from 1.16.1 to 1.18.0.
- [Release notes](https://github.com/ConductionNL/.github/releases)
- [Commits](ConductionNL/.github@v1.16.1...v1.18.0)

---
updated-dependencies:
- dependency-name: conduction/hydra-gates
  dependency-version: 1.18.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…#399)

Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.69.0 to 8.70.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.70.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.70.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [stylelint](https://github.com/stylelint/stylelint) from 17.14.1 to 17.15.0.
- [Release notes](https://github.com/stylelint/stylelint/releases)
- [Changelog](https://github.com/stylelint/stylelint/blob/main/CHANGELOG.md)
- [Commits](stylelint/stylelint@17.14.1...17.15.0)

---
updated-dependencies:
- dependency-name: stylelint
  dependency-version: 17.15.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [eslint](https://github.com/eslint/eslint) from 10.9.1 to 10.10.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](eslint/eslint@v10.9.1...v10.10.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 10.10.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…260912132047

chore(sync): carry beta back into development
* feat(e2e): refuse the shared instance unless the run names it

versioniq aims at a disposable rig on port 8099, so this guard is a no-op for
the default as it stands. It is here because a default is a thing that gets
edited: the moment someone points this suite at 8080 to save spinning a rig
up, it is pinning, unpinning and rewriting app config on the instance that
bind-mounts everyone's checkouts.

tests/e2e/base-url.ts becomes the one place a target enters the suite, keeping
the 8099 default and accepting all four fleet names. playwright.config.ts
sources NC_BASE_URL from it, so nothing that imports NC_BASE_URL changes.

Two things found while verifying, both fixed here:

Playwright's default testMatch takes *.test.ts as well as *.spec.ts, so it
collected the vitest spec, threw at import and reported 'Total: 0 tests in 0
files' rather than an error a reader would connect to that file. testIgnore
now states the split: every e2e spec in this app is *.spec.ts.

vitest.config.ts only included src/**, so a test left under tests/e2e would
never have run. It now includes tests/e2e/**/*.test.ts, and npx vitest run
picks the guard test up: 12 files, 68 tests.

* style(e2e): take the re-rendered guard so all 21 apps carry the same bytes

Formatting only, no behaviour change. The template was re-rendered
prettier-clean after the first copy went out, and the fleet is easier to
reason about when every app's shared-instance.ts is byte-identical apart from
its app id. versioniq ships no format script, so nothing here was red; this is
alignment, not a fix.

Re-verified after the swap: eslint clean, vitest 68 passing across 12 files,
playwright lists 103 tests in 17 files at the 8099 default and still refuses
localhost:8080 without the flag.
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.

1 participant