actions

package
v0.0.0-...-e6d1dd3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 21, 2024 License: Apache-2.0 Imports: 27 Imported by: 0

Documentation

Overview

SPDX-License-Identifier: Apache-2.0 Copyright © 2021 Roberto Hidalgo <milpa@un.rob.mx>

SPDX-License-Identifier: Apache-2.0 Copyright © 2021 Roberto Hidalgo <milpa@un.rob.mx>

SPDX-License-Identifier: Apache-2.0 Copyright © 2021 Roberto Hidalgo <milpa@un.rob.mx>

Index

Constants

This section is empty.

Variables

View Source
var AfterHelp = os.Exit
View Source
var CommandTree = &command.Command{
	Path:    []string{"__command_tree"},
	Hidden:  true,
	Summary: "Outputs a representation of the command tree at a given PREFIX",
	Description: `Prints out command names and descriptions, or optionally all properties as ﹅json﹅ or ﹅yaml﹅.

  ## Examples

  ﹅﹅﹅sh
  # print all known subcommands
  ` + runtime.Executable + ` __command_tree

  # print a tree of "` + runtime.Executable + ` itself" sub-commands
  ` + runtime.Executable + ` __command_tree

  # print out all commands, skipping groups
  ` + runtime.Executable + ` __command_tree --template '{{ if (not (eq .Meta.Kind "virtual")) }}{{ .FullName }}'$'\n''{{ end }}'

  # get all commands as a json tree
  ` + runtime.Executable + ` __command_tree --output json

  # same, but as the yaml representation of this command itself
  ` + runtime.Executable + ` __command_tree --output json itself command-tree
  ﹅﹅﹅`,
	Arguments: command.Arguments{
		{
			Name:        "prefix",
			Description: "The prefix to traverse the command tree from",
			Variadic:    true,
			Default:     []string{},
		},
	},
	Options: command.Options{
		"depth": &command.Option{
			Default:     15,
			Type:        command.ValueTypeInt,
			Description: "The maximum depth to search for commands",
		},
		"format": &command.Option{
			Default:     "text",
			Description: "The format to output results in",
			Values: &command.ValueSource{
				Static: &([]string{"yaml", "json", "text", "autocomplete"}),
			},
		},
		"template": &command.Option{
			Default:     "{{ .Name }} - {{ .Summary }}\n",
			Description: "a go-template to apply to every command",
		},
	},
	Action: func(cmd *command.Command) error {
		args := cmd.Arguments[0].ToValue().([]string)

		base, remainingArgs, err := cmd.Cobra.Root().Find(args)
		if err != nil {
			return err
		}

		if len(remainingArgs) > 0 && len(remainingArgs) == len(args) {
			return nil
		}

		depth := cmd.Options["depth"].ToValue().(int)
		format := cmd.Options["format"].ToString()

		ctLog.Debugf("looking for commands at %s depth: %d", base.Name(), depth)
		tree.Build(base, depth)

		var serializationFn serializar
		addMeta := func(res serializar) serializar {
			return func(i interface{}) ([]byte, error) {
				if t, ok := i.(*tree.CommandTree); ok {
					addMetaToTree(t)
				}
				return res(i)
			}
		}
		switch format {
		case "yaml":
			serializationFn = addMeta(yaml.Marshal)
		case "json":
			serializationFn = addMeta(func(t interface{}) ([]byte, error) { return json.MarshalIndent(t, "", "  ") })
		case "text":
			outputTpl := cmd.Options["template"].ToString()

			tpl := template.Must(template.New("treeItem").Funcs(render.TemplateFuncs).Parse(outputTpl))
			serializationFn = func(t interface{}) ([]byte, error) {
				tree := t.(*tree.CommandTree)
				addMetaToTree(tree)
				var output bytes.Buffer
				if err := tpl.Execute(&output, tree.Command); err != nil {
					return output.Bytes(), err
				}
				err := tree.Traverse(func(cmd *command.Command) error { return tpl.Execute(&output, cmd) })
				return output.Bytes(), err
			}
		case "autocomplete":
			serializationFn = func(interface{}) ([]byte, error) {
				return []byte(strings.Join(tree.ChildrenNames(), "\n") + "\n"), nil
			}
		default:
			return errors.BadArguments{Msg: fmt.Sprintf("Unknown format <%s> for command tree serialization", format)}
		}

		serialized, err := tree.Serialize(serializationFn)
		if err != nil {
			return err
		}
		fmt.Print(serialized)

		return nil
	},
}
View Source
var Docs = &command.Command{
	Path:        []string{"help", "docs"},
	Summary:     "Displays docs on TOPIC",
	Description: "Shows markdown-formatted documentation from milpa repos. See `" + _c.Milpa + " " + _c.HelpCommandName + " docs milpa repo docs` for more information on how to write your own.",
	Arguments: command.Arguments{
		&command.Argument{
			Name:        "topic",
			Description: "The topic to show docs for",
			Variadic:    true,
			Required:    false,
			Values: &command.ValueSource{
				Suggestion: true,
				Func: func(cmd *command.Command, currentValue, config string) (values []string, flag cobra.ShellCompDirective, err error) {
					args := cmd.Arguments[0].ToValue().([]string)
					dlog.Debugf("looking for docs given %v and %s", args, currentValue)

					cv := ""
					if len(args) > 1 && args[len(args)-1] == "" {

						cv = args[len(args)-1]
						args = args[0 : len(args)-1]
					}
					dlog.Debugf("looking for docs given %v and %s", args, cv)
					docs, err := lookup.Docs(args, cv, false)
					if err != nil {
						return nil, cobra.ShellCompDirectiveNoFileComp, err
					}

					return docs, cobra.ShellCompDirectiveNoFileComp, nil
				},
			},
		},
	},
	Options: command.Options{
		"server": {
			Description: "Starts an http server at the specified address",
			Type:        command.ValueTypeBoolean,
			Default:     false,
		},
		"listen": {
			Description: "The address to listen at when using `--server`",
			Type:        command.ValueTypeString,
			Default:     "localhost:4242",
		},
		"base": {
			Description: "A URL base to use for rendering html links",
			Type:        command.ValueTypeString,
			Default:     "http://localhost:4242",
		},
	},
	Meta: milpaCommand.Meta{
		Path: os.Getenv(_c.EnvVarMilpaRoot) + "/milpa/docs",
		Name: []string{_c.HelpCommandName, "docs"},
		Repo: os.Getenv(_c.EnvVarMilpaRoot),
		Kind: "docs",
	},
	Action: func(cmd *command.Command) error {
		args := cmd.Arguments[0].ToValue().([]string)
		if len(args) == 0 {
			if cmd.Options["server"].ToValue().(bool) {
				listen := cmd.Options["listen"].ToString()
				base := cmd.Options["base"]
				defaultListen := cmd.Options["listen"].Default.(string)
				address := strings.ReplaceAll(base.ToString(), defaultListen, listen)
				dlog.Infof("Starting docs server at http://%s, press CTRL-C to stop...", listen)
				return startServer(listen, address)
			}
			dlog.Debug("Rendering docs help page")
			err := cmd.Cobra.Help()
			if err != nil {
				return err
			}
			AfterHelp(statuscode.RenderHelp)
			dlog.Debug("Rendered docs help page")
			return nil
		}

		contents, err := docs.FromQuery(args)
		if err != nil {
			switch err.(type) {
			case errors.BadArguments:

				helpErr := cmd.Cobra.Help()
				if helpErr != nil {
					os.Exit(statuscode.ProgrammerError)
				}
				logrus.Error(err)
				os.Exit(statuscode.Usage)
			}
			return errors.NotFound{Msg: err.Error()}
		}

		titleExp := regexp.MustCompile("^title: (.+)")
		frontmatterSep := []byte("---\n")
		if len(contents) > 3 && string(contents[0:4]) == string(frontmatterSep) {

			parts := bytes.SplitN(contents, frontmatterSep, 3)
			title := titleExp.FindString(string(parts[1]))
			if title != "" {
				title = strings.TrimPrefix(title, "title: ")
			} else {
				title = strings.Join(args, " ")
			}
			contents = bytes.Join([][]byte{[]byte("# " + title + "\n"), parts[2]}, []byte("\n"))
		}

		withColor, _ := cmd.Cobra.Flags().GetBool("no-color")

		doc, err := render.Markdown(contents, !withColor)
		if err != nil {
			return err
		}

		if _, err := cmd.Cobra.OutOrStderr().Write(doc); err != nil {
			return err
		}
		AfterHelp(statuscode.RenderHelp)

		return nil
	},
}
View Source
var Doctor = &command.Command{
	Path:        []string{"itself", "doctor"},
	Summary:     "Validates all commands found on the `MILPA_PATH`",
	Description: `This command will run checks on all known commands, parsing specs and validating their values.`,
	Options: command.Options{
		"summary": {
			Type:        command.ValueTypeBoolean,
			Description: "Only print errors, if any",
		},
	},
	Action: func(cmd *command.Command) (err error) {
		bold := color.New(color.Bold)
		warn := color.New(color.FgYellow)
		fail := color.New(color.FgRed)
		success := color.New(color.FgGreen)
		failedOverall := false
		failures := map[string]uint8{}
		var out = cmd.Cobra.OutOrStdout()

		summarize := cmd.Options["summary"].ToValue().(bool)

		var milpaRoot string
		if mp := os.Getenv(_c.EnvVarMilpaRoot); mp != "" {
			milpaRoot = strings.Join(strings.Split(mp, ":"), "\n")
		} else {
			milpaRoot = warn.Sprint("empty")
		}
		bold.Fprintf(out, "%s is: %s\n", _c.EnvVarMilpaRoot, milpaRoot)

		var milpaPath string
		bold.Fprintf(out, "%s is: ", _c.EnvVarMilpaPath)
		if mp := os.Getenv(_c.EnvVarMilpaPath); mp != "" {
			milpaPath = "\n" + strings.Join(bootstrap.MilpaPath, "\n")
		} else {
			milpaPath = warn.Sprint("empty")
		}
		fmt.Fprintf(out, "%s\n", milpaPath)
		fmt.Fprintln(out, "")
		bold.Fprintf(out, "Runnable commands:\n")

		for _, cmd := range tree.CommandList() {
			if cmd.Hidden {
				continue
			}
			docLog.Debugf("Validating %s", cmd.FullName())

			message := ""

			hasFailures := false
			report := map[string]int{}
			if meta, ok := cmd.Meta.(mcmd.Meta); ok {

				parsingErrors := meta.ParsingErrors()
				if len(parsingErrors) > 0 {
					hasFailures = true

					for _, err := range parsingErrors {
						failures[cmd.FullName()]++
						message += fail.Sprintf("  - %s\n", err)
					}
				} else {
					report = cmd.Validate()
				}
			} else {
				report = cmd.Validate()
			}
			for property, status := range report {
				formatter := success
				if status == 1 {
					hasFailures = true
					failures[cmd.FullName()]++
					formatter = fail
				} else if status == 2 {
					formatter = warn
				}

				message += formatter.Sprintf("  - %s\n", property)
			}
			prefix := "✅"
			if hasFailures {
				failedOverall = true
				prefix = "❌"
			}

			fmt.Println(bold.Sprintf("%s %s", prefix, cmd.FullName()), "—", cmd.Path)
			if !summarize || hasFailures {
				if message != "" {
					fmt.Fprintln(out, message)
				}
				fmt.Fprintln(out, "-----------")
			}
		}

		if failedOverall {
			failureReport := []string{}
			for cmd, count := range failures {
				plural := ""
				if count > 1 {
					plural = "s"
				}
				failureReport = append(failureReport, fmt.Sprintf("%s - %d issue%s", cmd, count, plural))
			}

			return fmt.Errorf("your milpa could use some help with the following commands:\n%s", strings.Join(failureReport, "\n"))
		}

		return nil
	},
}

Functions

func DoctorModeEnabled

func DoctorModeEnabled() bool

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL