Files
dingtalk-workspace-cli/internal/cli/schema_command_registry.go
玉澜 4b0f71eedc test: close changed-code coverage gaps to satisfy the coverage gate
- fetch_mcp_metadata: extract run()/resolveToken()/writeMetadata with
  injectable deps (keychain, servers, lister, registry, exit); full-path
  tests reach 100% file coverage.
- internal/cli: drop dead initSafetyByCLIPath (superseded by ResolveMeta),
  split buildMetaByCLIPath / assembleSchemaCatalogSnapshot /
  assembleCommandRegistryFrom / mergedCommandRegistryJSON so shard and
  malformed-snapshot failure modes are testable; cover catalog structure
  violation formatting (sort/truncate) and RenderSafetyAnnotation.
- generators: cover registry shard merge and catalog shard write failure
  modes.
- helpers/cmdutil: cover LeafSpec default/server dispatch, transform error
  propagation, default env hint, devapp member remove validate chain, and
  the required-flags error helpers.

Local gate: overall 90.17% vs merge-base 89.96%, changed-code 100%
(861 statements); make policy and go test ./... green.
2026-07-28 18:05:20 +08:00

617 lines
25 KiB
Go

// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
package cli
import (
"bytes"
"crypto/sha256"
"embed"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"io/fs"
"regexp"
"sort"
"strings"
"sync"
"github.com/spf13/cobra"
)
const commandRegistrySchemaRef = "./schema_command_registry.schema.json"
// schema_command_registry.json is the reviewed, typed command registry and the
// sole source of stable command identity and navigation. Catalog and generated
// metadata are downstream views and must never be read back here.
//go:embed schema_command_registry/registry.json
var embeddedSchemaCommandRegistryEnvelopeJSON []byte
//go:embed schema_command_registry/products
var embeddedSchemaCommandRegistryProducts embed.FS
//go:embed schema_command_registry.schema.json
var embeddedSchemaCommandRegistrySchemaJSON []byte
var (
commandRegistryProductIDPattern = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9_-]*$`)
commandRegistryCanonicalPattern = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9_-]*\.[A-Za-z0-9][A-Za-z0-9_.-]*$`)
commandRegistryCLIPathToken = regexp.MustCompile(`^(?:[A-Za-z0-9][A-Za-z0-9._:-]*|\+[A-Za-z0-9][A-Za-z0-9._:-]*)$`)
)
type schemaCommandRegistrySnapshot struct {
Schema string `json:"$schema,omitempty"`
Version int `json:"version"`
Products []schemaCommandRegistryProduct `json:"products"`
}
type schemaCommandRegistryProduct struct {
ID string `json:"id"`
Tools []schemaCommandRegistryTool `json:"tools"`
}
type schemaCommandRegistryTool struct {
CanonicalPath string `json:"canonical_path"`
SourceProductID *string `json:"source_product_id,omitempty"`
CLIPath string `json:"cli_path"`
Aliases []string `json:"aliases,omitempty"`
Visibility *SchemaVisibility `json:"visibility,omitempty"`
}
// CommandSpec is one reviewed command identity. Identity and navigation are
// deliberately kept together so no downstream renderer can independently
// invent a canonical name, primary path, or alias.
type CommandSpec struct {
CanonicalPath string
SourceProductID string
PrimaryCLIPath string
Aliases []string
Visibility SchemaVisibility
Source string
ReviewReason string
}
// CommandRegistry is the decoded reviewed identity registry.
type CommandRegistry struct {
Commands []CommandSpec
ByCLIPath map[string]CommandSpec
ByCanonical map[string]CommandSpec
}
// EffectiveCommandRegistry is the reviewed registry after exact reviewed
// manual command additions have been merged. It remains independent of Cobra;
// binding is a separate fail-closed step.
type EffectiveCommandRegistry struct {
Commands []CommandSpec
ByCLIPath map[string]CommandSpec
ByCanonical map[string]CommandSpec
}
var (
embeddedSchemaCommandRegistryOnce sync.Once
embeddedSchemaCommandRegistryData CommandRegistry
embeddedSchemaCommandRegistryErr error
loadReviewedCommandRegistry = loadEmbeddedCommandRegistry
validateReviewedParameterBindings = ValidateEmbeddedSchemaParameterBindings
loadReviewedManualSchemaHints = embeddedManualSchemaHints
)
func loadEmbeddedCommandRegistry() (CommandRegistry, error) {
embeddedSchemaCommandRegistryOnce.Do(func() {
embeddedSchemaCommandRegistryData, embeddedSchemaCommandRegistryErr = assembleCommandRegistry()
})
return cloneCommandRegistry(embeddedSchemaCommandRegistryData), embeddedSchemaCommandRegistryErr
}
// assembleCommandRegistry reads the per-product shards embedded at build time
// and reassembles them into a single CommandRegistry, identical to the previous
// single-file layout. Mirrors assembleEmbeddedSchemaCatalog for catalog shards.
func assembleCommandRegistry() (CommandRegistry, error) {
return assembleCommandRegistryFrom(embeddedSchemaCommandRegistryEnvelopeJSON, embeddedSchemaCommandRegistryProducts, "schema_command_registry/products")
}
// assembleCommandRegistryFrom reassembles an envelope plus product shard
// directory. Split from assembleCommandRegistry so shard failure modes stay
// testable against fake filesystems.
func assembleCommandRegistryFrom(envelopeJSON []byte, shards fs.FS, dir string) (CommandRegistry, error) {
var envelope struct {
Schema string `json:"$schema,omitempty"`
Version int `json:"version"`
}
if err := json.Unmarshal(envelopeJSON, &envelope); err != nil {
return CommandRegistry{}, fmt.Errorf("decode embedded command registry envelope: %w", err)
}
entries, err := fs.ReadDir(shards, dir)
if err != nil {
return CommandRegistry{}, fmt.Errorf("read embedded command registry products: %w", err)
}
var snapshot schemaCommandRegistrySnapshot
snapshot.Schema = envelope.Schema
snapshot.Version = envelope.Version
for _, entry := range entries {
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".json") {
continue
}
data, readErr := fs.ReadFile(shards, dir+"/"+entry.Name())
if readErr != nil {
return CommandRegistry{}, fmt.Errorf("read embedded command registry shard %s: %w", entry.Name(), readErr)
}
var product schemaCommandRegistryProduct
if err := json.Unmarshal(data, &product); err != nil {
return CommandRegistry{}, fmt.Errorf("decode embedded command registry shard %s: %w", entry.Name(), err)
}
snapshot.Products = append(snapshot.Products, product)
}
// Encode to bytes and decode via the existing pipeline so all validation
// (canonical patterns, duplicate detection, visibility) runs identically.
// Marshal of this plain struct cannot fail.
data, _ := json.Marshal(snapshot)
return decodeCommandRegistry(data)
}
// EmbeddedCommandRegistryMergedJSON returns the per-product shards reassembled
// into a single JSON document matching the pre-split layout. Used by tests that
// need the full registry bytes.
func EmbeddedCommandRegistryMergedJSON() ([]byte, error) {
return mergedCommandRegistryJSON(embeddedSchemaCommandRegistryEnvelopeJSON, embeddedSchemaCommandRegistryProducts, "schema_command_registry/products")
}
// mergedCommandRegistryJSON is the injectable core of
// EmbeddedCommandRegistryMergedJSON.
func mergedCommandRegistryJSON(envelopeJSON []byte, shards fs.FS, dir string) ([]byte, error) {
var envelope struct {
Schema string `json:"$schema,omitempty"`
Version int `json:"version"`
}
if err := json.Unmarshal(envelopeJSON, &envelope); err != nil {
return nil, err
}
entries, err := fs.ReadDir(shards, dir)
if err != nil {
return nil, err
}
products := make([]json.RawMessage, 0, len(entries))
for _, entry := range entries {
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".json") {
continue
}
data, err := fs.ReadFile(shards, dir+"/"+entry.Name())
if err != nil {
return nil, err
}
products = append(products, json.RawMessage(data))
}
result := struct {
Schema string `json:"$schema,omitempty"`
Version int `json:"version"`
Products json.RawMessage `json:"products"`
}{
Schema: envelope.Schema,
Version: envelope.Version,
}
if result.Schema == "" {
result.Schema = "./schema_command_registry.schema.json"
}
result.Products, err = json.Marshal(products)
if err != nil {
return nil, err
}
return json.Marshal(result)
}
// ValidateCommandRegistrySource validates a compatibility --surface input and
// requires it to be semantically identical to the embedded reviewed registry.
// Generators may retain the flag for migration, but cannot use it to introduce
// a second identity source.
func ValidateCommandRegistrySource(data []byte) (CommandRegistry, error) {
candidate, err := decodeCommandRegistry(data)
if err != nil {
return CommandRegistry{}, err
}
embedded, err := loadReviewedCommandRegistry()
if err != nil {
return CommandRegistry{}, err
}
if !equalCommandRegistries(candidate, embedded) {
return CommandRegistry{}, fmt.Errorf("command registry source disagrees with the embedded reviewed registry")
}
return candidate, nil
}
// EmbeddedCommandRegistrySourceHash returns the stable semantic hash used by
// all generated downstream views.
func EmbeddedCommandRegistrySourceHash() (string, error) {
registry, err := loadReviewedCommandRegistry()
if err != nil {
return "", err
}
return registry.SourceHash(), nil
}
// SourceHash hashes only stable identity, navigation, and reviewed exposure.
// Formatting, product order, provenance labels, and omitted default
// source_product_id/visibility values do not affect it.
func (registry CommandRegistry) SourceHash() string {
return hashCommandSpecs(registry.Commands)
}
// SourceHash includes reviewed manual additions because those entries are part
// of the effective identity registry delivered to downstream consumers.
func (registry EffectiveCommandRegistry) SourceHash() string {
return hashCommandSpecs(registry.Commands)
}
func hashCommandSpecs(commands []CommandSpec) string {
rows := make([]string, 0, len(commands))
for _, spec := range commands {
productID, _, _ := splitManualSchemaCanonicalPath(spec.CanonicalPath)
sourceProductID := strings.TrimSpace(spec.SourceProductID)
if sourceProductID == productID {
sourceProductID = ""
}
aliases := normalizeCommandAliases(spec.Aliases, normalizeSchemaCLIPath(spec.PrimaryCLIPath))
visibility := spec.Visibility
if visibility == "" {
visibility = SchemaVisibilityPublic
}
rows = append(rows, productID+"\x00"+strings.TrimSpace(spec.CanonicalPath)+"\x00"+sourceProductID+"\x00"+normalizeSchemaCLIPath(spec.PrimaryCLIPath)+"\x00"+strings.Join(aliases, "\x00")+"\x00"+string(visibility))
}
sort.Strings(rows)
sum := sha256.Sum256([]byte(strings.Join(rows, "\n")))
return "sha256:" + hex.EncodeToString(sum[:])
}
func equalCommandRegistries(left, right CommandRegistry) bool {
if len(left.Commands) != len(right.Commands) || left.SourceHash() != right.SourceHash() {
return false
}
for canonical, leftSpec := range left.ByCanonical {
rightSpec, ok := right.ByCanonical[canonical]
if !ok || leftSpec.SourceProductID != rightSpec.SourceProductID || leftSpec.PrimaryCLIPath != rightSpec.PrimaryCLIPath || strings.Join(leftSpec.Aliases, "\x00") != strings.Join(rightSpec.Aliases, "\x00") || leftSpec.Visibility != rightSpec.Visibility {
return false
}
}
return true
}
func decodeCommandRegistry(data []byte) (CommandRegistry, error) {
var snapshot schemaCommandRegistrySnapshot
decoder := json.NewDecoder(bytes.NewReader(data))
decoder.DisallowUnknownFields()
if err := decoder.Decode(&snapshot); err != nil {
return CommandRegistry{}, fmt.Errorf("decode reviewed Schema command registry: %w", err)
}
if err := decoder.Decode(&struct{}{}); err != io.EOF {
if err == nil {
err = fmt.Errorf("multiple JSON values")
}
return CommandRegistry{}, fmt.Errorf("decode reviewed Schema command registry: %w", err)
}
if snapshot.Version != 1 {
return CommandRegistry{}, fmt.Errorf("unsupported Schema command registry version %d", snapshot.Version)
}
if strings.TrimSpace(snapshot.Schema) != commandRegistrySchemaRef {
return CommandRegistry{}, fmt.Errorf("schema command registry must declare $schema=%q", commandRegistrySchemaRef)
}
if len(snapshot.Products) == 0 {
return CommandRegistry{}, fmt.Errorf("schema command registry contains no products")
}
products := append([]schemaCommandRegistryProduct(nil), snapshot.Products...)
sort.Slice(products, func(i, j int) bool { return products[i].ID < products[j].ID })
commands := make([]CommandSpec, 0)
seenProducts := make(map[string]bool, len(products))
for _, product := range products {
productID := strings.TrimSpace(product.ID)
if productID != product.ID || !validCommandRegistryProductID(productID) {
return CommandRegistry{}, fmt.Errorf("schema command registry contains invalid product id %q", product.ID)
}
if seenProducts[productID] {
return CommandRegistry{}, fmt.Errorf("schema command registry contains duplicate product id %q", productID)
}
seenProducts[productID] = true
if len(product.Tools) == 0 {
return CommandRegistry{}, fmt.Errorf("schema command registry product %s contains no commands", productID)
}
for _, tool := range product.Tools {
canonical := strings.TrimSpace(tool.CanonicalPath)
canonicalProduct, _, ok := splitManualSchemaCanonicalPath(canonical)
if canonical != tool.CanonicalPath || !ok || !commandRegistryCanonicalPattern.MatchString(canonical) || canonicalProduct != productID {
return CommandRegistry{}, fmt.Errorf("schema command registry product %s contains invalid canonical path %q", productID, canonical)
}
sourceProductID := productID
if tool.SourceProductID != nil {
rawSourceProductID := *tool.SourceProductID
sourceProductID = strings.TrimSpace(rawSourceProductID)
if sourceProductID != rawSourceProductID || !validCommandRegistryProductID(sourceProductID) {
return CommandRegistry{}, fmt.Errorf("schema command registry tool %s has invalid source_product_id %q", canonical, rawSourceProductID)
}
}
if !validReviewedCommandRegistryCLIPath(tool.CLIPath) {
return CommandRegistry{}, fmt.Errorf("schema command registry tool %s has invalid primary cli path %q", canonical, tool.CLIPath)
}
seenAliases := make(map[string]bool, len(tool.Aliases))
for _, alias := range tool.Aliases {
if !validReviewedCommandRegistryCLIPath(alias) {
return CommandRegistry{}, fmt.Errorf("schema command registry tool %s has invalid alias path %q", canonical, alias)
}
if alias == tool.CLIPath {
return CommandRegistry{}, fmt.Errorf("schema command registry tool %s alias %q duplicates its primary cli path", canonical, alias)
}
if seenAliases[alias] {
return CommandRegistry{}, fmt.Errorf("schema command registry tool %s contains duplicate alias %q", canonical, alias)
}
seenAliases[alias] = true
}
visibility := SchemaVisibilityPublic
if tool.Visibility != nil {
visibility = *tool.Visibility
switch visibility {
case SchemaVisibilityPublic, SchemaVisibilityCompat, SchemaVisibilityInternal:
default:
return CommandRegistry{}, fmt.Errorf("schema command registry tool %s has invalid visibility %q", canonical, visibility)
}
}
commands = append(commands, CommandSpec{
CanonicalPath: canonical,
SourceProductID: sourceProductID,
PrimaryCLIPath: tool.CLIPath,
Aliases: tool.Aliases,
Visibility: visibility,
Source: "reviewed_command_registry",
})
}
}
return newCommandRegistry(commands)
}
func newCommandRegistry(commands []CommandSpec) (CommandRegistry, error) {
normalized, byPath, byCanonical, err := indexCommandSpecs(commands)
if err != nil {
return CommandRegistry{}, err
}
return CommandRegistry{Commands: normalized, ByCLIPath: byPath, ByCanonical: byCanonical}, nil
}
func newEffectiveCommandRegistry(commands []CommandSpec) (EffectiveCommandRegistry, error) {
normalized, byPath, byCanonical, err := indexCommandSpecs(commands)
if err != nil {
return EffectiveCommandRegistry{}, err
}
return EffectiveCommandRegistry{Commands: normalized, ByCLIPath: byPath, ByCanonical: byCanonical}, nil
}
func indexCommandSpecs(commands []CommandSpec) ([]CommandSpec, map[string]CommandSpec, map[string]CommandSpec, error) {
normalized := make([]CommandSpec, 0, len(commands))
byPath := make(map[string]CommandSpec, len(commands))
byCanonical := make(map[string]CommandSpec, len(commands))
for _, raw := range commands {
spec := cloneCommandSpec(raw)
spec.CanonicalPath = strings.TrimSpace(spec.CanonicalPath)
productID, _, ok := splitManualSchemaCanonicalPath(spec.CanonicalPath)
if !ok || !commandRegistryCanonicalPattern.MatchString(spec.CanonicalPath) {
return nil, nil, nil, fmt.Errorf("schema command registry contains invalid canonical path %q", raw.CanonicalPath)
}
spec.SourceProductID = strings.TrimSpace(spec.SourceProductID)
if spec.SourceProductID == "" {
spec.SourceProductID = productID
}
if !validCommandRegistryProductID(spec.SourceProductID) {
return nil, nil, nil, fmt.Errorf("schema command registry tool %s has invalid source_product_id %q", spec.CanonicalPath, raw.SourceProductID)
}
spec.PrimaryCLIPath = normalizeSchemaCLIPath(spec.PrimaryCLIPath)
if !validReviewedCommandRegistryCLIPath(spec.PrimaryCLIPath) {
return nil, nil, nil, fmt.Errorf("schema command registry tool %s has invalid primary cli path %q", spec.CanonicalPath, raw.PrimaryCLIPath)
}
aliases := make([]string, 0, len(spec.Aliases))
seenAliases := make(map[string]bool, len(spec.Aliases))
for _, rawAlias := range spec.Aliases {
alias := normalizeSchemaCLIPath(rawAlias)
if !validReviewedCommandRegistryCLIPath(alias) {
return nil, nil, nil, fmt.Errorf("schema command registry tool %s has invalid alias path %q", spec.CanonicalPath, alias)
}
if alias == spec.PrimaryCLIPath {
return nil, nil, nil, fmt.Errorf("schema command registry tool %s alias %q duplicates its primary path", spec.CanonicalPath, alias)
}
if seenAliases[alias] {
return nil, nil, nil, fmt.Errorf("schema command registry tool %s has duplicate alias path %q", spec.CanonicalPath, alias)
}
seenAliases[alias] = true
aliases = append(aliases, alias)
}
spec.Aliases = sortedUniqueStrings(aliases)
if spec.Visibility == "" {
spec.Visibility = SchemaVisibilityPublic
}
switch spec.Visibility {
case SchemaVisibilityPublic, SchemaVisibilityCompat, SchemaVisibilityInternal:
default:
return nil, nil, nil, fmt.Errorf("schema command registry tool %s has invalid visibility %q", spec.CanonicalPath, spec.Visibility)
}
spec.Source = strings.TrimSpace(spec.Source)
if spec.Source == "" {
spec.Source = "reviewed_command_registry"
}
spec.ReviewReason = strings.TrimSpace(spec.ReviewReason)
if previous, exists := byCanonical[spec.CanonicalPath]; exists {
return nil, nil, nil, fmt.Errorf("duplicate Schema command registry canonical path %s (primary paths %q and %q)", spec.CanonicalPath, previous.PrimaryCLIPath, spec.PrimaryCLIPath)
}
byCanonical[spec.CanonicalPath] = spec
for _, path := range append([]string{spec.PrimaryCLIPath}, spec.Aliases...) {
if previous, exists := byPath[path]; exists {
return nil, nil, nil, fmt.Errorf("schema command registry path %q belongs to both %s and %s", path, previous.CanonicalPath, spec.CanonicalPath)
}
byPath[path] = spec
}
normalized = append(normalized, spec)
}
for path, owner := range byPath {
if canonicalOwner, exists := byCanonical[path]; exists {
return nil, nil, nil, fmt.Errorf(
"schema command registry CLI path %q for %s conflicts with canonical identity %s",
path,
owner.CanonicalPath,
canonicalOwner.CanonicalPath,
)
}
}
sort.Slice(normalized, func(i, j int) bool { return normalized[i].CanonicalPath < normalized[j].CanonicalPath })
return normalized, byPath, byCanonical, nil
}
func validCommandRegistryProductID(value string) bool {
return commandRegistryProductIDPattern.MatchString(strings.TrimSpace(value))
}
// validReviewedCommandRegistryCLIPath is intentionally stricter than
// normalizeSchemaCLIPath: reviewed source must already be canonical and may
// not rely on normalization to hide a leading dws, repeated whitespace,
// flags, or wildcard syntax.
func validReviewedCommandRegistryCLIPath(value string) bool {
if value == "" || value != strings.TrimSpace(value) || strings.HasPrefix(value, "dws ") || strings.ContainsAny(value, "*?[]") {
return false
}
parts := strings.Split(value, " ")
for _, part := range parts {
if !commandRegistryCLIPathToken.MatchString(part) {
return false
}
}
return true
}
func normalizeCommandAliases(aliases []string, primary string) []string {
normalized := make([]string, 0, len(aliases))
for _, alias := range aliases {
alias = normalizeSchemaCLIPath(alias)
if alias != "" && alias != primary {
normalized = append(normalized, alias)
}
}
return sortedUniqueStrings(normalized)
}
// BuildEffectiveCommandRegistry loads the reviewed registry and merges the
// embedded manual additions. Manual entries may only name an exact existing
// public runnable leaf. They may add a new command identity, but can never
// rewrite an existing registry identity or create an alias; aliases belong in
// the reviewed base CommandRegistry.
func BuildEffectiveCommandRegistry(root *cobra.Command) (EffectiveCommandRegistry, error) {
if root == nil {
return EffectiveCommandRegistry{}, fmt.Errorf("build effective Schema command registry: root is nil")
}
if err := validateReviewedParameterBindings(); err != nil {
return EffectiveCommandRegistry{}, fmt.Errorf("validate reviewed Schema parameter bindings: %w", err)
}
reviewed, err := loadReviewedCommandRegistry()
if err != nil {
return EffectiveCommandRegistry{}, err
}
manual, err := loadReviewedManualSchemaHints()
if err != nil {
return EffectiveCommandRegistry{}, err
}
return buildEffectiveCommandRegistry(root, reviewed, manual)
}
func buildEffectiveCommandRegistry(root *cobra.Command, reviewed CommandRegistry, manual ManualSchemaHintSnapshot) (EffectiveCommandRegistry, error) {
if root == nil {
return EffectiveCommandRegistry{}, fmt.Errorf("build effective Schema command registry: root is nil")
}
if manual.Version != manualSchemaHintVersion {
return EffectiveCommandRegistry{}, fmt.Errorf("unsupported manual Schema hint version %d", manual.Version)
}
base, err := newCommandRegistry(reviewed.Commands)
if err != nil {
return EffectiveCommandRegistry{}, err
}
commands := append([]CommandSpec(nil), base.Commands...)
byCanonical := base.ByCanonical
byPath := base.ByCLIPath
seenManualPaths := map[string]bool{}
for _, raw := range manual.Commands {
path := normalizeSchemaCLIPath(raw.CLIPath)
canonical := strings.TrimSpace(raw.CanonicalPath)
reason := strings.TrimSpace(raw.Reason)
if path == "" || strings.ContainsAny(path, "*?[]") {
return EffectiveCommandRegistry{}, fmt.Errorf("manual Schema hint has invalid exact cli_path %q", raw.CLIPath)
}
if seenManualPaths[path] {
return EffectiveCommandRegistry{}, fmt.Errorf("duplicate manual Schema hint for %q", path)
}
seenManualPaths[path] = true
if !raw.Reviewed || reason == "" {
return EffectiveCommandRegistry{}, fmt.Errorf("manual Schema hint %q is not reviewed or has no reason", path)
}
productID, _, ok := splitManualSchemaCanonicalPath(canonical)
if !ok {
return EffectiveCommandRegistry{}, fmt.Errorf("manual Schema hint %q has invalid canonical_path %q", path, canonical)
}
match, resolveErr := resolveExactCobraPath(root, path)
if resolveErr != nil {
return EffectiveCommandRegistry{}, fmt.Errorf("manual Schema hint %q cannot be resolved exactly: %w", path, resolveErr)
}
command := match.Command
if command == nil {
return EffectiveCommandRegistry{}, fmt.Errorf("manual Schema hint %q does not resolve to an existing Cobra command", path)
}
if !publicRunnableSchemaLeaf(command) {
return EffectiveCommandRegistry{}, fmt.Errorf("manual Schema hint %q must target a public runnable Cobra leaf", path)
}
namePath := normalizeSchemaCLIPath(strings.Join(commandPathParts(command), " "))
if match.UsedAlias {
nameSpec, nameExists := byPath[namePath]
if !nameExists {
return EffectiveCommandRegistry{}, fmt.Errorf("manual Schema hint %q uses a Cobra alias, but real command path %q is not present in reviewed CommandRegistry", path, namePath)
}
if nameSpec.CanonicalPath != canonical {
return EffectiveCommandRegistry{}, fmt.Errorf("manual Schema hint %q canonical path %q conflicts with real command path %q canonical path %q", path, canonical, namePath, nameSpec.CanonicalPath)
}
}
pathSpec, pathExists := byPath[path]
canonicalSpec, canonicalExists := byCanonical[canonical]
if pathExists && pathSpec.CanonicalPath != canonical {
return EffectiveCommandRegistry{}, fmt.Errorf("manual Schema hint %q canonical path %q conflicts with command registry canonical path %q", path, canonical, pathSpec.CanonicalPath)
}
if pathExists {
continue
}
if canonicalExists {
return EffectiveCommandRegistry{}, fmt.Errorf("manual Schema hint %q cannot create an alias for %s; add the alias to the reviewed CommandRegistry", path, canonicalSpec.CanonicalPath)
}
commands = append(commands, CommandSpec{
CanonicalPath: canonical,
SourceProductID: productID,
PrimaryCLIPath: path,
Visibility: SchemaVisibilityPublic,
Source: "reviewed_manual_hint",
ReviewReason: reason,
})
// Update the working indexes so later manual entries cannot collide.
added := commands[len(commands)-1]
byCanonical[canonical] = added
byPath[path] = added
}
return newEffectiveCommandRegistry(commands)
}
func cloneCommandRegistry(registry CommandRegistry) CommandRegistry {
clone, err := newCommandRegistry(registry.Commands)
if err != nil {
return CommandRegistry{}
}
return clone
}
func cloneCommandSpec(spec CommandSpec) CommandSpec {
spec.Aliases = append([]string(nil), spec.Aliases...)
return spec
}