Skip to content

feat(drivers): pick a version, and see when your own copy has fallen behind#672

Merged
frahlg merged 1 commit into
masterfrom
672-driver-visibility
Jul 26, 2026
Merged

feat(drivers): pick a version, and see when your own copy has fallen behind#672
frahlg merged 1 commit into
masterfrom
672-driver-visibility

Conversation

@frahlg

@frahlg frahlg commented Jul 26, 2026

Copy link
Copy Markdown
Member

Two gaps, both about an operator not being told something.

A local override never heard about updates

EnrichCatalog skipped local entries outright:

if entries[i].Source == "local" {
    continue
}

So UpstreamVersion and UpdateAvailable were never computed for a driver you supply yourself. An override shadows the channel, which means running your own copy meant never finding out a newer version existed.

It is now told. Resolution is untouched — the local file keeps winning, and the UI says so rather than offering an Update button that would install a version the override goes on shadowing:

local · v1.0.0   [channel has v1.1.1]   your own copy is used while it is present

Being told is not being changed. You keep your copy until you decide otherwise.

Two details that matter:

  • UpdateAvailable is gated on both versions being real semver. compareSemver falls back to comparing strings, so an override versioned "local" — or not versioned at all — would have announced an update on alphabetical luck.
  • RepositoryID stays empty for a local file. Naming a repository there would read as provenance it does not have, and an existing test rightly guards that.

The version list existed and nothing used it

GET /api/device_repository/drivers/{id}/versions has been there all along with no caller. Rollback steps back exactly one version, so a specific older one was unreachable — even though the channel keeps every version it has ever signed, 110 of them today. That history is the whole point of an immutable channel and none of it was reachable.

Each managed driver now has a Versions… list:

Already downloaded v1.4.0 · running, v1.3.1Activate (no network)
In the channel only v2.0.0Fetch and activate

The labels distinguish the two so it is clear why one is instant. The running version carries no button.

The panel builds DOM rather than assembling HTML. Version strings come from a signed manifest, but a signed manifest is still remote input.

Tests

web/driver-versions.test.mjs pins both properties, including the one that pulls against the other: an override must be told, and must not be quietly replaced.

Go: internal/driverrepo covers the override being told what the channel has, an older override genuinely offering an update, and an unversioned one making no claim either way.

Web 117 passed · make verify clean.

🤖 Generated with Claude Code

…behind

Two gaps, both about an operator not being told something.

A local override was skipped entirely by EnrichCatalog, so UpstreamVersion and
UpdateAvailable were never computed for it. An override shadows the channel, so
running your own copy meant never finding out a newer version existed.

It is now told. Resolution is untouched: the local file keeps winning, and the
UI says so rather than offering an Update button that would install a version
the override goes on shadowing. Being told is not being changed.

UpdateAvailable is also gated on both versions being real semver.
compareSemver falls back to comparing strings, so an override versioned
"local" or not versioned at all would have announced an update on alphabetical
luck. RepositoryID stays empty for a local file, because naming a repository
there would read as provenance it does not have.

The second gap: GET /api/device_repository/drivers/{id}/versions has always
existed and no UI called it. Rollback steps back exactly one version, so a
specific older one was unreachable even though the channel keeps every version
it has ever signed -- 110 of them today. Each managed driver now has a Versions
list showing what is downloaded, what the channel offers, and which is live.
Activating something already on disk needs no network; anything else is fetched
first, and the labels say which is happening.

The panel builds DOM rather than assembling HTML. Version strings come from a
signed manifest, but a signed manifest is still remote input.

Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
@frahlg
frahlg merged commit 25c5604 into master Jul 26, 2026
11 checks passed
@frahlg
frahlg deleted the 672-driver-visibility branch July 26, 2026 09:59

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 721c65ea56

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +67 to +70
available.forEach(function (v) {
var version = typeof v === "string" ? v : (v && v.version);
if (!version || installedVersions[version]) return;
rows.push({version: version, active: false, local: false});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Read the version from the nested candidate

When a version exists only in the signed repository, the versions endpoint returns each available item as a VersionCandidate shaped like {repository_id, driver: {version, ...}}, not as an object with a top-level version (the existing update-badge.js consumer follows this shape). Consequently v.version is undefined and every channel-only candidate is skipped, leaving bundled drivers with a blank panel and preventing the advertised selection of older remote versions.

Useful? React with 👍 / 👎.

Comment on lines +96 to +98
var payload = row.local
? {version: row.version, sha256: row.sha256 || ""}
: {version: row.version};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Include the repository when fetching a version

When the operator selects a version that is not downloaded, this payload contains only version, but handleDeviceRepositoryInstall rejects requests unless either repository_id or channel is present. Thus every “Fetch and activate” request will return HTTP 400 even after the available candidates are rendered; preserve the candidate's repository_id in the row and send it with the install request.

Useful? React with 👍 / 👎.

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