github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/cmd/qri.go (about) 1 package cmd 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "os" 8 "path/filepath" 9 "runtime" 10 "sync" 11 12 "github.com/qri-io/ioes" 13 "github.com/qri-io/qri/auth/key" 14 "github.com/qri-io/qri/config" 15 "github.com/qri-io/qri/lib" 16 qhttp "github.com/qri-io/qri/lib/http" 17 "github.com/qri-io/qri/p2p" 18 "github.com/qri-io/qri/remote" 19 "github.com/qri-io/qri/remote/access" 20 "github.com/spf13/cobra" 21 ) 22 23 // Constructors encapsulates constructors required by the qri command 24 type Constructors struct { 25 CryptoGenerator key.CryptoGenerator 26 InitIPFS func(repoPath, configPath string) error 27 } 28 29 // NewQriCommand represents the base command when called without any subcommands 30 func NewQriCommand(ctx context.Context, repoPath string, ctors Constructors, ioStreams ioes.IOStreams, libOpts ...lib.Option) (*cobra.Command, func() <-chan error) { 31 opt := NewQriOptions(ctx, repoPath, ctors, ioStreams, libOpts) 32 33 cmd := &cobra.Command{ 34 Use: "qri", 35 Short: "qri GDVCS CLI", 36 Long: `qri ("query") is a set of tools for building & sharing datasets: https://qri.io 37 Feedback, questions, bug reports, and contributions are welcome! 38 https://github.com/qri-io/qri/issues`, 39 PersistentPreRun: func(cmd *cobra.Command, args []string) { 40 }, 41 BashCompletionFunction: bashCompletionFunc, 42 } 43 44 cmd.SetUsageTemplate(rootUsageTemplate) 45 cmd.PersistentFlags().BoolVarP(&opt.Migrate, "migrate", "", false, "automatically run migrations if necessary") 46 cmd.PersistentFlags().BoolVarP(&opt.NoPrompt, "no-prompt", "", false, "disable all interactive prompts") 47 cmd.PersistentFlags().BoolVarP(&opt.NoColor, "no-color", "", false, "disable colorized output") 48 cmd.PersistentFlags().StringVar(&opt.repoPath, "repo", repoPath, "filepath to load qri data from") 49 cmd.PersistentFlags().BoolVarP(&opt.LogAll, "log-all", "", false, "log all activity") 50 51 cmd.AddCommand( 52 NewAccessCommand(opt, ioStreams), 53 NewAnalyzeTransformCommand(opt, ioStreams), 54 NewApplyCommand(opt, ioStreams), 55 NewAutocompleteCommand(opt, ioStreams), 56 NewConfigCommand(opt, ioStreams), 57 NewConnectCommand(opt, ioStreams), 58 NewDAGCommand(opt, ioStreams), 59 NewDiffCommand(opt, ioStreams), 60 NewGetCommand(opt, ioStreams), 61 NewListCommand(opt, ioStreams), 62 NewLogCommand(opt, ioStreams), 63 NewLogbookCommand(opt, ioStreams), 64 NewPushCommand(opt, ioStreams), 65 NewPullCommand(opt, ioStreams), 66 NewPeersCommand(opt, ioStreams), 67 NewPreviewCommand(opt, ioStreams), 68 NewRegistryCommand(opt, ioStreams), 69 NewRemoveCommand(opt, ioStreams), 70 NewRenameCommand(opt, ioStreams), 71 NewRenderCommand(opt, ioStreams), 72 NewSaveCommand(opt, ioStreams), 73 NewSearchCommand(opt, ioStreams), 74 NewSetupCommand(opt, ioStreams), 75 NewValidateCommand(opt, ioStreams), 76 NewVersionCommand(opt, ioStreams), 77 NewWhatChangedCommand(opt, ioStreams), 78 ) 79 80 for _, sub := range cmd.Commands() { 81 sub.SetUsageTemplate(defaultUsageTemplate) 82 } 83 84 return cmd, opt.Shutdown 85 } 86 87 // QriOptions holds the Root Command State 88 type QriOptions struct { 89 ioes.IOStreams 90 91 // TODO (b5) - this context should be refactored away, preferring to pass 92 // this stored context object down through function calls 93 ctx context.Context 94 releasers sync.WaitGroup 95 doneCh chan struct{} 96 97 // path to the qri data directory 98 repoPath string 99 // generator is source of generating cryptographic info 100 ctors Constructors 101 // automatically run migrations if necessary 102 Migrate bool 103 // NoPrompt Disables all promt messages 104 NoPrompt bool 105 // NoColor disables colorized output 106 NoColor bool 107 // path to configuration object 108 ConfigPath string 109 // Whether to log all activity by enabling logging for all packages 110 LogAll bool 111 libOpts []lib.Option 112 // inst is the Instance that holds state needed by qri's methods 113 inst *lib.Instance 114 } 115 116 // NewQriOptions creates an options object 117 func NewQriOptions(ctx context.Context, repoPath string, ctors Constructors, ioStreams ioes.IOStreams, libOpts []lib.Option) *QriOptions { 118 return &QriOptions{ 119 IOStreams: ioStreams, 120 ctx: ctx, 121 doneCh: make(chan struct{}), 122 repoPath: repoPath, 123 libOpts: libOpts, 124 ctors: ctors, 125 } 126 } 127 128 // Init will initialize the internal state before any command is run (excluding `qri setup`) 129 func (o *QriOptions) Init() (err error) { 130 if o.inst != nil { 131 return 132 } 133 setNoPrompt(o.NoPrompt) 134 135 repoErr := lib.QriRepoExists(o.repoPath) 136 if repoErr != nil { 137 return errors.New("no qri repo exists\nhave you run 'qri setup'?") 138 } 139 140 opts := []lib.Option{ 141 lib.OptIOStreams(o.IOStreams), // transfer iostreams to instance 142 lib.OptCheckConfigMigrations(o.migrationApproval, (!o.Migrate && !o.NoPrompt)), 143 lib.OptSetLogAll(o.LogAll), 144 lib.OptRemoteServerOptions([]remote.OptionsFunc{ 145 // look for a remote policy 146 remote.OptLoadPolicyFileIfExists(filepath.Join(o.repoPath, access.DefaultAccessControlPolicyFilename)), 147 }), 148 } 149 150 if o.libOpts != nil { 151 opts = append(o.libOpts, o.libOpts...) 152 } 153 154 o.inst, err = lib.NewInstance(o.ctx, o.repoPath, opts...) 155 if err != nil { 156 return 157 } 158 159 // Handle color and prompt flags which apply to every command 160 shouldColorOutput := !o.NoColor 161 cfg := o.inst.GetConfig() 162 if cfg != nil && cfg.CLI != nil { 163 shouldColorOutput = cfg.CLI.ColorizeOutput 164 } 165 // todo(arqu): have a config var to indicate force override for windows 166 if runtime.GOOS == "windows" { 167 shouldColorOutput = false 168 } 169 setNoColor(!shouldColorOutput) 170 171 // TODO (b5) - this is a hack to make progress bars not show up while running 172 // tests. It does have the real-world implication that "shouldColorOutput" 173 // being false also disables progress bars, which may be what we want (ahem: TTY 174 // detection), but even if so, isn't the right use of this variable name 175 if shouldColorOutput { 176 // TODO(ramfox): we guard for a nil bus in `PrintProgressBarsOnEvents` 177 // but noting here that no requests that go through http rpc will have 178 // a working bus, so we won't get any progress bars when working over 179 // http rpc until this is adjusted (once we get the events "rpc-ified") 180 PrintProgressBarsOnEvents(o.IOStreams.ErrOut, o.inst.Bus()) 181 } 182 183 log.Debugf("running cmd %q", os.Args) 184 185 return 186 } 187 188 // migrationApproval returns a boolen based on either flag-derived state or user 189 // input approving the execution of migrations 190 func (o *QriOptions) migrationApproval() bool { 191 if o.Migrate { 192 return true 193 } else if o.NoPrompt { 194 return false 195 } 196 197 msg := `Your repo needs updating before qri can start. 198 Run migration now?` 199 return confirm(o.Out, o.In, msg, false) 200 } 201 202 // Instance returns the instance this options is using 203 func (o *QriOptions) Instance() (*lib.Instance, error) { 204 if err := o.Init(); err != nil { 205 return nil, err 206 } 207 return o.inst, nil 208 } 209 210 // RepoPath returns the path to the qri data directory 211 func (o *QriOptions) RepoPath() string { 212 return o.repoPath 213 } 214 215 // Config returns from internal state 216 func (o *QriOptions) Config() (*config.Config, error) { 217 if err := o.Init(); err != nil { 218 return nil, err 219 } 220 return o.inst.GetConfig(), nil 221 } 222 223 // Constructors returns a struct for expensive constructor functions 224 func (o *QriOptions) Constructors() Constructors { 225 return o.ctors 226 } 227 228 // HTTPClient returns a client for performing RPC over HTTP 229 func (o *QriOptions) HTTPClient() *qhttp.Client { 230 if err := o.Init(); err != nil { 231 return nil 232 } 233 return o.inst.HTTPClient() 234 } 235 236 // ConnectionNode returns the internal QriNode, if it is available 237 func (o *QriOptions) ConnectionNode() (*p2p.QriNode, error) { 238 if o.inst == nil { 239 return nil, fmt.Errorf("repo not available") 240 } 241 return o.inst.Node(), nil 242 } 243 244 // Shutdown closes the instance 245 func (o *QriOptions) Shutdown() <-chan error { 246 if o.inst == nil { 247 done := make(chan error) 248 go func() { done <- nil }() 249 return done 250 } 251 return o.inst.Shutdown() 252 }