162 lines
5.6 KiB
Go
162 lines
5.6 KiB
Go
// 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)
|
|
}
|