← Files CerebriumARCHIVED FILE

tools/surfacedump/main.go

10.8 KB · Sep 30, 2026 · 23:15 UTC

↓ Download file

// Command surfacedump prints the Cerebrium CLI surface as JSON.
//
// It is copied into a checkout of CerebriumAI/cerebrium at run time, executed
// there, and discarded. It is never committed to that repository and it never
// modifies go.mod or go.sum: cobra and pflag are already required by that
// module, and a package inside the module may import internal/... .
//
// The surface is read by reflecting over the real cobra tree and the real
// cerebrium.toml config struct. Parsing `--help` output was tried and rejected:
// cobra pads the command-name column to the longest command name, so a
// two-space split silently drops `save-auth-config`. That is column arithmetic,
// not a fact about the CLI.
//
// Usage:
//
//	go run ./skilldrift/surfacedump > surface.json
package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"os"
	"reflect"
	"sort"
	"strconv"
	"strings"

	"github.com/cerebriumai/cerebrium/internal/commands"
	"github.com/cerebriumai/cerebrium/pkg/projectconfig"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
)

// Flag is one pflag definition.
type Flag struct {
	Name      string `json:"name"`
	Shorthand string `json:"shorthand,omitempty"`
	Usage     string `json:"usage"`
	Default   string `json:"default,omitempty"`
	Hidden    bool   `json:"hidden,omitempty"`
}

// Command is one node of the cobra tree. Path excludes the binary name, so the
// root is "" and `cerebrium apps get` is "apps get".
type Command struct {
	Path      string   `json:"path"`
	Aliases   []string `json:"aliases,omitempty"`
	Hidden    bool     `json:"hidden,omitempty"`
	Runnable  bool     `json:"runnable"`
	Flags     []Flag   `json:"flags"`
	Inherited []string `json:"inherited,omitempty"`
	// UnknownFlagsOK is cobra's FParseErrWhitelist. `cerebrium run` sets it so
	// that any --key value pair is passed through to the function being run,
	// which means an undeclared flag there is not a documentation error.
	UnknownFlagsOK bool `json:"unknown_flags_ok,omitempty"`
}

// Section is one cerebrium.toml table and the keys the CLI parses from it.
type Section struct {
	Section  string   `json:"section"`
	Keys     []string `json:"keys"`
	FreeForm bool     `json:"free_form,omitempty"`
}

// Surface is the whole extracted surface.
type Surface struct {
	Binary          string    `json:"binary"`
	GlobalFlags     []Flag    `json:"global_flags"`
	Commands        []Command `json:"commands"`
	ConfigSections  []Section `json:"config_sections"`
	RawTOMLUploaded bool      `json:"raw_toml_uploaded"`
}

// configSectionForField maps a ProjectConfig field to the cerebrium.toml table
// the loader reads it from. The struct tags do not carry the full path, so this
// mapping is explicit and an unmapped field is a hard error: a new section can
// never be dropped in silence. "@partner" expands to one table per partner
// service, "@ignore" is a field that is not a TOML key, and "section:key" is a
// scalar key rather than a table of its own.
var configSectionForField = map[string]string{
	"Deployment":       "cerebrium.deployment",
	"Hardware":         "cerebrium.hardware",
	"Scaling":          "cerebrium.scaling",
	"Dependencies":     "cerebrium.dependencies",
	"CustomRuntime":    "cerebrium.runtime.custom",
	"PartnerService":   "@partner",
	"ContainerRuntime": "cerebrium.runtime:container_runtime",
	"RawTOML":          "@ignore",
}

func main() {
	loaderPath := flag.String("loader", "pkg/projectconfig/loader.go",
		"path to the cerebrium.toml loader, read for the partner service table names")
	flag.Parse()

	partnerNames, partnerKeys, viperKeys, err := readLoader(*loaderPath)
	if err != nil {
		fatal(err)
	}

	sections, err := configSections(partnerNames, partnerKeys, viperKeys)
	if err != nil {
		fatal(err)
	}

	root := commands.NewRootCmd()
	surface := Surface{
		Binary:          root.Name(),
		GlobalFlags:     flagSet(root.PersistentFlags()),
		Commands:        walk(root, ""),
		ConfigSections:  sections,
		RawTOMLUploaded: hasField(reflect.TypeOf(projectconfig.ProjectConfig{}), "RawTOML"),
	}

	out := json.NewEncoder(os.Stdout)
	out.SetIndent("", "  ")
	if err := out.Encode(surface); err != nil {
		fatal(err)
	}
}

func fatal(err error) {
	fmt.Fprintln(os.Stderr, "surfacedump:", err)
	os.Exit(1)
}

// walk returns the command and every descendant, depth first.
func walk(cmd *cobra.Command, prefix string) []Command {
	path := strings.TrimSpace(prefix + " " + cmd.Name())
	if cmd.HasParent() == false {
		path = ""
	}

	node := Command{
		Path:           path,
		Aliases:        cmd.Aliases,
		Hidden:         cmd.Hidden,
		Runnable:       cmd.Runnable(),
		Flags:          flagSet(cmd.LocalFlags()),
		Inherited:      flagNames(cmd.InheritedFlags()),
		UnknownFlagsOK: cmd.FParseErrWhitelist.UnknownFlags,
	}
	out := []Command{node}
	for _, child := range cmd.Commands() {
		out = append(out, walk(child, path)...)
	}
	return out
}

func flagSet(fs *pflag.FlagSet) []Flag {
	var out []Flag
	fs.VisitAll(func(f *pflag.Flag) {
		if f.Name == "help" {
			return // cobra adds this to every command
		}
		out = append(out, Flag{
			Name:      f.Name,
			Shorthand: f.Shorthand,
			Usage:     f.Usage,
			Default:   f.DefValue,
			Hidden:    f.Hidden,
		})
	})
	sort.Slice(out, func(i, j int) bool { return out[i].Name < out[j].Name })
	return out
}

func flagNames(fs *pflag.FlagSet) []string {
	var out []string
	fs.VisitAll(func(f *pflag.Flag) {
		if f.Name != "help" {
			out = append(out, f.Name)
		}
	})
	sort.Strings(out)
	return out
}

// configSections reflects ProjectConfig into one entry per cerebrium.toml
// table. Only the top level is reflected: the keys of a table are the fields of
// the struct the loader unmarshals into it.
func configSections(partnerNames, partnerKeys, viperKeys []string) ([]Section, error) {
	byName := map[string]*Section{}
	order := []string{}
	// Partner tables are named by the loader through fmt.Sprintf, so they never
	// appear as a literal and are exempt from the staleness guard below. They
	// were read out of the loader source in the first place.
	fromLiteral := map[string]bool{}
	add := func(name string, keys []string, freeForm bool) {
		s, ok := byName[name]
		if !ok {
			s = &Section{Section: name, FreeForm: freeForm}
			byName[name] = s
			order = append(order, name)
		}
		s.Keys = append(s.Keys, keys...)
	}

	t := reflect.TypeOf(projectconfig.ProjectConfig{})
	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i)
		target, ok := configSectionForField[field.Name]
		if !ok {
			return nil, fmt.Errorf("ProjectConfig field %q has no cerebrium.toml table mapping; "+
				"add it to configSectionForField", field.Name)
		}
		switch {
		case target == "@ignore":
		case target == "@partner":
			for _, partner := range partnerNames {
				add("cerebrium.runtime."+partner, partnerKeys, false)
			}
		case strings.Contains(target, ":"):
			parts := strings.SplitN(target, ":", 2)
			add(parts[0], []string{parts[1]}, false)
			fromLiteral[parts[0]] = true
		default:
			keys, freeForm := structKeys(field.Type)
			add(target, keys, freeForm)
			fromLiteral[target] = true
		}
	}

	// Guard against a renamed table: every mapped path must still appear as, or
	// as the prefix of, a viper key the loader actually reads.
	for _, name := range order {
		if !fromLiteral[name] {
			continue
		}
		found := false
		for _, key := range viperKeys {
			if key == name || strings.HasPrefix(key, name+".") {
				found = true
				break
			}
		}
		if !found {
			return nil, fmt.Errorf("table %q is not read by the loader; configSectionForField is stale", name)
		}
	}

	out := make([]Section, 0, len(order))
	for _, name := range order {
		s := byName[name]
		sort.Strings(s.Keys)
		out = append(out, *s)
	}
	sort.Slice(out, func(i, j int) bool { return out[i].Section < out[j].Section })
	return out, nil
}

// structKeys returns the mapstructure keys of a struct type. A map type has no
// fixed keys, which it reports as free form.
func structKeys(t reflect.Type) ([]string, bool) {
	for t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	if t.Kind() == reflect.Map {
		return nil, true
	}
	if t.Kind() != reflect.Struct {
		return nil, true
	}
	var keys []string
	for i := 0; i < t.NumField(); i++ {
		tag := t.Field(i).Tag.Get("mapstructure")
		name := strings.Split(tag, ",")[0]
		if name == "" || name == "-" {
			continue
		}
		keys = append(keys, name)
	}
	return keys, false
}

func hasField(t reflect.Type, name string) bool {
	_, ok := t.FieldByName(name)
	return ok
}

// readLoader extracts three things from the config loader source:
//
//   - the partner service names, from the `partnerNames` slice literal;
//   - the keys read inside the loop over those names, from the `key + ".port"`
//     style expressions, so the reported keys are the ones the loader actually
//     reads rather than every field on the struct;
//   - every "cerebrium.*" viper key, used to check the table mapping is current.
func readLoader(path string) (partnerNames, partnerKeys, viperKeys []string, err error) {
	fset := token.NewFileSet()
	file, err := parser.ParseFile(fset, path, nil, 0)
	if err != nil {
		return nil, nil, nil, fmt.Errorf("parsing %s: %w", path, err)
	}

	ast.Inspect(file, func(n ast.Node) bool {
		if lit, ok := n.(*ast.BasicLit); ok && lit.Kind == token.STRING {
			if s, err := strconv.Unquote(lit.Value); err == nil && strings.HasPrefix(s, "cerebrium.") {
				viperKeys = append(viperKeys, s)
			}
		}
		assign, ok := n.(*ast.AssignStmt)
		if !ok || len(assign.Lhs) != 1 || len(assign.Rhs) != 1 {
			return true
		}
		ident, ok := assign.Lhs[0].(*ast.Ident)
		if !ok || ident.Name != "partnerNames" {
			return true
		}
		composite, ok := assign.Rhs[0].(*ast.CompositeLit)
		if !ok {
			return true
		}
		for _, elt := range composite.Elts {
			if lit, ok := elt.(*ast.BasicLit); ok && lit.Kind == token.STRING {
				if s, err := strconv.Unquote(lit.Value); err == nil {
					partnerNames = append(partnerNames, s)
				}
			}
		}
		return true
	})

	ast.Inspect(file, func(n ast.Node) bool {
		rng, ok := n.(*ast.RangeStmt)
		if !ok {
			return true
		}
		if ident, ok := rng.X.(*ast.Ident); !ok || ident.Name != "partnerNames" {
			return true
		}
		seen := map[string]bool{}
		ast.Inspect(rng.Body, func(inner ast.Node) bool {
			bin, ok := inner.(*ast.BinaryExpr)
			if !ok || bin.Op != token.ADD {
				return true
			}
			lit, ok := bin.Y.(*ast.BasicLit)
			if !ok || lit.Kind != token.STRING {
				return true
			}
			s, err := strconv.Unquote(lit.Value)
			if err != nil || !strings.HasPrefix(s, ".") {
				return true
			}
			key := strings.TrimPrefix(s, ".")
			if key != "" && !seen[key] {
				seen[key] = true
				partnerKeys = append(partnerKeys, key)
			}
			return true
		})
		return false
	})

	if len(partnerNames) == 0 {
		return nil, nil, nil, fmt.Errorf("no partnerNames literal found in %s", path)
	}
	if len(partnerKeys) == 0 {
		return nil, nil, nil, fmt.Errorf("no partner service keys found in the partnerNames loop in %s", path)
	}
	sort.Strings(partnerKeys)
	return partnerNames, partnerKeys, viperKeys, nil
}

SHA-256: fd3b93f9ef796e2899a84e784c8fe3559ea8f62d8c0e0c0eabc85557ed2855da