123 lines
4.3 KiB
Go
123 lines
4.3 KiB
Go
// Copyright 2026 Alibaba Group
|
|
// Licensed under the Apache License, Version 2.0
|
|
|
|
// Command cmd_command_path_fallbacks validates the reviewed recovery-only
|
|
// command path table against the live distribution Cobra tree and writes the
|
|
// deterministic runtime lookup source. It does not add paths to Cobra, Help,
|
|
// Skill, Schema, or the stable CommandRegistry identity surface.
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"flag"
|
|
"fmt"
|
|
"go/format"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/app"
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cli"
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/generator/outputguard"
|
|
)
|
|
|
|
const defaultOutput = "internal/cli/command_path_fallbacks_generated.go"
|
|
|
|
var (
|
|
newCommandFallbackRoot = app.NewSchemaSourceRootCommand
|
|
reduceCommandFallbackEntries = cli.ReduceCommandPathFallbacks
|
|
formatCommandFallbackSource = format.Source
|
|
writeCommandFallbackFile = os.WriteFile
|
|
exitCommandFallbackProcess = os.Exit
|
|
)
|
|
|
|
func main() {
|
|
var rootPath string
|
|
var outputPath string
|
|
flag.StringVar(&rootPath, "root", ".", "Repository root used to protect generator inputs")
|
|
flag.StringVar(&outputPath, "output", defaultOutput, "Output generated command-path fallback table")
|
|
flag.Parse()
|
|
|
|
if err := generateCommandPathFallbacks(rootPath, outputPath); err != nil {
|
|
fail(err)
|
|
}
|
|
}
|
|
|
|
func generateCommandPathFallbacks(rootPath, outputPath string) error {
|
|
if err := validateOutputIsolation(rootPath, outputPath); err != nil {
|
|
return err
|
|
}
|
|
entries, err := reduceCommandFallbackEntries(newCommandFallbackRoot())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
source, err := renderCommandPathFallbacks(entries)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := writeCommandFallbackFile(outputPath, source, 0o644); err != nil {
|
|
return fmt.Errorf("write generated command path fallbacks: %w", err)
|
|
}
|
|
_, _ = fmt.Fprintf(os.Stderr, "generated command path fallbacks: output=%s entries=%d\n", outputPath, len(entries))
|
|
return nil
|
|
}
|
|
|
|
func validateOutputIsolation(rootPath, outputPath string) error {
|
|
inputs := []outputguard.Input{
|
|
{Name: "reviewed command path fallbacks", Path: "internal/cli/command_path_fallbacks.json"},
|
|
{Name: "reviewed command path fallback schema", Path: "internal/cli/command_path_fallbacks.schema.json"},
|
|
}
|
|
target := outputguard.Target{Name: "--output", Path: outputPath}
|
|
if err := outputguard.Validate(rootPath, inputs, []outputguard.Target{target}); err != nil {
|
|
return err
|
|
}
|
|
return outputguard.ValidateRepoTargetAllowlist(rootPath, target, defaultOutput)
|
|
}
|
|
|
|
func renderCommandPathFallbacks(entries []cli.CommandPathFallback) ([]byte, error) {
|
|
var b bytes.Buffer
|
|
b.WriteString("// Copyright 2026 Alibaba Group\n")
|
|
b.WriteString("// Licensed under the Apache License, Version 2.0\n\n")
|
|
b.WriteString("// Code generated by cmd_command_path_fallbacks; DO NOT EDIT.\n")
|
|
b.WriteString("// Source: internal/cli/command_path_fallbacks.json validated against the live Cobra tree.\n")
|
|
b.WriteString("// Regenerate with `make generate-schema` (or `go generate ./internal/cli`).\n\n")
|
|
b.WriteString("package cli\n\n")
|
|
b.WriteString("var generatedCommandPathFallbacks = []CommandPathFallback{\n")
|
|
for _, entry := range entries {
|
|
b.WriteString("\t{\n")
|
|
fmt.Fprintf(&b, "\t\tFrom: %q,\n", entry.From)
|
|
fmt.Fprintf(&b, "\t\tMode: %q,\n", entry.Mode)
|
|
if entry.To != "" {
|
|
fmt.Fprintf(&b, "\t\tTo: %q,\n", entry.To)
|
|
}
|
|
if len(entry.Candidates) > 0 {
|
|
fmt.Fprintf(&b, "\t\tCandidates: %s,\n", renderStringSlice(entry.Candidates))
|
|
}
|
|
b.WriteString("\t\tReviewed: true,\n")
|
|
fmt.Fprintf(&b, "\t\tReviewReason: %q,\n", entry.ReviewReason)
|
|
b.WriteString("\t},\n")
|
|
}
|
|
b.WriteString("}\n\n")
|
|
b.WriteString("func loadGeneratedCommandPathFallbacks() []CommandPathFallback {\n")
|
|
b.WriteString("\treturn generatedCommandPathFallbacks\n")
|
|
b.WriteString("}\n")
|
|
|
|
formatted, err := formatCommandFallbackSource(b.Bytes())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("format generated command path fallbacks: %w", err)
|
|
}
|
|
return formatted, nil
|
|
}
|
|
|
|
func renderStringSlice(values []string) string {
|
|
quoted := make([]string, len(values))
|
|
for index, value := range values {
|
|
quoted[index] = fmt.Sprintf("%q", value)
|
|
}
|
|
return "[]string{" + strings.Join(quoted, ", ") + "}"
|
|
}
|
|
|
|
func fail(err error) {
|
|
_, _ = fmt.Fprintln(os.Stderr, "error:", err)
|
|
exitCommandFallbackProcess(1)
|
|
}
|