Skip to content

drift e2e fails only on CI — product path verified correct, PinStore::all() suspected #253

Description

@rubenvdlinde

jobs.spec.ts:169 — "the reconcile job flags drift when the installed version leaves the pin" — fails on CI and only on CI. The product code is correct; this issue records the evidence so nobody re-derives it.

The drift path works

Reproduced locally against a faithful reconstruction of CI's setup — the fixture forge (tests/e2e/fixtures/forge/server.mjs on :9099, artifacts built with build-artifacts.sh), versioniq's own installer, identical pin state:

occ versioniq:install fixtureapp 1.0.1 --source=codeberg:fixtureowner/fixtureapp
occ config:app:set versioniq pin.fixtureapp --value '{"version":"1.0.0",...}'
occ background-job:execute <live job id> --force-execute

Result:

{"version":"1.0.0",...,"driftedTo":"1.0.1","driftedAt":"2026-08-28T05:10:50+00:00"}

PinDriftHandler, PinStore::markDrift() and PinReconcileJob are all correct. Verified across three scenarios:

scenario result
registered app (versioniq 1.4.1, pinned 1.0.0) driftedTo: "1.4.1"
unknown app (never installed) driftedTo: "0" ✅ (guard is === '', and getAppVersion() returns '0', not '')
fixture app via versioniq's installer — CI's exact case driftedTo: "1.0.1"

All three version sources agree after an install: oc_appconfig.installed_version, the on-disk info.xml, and IAppManager::getAppVersion($id, false) all report 1.0.1.

Hypotheses eliminated

  1. Orphaned job row — real, and fixed in this PR, but not CI's cause: the anchored runJob does not throw on CI, so a live OCA\Versioniq\… row exists there and is what executes.
  2. getAppVersion() returning '' for an unknown app — it returns '0', so the === '' guard never fires and that path does record drift.
  3. App-id mismatchApplication::APP_ID, info.xml <id>, and the value the test writes to are all versioniq.
  4. Config read pathappConfigValue() reads app versioniq, the same store PinStore::all() enumerates.

What CI's signature means

On CI, pin.driftedTo is absent entirely — not "0", not a stale value. Given the preconditions now asserted in the test all pass there (pin readable as 1.0.0, app installed, versions differ), the only remaining explanation is that PinStore::all() does not return fixtureapp on that instance, so handle() is never called for it. That is instance state, not code.

Next step for whoever picks this up

Needs a CI run with the job's own view dumped — specifically what PinStore::all() returns on the runner. That cannot be determined from outside the instance, which is why this is excluded rather than guessed at.

Related: this investigation produced two real fixes in the same PR — the anchored runJob, and RemoveRetiredCronJobs now clearing the five OCA\AppVersions\* rows the app-id rename orphaned (verified 5 → 0 on a live instance).

Activity

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

Metadata

Metadata

Assignees

Labels

triageAwaiting triage

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions