1// Copyright 2018 The CUE Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package cmd 16 17import ( 18 "fmt" 19 "os" 20 21 "github.com/spf13/cobra" 22) 23 24// TODO: generate long description from documentation. 25 26func newCmdCmd(c *Command) *cobra.Command { 27 cmd := &cobra.Command{ 28 Use: "cmd <name> [inputs]", 29 Short: "run a user-defined shell command", 30 Long: `cmd executes the named command for each of the named instances. 31 32Commands define actions on instances. For example, they may 33specify how to upload a configuration to Kubernetes. Commands are 34defined directly in tool files, which are regular CUE files 35within the same package with a filename ending in _tool.cue. 36These are typically defined at the module root so that they apply 37to all instances. 38 39Each command consists of one or more tasks. A task may, for 40example, load or write a file, consult a user on the command 41line, fetch a web page, and so on. Each task has inputs and 42outputs. Outputs are typically filled out by the task 43implementation as the task completes. 44 45Inputs of tasks my refer to outputs of other tasks. The cue tool 46does a static analysis of the configuration and only starts tasks 47that are fully specified. Upon completion of each task, cue 48rewrites the instance, filling in the completed task, and 49reevaluates which other tasks can now start, and so on until all 50tasks have completed. 51 52Available tasks can be found in the package documentation at 53 54 https://pkg.go.dev/cuelang.org/go/pkg/tool?tab=subdirectories 55 56Examples: 57 58In this simple example, we define a command called "hello", 59which declares a single task called "print" which uses 60"tool/exec.Run" to execute a shell command that echos output to 61the terminal: 62 63 $ cat <<EOF > hello_tool.cue 64 package foo 65 66 import "tool/exec" 67 68 city: "Amsterdam" 69 who: *"World" | string @tag(who) 70 71 // Say hello! 72 command: hello: { 73 print: exec.Run & { 74 cmd: "echo Hello \(who)! Welcome to \(city)." 75 } 76 } 77 EOF 78 79We run the "hello" command like this: 80 81 $ cue cmd hello 82 Hello World! Welcome to Amsterdam. 83 84 $ cue cmd --inject who=Jan hello 85 Hello Jan! Welcome to Amsterdam. 86 87 88In this example we declare the "prompted" command which has four 89tasks. The first task prompts the user for a string input. The 90second task depends on the first, and echos the response back to 91the user with a friendly message. The third task pipes the output 92from the second to a file. The fourth task pipes the output from 93the second to standard output (i.e. it echos it again). 94 95 package foo 96 97 import ( 98 "tool/cli" 99 "tool/exec" 100 "tool/file" 101 ) 102 103 city: "Amsterdam" 104 105 // Say hello! 106 command: prompter: { 107 // save transcript to this file 108 var: file: *"out.txt" | string @tag(file) 109 110 ask: cli.Ask & { 111 prompt: "What is your name?" 112 response: string 113 } 114 115 // starts after ask 116 echo: exec.Run & { 117 cmd: ["echo", "Hello", ask.response + "!"] 118 stdout: string // capture stdout 119 } 120 121 // starts after echo 122 append: file.Append & { 123 filename: var.file 124 contents: echo.stdout 125 } 126 127 // also starts after echo 128 print: cli.Print & { 129 text: echo.stdout 130 } 131 } 132 133Run "cue help commands" for more details on tasks and commands. 134`, 135 RunE: mkRunE(c, func(cmd *Command, args []string) error { 136 w := cmd.Stderr() 137 if len(args) == 0 { 138 fmt.Fprintln(w, "cmd must be run as one of its subcommands") 139 } else { 140 const msg = `cmd must be run as one of its subcommands: unknown subcommand %q 141Ensure commands are defined in a "_tool.cue" file. 142` 143 fmt.Fprintf(w, msg, args[0]) 144 } 145 fmt.Fprintln(w, "Run 'cue help cmd' for known subcommands.") 146 os.Exit(1) // TODO: get rid of this 147 return nil 148 }), 149 } 150 151 cmd.Flags().SetInterspersed(false) 152 153 addInjectionFlags(cmd.Flags(), true) 154 155 return cmd 156} 157