github.com/swaros/contxt/module/runner@v0.0.0-20240305083542-3dbd4436ac40/cobrainit.go (about) 1 // Copyright (c) 2023 Thomas Ziegler <thomas.zglr@googlemail.com>. All rights reserved. 2 // 3 // # Licensed under the MIT License 4 // 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 // 12 // The above copyright notice and this permission notice shall be included in all 13 // copies or substantial portions of the Software. 14 // 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 // SOFTWARE. 22 package runner 23 24 import ( 25 "errors" 26 "fmt" 27 "os" 28 "path/filepath" 29 30 "github.com/spf13/cobra" 31 "github.com/swaros/contxt/module/configure" 32 "github.com/swaros/contxt/module/ctxout" 33 "github.com/swaros/contxt/module/dirhandle" 34 "github.com/swaros/contxt/module/mimiclog" 35 "github.com/swaros/contxt/module/systools" 36 ) 37 38 type SessionCobra struct { 39 RootCmd *cobra.Command // the root command 40 ExternalCmdHndl CmdExecutor // the command executor that is used to execute the commands logic 41 Options CobraOptions // all flags for the root command 42 } 43 44 type CobraOptions struct { 45 ShowColors bool // flag for show colors in output 46 DisableTable bool // flag for disable table output 47 RunOnceTimeOutMillis int // timeout for run once mode 48 ShowHints bool 49 LogLevel string 50 DirAll bool // dir flag for show all dirs in any workspace 51 ShowFullTargets bool 52 ShowBuild bool 53 LastDirIndex bool 54 UseContext string // flag to switch to a workspace in the context of another workspace 55 InContext bool // flag to use the current workspace as context 56 PreVars map[string]string // preset variables they will set variables from commandline. they will be overwritten by the template 57 } 58 59 // this is the main entry point for the cobra command 60 func NewCobraCmds() *SessionCobra { 61 return &SessionCobra{ 62 RootCmd: &cobra.Command{ 63 Use: configure.GetBinaryName(), 64 Short: "organize workspaces in the shell", 65 Long: `contxt is a tool to manage your projects. 66 it setups your shell environment to fast switch between projects 67 without the need to search for the right directory. 68 also it includes a task runner to execute commands in the right context. 69 `, 70 Run: func(cmd *cobra.Command, args []string) { 71 cmd.Help() 72 }, 73 }, 74 } 75 } 76 77 // init the cobra command tree 78 func (c *SessionCobra) Init(cmd CmdExecutor) error { 79 c.ExternalCmdHndl = cmd 80 if c.ExternalCmdHndl == nil { 81 return fmt.Errorf("no command executor defined") 82 } 83 c.RootCmd.PersistentFlags().BoolVarP(&c.Options.ShowColors, "coloroff", "c", false, "disable usage of colors in output") 84 c.RootCmd.PersistentFlags().BoolVarP(&c.Options.ShowHints, "nohints", "n", false, "disable printing hints") 85 c.RootCmd.PersistentFlags().StringVar(&c.Options.LogLevel, "loglevel", "FATAL", "set loglevel") 86 c.RootCmd.PersistentFlags().BoolVar(&c.Options.DisableTable, "notable", false, "disable table format output") 87 c.RootCmd.PersistentFlags().StringVar(&c.Options.UseContext, "usecontext", "", "use a different workspace as context") 88 c.RootCmd.PersistentFlags().BoolVarP(&c.Options.InContext, "incontext", "I", false, "use the current workspace as context") 89 c.RootCmd.PersistentFlags().StringToStringVarP(&c.Options.PreVars, "var", "v", nil, "set variables by keyname and value.") 90 91 c.RootCmd.AddCommand( 92 c.GetWorkspaceCmd(), 93 c.getCompletion(), 94 c.GetGotoCmd(), 95 c.GetDirCmd(), 96 c.GetInteractiveCmd(), 97 c.GetRunCmd(), 98 c.GetLintCmd(), 99 c.GetInstallCmd(), 100 c.GetVersionCmd(), 101 c.getSharedCmd(), 102 ) 103 104 return nil 105 } 106 107 func (c *SessionCobra) getSharedCmd() *cobra.Command { 108 shared := &cobra.Command{ 109 Use: "shared", 110 Short: "manage shared libraries", 111 Long: "manage shared libraries", 112 Run: func(cmd *cobra.Command, args []string) { 113 if len(args) == 0 { 114 cmd.Help() 115 } 116 }, 117 } 118 shared.AddCommand( 119 c.sharedCmdList(), 120 c.getSharedUpdateCmd(), 121 ) 122 return shared 123 } 124 125 func (c *SessionCobra) sharedCmdList() *cobra.Command { 126 return &cobra.Command{ 127 Use: "list", 128 Short: "show all shared libraries", 129 Long: "show all shared libraries", 130 Run: func(cmd *cobra.Command, args []string) { 131 c.checkDefaultFlags(cmd, args) 132 c.ExternalCmdHndl.PrintShared() 133 }, 134 } 135 } 136 137 func (c *SessionCobra) getSharedUpdateCmd() *cobra.Command { 138 return &cobra.Command{ 139 Use: "update", 140 Short: "update shared libraries", 141 Long: "update shared libraries", 142 Run: func(cmd *cobra.Command, args []string) { 143 c.checkDefaultFlags(cmd, args) 144 shared := NewSharedHelper() 145 useCases, err := shared.ListUseCases(true) 146 if err == nil { 147 for _, path := range useCases { 148 c.println("check usage ", ctxout.ForeBlue, path, ctxout.CleanTag) 149 shared.UpdateUseCase(path) 150 } 151 } 152 }, 153 } 154 } 155 156 // til here we define the commands 157 // they needs to be added to the root command 158 159 // getCompletion returns the completion command for the root command. 160 func (c *SessionCobra) getCompletion() *cobra.Command { 161 return &cobra.Command{ 162 Use: "completion [bash|zsh|fish|powershell]", 163 Short: "Generate completion script", 164 Long: `To load completions: 165 166 Bash: 167 168 $ source <(contxt completion bash) 169 170 # To load completions for each session, execute once: 171 # Linux: 172 $ contxt completion bash > /etc/bash_completion.d/contxt 173 # macOS: 174 $ contxt completion bash > /usr/local/etc/bash_completion.d/contxt 175 176 Zsh: 177 178 # If shell completion is not already enabled in your environment, 179 # you will need to enable it. You can execute the following once: 180 181 $ echo "autoload -U compinit; compinit" >> ~/.zshrc 182 183 # To load completions for each session, execute once: 184 $ contxt completion zsh > "${fpath[1]}/_contxt" 185 186 # You will need to start a new shell for this setup to take effect. 187 188 fish: 189 190 $ contxt completion fish | source 191 192 # To load completions for each session, execute once: 193 $ contxt completion fish > ~/.config/fish/completions/contxt.fish 194 PowerShell: 195 196 PS> contxt completion powershell | Out-String | Invoke-Expression 197 198 # To load completions for every new session, run: 199 PS> contxt completion powershell > contxt.ps1 200 # and source this file from your PowerShell profile. 201 202 `, 203 DisableFlagsInUseLine: true, 204 ValidArgs: []string{"bash", "zsh", "fish", "powershell"}, 205 Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), 206 Run: func(cmd *cobra.Command, args []string) { 207 switch args[0] { 208 case "bash": 209 cmd.Root().GenBashCompletion(os.Stdout) 210 case "zsh": 211 cmd.Root().GenZshCompletion(os.Stdout) 212 case "fish": 213 cmd.Root().GenFishCompletion(os.Stdout, true) 214 case "powershell": 215 cmd.Root().GenPowerShellCompletion(os.Stdout) 216 } 217 }, 218 } 219 220 } 221 222 func (c *SessionCobra) GetGotoCmd() *cobra.Command { 223 return &cobra.Command{ 224 Use: "switch", 225 Short: "switch workspace", 226 Long: `switch the workspace to a existing ones. 227 all defined onEnter and onLeave task will be executed 228 if these task are defined 229 `, 230 RunE: func(_ *cobra.Command, args []string) error { 231 current := dirhandle.Pushd() 232 current.Popd() 233 var cmderr error 234 found := false 235 if len(args) > 0 { 236 configure.GetGlobalConfig().ExecOnWorkSpaces(func(index string, cfg configure.ConfigurationV2) { 237 if args[0] == index { 238 found = true 239 if err := configure.GetGlobalConfig().ChangeWorkspace(index, c.ExternalCmdHndl.CallBackOldWs, c.ExternalCmdHndl.CallBackNewWs); err != nil { 240 cmderr = err 241 } 242 } 243 }) 244 } 245 if !found { 246 cmderr = fmt.Errorf("workspace %s not found", args[0]) 247 } 248 return cmderr 249 }, 250 ValidArgsFunction: func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) { 251 if len(args) != 0 { 252 return nil, cobra.ShellCompDirectiveNoFileComp 253 } 254 targets := configure.GetGlobalConfig().ListWorkSpaces() 255 return targets, cobra.ShellCompDirectiveNoFileComp 256 }, 257 } 258 } 259 260 func (c *SessionCobra) GetPrintWsCmd() *cobra.Command { 261 return &cobra.Command{ 262 Use: "show", 263 Short: "show all workspaces", 264 Long: `show all workspaces and mark the current one`, 265 Run: func(cmd *cobra.Command, args []string) { 266 c.checkDefaultFlags(cmd, args) 267 c.ExternalCmdHndl.PrintWorkspaces() 268 }, 269 } 270 } 271 272 // prints the current workspace 273 func (c *SessionCobra) PrintCurrentWs() *cobra.Command { 274 return &cobra.Command{ 275 Use: "current", 276 Short: "prints the current workspace", 277 Long: `prints the current active workspace. This is the workspace which is used for the current session`, 278 Run: func(cmd *cobra.Command, args []string) { 279 c.checkDefaultFlags(cmd, args) 280 c.println(c.ExternalCmdHndl.GetCurrentWorkSpace()) 281 }, 282 } 283 } 284 285 func (c *SessionCobra) GetListWsCmd() *cobra.Command { 286 return &cobra.Command{ 287 Use: "ls", 288 Short: "list all workspaces", 289 Long: `list all workspaces`, 290 Run: func(cmd *cobra.Command, args []string) { 291 c.checkDefaultFlags(cmd, args) 292 ws := c.ExternalCmdHndl.GetWorkspaces() 293 for _, w := range ws { 294 out, printer := c.ExternalCmdHndl.GetOuputHandler() 295 ctxout.CtxOut(out, printer, w) 296 } 297 }, 298 } 299 } 300 301 func (c *SessionCobra) GetNewWsCmd() *cobra.Command { 302 return &cobra.Command{ 303 Use: "new", 304 Short: "create a new workspace", 305 Long: ` 306 create a new workspace. 307 this will trigger any onLeave task defined in the workspace 308 and also onEnter task defined in the new workspace 309 `, 310 RunE: func(cmd *cobra.Command, args []string) error { 311 //checkDefaultFlags(cmd, args) 312 if len(args) == 1 { 313 if err := configure.GetGlobalConfig().AddWorkSpace(args[0], c.ExternalCmdHndl.CallBackOldWs, c.ExternalCmdHndl.CallBackNewWs); err != nil { 314 return err 315 } else { 316 c.print("workspace created ", args[0]) 317 } 318 319 } else { 320 if len(args) == 0 { 321 return errors.New("no workspace name given") 322 } else { 323 return errors.New("to many arguments") 324 } 325 } 326 return nil 327 }, 328 } 329 } 330 331 func (c *SessionCobra) GetRmWsCmd() *cobra.Command { 332 return &cobra.Command{ 333 Use: "rm", 334 Short: "remove a workspace by given name", 335 Long: ` 336 remove a workspace. 337 this will trigger any onLeave task defined in the workspace 338 and also onEnter task defined in the new workspace 339 `, 340 RunE: func(cmd *cobra.Command, args []string) error { 341 //checkDefaultFlags(cmd, args) 342 if len(args) > 0 { 343 if err := configure.GetGlobalConfig().RemoveWorkspace(args[0]); err != nil { 344 c.log().Error("error while trying to remove workspace", err) 345 return err 346 } else { 347 if err := configure.GetGlobalConfig().SaveConfiguration(); err != nil { 348 c.log().Error("error while trying to save configuration", err) 349 return err 350 } 351 c.print("workspace removed ", args[0]) 352 } 353 } else { 354 return errors.New("no workspace name given") 355 } 356 return nil 357 }, 358 ValidArgsFunction: func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) { 359 if len(args) != 0 { 360 return nil, cobra.ShellCompDirectiveNoFileComp 361 } 362 targets := configure.GetGlobalConfig().ListWorkSpaces() 363 return targets, cobra.ShellCompDirectiveNoFileComp 364 }, 365 } 366 } 367 368 func (c *SessionCobra) GetScanCmd() *cobra.Command { 369 return &cobra.Command{ 370 Use: "scan", 371 Short: "scan all projects in the workspace", 372 Long: "looking for project information, like names and roles, in all the workspaces, and update the global project list", 373 Run: func(cmd *cobra.Command, args []string) { 374 c.log().Debug("scan for new projects") 375 c.checkDefaultFlags(cmd, args) 376 c.println(ctxout.BoldTag, "Scanning for new projects", ctxout.CleanTag, " ... ") 377 c.println("checking any known contxt project if there are information to update") 378 c.print("\n") 379 c.print("<table>") 380 c.print("<row>", ctxout.BoldTag, "<tab size='15'> project</tab><tab size='70'>path</tab><tab size='15' origin='2'>status</tab>", ctxout.CleanTag, "</row>") 381 current := dirhandle.Pushd() 382 all, updated := c.ExternalCmdHndl.FindWorkspaceInfoByTemplate(func(ws string, cnt int, update bool, info configure.WorkspaceInfoV2) { 383 color := ctxout.ForeYellow 384 msg := "found" 385 if !update { 386 msg = "nothing new" 387 color = ctxout.ForeLightGreen 388 } 389 c.print("<row>", ctxout.ForeBlue, "<tab size='15'> ", ws, "</tab>", ctxout.ForeLightBlue, "<tab size='70'>", info.Path, color, "</tab><tab size='15' origin='2'>", msg, "</tab></row>") 390 }) 391 current.Popd() 392 c.print("</table>") 393 c.println(ctxout.CleanTag, "") 394 c.println("found ", ctxout.ForeLightBlue, all, ctxout.CleanTag, " projects and updated ", ctxout.ForeLightBlue, updated, ctxout.CleanTag, " projects") 395 396 }, 397 } 398 } 399 400 func (c *SessionCobra) GetWorkspaceCmd() *cobra.Command { 401 wsCmd := &cobra.Command{ 402 Use: "workspace", 403 Short: "manage workspaces", 404 Long: `create a new workspace 'ctx workspace new <name>'. 405 Remove a workspace 'ctx workspace rm <name>'. 406 list all workspaces 'ctx workspace list'. 407 scan for new projects in the workspace 'ctx workspace scan'`, 408 } 409 wsCmd.AddCommand( 410 c.GetNewWsCmd(), 411 c.GetRmWsCmd(), 412 c.GetScanCmd(), 413 c.GetPrintWsCmd(), 414 c.GetListWsCmd(), 415 c.PrintCurrentWs(), 416 ) 417 return wsCmd 418 } 419 420 // -- lint cmd 421 422 func (c *SessionCobra) GetLintCmd() *cobra.Command { 423 var showAll bool 424 lCmd := &cobra.Command{ 425 Use: "lint", 426 Short: "lint the .contxt.yaml file", 427 Long: "lint the .contxt.yaml and shows unexpected fields", 428 RunE: func(cmd *cobra.Command, args []string) error { 429 c.checkDefaultFlags(cmd, args) 430 431 return c.ExternalCmdHndl.Lint(showAll) 432 }, 433 } 434 435 lCmd.Flags().BoolVarP(&showAll, "show-issues", "i", false, "show all issues") 436 lCmd.AddCommand(c.GetLintTemplateCmd()) 437 438 return lCmd 439 } 440 441 // a lint sub command to display the current loaded template 442 // as yaml 443 func (c *SessionCobra) GetLintTemplateCmd() *cobra.Command { 444 return &cobra.Command{ 445 Use: "yaml", 446 Short: "show the current loaded template", 447 Long: "show the current loaded template as yaml", 448 Run: func(cmd *cobra.Command, args []string) { 449 c.checkDefaultFlags(cmd, args) 450 c.ExternalCmdHndl.PrintTemplate() 451 }, 452 } 453 } 454 455 // -- Run cmd 456 457 func (c *SessionCobra) GetRunCmd() *cobra.Command { 458 rCmd := &cobra.Command{ 459 Use: "run", 460 Short: "run a command in the context of a project", 461 Long: "run a command in the context of a project", 462 RunE: func(cmd *cobra.Command, args []string) error { 463 c.checkDefaultFlags(cmd, args) 464 if len(args) > 0 { 465 c.log().Debug("run command in context of project", args) 466 for _, p := range args { 467 if err := c.ExternalCmdHndl.RunTargets(p, true); err != nil { 468 return err 469 } 470 } 471 } else { 472 targets := c.ExternalCmdHndl.GetTargets(false) 473 for _, p := range targets { 474 c.println(p) 475 } 476 return nil 477 } 478 return nil 479 }, 480 ValidArgsFunction: func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) { 481 if len(args) != 0 { 482 return nil, cobra.ShellCompDirectiveNoFileComp 483 } 484 targets := c.ExternalCmdHndl.GetTargets(false) 485 return targets, cobra.ShellCompDirectiveNoFileComp 486 }, 487 } 488 return rCmd 489 } 490 491 // -- Dir Command 492 493 func (c *SessionCobra) GetDirCmd() *cobra.Command { 494 dCmd := &cobra.Command{ 495 Use: "dir", 496 Short: "handle workspaces and assigned paths", 497 Long: "manage workspaces and paths they are assigned", 498 Run: func(cmd *cobra.Command, args []string) { 499 c.checkDefaultFlags(cmd, args) 500 501 // if we do not have any workspace, we do not need to do anything. 502 // without an workspace we also have no paths to show 503 if len(configure.GetGlobalConfig().ListWorkSpaces()) == 0 { 504 c.log().Debug("no workspace found, nothing to do") 505 c.println("no workspace found, nothing to do. create a new workspace with 'ctx workspace new <name>'") 506 return 507 } 508 509 if c.Options.LastDirIndex { 510 pathStr := configure.GetGlobalConfig().GetActivePath(".") 511 fmt.Println(pathStr) 512 c.Options.LastDirIndex = false // one time usage 513 return 514 } 515 516 if len(args) == 0 { 517 c.log().Debug("show all paths in any workspace", c.Options.DirAll) 518 current := configure.GetGlobalConfig().UsedV2Config.CurrentSet 519 c.print("<table>") 520 if c.Options.DirAll { 521 configure.GetGlobalConfig().ExecOnWorkSpaces(func(index string, cfg configure.ConfigurationV2) { 522 configure.GetGlobalConfig().UsedV2Config.CurrentSet = index 523 // header for each workspace 524 c.print("<row>", ctxout.BoldTag, "<tab size='100' fill=' '>", index, ctxout.CleanTag, ctxout.ForeDarkGrey, ": index (", cfg.CurrentIndex, ")</tab></row>") 525 c.ExternalCmdHndl.PrintPaths(true, c.Options.ShowFullTargets) 526 c.print("<row>", ctxout.ForeDarkGrey, "<tab size='100' fill='─'> </tab>", ctxout.CleanTag, "</row>") 527 528 }) 529 } else { 530 c.ExternalCmdHndl.PrintPaths(false, c.Options.ShowFullTargets) 531 } 532 c.println("</table>") 533 configure.GetGlobalConfig().UsedV2Config.CurrentSet = current 534 } 535 }, 536 } 537 dCmd.Flags().BoolVarP(&c.Options.DirAll, "all", "a", false, "show all paths in any workspace") 538 dCmd.Flags().BoolVarP(&c.Options.ShowFullTargets, "full", "f", false, "show full amount of targets") 539 dCmd.Flags().BoolVarP(&c.Options.LastDirIndex, "last", "l", false, "show last used path") 540 dCmd.AddCommand(c.GetDirFindCmd(), c.GetDirAddCmd(), c.GetDirRmCmd(), c.GetDirLsCmd()) 541 return dCmd 542 } 543 544 func (c *SessionCobra) GetDirFindCmd() *cobra.Command { 545 fCmd := &cobra.Command{ 546 Use: "find", 547 Short: "find a path in the current workspace", 548 Long: "find a path in the current workspace by the given argument combination", 549 Run: func(cmd *cobra.Command, args []string) { 550 if len(args) < 1 { 551 pathStr := configure.GetGlobalConfig().GetActivePath(".") 552 // we use plain output here. so we can use it in the shell and is not affected by the output handler 553 fmt.Println(pathStr) 554 } else { 555 path, _ := c.ExternalCmdHndl.DirFindApplyAndSave(args) 556 fmt.Println(path) // path only as output. so cn can handle it. and again plain fmt usage 557 } 558 }, 559 } 560 return fCmd 561 } 562 563 // GetDirLsCmd returns the command to list all paths in the current workspace 564 func (c *SessionCobra) GetDirLsCmd() *cobra.Command { 565 fCmd := &cobra.Command{ 566 Use: "list", 567 Aliases: []string{"ls"}, 568 Short: "list all paths in the current workspace", 569 Long: "list all paths in the current workspace in a simple list", 570 Run: func(cmd *cobra.Command, args []string) { 571 paths := configure.GetGlobalConfig().ListPaths() 572 for _, path := range paths { 573 c.println(path) 574 } 575 576 }, 577 } 578 return fCmd 579 } 580 581 func (c *SessionCobra) GetDirAddCmd() *cobra.Command { 582 aCmd := &cobra.Command{ 583 Use: "add", 584 Short: "add path(s) to the workspace", 585 Long: `add current path (pwd) if no argument is set. 586 else add the given paths to the workspace 587 like 'ctx dir add /path/to/dir /path/to/other/dir' 588 paths need to be absolute paths`, 589 RunE: func(cmd *cobra.Command, args []string) error { 590 c.checkDefaultFlags(cmd, args) 591 c.print("add path(s) to workspace: ", ctxout.ForeGreen, configure.GetGlobalConfig().UsedV2Config.CurrentSet, ctxout.CleanTag) 592 if len(args) == 0 { 593 dir, err := os.Getwd() 594 if err != nil { 595 c.log().Error(err) 596 return err 597 } 598 args = append(args, dir) 599 } 600 c.println(" (", ctxout.ForeDarkGrey, len(args), ctxout.CleanTag, " paths)") 601 for _, arg := range args { 602 if arg == "" { 603 return errors.New("empty path is not allowed") 604 } 605 c.println("try ... ", ctxout.ForeLightBlue, arg, ctxout.CleanTag) 606 // we need to check if the path is absolute 607 if !filepath.IsAbs(arg) { 608 c.println("error: ", ctxout.ForeRed, "path is not absolute", ctxout.CleanTag) 609 currentDir, _ := filepath.Abs(".") 610 return errors.New("given path for adding is not absolute [" + arg + "], current path:" + currentDir) 611 } 612 613 if ok, err := dirhandle.Exists(arg); !ok || err != nil { 614 if err != nil { 615 c.println("error: ", ctxout.ForeRed, err, ctxout.CleanTag) 616 return err 617 } 618 c.println("error: ", ctxout.ForeRed, "path does not exist", ctxout.CleanTag) 619 return errors.New("path does not exist") 620 } 621 if err := configure.GetGlobalConfig().AddPath(arg); err == nil { 622 c.println("add ", ctxout.ForeBlue, arg, ctxout.CleanTag) 623 configure.GetGlobalConfig().SaveConfiguration() 624 cmd := c.GetScanCmd() // we use the scan command to update the project infos 625 cmd.Run(cmd, nil) // this is parsing all templates in all workspaces and updates the project Infos 626 } else { 627 c.println("error: ", ctxout.ForeRed, err, ctxout.CleanTag) 628 return err 629 } 630 } 631 return nil 632 }, 633 } 634 return aCmd 635 } 636 637 func (c *SessionCobra) GetDirRmCmd() *cobra.Command { 638 rCmd := &cobra.Command{ 639 Use: "rm", 640 Short: "remove path(s) from the workspace", 641 Long: `remove the given paths from the workspace 642 like 'ctx dir rm /path/to/dir /path/to/other/dir'`, 643 RunE: func(cmd *cobra.Command, args []string) error { 644 c.checkDefaultFlags(cmd, args) 645 c.print("remove path(s) from workspace: ", ctxout.ForeGreen, configure.GetGlobalConfig().UsedV2Config.CurrentSet, ctxout.CleanTag) 646 if len(args) == 0 { 647 dir, err := os.Getwd() 648 if err != nil { 649 c.log().Error(err) 650 return err 651 } 652 args = append(args, dir) 653 } 654 c.println(" (", ctxout.ForeDarkGrey, len(args), ctxout.CleanTag, " paths)") 655 for _, arg := range args { 656 c.println("try ... ", ctxout.ForeLightBlue, arg, ctxout.CleanTag) 657 // we need to check if the path is absolute 658 if !filepath.IsAbs(arg) { 659 c.println("error: ", ctxout.ForeRed, "path is not absolute", ctxout.CleanTag) 660 return errors.New("path is not absolute for dir cmd") 661 } 662 663 // we don not check if the path exists. we just remove it 664 // it is possible that the path is not existing anymore 665 if ok := configure.GetGlobalConfig().RemovePath(arg); ok { 666 c.println("remove ", ctxout.ForeBlue, arg, ctxout.CleanTag) 667 configure.GetGlobalConfig().SaveConfiguration() 668 cmd := c.GetScanCmd() // we use the scan command to update the project infos 669 cmd.Run(cmd, nil) // this is parsing all templates in all workspaces and updates the project Infos 670 } else { 671 c.println("error: ", ctxout.ForeRed, "could not remove path", ctxout.CleanTag) 672 return errors.New("could not remove path") 673 } 674 } 675 return nil 676 }, 677 } 678 return rCmd 679 } 680 681 func (c *SessionCobra) GetInstallCmd() *cobra.Command { 682 iCmd := &cobra.Command{ 683 Use: "install", 684 Short: "install shell support", 685 Long: `install shell support for different shells. 686 supported shells are: bash, zsh, fish, powershell`, 687 RunE: func(cmd *cobra.Command, args []string) error { 688 c.checkDefaultFlags(cmd, args) 689 c.print("install shell support") 690 return nil 691 }, 692 } 693 iCmd.AddCommand(c.GetInstallBashCmd()) 694 iCmd.AddCommand(c.GetInstallZshCmd()) 695 iCmd.AddCommand(c.GetInstallFishCmd()) 696 iCmd.AddCommand(c.GetInstallPowershellCmd()) 697 return iCmd 698 } 699 700 func (c *SessionCobra) GetInstallBashCmd() *cobra.Command { 701 iCmd := &cobra.Command{ 702 Use: "bash", 703 Short: "install shell support for bash", 704 Long: `install shell support for bash. 705 this is done by adding some functions and a source command to the bashrc file. 706 Afterwards you can use the ctx command in your bash shell instead of contxt. 707 So after an switch command, you will automatically change the dir to the new workspace. 708 You can also use the cn command to change one of the assigned paths to the current workspace.`, 709 710 RunE: func(cmd *cobra.Command, args []string) error { 711 c.ExternalCmdHndl.MainInit() 712 installer := NewShellInstall(c.log()) 713 if err := installer.BashUserInstall(); err != nil { 714 c.log().Error(err) 715 return err 716 } 717 return nil 718 }, 719 } 720 return iCmd 721 } 722 723 func (c *SessionCobra) GetInstallZshBaseFunc() *cobra.Command { 724 iCmd := &cobra.Command{ 725 Use: "base", 726 Short: "get the zsh base functions for completion", 727 Long: `get the zsh base functions for completion. 728 this is sometimes helpfull to get completion in zsh running, because of some 729 restrictions in zsh. 730 `, 731 732 Run: func(cmd *cobra.Command, args []string) { 733 c.ExternalCmdHndl.MainInit() 734 // check the shortcut flag 735 shortcut, _ := cmd.Flags().GetBool("shortcut") 736 installer := NewShellInstall(c.log()) 737 fmt.Println(installer.GetBinFunc(c.RootCmd, shortcut)) 738 739 }, 740 } 741 iCmd.Flags().BoolP("shortcut", "s", false, "show the shortcut functions") 742 return iCmd 743 } 744 745 func (c *SessionCobra) GetInstallZshShellScript() *cobra.Command { 746 iCmd := &cobra.Command{ 747 Use: "compscript", 748 Short: "creates a script for zsh completion", 749 Long: `creates a script for zsh completion. 750 this script try to create the completions for zsh locally. 751 and uses then sudo to copy the file to the zsh completion dir. 752 `, 753 754 Run: func(cmd *cobra.Command, args []string) { 755 c.ExternalCmdHndl.MainInit() 756 757 installer := NewShellInstall(c.log()) 758 fmt.Println(installer.GetScript(c.RootCmd)) 759 760 }, 761 } 762 return iCmd 763 } 764 765 func (c *SessionCobra) GetInstallZshCmd() *cobra.Command { 766 iCmd := &cobra.Command{ 767 Use: "zsh", 768 Short: "install shell support for zsh", 769 Long: `install shell support for zsh. 770 this is done by adding some functions and a source command to the zshrc file. 771 Afterwards you can use the ctx command in your zsh shell instead of contxt. 772 So after an switch command, you will automatically change the dir to the new workspace. 773 You can also use the cn command to change one of the assigned paths to the current workspace.`, 774 775 RunE: func(cmd *cobra.Command, args []string) error { 776 c.ExternalCmdHndl.MainInit() 777 installer := NewShellInstall(c.log()) 778 if err := installer.ZshUpdate(c.RootCmd); err != nil { 779 return err 780 } 781 return nil 782 }, 783 } 784 iCmd.AddCommand(c.GetInstallZshBaseFunc()) 785 iCmd.AddCommand(c.GetInstallZshShellScript()) 786 return iCmd 787 } 788 789 func (c *SessionCobra) GetInstallFishCmd() *cobra.Command { 790 iCmd := &cobra.Command{ 791 Use: "fish", 792 Short: "install shell support for fish", 793 Long: `install shell support for fish. 794 this is done by adding some functions and a source command to the fish config file. 795 Afterwards you can use the ctx command in your fish shell instead of contxt. 796 So after an switch command, you will automatically change the dir to the new workspace. 797 You can also use the cn command to change one of the assigned paths to the current workspace.`, 798 799 RunE: func(cmd *cobra.Command, args []string) error { 800 c.ExternalCmdHndl.MainInit() 801 installer := NewShellInstall(c.log()) 802 if err := installer.FishUpdate(c.RootCmd); err != nil { 803 c.log().Error(err) 804 return err 805 } 806 return nil 807 }, 808 } 809 return iCmd 810 } 811 812 func (c *SessionCobra) GetInstallPowershellCmd() *cobra.Command { 813 iCmd := &cobra.Command{ 814 Use: "powershell", 815 Short: "install shell support for powershell", 816 Long: `install shell support for powershell. 817 this is done by adding some functions and a source command to the powershell profile file. 818 Afterwards you can use the ctx command in your powershell shell instead of contxt. 819 You can also use the cn command to change one of the assigned paths to the current workspace.`, 820 RunE: func(cmd *cobra.Command, args []string) error { 821 c.ExternalCmdHndl.MainInit() 822 installer := NewShellInstall(c.log()) 823 if err := installer.PwrShellUpdate(c.RootCmd); err != nil { 824 c.log().Error(err) 825 return err 826 } 827 return nil 828 }, 829 } 830 return iCmd 831 } 832 833 func (c *SessionCobra) GetVersionCmd() *cobra.Command { 834 vCmd := &cobra.Command{ 835 Use: "version", 836 Short: "print the version number of contxt", 837 Long: `All software has versions. This is contxt's`, 838 RunE: func(cmd *cobra.Command, args []string) error { 839 c.checkDefaultFlags(cmd, args) 840 if c.Options.ShowBuild { 841 c.println("contxt version: ", configure.GetVersion(), " build ", configure.GetBuild()) 842 } else { 843 c.println("contxt version: ", configure.GetVersion()) 844 } 845 return nil 846 }, 847 } 848 vCmd.Flags().BoolVarP(&c.Options.ShowBuild, "build", "b", false, "show build information") 849 return vCmd 850 } 851 852 // log returns the logger 853 func (c *SessionCobra) log() mimiclog.Logger { 854 return c.ExternalCmdHndl.GetLogger() 855 } 856 857 // print prints the given message to the output handler 858 func (c *SessionCobra) print(msg ...interface{}) { 859 c.ExternalCmdHndl.Print(msg...) 860 } 861 862 // println prints the given message to the output handler with a new line 863 func (c *SessionCobra) println(msg ...interface{}) { 864 c.ExternalCmdHndl.Println(msg...) 865 } 866 867 func (c *SessionCobra) checkDefaultFlags(cmd *cobra.Command, _ []string) { 868 envColorOff := os.Getenv("CTX_COLOROFF") 869 // TODO: why this way? is the global flag not working? 870 color, err := cmd.Flags().GetBool("coloroff") 871 if err == nil && (color || envColorOff == "true") { 872 behave := ctxout.GetBehavior() 873 behave.NoColored = true 874 ctxout.SetBehavior(behave) 875 } else if err != nil { 876 c.log().Error(err) 877 systools.Exit(systools.ErrorInitApp) 878 } 879 880 c.Options.LogLevel, _ = cmd.Flags().GetString("loglevel") 881 if err := c.ExternalCmdHndl.SetLogLevel(c.Options.LogLevel); err != nil { 882 c.log().Error(err) 883 systools.Exit(systools.ErrorInitApp) 884 } 885 886 // force the log level by env var 887 envLogLevel := os.Getenv("CTX_LOGLEVEL") 888 if envLogLevel != "" { 889 c.Options.LogLevel = envLogLevel 890 } 891 892 // force the disable table flag by env var 893 envDisableTable := os.Getenv("CTX_DISABLE_TABLE") 894 if envDisableTable == "true" { 895 c.Options.DisableTable = true 896 } 897 if c.Options.DisableTable { 898 // overwrite the table plugin with a disabled one 899 ctxout.UpdateFilterByRef(ctxout.NewTabOut(), ctxout.PostFilterInfo{Disabled: true}) 900 } 901 902 // preset all variables from command line 903 // set variables by argument. 904 for preKey, preValue := range c.Options.PreVars { 905 c.log().Debug("preset Value", preKey, preValue) 906 c.ExternalCmdHndl.SetPreValue(preKey, preValue) 907 } 908 909 // forces to change to the current used workspace and the assosiated path 910 if c.Options.InContext { 911 if c.Options.UseContext != "" { 912 c.log().Critical("use of --incontext and --usecontext is not allowed") 913 systools.Exit(systools.ErrorInitApp) 914 } 915 c.log().Info("force to change to the current workspace and path", configure.GetGlobalConfig().CurrentWorkspace()) 916 path := configure.GetGlobalConfig().GetActivePath("") 917 if path != "" { 918 c.log().Info("change to path", path) 919 os.Chdir(path) 920 // important to set the default variables in the context of the used workspace 921 c.ExternalCmdHndl.MainInit() 922 } 923 } 924 925 // if this flag is set, we do change into the workspace and using the current directory there. 926 // so any command will be executed in the context of the workspace 927 if c.Options.UseContext != "" { 928 c.log().Info("working in context of ", c.Options.UseContext) 929 configure.GetGlobalConfig().ExecOnWorkSpaces(func(index string, cfg configure.ConfigurationV2) { 930 if c.Options.UseContext == index { 931 932 if err := configure.GetGlobalConfig().ChangeWorkspaceNotSaved(index); err != nil { 933 c.log().Error("error while trying to change workspace", err) 934 systools.Exit(systools.ErrorWhileLoadCfg) 935 } 936 937 path := configure.GetGlobalConfig().GetActivePath("") 938 if path != "" { 939 c.println("going to path: ", ctxout.ForeLightBlue, path, ctxout.CleanTag) 940 os.Chdir(path) 941 // important to set the default variables in the context of the used workspace 942 c.ExternalCmdHndl.MainInit() 943 } 944 } 945 }) 946 947 } 948 } 949 950 func (c *SessionCobra) GetInteractiveCmd() *cobra.Command { 951 iCmd := &cobra.Command{ 952 Use: "interactive", 953 Short: "start the interactive mode", 954 Long: `start the interactive mode`, 955 Run: func(cmd *cobra.Command, args []string) { 956 c.checkDefaultFlags(cmd, args) 957 c.println("start interactive mode") 958 c.ExternalCmdHndl.InteractiveScreen() 959 }, 960 } 961 iCmd.AddCommand(c.GetInteractiveOnceCmd()) 962 return iCmd 963 } 964 965 func (c *SessionCobra) GetInteractiveOnceCmd() *cobra.Command { 966 iCmd := &cobra.Command{ 967 Use: "once", 968 Short: "run a command in the context ot the contxt shell", 969 Long: `run a command in the contxt shell. so you have access to all variables and functions 970 they are only available in the contxt shell`, 971 Run: func(cmd *cobra.Command, args []string) { 972 c.checkDefaultFlags(cmd, args) 973 c.println("...using shell to execute commands:") 974 for _, arg := range args { 975 c.println("... ", ctxout.ForeLightBlue, arg, ctxout.CleanTag) 976 } 977 c.println("... timeout is ", ctxout.ForeLightBlue, c.Options.RunOnceTimeOutMillis, ctxout.CleanTag, " milliseconds") 978 c.ExternalCmdHndl.ShellWithComands(args, c.Options.RunOnceTimeOutMillis) 979 }, 980 } 981 iCmd.PersistentFlags().IntVarP(&c.Options.RunOnceTimeOutMillis, "timeout", "t", 10000, "timeout for the interactive once commands in milliseconds") 982 return iCmd 983 }