// Copyright 2026 Alibaba Group // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Command cmd_param_aliases reduces the reviewed parameter concept dictionary // (internal/cli/param_concepts.json) against the live Cobra command tree and // writes the deterministic per-command alias table to // internal/cli/param_aliases_generated.go. // // The reduction algorithm lives in package cli (cli.ReduceParamAliases) so the // build-time intersection and the runtime normalizer share exactly one // implementation. This command is a thin, deterministic serializer: it never // invents identity, and it fails closed on any un-whitelisted co-occurrence, // missing override path, or non-real alias target. package main import ( "bytes" "flag" "fmt" "go/format" "os" "sort" "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/param_aliases_generated.go" var ( newParamAliasRoot = app.NewSchemaSourceRootCommand reduceParamAliasEntries = cli.ReduceParamAliases formatParamAliasSource = format.Source writeParamAliasFile = os.WriteFile exitParamAliasProcess = 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 parameter-alias table") flag.Parse() if err := generateParamAliases(rootPath, outputPath); err != nil { fail(err) } } func generateParamAliases(rootPath, outputPath string) error { if err := validateOutputIsolation(rootPath, outputPath); err != nil { return err } entries, err := reduceParamAliasEntries(newParamAliasRoot()) if err != nil { return err } source, err := renderParamAliases(entries) if err != nil { return err } if err := writeParamAliasFile(outputPath, source, 0o644); err != nil { return fmt.Errorf("write generated parameter aliases: %w", err) } _, _ = fmt.Fprintf(os.Stderr, "generated parameter aliases: output=%s commands=%d\n", outputPath, len(entries)) return nil } func validateOutputIsolation(rootPath, outputPath string) error { inputs := []outputguard.Input{ {Name: "reviewed parameter concept dictionary", Path: "internal/cli/param_concepts.json"}, {Name: "reviewed parameter concept schema", Path: "internal/cli/param_concepts.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) } // renderParamAliases serializes the reduced entries into gofmt-stable Go // source. Entries and every map/slice are emitted in sorted order so // consecutive generations are byte-identical. func renderParamAliases(entries []cli.ParamAliasEntry) ([]byte, error) { var b bytes.Buffer b.WriteString("// Copyright 2026 Alibaba Group\n") b.WriteString("// Licensed under the Apache License, Version 2.0 (the \"License\");\n\n") b.WriteString("// Code generated by cmd_param_aliases; DO NOT EDIT.\n") b.WriteString("// Source: internal/cli/param_concepts.json reduced 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("// generatedParamAliases is the per-command parameter-alias table reduced from\n") b.WriteString("// the reviewed concept dictionary. Each entry binds one runnable Cobra leaf.\n") b.WriteString("var generatedParamAliases = []ParamAliasEntry{\n") for _, entry := range entries { b.WriteString("\t{\n") fmt.Fprintf(&b, "\t\tCLIPath: %q,\n", entry.CLIPath) if len(entry.Aliases) > 0 { b.WriteString("\t\tAliases: map[string]string{\n") for _, key := range sortedKeys(entry.Aliases) { fmt.Fprintf(&b, "\t\t\t%q: %q,\n", key, entry.Aliases[key]) } b.WriteString("\t\t},\n") } if len(entry.Blocked) > 0 { fmt.Fprintf(&b, "\t\tBlocked: %s,\n", renderStringSlice(entry.Blocked)) } if len(entry.Ambiguous) > 0 { fmt.Fprintf(&b, "\t\tAmbiguous: %s,\n", renderStringSlice(entry.Ambiguous)) } b.WriteString("\t},\n") } b.WriteString("}\n") b.WriteString("\nfunc loadGeneratedParamAliases() []ParamAliasEntry {\n") b.WriteString("\treturn generatedParamAliases\n") b.WriteString("}\n") formatted, err := formatParamAliasSource(b.Bytes()) if err != nil { return nil, fmt.Errorf("format generated parameter aliases: %w", err) } return formatted, nil } func renderStringSlice(values []string) string { quoted := make([]string, len(values)) for i, v := range values { quoted[i] = fmt.Sprintf("%q", v) } return "[]string{" + strings.Join(quoted, ", ") + "}" } func sortedKeys(m map[string]string) []string { keys := make([]string, 0, len(m)) for key := range m { keys = append(keys, key) } sort.Strings(keys) return keys } func fail(err error) { _, _ = fmt.Fprintln(os.Stderr, "error:", err) exitParamAliasProcess(1) }