github.com/swaros/contxt/module/taskrun@v0.0.0-20240305083542-3dbd4436ac40/runner.go (about) 1 // Copyright (c) 2020 Thomas Ziegler <thomas.zglr@googlemail.com>. All rights reserved. 2 // 3 // Licensed under the MIT License 4 // 5 // 6 // Permission is hereby granted, free of charge, to any person obtaining a copy 7 // of this software and associated documentation files (the "Software"), to deal 8 // in the Software without restriction, including without limitation the rights 9 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 // copies of the Software, and to permit persons to whom the Software is 11 // furnished to do so, subject to the following conditions: 12 // 13 // The above copyright notice and this permission notice shall be included in all 14 // copies or substantial portions of the Software. 15 // 16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 // SOFTWARE. 23 24 package taskrun 25 26 import ( 27 "fmt" 28 "os" 29 "strconv" 30 31 "github.com/sirupsen/logrus" 32 "github.com/spf13/cobra" 33 "golang.org/x/term" 34 35 "github.com/swaros/contxt/module/configure" 36 "github.com/swaros/contxt/module/dirhandle" 37 "github.com/swaros/contxt/module/systools" 38 "github.com/swaros/manout" 39 ) 40 41 var ( 42 log = &logrus.Logger{ 43 Out: os.Stdout, 44 Formatter: new(logrus.TextFormatter), 45 Hooks: make(logrus.LevelHooks), 46 Level: logrus.ErrorLevel, 47 } 48 49 // cobra stuff 50 showColors bool 51 loglevel string 52 pathIndex int 53 deleteWs string 54 clearTask bool 55 setWs string 56 runAtAll bool 57 leftLen int 58 rightLen int 59 yamlIndent int 60 showInvTarget bool 61 uselastIndex bool 62 showHints bool 63 preVars map[string]string 64 65 rootCmd = &cobra.Command{ 66 Use: "contxt", 67 Short: "workspaces for the shell", 68 Long: `Contxt helps you to organize projects. 69 it helps also to execute tasks depending these projects. 70 this task can be used to setup and cleanup the workspace 71 if you enter or leave them.`, 72 Run: func(cmd *cobra.Command, args []string) { 73 checkDefaultFlags(cmd, args) 74 75 }, 76 } 77 78 completionCmd = &cobra.Command{ 79 Use: "completion [bash|zsh|fish|powershell]", 80 Short: "Generate completion script", 81 Long: `To load completions: 82 83 Bash: 84 85 $ source <(contxt completion bash) 86 87 # To load completions for each session, execute once: 88 # Linux: 89 $ contxt completion bash > /etc/bash_completion.d/contxt 90 # macOS: 91 $ contxt completion bash > /usr/local/etc/bash_completion.d/contxt 92 93 Zsh: 94 95 # If shell completion is not already enabled in your environment, 96 # you will need to enable it. You can execute the following once: 97 98 $ echo "autoload -U compinit; compinit" >> ~/.zshrc 99 100 # To load completions for each session, execute once: 101 $ contxt completion zsh > "${fpath[1]}/_contxt" 102 103 # You will need to start a new shell for this setup to take effect. 104 105 fish: 106 107 $ contxt completion fish | source 108 109 # To load completions for each session, execute once: 110 $ contxt completion fish > ~/.config/fish/completions/contxt.fish 111 PowerShell: 112 113 PS> contxt completion powershell | Out-String | Invoke-Expression 114 115 # To load completions for every new session, run: 116 PS> contxt completion powershell > contxt.ps1 117 # and source this file from your PowerShell profile. 118 119 `, 120 DisableFlagsInUseLine: true, 121 ValidArgs: []string{"bash", "zsh", "fish", "powershell"}, 122 Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), 123 Run: func(cmd *cobra.Command, args []string) { 124 switch args[0] { 125 case "bash": 126 cmd.Root().GenBashCompletion(os.Stdout) 127 case "zsh": 128 cmd.Root().GenZshCompletion(os.Stdout) 129 case "fish": 130 cmd.Root().GenFishCompletion(os.Stdout, true) 131 case "powershell": 132 cmd.Root().GenPowerShellCompletion(os.Stdout) 133 } 134 }, 135 } 136 137 gotoCmd = &cobra.Command{ 138 Use: "switch", 139 Short: "switch workspace", 140 Long: `switch the workspace to a existing ones. 141 all defined onEnter and onLeave task will be executed 142 if these task are defined 143 `, 144 Run: func(_ *cobra.Command, args []string) { 145 FindWorkspaceInfoByTemplate(nil) 146 if len(args) > 0 { 147 for _, arg := range args { 148 doMagicParamOne(arg) 149 } 150 } 151 }, 152 ValidArgsFunction: func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) { 153 if len(args) != 0 { 154 return nil, cobra.ShellCompDirectiveNoFileComp 155 } 156 targets := configure.GetGlobalConfig().ListWorkSpaces() 157 return targets, cobra.ShellCompDirectiveNoFileComp 158 }, 159 } 160 161 workspaceCmd = &cobra.Command{ 162 Use: "workspace", 163 Short: "manage workspaces", 164 Long: `create a new workspace 'ctx workspace new <name>'. 165 Remove a workspace 'ctx workspace rm <name>'. 166 list all workspaces 'ctx workspace list'. 167 scan for new projects in the workspace 'ctx workspace scan'`, 168 } 169 170 wsNewCmd = &cobra.Command{ 171 Use: "new", 172 Short: "create a new workspace", 173 Long: ` 174 create a new workspace. 175 this will trigger any onLeave task defined in the workspace 176 and also onEnter task defined in the new workspace 177 `, 178 Run: func(cmd *cobra.Command, args []string) { 179 checkDefaultFlags(cmd, args) 180 if len(args) > 0 { 181 if err := configure.GetGlobalConfig().AddWorkSpace(args[0], CallBackOldWs, CallBackNewWs); err != nil { 182 fmt.Println(err) 183 } else { 184 CtxOut("workspace created ", args[0]) 185 } 186 187 } else { 188 fmt.Println("no workspace name given") 189 } 190 }, 191 } 192 193 wsRmCmd = &cobra.Command{ 194 Use: "rm", 195 Short: "remove a workspace by given name", 196 Long: ` 197 remove a workspace. 198 this will trigger any onLeave task defined in the workspace 199 and also onEnter task defined in the new workspace 200 `, 201 Run: func(cmd *cobra.Command, args []string) { 202 checkDefaultFlags(cmd, args) 203 if len(args) > 0 { 204 if err := configure.GetGlobalConfig().RemoveWorkspace(args[0]); err != nil { 205 manout.Error("error while trying to remove workspace", err) 206 systools.Exit(systools.ErrorBySystem) 207 } else { 208 if err := configure.GetGlobalConfig().SaveConfiguration(); err != nil { 209 manout.Error("error while trying to save configuration", err) 210 systools.Exit(systools.ErrorBySystem) 211 } 212 CtxOut("workspace removed ", args[0]) 213 } 214 } else { 215 fmt.Println("no workspace name given") 216 } 217 }, 218 ValidArgsFunction: func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) { 219 if len(args) != 0 { 220 return nil, cobra.ShellCompDirectiveNoFileComp 221 } 222 targets := configure.GetGlobalConfig().ListWorkSpaces() 223 return targets, cobra.ShellCompDirectiveNoFileComp 224 }, 225 } 226 227 wsListCmd = &cobra.Command{ 228 Use: "list", 229 Short: "list all workspaces", 230 Long: "list all workspaces", 231 Run: func(cmd *cobra.Command, args []string) { 232 checkDefaultFlags(cmd, args) 233 workspacesList := configure.GetGlobalConfig().ListWorkSpaces() 234 for _, ws := range workspacesList { 235 fmt.Println(ws) 236 } 237 }, 238 } 239 240 wsScanCmd = &cobra.Command{ 241 Use: "scan", 242 Short: "scan for new projects in the workspace", 243 Long: "scan for new projects in the workspace", 244 Run: func(cmd *cobra.Command, args []string) { 245 checkDefaultFlags(cmd, args) 246 all, updated := FindWorkspaceInfoByTemplate(func(ws string, cnt int, update bool, info configure.WorkspaceInfoV2) { 247 if update { 248 CtxOut(manout.ForeBlue, ws, " ", manout.ForeDarkGrey, " ", info.Path, manout.ForeGreen, "\tupdated") 249 } else { 250 CtxOut(manout.ForeBlue, ws, " ", manout.ForeDarkGrey, " ", info.Path, manout.ForeYellow, "\tignored. nothing to do.") 251 } 252 }) 253 CtxOut("found ", all, " projects and updated ", updated, " projects") 254 255 }, 256 } 257 258 wsUseCmd = &cobra.Command{ 259 Use: "use", 260 Short: "use a workspace", 261 Long: `use a workspace. this is then the new active workspace 262 this will trigger any onLeave task defined in the workspace 263 and also onEnter task defined in the new workspace 264 `, 265 Run: func(cmd *cobra.Command, args []string) { 266 checkDefaultFlags(cmd, args) 267 if len(args) > 0 { 268 if err := configure.GetGlobalConfig().ChangeWorkspace(args[0], CallBackOldWs, CallBackNewWs); err != nil { 269 fmt.Println(err) 270 } else { 271 CtxOut("workspace used ", args[0]) 272 } 273 } else { 274 fmt.Println("no workspace name given") 275 } 276 }, 277 } 278 dirCmd = &cobra.Command{ 279 Use: "dir", 280 Short: "handle workspaces and assigned paths", 281 Long: "manage workspaces and paths they are assigned", 282 Run: func(cmd *cobra.Command, args []string) { 283 checkDefaultFlags(cmd, args) 284 checkDirFlags(cmd, args) 285 defaulttask := true 286 if pathIndex >= 0 { 287 pathStr := configure.GetGlobalConfig().GetPathByIndex(strconv.Itoa(pathIndex), ".") 288 fmt.Println(pathStr) 289 defaulttask = false 290 } 291 292 if uselastIndex { 293 GetLogger().WithField("dirIndex", configure.GetGlobalConfig().UsedV2Config.CurrentSet).Debug("current stored index") 294 pathStr := configure.GetGlobalConfig().GetActivePath(".") 295 fmt.Println(pathStr) 296 defaulttask = false 297 } 298 299 if clearTask { 300 GetLogger().Info("got clear command") 301 configure.GetGlobalConfig().ClearPaths() 302 defaulttask = false 303 } 304 305 if deleteWs != "" { 306 GetLogger().WithField("workspace", deleteWs).Info("got remove workspace option") 307 if err := configure.GetGlobalConfig().RemoveWorkspace(deleteWs); err != nil { 308 manout.Error("error while trying to deleting workspace", err) 309 systools.Exit(systools.ErrorBySystem) 310 } 311 if err := configure.GetGlobalConfig().SaveConfiguration(); err != nil { 312 manout.Error("error while trying to save configuration", err) 313 systools.Exit(systools.ErrorBySystem) 314 } 315 defaulttask = false 316 } 317 318 if setWs != "" { 319 GetLogger().WithField("workspace", setWs).Info("create a new worspace") 320 configure.GetGlobalConfig().ChangeWorkspace(setWs, CallBackOldWs, CallBackNewWs) 321 defaulttask = false 322 } 323 324 if defaulttask { 325 printInfo() 326 } 327 }, 328 } 329 330 showPaths = &cobra.Command{ 331 Use: "paths", 332 Short: "show assigned paths", 333 Run: func(cmd *cobra.Command, args []string) { 334 checkDefaultFlags(cmd, args) 335 PrintCnPaths() 336 }, 337 } 338 339 findPath = &cobra.Command{ 340 Use: "find", 341 Short: "find path by a part of them", 342 Run: func(cmd *cobra.Command, args []string) { 343 checkDefaultFlags(cmd, args) 344 if len(args) < 1 { 345 pathStr := configure.GetGlobalConfig().GetActivePath(".") 346 fmt.Println(pathStr) 347 } else { 348 path, _ := DirFindApplyAndSave(args) 349 fmt.Println(path) // path only as output. so cn can handle it 350 } 351 }, 352 } 353 354 listPaths = &cobra.Command{ 355 Use: "list", 356 Short: "show assigned paths", 357 Run: func(cmd *cobra.Command, args []string) { 358 checkDefaultFlags(cmd, args) 359 360 for _, p := range configure.GetGlobalConfig().ListWorkSpaces() { 361 fmt.Println(p) 362 } 363 }, 364 } 365 366 addPaths = &cobra.Command{ 367 Use: "add", 368 Short: "add current path (pwd) to the current workspace", 369 Run: func(cmd *cobra.Command, args []string) { 370 checkDefaultFlags(cmd, args) 371 dir, err := dirhandle.Current() 372 if err == nil { 373 fmt.Println(manout.MessageCln("add ", manout.ForeBlue, dir)) 374 configure.GetGlobalConfig().AddPath(dir) 375 configure.GetGlobalConfig().SaveConfiguration() 376 FindWorkspaceInfoByTemplate(nil) // this is parsing all templates in all workspaces and updates the project Infos 377 } 378 }, 379 } 380 381 removePath = &cobra.Command{ 382 Use: "rm", 383 Short: "remove current path (pwd) from the current workspace", 384 Run: func(cmd *cobra.Command, args []string) { 385 checkDefaultFlags(cmd, args) 386 dir, err := dirhandle.Current() 387 if err == nil { 388 fmt.Println(manout.MessageCln("try to remove ", manout.ForeBlue, dir, manout.CleanTag, " from workspace")) 389 removed := configure.GetGlobalConfig().RemovePath(dir) 390 if !removed { 391 fmt.Println(manout.MessageCln(manout.ForeRed, "error", manout.CleanTag, " path is not part of the current workspace")) 392 systools.Exit(1) 393 } else { 394 fmt.Println(manout.MessageCln(manout.ForeGreen, "success")) 395 configure.GetGlobalConfig().SaveConfiguration() 396 } 397 } 398 }, 399 } 400 401 createCmd = &cobra.Command{ 402 Use: "create", 403 Short: "create taskfile templates", 404 Run: func(cmd *cobra.Command, args []string) { 405 checkDefaultFlags(cmd, args) 406 WriteTemplate() 407 }, 408 } 409 410 createImport = &cobra.Command{ 411 Use: "import", 412 Short: "Create importfile that can be used for templating", 413 Run: func(cmd *cobra.Command, args []string) { 414 checkDefaultFlags(cmd, args) 415 if len(args) == 0 { 416 fmt.Println("No paths submitted") 417 systools.Exit(1) 418 } 419 _, path, exists, terr := GetTemplate() 420 if terr != nil { 421 fmt.Println(manout.MessageCln(manout.ForeRed, "Error ", manout.CleanTag, terr.Error())) 422 systools.Exit(33) 423 return 424 } 425 if exists { 426 for _, addPath := range args { 427 err := CreateImport(path, addPath) 428 if err != nil { 429 fmt.Println("Error adding imports:", err) 430 systools.Exit(1) 431 } 432 } 433 } else { 434 fmt.Println("no taskfile exists. create these first by contxt create") 435 systools.Exit(1) 436 } 437 438 }, 439 } 440 441 versionCmd = &cobra.Command{ 442 Use: "version", 443 Short: "prints current version", 444 Run: func(cmd *cobra.Command, args []string) { 445 checkDefaultFlags(cmd, args) 446 fmt.Println("version", configure.GetVersion(), "build", configure.GetBuild()) 447 }, 448 } 449 450 exportCmd = &cobra.Command{ 451 Use: "export", 452 Short: "exports the script section of an target like a bash script", 453 Long: `for extracting tasks commands in a format that can be executed as a shell script. 454 this will be a plain export without handling dynamic generated placeholders (default placeholders will be parsed) and contxt macros. 455 also go-template imports will be handled. 456 `, 457 Run: func(cmd *cobra.Command, args []string) { 458 checkDefaultFlags(cmd, args) 459 for _, target := range args { 460 outStr, err := ExportTask(target) 461 if err == nil { 462 fmt.Println("# --- -------------- ---------- ----- ------ ") 463 fmt.Println("# --- contxt export of target " + target) 464 fmt.Println("# --- -------------- ---------- ----- ------ ") 465 fmt.Println() 466 fmt.Println(HandlePlaceHolder(outStr)) 467 } else { 468 panic(err) 469 } 470 471 } 472 }, 473 ValidArgsFunction: func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) { 474 if len(args) != 0 { 475 return nil, cobra.ShellCompDirectiveNoFileComp 476 } 477 //targets, found := targetsAsMap() 478 targets, found := GetAllTargets() 479 if !found { 480 return nil, cobra.ShellCompDirectiveNoFileComp 481 } 482 return targets, cobra.ShellCompDirectiveNoFileComp 483 }, 484 } 485 486 lintCmd = &cobra.Command{ 487 Use: "lint", 488 Short: "checking the task file", 489 Long: `to check if the task file contains the expected changes. 490 use --full to see properties they are nor used. 491 you will also see if a unexpected propertie found `, 492 Run: func(cmd *cobra.Command, args []string) { 493 checkDefaultFlags(cmd, args) 494 leftLen, _ := cmd.Flags().GetInt("left") 495 rightLen, _ := cmd.Flags().GetInt("right") 496 showall, _ := cmd.Flags().GetBool("full") 497 yamlParse, _ := cmd.Flags().GetBool("yaml") 498 yamlIndent, _ := cmd.Flags().GetInt("indent") 499 okay := false 500 if yamlParse { 501 ShowAsYaml(true, false, yamlIndent) 502 okay = LintOut(leftLen, 0, false, true) 503 } else { 504 okay = LintOut(leftLen, rightLen, showall, false) 505 } 506 507 if !okay { 508 systools.Exit(1) 509 } 510 511 }, 512 } 513 514 installCmd = &cobra.Command{ 515 Use: "install", 516 Short: "install shell functions", 517 Long: `updates shell related files to get contxt running 518 as shortcut ctx. this will allow changing directories depending 519 on a context switch. 520 `, 521 Run: func(cmd *cobra.Command, args []string) { 522 checkDefaultFlags(cmd, args) 523 }, 524 } 525 526 installBashRc = &cobra.Command{ 527 Use: "bashrc", 528 Short: "updates bashrc for using ctx alias", 529 Long: `writes needed functions into the users private .bashrc file. 530 This includes code completion and the ctx alias. 531 `, 532 Run: func(_ *cobra.Command, _ []string) { 533 BashUser() 534 }, 535 } 536 537 installFish = &cobra.Command{ 538 Use: "fish", 539 Short: "create fish shell env for ctx", 540 Long: `create needed fish functions, auto completion for ctx 541 `, 542 Run: func(cmd *cobra.Command, _ []string) { 543 FishUpdate(cmd) 544 }, 545 } 546 547 installZsh = &cobra.Command{ 548 Use: "zsh", 549 Short: "create zsh shell env for ctx", 550 Long: `create needed zsh functions and auto completion for zsh 551 `, 552 Run: func(cmd *cobra.Command, _ []string) { 553 ZshUpdate(cmd) 554 }, 555 } 556 557 installPwrShell = &cobra.Command{ 558 Use: "powershell", 559 Short: "create powershell shell functions", 560 Long: `create needed powershell functions and auto completion for powershell. 561 for powershell the ctx shortcut is not aviable right now. 562 `, 563 Run: func(cmd *cobra.Command, _ []string) { 564 PwrShellUpdate(cmd) 565 }, 566 } 567 568 runCmd = &cobra.Command{ 569 Use: "run", 570 Short: "run a target in contxt.yml task file", 571 Run: func(cmd *cobra.Command, args []string) { 572 checkDefaultFlags(cmd, args) 573 checkRunFlags(cmd, args) 574 GetLogger().WithField("args", args).Info("Run triggered") 575 GetLogger().WithField("all", runAtAll).Info("all workspaces?") 576 577 // set variables by argument 578 for preKey, preValue := range preVars { 579 GetLogger().WithFields(logrus.Fields{"key": preKey, "val": preValue}).Info("prevalue set by argument") 580 SetPH(preKey, preValue) 581 } 582 583 if len(args) == 0 { 584 printTargets() 585 } 586 587 for _, arg := range args { 588 GetLogger().WithField("target", arg).Info("try to run target") 589 590 path, err := dirhandle.Current() 591 if err == nil { 592 if runAtAll { 593 configure.GetGlobalConfig().PathWorkerNoCd(func(_ string, path string) { 594 GetLogger().WithField("path", path).Info("change dir") 595 os.Chdir(path) 596 runTargets(path, arg) 597 }) 598 } else { 599 runTargets(path, arg) 600 } 601 } 602 } 603 604 }, 605 ValidArgsFunction: func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) { 606 if len(args) != 0 { 607 return nil, cobra.ShellCompDirectiveNoFileComp 608 } 609 targets, found := GetAllTargets() 610 if !found { 611 return nil, cobra.ShellCompDirectiveNoFileComp 612 } 613 return targets, cobra.ShellCompDirectiveNoFileComp 614 }, 615 } 616 sharedCmd = &cobra.Command{ 617 Use: "shared", 618 Short: "manage shared tasks", 619 Run: func(cmd *cobra.Command, args []string) { 620 checkDefaultFlags(cmd, args) 621 }, 622 } 623 624 sharedListCmd = &cobra.Command{ 625 Use: "list", 626 Short: "list local shared tasks", 627 Run: func(cmd *cobra.Command, args []string) { 628 checkDefaultFlags(cmd, args) 629 sharedDirs, _ := ListUseCases(false) 630 for _, sharedPath := range sharedDirs { 631 fmt.Println(sharedPath) 632 } 633 }, 634 } 635 636 sharedUpdateCmd = &cobra.Command{ 637 Use: "update", 638 Short: "updates shared uses if possible (git based)", 639 Run: func(cmd *cobra.Command, args []string) { 640 checkDefaultFlags(cmd, args) 641 useCases, err := ListUseCases(true) 642 if err == nil { 643 for _, path := range useCases { 644 fmt.Println(manout.MessageCln("check usage ", manout.ForeCyan, path)) 645 UpdateUseCase(path) 646 } 647 } 648 }, 649 } 650 ) 651 652 func checkRunFlags(cmd *cobra.Command, _ []string) { 653 runAtAll, _ = cmd.Flags().GetBool("all-paths") 654 showInvTarget, _ = cmd.Flags().GetBool("all-targets") 655 } 656 657 func checkDirFlags(cmd *cobra.Command, _ []string) { 658 659 if pindex, err := cmd.Flags().GetString("index"); err == nil { 660 configure.GetGlobalConfig().ChangeActivePath(pindex) 661 } 662 663 clearTask, _ = cmd.Flags().GetBool("clear") 664 deleteWs, _ = cmd.Flags().GetString("delete") 665 setWs, _ = cmd.Flags().GetString("workspace") 666 uselastIndex, _ = cmd.Flags().GetBool("last") 667 } 668 669 func checkDefaultFlags(cmd *cobra.Command, _ []string) { 670 color, err := cmd.Flags().GetBool("coloroff") 671 if err == nil && color { 672 manout.ColorEnabled = false 673 } 674 675 loglevel, _ = cmd.Flags().GetString("loglevel") 676 setLoggerByArg() 677 } 678 679 func initCobra() { 680 // create dir command 681 dirCmd.AddCommand(showPaths) 682 dirCmd.AddCommand(addPaths) 683 dirCmd.AddCommand(listPaths) 684 dirCmd.AddCommand(removePath) 685 dirCmd.AddCommand(findPath) 686 687 dirCmd.Flags().IntVarP(&pathIndex, "index", "i", -1, "get path by the index in order the paths are stored") 688 dirCmd.Flags().BoolP("clear", "C", false, "remove all path assigments") 689 dirCmd.Flags().BoolP("last", "l", false, "get last used path index number") 690 dirCmd.Flags().StringP("delete", "d", "", "remove workspace") 691 dirCmd.Flags().StringP("workspace", "w", "", "set workspace. if not exists a new workspace will be created") 692 693 runCmd.Flags().BoolP("all-paths", "a", false, "run targets in all paths in the current workspace") 694 runCmd.Flags().Bool("all-targets", false, "show all targets. including invisible") 695 runCmd.Flags().StringToStringVarP(&preVars, "var", "v", nil, "set variables by keyname and value.") 696 697 createCmd.AddCommand(createImport) 698 699 //rootCmd.PersistentFlags().BoolVarP(&Experimental, "experimental", "E", true, "enable experimental features") 700 rootCmd.PersistentFlags().BoolVarP(&showColors, "coloroff", "c", false, "disable usage of colors in output") 701 rootCmd.PersistentFlags().BoolVarP(&showHints, "nohints", "n", false, "disable printing hints") 702 rootCmd.PersistentFlags().StringVar(&loglevel, "loglevel", "FATAL", "set loglevel") 703 rootCmd.AddCommand(dirCmd) 704 rootCmd.AddCommand(runCmd) 705 rootCmd.AddCommand(createCmd) 706 rootCmd.AddCommand(versionCmd) 707 rootCmd.AddCommand(exportCmd) 708 709 lintCmd.Flags().IntVar(&leftLen, "left", 45, "set the width for the source code") 710 lintCmd.Flags().IntVar(&rightLen, "right", 55, "set the witdh for the current state view") 711 lintCmd.Flags().IntVar(&yamlIndent, "indent", 2, "set indent for yaml output by using lint --yaml") 712 lintCmd.Flags().Bool("full", false, "print also unset properties") 713 lintCmd.Flags().Bool("yaml", false, "display parsed taskfile as yaml file") 714 lintCmd.Flags().Bool("parse", false, "parse second level keywords (#@...)") 715 716 rootCmd.AddCommand(lintCmd) 717 718 rootCmd.AddCommand(completionCmd) 719 rootCmd.AddCommand(gotoCmd) 720 721 installPwrShell.Flags().Bool("create-profile", false, "create a profile for powershell if not exists already") 722 installCmd.AddCommand(installBashRc) 723 installCmd.AddCommand(installFish) 724 installCmd.AddCommand(installZsh) 725 installCmd.AddCommand(installPwrShell) 726 rootCmd.AddCommand(installCmd) 727 728 workspaceCmd.AddCommand(wsNewCmd, wsRmCmd, wsListCmd, wsScanCmd, wsUseCmd) 729 rootCmd.AddCommand(workspaceCmd) 730 731 sharedCmd.AddCommand(sharedListCmd) 732 sharedCmd.AddCommand(sharedUpdateCmd) 733 rootCmd.AddCommand(sharedCmd) 734 735 } 736 737 func setLoggerByArg() { 738 if loglevel != "" { 739 lvl, err := logrus.ParseLevel(loglevel) 740 if err != nil { 741 log.Fatal(err) 742 } 743 log.SetLevel(lvl) 744 } 745 } 746 747 func initLogger() { 748 //log.Out = os.Stdout 749 //log.SetLevel(logrus.DebugLevel) 750 751 } 752 753 func executeCobra() error { 754 return rootCmd.Execute() 755 } 756 757 // GetLogger is the main Logger instance 758 func GetLogger() *logrus.Logger { 759 return log 760 } 761 762 func shortcuts() bool { 763 if len(os.Args) == 2 { 764 765 switch os.Args[1] { 766 case "dir", "run", "create", "version": 767 return false 768 default: 769 foundATask := doMagicParamOne(os.Args[1]) 770 return foundATask 771 772 } 773 } 774 return false 775 } 776 777 func InitDefaultVars() { 778 SetPH("CTX_OS", configure.GetOs()) 779 SetPH("CTX_VERSION", configure.GetVersion()) 780 SetPH("CTX_BUILDNO", configure.GetBuild()) 781 // on windows we have to deal with old powershell and cmd versions, the do not 782 // support ANSII. 783 if configure.GetOs() == "windows" { 784 manout.ColorEnabled = false // by default we turn off the colors. 785 if os.Getenv("CTX_COLOR") == "ON" { // then lets see if this should forced for beeing enabled by env-var 786 manout.ColorEnabled = true 787 } else { 788 // if not forced already we try to figure out, by oure own, if the powershell is able to support ANSII 789 // this is since version 7 the case 790 version := PwrShellExec(PWRSHELL_CMD_VERSION) 791 SetPH("CTX_PS_VERSION", version) // also setup varibale to have the PS version in place 792 if version >= "7" { 793 manout.ColorEnabled = true // enable colors if we have powershell equals or greater then 7 794 } 795 } 796 } 797 // we checking the console support 798 // and turn the color off again if we do not have an terminal 799 inTerminal := "YES" 800 if !term.IsTerminal(int(os.Stdout.Fd())) { 801 manout.ColorEnabled = false 802 inTerminal = "NO" 803 } 804 SetPH("CTX_IN_TERMINAL", inTerminal) // do we have terminal running? 805 806 if currentDir, err := os.Getwd(); err == nil { // location as var 807 SetPH("CTX_PWD", currentDir) 808 } else { 809 CtxOut("Critical error while reading directory", err) 810 systools.Exit(systools.ErrorBySystem) 811 } 812 } 813 814 func setWorkspaceVariables() { 815 SetPH("CTX_WS", configure.GetGlobalConfig().UsedV2Config.CurrentSet) 816 configure.GetGlobalConfig().ExecOnWorkSpaces(func(index string, cfg configure.ConfigurationV2) { 817 for _, ws2 := range cfg.Paths { 818 setConfigVaribales(ws2, "WS") 819 } 820 821 }) 822 } 823 824 // InitWsVariables is setting up variables depending the current found configuration (.contxt.yml) 825 func InitWsVariables() { 826 setWorkspaceVariables() 827 } 828 829 func setConfigVaribales(wsInfo configure.WorkspaceInfoV2, varPrefix string) { 830 if wsInfo.Project != "" && wsInfo.Role != "" { 831 prefix := wsInfo.Project + "_" + wsInfo.Role 832 SetPH(varPrefix+"0_"+prefix, wsInfo.Path) // at least XXX0 without any version. this could be overwritten by other checkouts 833 if wsInfo.Version != "" { 834 // if version is set, we use them for avoid conflicts with different checkouts 835 if versionSan, err := systools.CheckForCleanString(wsInfo.Version); err == nil { 836 prefix += "_" + versionSan 837 // add it to ws1 as prefix for versionized keys 838 SetPH(varPrefix+"1_"+prefix, wsInfo.Path) 839 } 840 } 841 } 842 } 843 844 // FindWorkspaceInfoByTemplate is searching for a template file and if found, it will set the project and role 845 // from the template file to the workspace info 846 // this is only done if the workspace info is not set yet 847 // this is automatically done on each workspace, if the workspace is not set yet 848 // but only on the command switch and 'dir add' 849 func FindWorkspaceInfoByTemplate(updateFn func(workspace string, cnt int, update bool, info configure.WorkspaceInfoV2)) (allCount int, updatedCount int) { 850 wsCount := 0 851 wsUpdated := 0 852 if currentPath, err := os.Getwd(); err != nil { 853 CtxOut("Error while reading current directory", err) 854 systools.Exit(systools.ErrorBySystem) 855 } else { 856 haveUpdate := false 857 configure.GetGlobalConfig().ExecOnWorkSpaces(func(index string, cfg configure.ConfigurationV2) { 858 wsCount++ 859 for pathIndex, ws2 := range cfg.Paths { 860 if err := os.Chdir(ws2.Path); err == nil && ws2.Project == "" && ws2.Role == "" { 861 template, _, found, err := GetTemplate() 862 if found && err == nil { 863 if template.Workspace.Project != "" && template.Workspace.Role != "" { 864 ws2.Project = template.Workspace.Project 865 ws2.Role = template.Workspace.Role 866 cfg.Paths[pathIndex] = ws2 867 CtxOut("Found template for workspace ", index, " and set project and role to ", ws2.Project, ":", ws2.Role) 868 configure.GetGlobalConfig().UpdateCurrentConfig(cfg) 869 haveUpdate = true 870 wsUpdated++ 871 if updateFn != nil { 872 updateFn(index, wsCount, true, ws2) 873 } 874 } 875 } else { 876 if updateFn != nil { 877 updateFn(index, wsCount, false, ws2) 878 } 879 } 880 } 881 } 882 883 }) 884 if haveUpdate { 885 configure.GetGlobalConfig().SaveConfiguration() 886 } 887 os.Chdir(currentPath) 888 } 889 return wsCount, wsUpdated 890 } 891 892 // MainInit initilaize the Application. 893 // this is required for any entrie-point 894 // currently we have two of them. 895 // by running in interactive in ishell, and by running with parameters. 896 func MainInit() { 897 ResetVariables() // needed because we could run in a shell 898 pathIndex = -1 // this is the path index used for the current path. -1 means unset 899 initLogger() // init the logger. currently there is nothing happens except sometime for local debug 900 InitDefaultVars() // init all the default variables first, they are independend from any configuration 901 CopyPlaceHolder2Origin() // doing this 1/2 to have the current variables already in palce until we parse the config 902 var configErr = configure.GetGlobalConfig().InitConfig() // try to initialize current config 903 if configErr != nil { 904 log.Fatal(configErr) 905 } 906 InitWsVariables() // like InitDefaultVars but these variables needs the configuration initalized 907 CopyPlaceHolder2Origin() // make placeholders usable as golang/template again 908 } 909 910 // MainExecute runs main. parsing flags 911 func MainExecute() { 912 MainInit() 913 // first handle shortcuts 914 // before we get cobra controll 915 if !shortcuts() { 916 initCobra() 917 err := executeCobra() 918 if err != nil { 919 manout.Error("error", err) 920 systools.Exit(systools.ErrorInitApp) 921 } 922 923 } 924 925 } 926 927 func CallBackOldWs(oldws string) bool { 928 GetLogger().Info("OLD workspace: ", oldws) 929 // get all paths first 930 configure.GetGlobalConfig().PathWorkerNoCd(func(_ string, path string) { 931 932 os.Chdir(path) 933 template, templateFile, exists, _ := GetTemplate() 934 935 GetLogger().WithFields(logrus.Fields{ 936 "templateFile": templateFile, 937 "exists": exists, 938 "path": path, 939 }).Debug("path parsing") 940 941 if exists && template.Config.Autorun.Onleave != "" { 942 onleaveTarget := template.Config.Autorun.Onleave 943 GetLogger().WithFields(logrus.Fields{ 944 "templateFile": templateFile, 945 "target": onleaveTarget, 946 }).Info("execute leave-action") 947 RunTargets(onleaveTarget, true) 948 949 } 950 951 }) 952 return true 953 } 954 955 func GetColorEnabled() bool { 956 return showColors 957 } 958 959 func SetColorEnabled(enabled bool) { 960 showColors = enabled 961 } 962 963 func CallBackNewWs(newWs string) { 964 ResetVariables() // reset old variables while change the workspace. (req for shell mode) 965 MainInit() // initialize the workspace 966 GetLogger().Info("NEW workspace: ", newWs) 967 configure.GetGlobalConfig().PathWorker(func(_ string, path string) { // iterate any path 968 template, templateFile, exists, _ := GetTemplate() 969 970 GetLogger().WithFields(logrus.Fields{ 971 "templateFile": templateFile, 972 "exists": exists, 973 "path": path, 974 }).Debug("path parsing") 975 976 // try to run onEnter func at any possible target in the workspace 977 if exists && template.Config.Autorun.Onenter != "" { 978 onEnterTarget := template.Config.Autorun.Onenter 979 GetLogger().WithFields(logrus.Fields{ 980 "templateFile": templateFile, 981 "target": onEnterTarget, 982 }).Info("execute enter-action") 983 RunTargets(onEnterTarget, true) 984 } 985 986 }, func(origin string) { 987 GetLogger().WithFields(logrus.Fields{ 988 "current-dir": origin, 989 }).Debug("done calling autoruns on sub-dirs") 990 }) 991 } 992 993 func doMagicParamOne(param string) bool { 994 result := false 995 if param == "show-the-rainbow" { 996 systools.TestColorCombinations() 997 return true 998 } 999 // param is a workspace ? 1000 configure.GetGlobalConfig().ExecOnWorkSpaces(func(index string, cfg configure.ConfigurationV2) { 1001 if param == index { 1002 configure.GetGlobalConfig().ChangeWorkspace(index, CallBackOldWs, CallBackNewWs) 1003 result = true 1004 } 1005 }) 1006 1007 return result 1008 } 1009 1010 func runTargets(_ string, targets string) { 1011 RunTargets(targets, true) 1012 } 1013 1014 func printOutHeader() { 1015 fmt.Println(manout.MessageCln(manout.BoldTag, manout.ForeWhite, "cont(e)xt ", manout.CleanTag, configure.GetVersion())) 1016 fmt.Println(manout.MessageCln(manout.Dim, " build-no [", manout.ResetDim, configure.GetBuild(), manout.Dim, "]")) 1017 if configure.GetOs() == "windows" { 1018 fmt.Println(manout.MessageCln(manout.BoldTag, manout.ForeWhite, " powershell version ", manout.CleanTag, GetPH("CTX_PS_VERSION"))) 1019 } 1020 } 1021 1022 func printInfo() { 1023 printOutHeader() 1024 printPaths() 1025 } 1026 1027 func ResetVariables() { 1028 ClearAll() // clears all placeholders 1029 ClearAllData() // clears all stored maps 1030 }