yao/registry/manager/common/deps.go
Max 44af6ba759 Enhance command structure with MCP and Robot functionalities
- Introduce new MCP and Robot command groups in the CLI, allowing for better organization of package management commands.
- Add corresponding command descriptions for MCP and Robot functionalities to improve user guidance.
- Update the agent command group to include additional commands for enhanced agent management.
- Modify .gitignore to exclude specific design markdown files from version control.
- Add environment variable for test application path in GitHub workflows to streamline testing setup.
2026-03-03 08:11:43 +08:00

148 lines
3.8 KiB
Go

package common
import (
"fmt"
"strings"
)
// DepStatus represents the status of a dependency check.
type DepStatus int
const (
DepNotInstalled DepStatus = iota // Not installed at all
DepSatisfied // Installed and version satisfies requirement
DepConflict // Installed but version does not satisfy requirement
)
// DepCheckResult holds the result of checking a single dependency.
type DepCheckResult struct {
PackageID string
RequiredVersion string
InstalledVersion string
Status DepStatus
}
// CheckDependencies checks each dependency from the manifest against the lockfile.
// Returns missing, conflicting, and satisfied dependencies.
func CheckDependencies(deps map[string]string, lf *RegistryYao) (missing, conflicts, satisfied []DepCheckResult) {
for pkgID, requiredVer := range deps {
installed, ok := lf.GetPackage(pkgID)
if !ok {
missing = append(missing, DepCheckResult{
PackageID: pkgID,
RequiredVersion: requiredVer,
Status: DepNotInstalled,
})
continue
}
if VersionSatisfies(installed.Version, requiredVer) {
satisfied = append(satisfied, DepCheckResult{
PackageID: pkgID,
RequiredVersion: requiredVer,
InstalledVersion: installed.Version,
Status: DepSatisfied,
})
} else {
conflicts = append(conflicts, DepCheckResult{
PackageID: pkgID,
RequiredVersion: requiredVer,
InstalledVersion: installed.Version,
Status: DepConflict,
})
}
}
return
}
// DetectCycle checks if adding pkgID to the installing set would cause a cycle.
// Returns true if a cycle is detected.
func DetectCycle(installing map[string]bool, pkgID string) bool {
return installing[pkgID]
}
// VersionSatisfies checks if installedVer satisfies the constraint.
// Supports:
// - "^X.Y.Z" — same major, >= minor.patch
// - ">=X.Y.Z" — greater or equal
// - "X.Y.Z" — exact match
// - "*" — any version
func VersionSatisfies(installedVer, constraint string) bool {
constraint = strings.TrimSpace(constraint)
if constraint == "" || constraint == "*" {
return true
}
if strings.HasPrefix(constraint, "^") {
return caretSatisfies(installedVer, constraint[1:])
}
if strings.HasPrefix(constraint, ">=") {
return compareVersions(installedVer, strings.TrimSpace(constraint[2:])) >= 0
}
// Exact match
return installedVer == constraint
}
// caretSatisfies implements ^X.Y.Z: same major version, >= the specified version.
func caretSatisfies(installed, minVer string) bool {
iMajor, iMinor, iPatch, err := parseVersion(installed)
if err != nil {
return false
}
mMajor, mMinor, mPatch, err := parseVersion(minVer)
if err != nil {
return false
}
if iMajor != mMajor {
return false
}
if iMinor > mMinor {
return true
}
if iMinor == mMinor {
return iPatch >= mPatch
}
return false
}
func compareVersions(a, b string) int {
aMaj, aMin, aPat, err1 := parseVersion(a)
bMaj, bMin, bPat, err2 := parseVersion(b)
if err1 != nil || err2 != nil {
if a == b {
return 0
}
if a > b {
return 1
}
return -1
}
if aMaj != bMaj {
return aMaj - bMaj
}
if aMin != bMin {
return aMin - bMin
}
return aPat - bPat
}
func parseVersion(v string) (major, minor, patch int, err error) {
v = strings.TrimSpace(v)
parts := strings.SplitN(v, ".", 3)
if len(parts) != 3 {
return 0, 0, 0, fmt.Errorf("invalid version %q", v)
}
if _, err := fmt.Sscanf(parts[0], "%d", &major); err != nil {
return 0, 0, 0, fmt.Errorf("invalid major in %q", v)
}
if _, err := fmt.Sscanf(parts[1], "%d", &minor); err != nil {
return 0, 0, 0, fmt.Errorf("invalid minor in %q", v)
}
if _, err := fmt.Sscanf(parts[2], "%d", &patch); err != nil {
return 0, 0, 0, fmt.Errorf("invalid patch in %q", v)
}
return major, minor, patch, nil
}