Skip to content

Add plugin lock-file upgrade - #6317

Open
samuv wants to merge 4 commits into
plugins-lock/04-syncfrom
plugins-lock/05-upgrade
Open

Add plugin lock-file upgrade#6317
samuv wants to merge 4 commits into
plugins-lock/04-syncfrom
plugins-lock/05-upgrade

Conversation

@samuv

@samuv samuv commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Why: Sync restores a pin; teams also need a reviewed path to advance that pin when a mutable source has newer content.
  • What:
    • thv ai-plugin upgrade and POST /plugins/upgrade re-resolve each plugins: lock entry's source and install newer content when the digest moved (--preview / --allow-ref-change / --fail-on-changes).
    • Immutable sources (OCI digest or full git commit hash) are reported not-upgradable. Source is never rewritten.
    • A repository move is blocked unless --allow-ref-change is passed. Signer-change guarding is Stack 2 / PR9 — --allow-signer-change is not exposed yet.
    • Preview and --fail-on-changes still fetch OCI artifacts to compare digests (RFC: preview is not side-effect-free) but do not write the lock or install.
    • Gated by TOOLHIVE_PLUGINS_LOCK_ENABLED (403 when off).

Part of #6300. Stack 5/5 — schema → lock-service → install-hooks → sync → upgrade.

Type of change

  • New feature

Test plan

  • Unit tests (./pkg/plugins/pluginsvc upgrade tests and ./pkg/api/v1 upgrade endpoint tests, with the Taskfile race/ldflags flags)
  • Linting (task lint-fix)

Does this introduce a user-facing change?

No by default — the feature is inert unless TOOLHIVE_PLUGINS_LOCK_ENABLED=true. With the gate on, thv ai-plugin upgrade re-resolves plugins: lock entries.

Special notes for reviewers

  • No signer-change guard in this PR (PR9). The options type already aliases AllowSignerChange from skills; this PR does not enforce it and does not add the CLI flag.
  • resolveLatestState mirrors Install's dispatch (git → OCI → registry name) but stops short of extraction / DB / lock writes.
  • Git resolve clones to read HEAD; there is no lighter digest-only primitive, matching skills' "preview is not side-effect-free" note for OCI.

@github-actions github-actions Bot added the size/XL Extra large PR: 1000+ lines changed label Aug 13, 2026
@samuv samuv self-assigned this Aug 13, 2026
@github-actions github-actions Bot added size/XL Extra large PR: 1000+ lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Aug 13, 2026
@codecov

codecov Bot commented Aug 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 58.40336% with 99 lines in your changes missing coverage. Please review.
✅ Project coverage is 72.94%. Comparing base (2ebd4a8) to head (f5b5f83).

Files with missing lines Patch % Lines
pkg/plugins/pluginsvc/upgrade.go 58.50% 60 Missing and 23 partials ⚠️
pkg/plugins/client/client.go 0.00% 12 Missing ⚠️
pkg/api/v1/plugins.go 90.90% 1 Missing and 1 partial ⚠️
pkg/plugins/pluginsvc/install.go 33.33% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@                   Coverage Diff                    @@
##           plugins-lock/04-sync    #6317      +/-   ##
========================================================
- Coverage                 72.95%   72.94%   -0.02%     
========================================================
  Files                       747      748       +1     
  Lines                     78910    79144     +234     
========================================================
+ Hits                      57571    57733     +162     
- Misses                    17263    17307      +44     
- Partials                   4076     4104      +28     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@JAORMX JAORMX left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Panel review found inconsistent plain-name resolution, ambiguous preview API status, and missing required CLI coverage/completion. Please address the inline findings before merge. The transaction fixes requested on the earlier stack PRs also apply to this upgrade path.

Comment thread pkg/plugins/pluginsvc/upgrade.go Outdated
Comment thread pkg/plugins/pluginsvc/upgrade.go
Comment thread cmd/thv/app/ai_plugin_upgrade.go
Comment thread cmd/thv/app/ai_plugin_upgrade.go
@samuv
samuv force-pushed the plugins-lock/05-upgrade branch from 9459007 to eb5e5b4 Compare August 14, 2026 08:16
@github-actions github-actions Bot added size/XL Extra large PR: 1000+ lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Aug 14, 2026
@samuv
samuv force-pushed the plugins-lock/05-upgrade branch from eb5e5b4 to d071d5a Compare August 14, 2026 08:46
@github-actions github-actions Bot added size/XL Extra large PR: 1000+ lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Aug 14, 2026
@samuv
samuv force-pushed the plugins-lock/05-upgrade branch from d071d5a to f51bf78 Compare August 14, 2026 08:57
@github-actions github-actions Bot added size/XL Extra large PR: 1000+ lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Aug 14, 2026

@JAORMX JAORMX left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Local-store resolution, E2E coverage, and completion were addressed. The local apply path still drops the artifact/reference needed by persisted state and later sync.

Comment on lines +142 to +151
if len(latest.layerData) > 0 {
// Local-store hit: carry the exact artifact. buildPinnedReference
// would parse a bare tag as index.docker.io/library/<tag>@digest.
outcome.Status = plugins.UpgradeStatusUpgraded
return upgradePlan{
entry: entry,
outcome: outcome,
pinnedRef: entry.Name,
layerData: latest.layerData,
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

High — applying a local-store upgrade drops its persisted reference. The local plan keeps latest.layerData but not latest.ref, leaving plan.resolvedRef empty. Apply then passes raw layer data without Reference, and LockResolvedReference becomes empty too; buildInstalledPlugin stores an empty reference and the lock rewrite loses the local tag. Subsequent sync cannot build a pinned reference, and info/upgrade no longer know which local artifact was installed. Carry latest.ref in the plan, pass it as InstallOptions.Reference, and preserve it in the lock (or explicitly retain the previous resolved reference if local tags are intentionally non-restorable). Add DB and lock reference assertions to the local-apply tests.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed. The local-store plan now carries latest.ref and apply sets InstallOptions.Reference so the DB keeps the local tag. The lock cannot store a bare tag: we keep the previous resolvedReference if any, otherwise leave it empty. f5b5f83fa.

samuv added 3 commits August 14, 2026 14:52
Re-resolve plugins: lock entries and install newer content via
thv ai-plugin upgrade and POST /plugins/upgrade.
Plain-name lock entries now resolve the same way Install does, so a
local rebuild is visible to upgrade. Also complete upgrade args from
lock entries and cover fail-on-changes in the plugin CLI e2e.
A bare local-store tag must not be rewritten as a Docker Hub
digest reference; apply the resolved layer bytes instead.
The DB should keep the local tag while the lock file keeps
the previous restorable pin instead of a Docker Hub rewrite.
@samuv
samuv force-pushed the plugins-lock/05-upgrade branch from f51bf78 to f5b5f83 Compare August 14, 2026 12:52
@github-actions github-actions Bot added size/XL Extra large PR: 1000+ lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Aug 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/XL Extra large PR: 1000+ lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants