Files
BrowserOS/packages/browseros/tools/patch/internal/engine/status.go
Nikhil e7105ae50b fix: improve browseros-patch workspace feedback (#921)
* fix: make patch list registry-only

* feat: add patch command progress logs

* fix: address review feedback for PR #921
2026-05-02 15:09:31 -07:00

96 lines
3.2 KiB
Go

package engine
import (
"context"
"github.com/browseros-ai/BrowserOS/packages/browseros/tools/patch/internal/git"
"github.com/browseros-ai/BrowserOS/packages/browseros/tools/patch/internal/patch"
"github.com/browseros-ai/BrowserOS/packages/browseros/tools/patch/internal/repo"
"github.com/browseros-ai/BrowserOS/packages/browseros/tools/patch/internal/resolve"
"github.com/browseros-ai/BrowserOS/packages/browseros/tools/patch/internal/workspace"
)
type WorkspaceStatus struct {
Workspace workspace.Entry `json:"workspace"`
RepoHead string `json:"repo_head"`
BaseCommit string `json:"base_commit"`
LastApplyRev string `json:"last_apply_rev,omitempty"`
LastSyncRev string `json:"last_sync_rev,omitempty"`
LastExtractRev string `json:"last_extract_rev,omitempty"`
ActiveResolve bool `json:"active_resolve"`
NeedsApply []string `json:"needs_apply"`
NeedsUpdate []string `json:"needs_update"`
Orphaned []string `json:"orphaned"`
UpToDate []string `json:"up_to_date"`
SyncState string `json:"sync_state"`
}
type InspectWorkspaceOptions struct {
Workspace workspace.Entry
Repo *repo.Info
Progress Progress
}
// InspectWorkspace compares a workspace against the patch repo and classifies drift.
func InspectWorkspace(ctx context.Context, opts InspectWorkspaceOptions) (*WorkspaceStatus, error) {
reportProgress(opts.Progress, "Inspecting workspace drift")
head, err := git.HeadRev(ctx, opts.Repo.Root)
if err != nil {
return nil, err
}
state, err := workspace.LoadState(opts.Workspace.Path)
if err != nil {
return nil, err
}
reportProgress(opts.Progress, "Loading repo patch set")
repoSet, err := patch.LoadRepoPatchSet(opts.Repo.PatchesDir, nil)
if err != nil {
return nil, err
}
reportProgress(opts.Progress, "Building workspace patch set")
localSet, err := patch.BuildWorkingTreePatchSet(ctx, opts.Workspace.Path, opts.Repo.BaseCommit, nil)
if err != nil {
return nil, err
}
status := &WorkspaceStatus{
Workspace: opts.Workspace,
RepoHead: head,
BaseCommit: opts.Repo.BaseCommit,
LastApplyRev: state.LastApplyRev,
LastSyncRev: state.LastSyncRev,
LastExtractRev: state.LastExtractRev,
ActiveResolve: resolve.Exists(opts.Workspace.Path),
}
for _, delta := range patch.Compare(repoSet, localSet) {
switch delta.Kind {
case patch.NeedsApply:
status.NeedsApply = append(status.NeedsApply, delta.Path)
case patch.NeedsUpdate:
status.NeedsUpdate = append(status.NeedsUpdate, delta.Path)
case patch.Orphaned:
status.Orphaned = append(status.Orphaned, delta.Path)
case patch.UpToDate:
status.UpToDate = append(status.UpToDate, delta.Path)
}
}
status.SyncState = inferSyncState(status)
return status, nil
}
func inferSyncState(status *WorkspaceStatus) string {
switch {
case status.ActiveResolve:
return "conflicted"
case status.LastSyncRev == "":
return "never-synced"
case status.LastSyncRev != status.RepoHead:
return "needs-sync"
case len(status.NeedsApply) > 0:
return "drifted"
case len(status.NeedsUpdate) > 0 || len(status.Orphaned) > 0:
return "local-changes"
default:
return "synced"
}
}