94 lines
2.5 KiB
Go
94 lines
2.5 KiB
Go
package client
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"sort"
|
||
|
|
|
||
|
|
"github.com/m3tam3re/agent-lib/internal/discovery"
|
||
|
|
"github.com/m3tam3re/agent-lib/internal/gitsource"
|
||
|
|
"github.com/m3tam3re/agent-lib/internal/lockfile"
|
||
|
|
)
|
||
|
|
|
||
|
|
// InventoryItem is one deployable item found in the work repository.
|
||
|
|
type InventoryItem struct {
|
||
|
|
Type string
|
||
|
|
Name string
|
||
|
|
Origin string
|
||
|
|
RelPath string
|
||
|
|
Hash string
|
||
|
|
Warnings []string
|
||
|
|
}
|
||
|
|
|
||
|
|
// Inventory is the full content of a work-repo revision: own contributions
|
||
|
|
// plus every external area. MCP fragments are inventoried but filtered at
|
||
|
|
// deployment time.
|
||
|
|
type Inventory struct {
|
||
|
|
Revision string
|
||
|
|
Items []InventoryItem
|
||
|
|
}
|
||
|
|
|
||
|
|
// BuildInventory reads the work repository tree and the lockfile (when
|
||
|
|
// present) and resolves the flat deployed namespace.
|
||
|
|
func BuildInventory(tree *gitsource.GitTree, rev string) (*Inventory, error) {
|
||
|
|
inv := &Inventory{Revision: rev}
|
||
|
|
|
||
|
|
own, err := discovery.Scan(tree, discovery.Config{})
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("scanning own items: %w", err)
|
||
|
|
}
|
||
|
|
for _, it := range own {
|
||
|
|
inv.Items = append(inv.Items, InventoryItem{
|
||
|
|
Type: it.Type, Name: it.UpstreamID, Origin: "own", RelPath: it.RelPath,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
var lf *lockfile.Lockfile
|
||
|
|
if data, err := tree.ReadFile(lockfile.FileName); err == nil {
|
||
|
|
lf, err = lockfile.ParseBytes(data)
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("work repository lockfile is invalid: %w", err)
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
lf = lockfile.New()
|
||
|
|
}
|
||
|
|
|
||
|
|
sources := make([]string, 0, len(lf.Sources))
|
||
|
|
for name := range lf.Sources {
|
||
|
|
sources = append(sources, name)
|
||
|
|
}
|
||
|
|
sort.Strings(sources)
|
||
|
|
for _, name := range sources {
|
||
|
|
sub := &discovery.PrefixTree{Tree: tree, Prefix: "external/" + name}
|
||
|
|
items, err := discovery.Scan(sub, discovery.Config{})
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("scanning external area of %q: %w", name, err)
|
||
|
|
}
|
||
|
|
for _, it := range items {
|
||
|
|
inv.Items = append(inv.Items, InventoryItem{
|
||
|
|
Type: it.Type, Name: it.UpstreamID, Origin: name, RelPath: "external/" + name + "/" + it.RelPath,
|
||
|
|
Warnings: it.Warnings,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
sort.Slice(inv.Items, func(i, j int) bool {
|
||
|
|
if inv.Items[i].Type != inv.Items[j].Type {
|
||
|
|
return inv.Items[i].Type < inv.Items[j].Type
|
||
|
|
}
|
||
|
|
return inv.Items[i].Name < inv.Items[j].Name
|
||
|
|
})
|
||
|
|
return inv, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Deployable filters the inventory down to the types the target deploys.
|
||
|
|
func (inv *Inventory) Deployable() []InventoryItem {
|
||
|
|
var out []InventoryItem
|
||
|
|
for _, it := range inv.Items {
|
||
|
|
if it.Type == lockfile.TypeMcp {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
out = append(out, it)
|
||
|
|
}
|
||
|
|
return out
|
||
|
|
}
|