wip
This commit is contained in:
49
cmd/alias.go
49
cmd/alias.go
@ -1,57 +1,18 @@
|
||||
/*
|
||||
Copyright © 2023 Ryan McGuire <ryand_mcguire@sweetwater.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// aliasCmd represents the alias command
|
||||
var aliasCmd = &cobra.Command{
|
||||
Use: "alias",
|
||||
Short: "A brief description of your command",
|
||||
Long: `A longer description that spans multiple lines and likely contains examples
|
||||
and usage of using your command. For example:
|
||||
|
||||
Cobra is a CLI library for Go that empowers applications.
|
||||
This application is a tool to generate the needed files
|
||||
to quickly create a Cobra application.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("alias called")
|
||||
},
|
||||
Short: "Manage project aliases",
|
||||
Long: aliasCmdLong,
|
||||
// Just re-use the hooks for project
|
||||
PersistentPreRun: initProjectCmd,
|
||||
PersistentPostRun: postProjectCmd,
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(aliasCmd)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
|
||||
// Cobra supports Persistent Flags which will work for this command
|
||||
// and all subcommands, e.g.:
|
||||
// aliasCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||
|
||||
// Cobra supports local flags which will only run when this command
|
||||
// is called directly, e.g.:
|
||||
// aliasCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
}
|
||||
|
95
cmd/alias_add.go
Normal file
95
cmd/alias_add.go
Normal file
@ -0,0 +1,95 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/pterm/pterm"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/gitlab"
|
||||
"gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/projects"
|
||||
)
|
||||
|
||||
var aliasAddCmd = &cobra.Command{
|
||||
Use: "add",
|
||||
Short: "Add a project alias",
|
||||
Long: aliasAddCmdLong,
|
||||
Run: runAddAliasCmd,
|
||||
}
|
||||
|
||||
func runAddAliasCmd(cmd *cobra.Command, args []string) {
|
||||
var project *gitlab.Project
|
||||
|
||||
// Check by flag
|
||||
if projectID := viper.GetInt("alias.add.projectid"); projectID > 0 {
|
||||
plog.Debug(fmt.Sprintf("Adding for inbound project ID %d", projectID))
|
||||
project = cache.GetProjectByID(projectID)
|
||||
}
|
||||
|
||||
// Collect by fzf
|
||||
if project == nil {
|
||||
var err error
|
||||
project, err = fzfProject(cmd.Context())
|
||||
if err != nil || project == nil {
|
||||
plog.Fatal("No project to alias, nothing to do", plog.Args("error", err))
|
||||
}
|
||||
}
|
||||
|
||||
addNewAliases(project.ID)
|
||||
}
|
||||
|
||||
func addNewAliases(projectID int) {
|
||||
project := cache.GetProjectByID(projectID)
|
||||
if project == nil {
|
||||
plog.Error("Failed to find project to alias", plog.Args("projectID", projectID))
|
||||
return
|
||||
}
|
||||
|
||||
// Collect the aliases
|
||||
aliases := promptAliasesForProject(project)
|
||||
|
||||
// Add aliases
|
||||
for _, a := range aliases {
|
||||
a = strings.Trim(a, " '\"%")
|
||||
if a == "" {
|
||||
continue
|
||||
}
|
||||
if err := cache.AddAlias(a, project.ID); err != nil {
|
||||
plog.Debug("Skipping alias add", plog.Args(
|
||||
"error", err,
|
||||
"alias", a,
|
||||
))
|
||||
} else {
|
||||
plog.Info("Successfully added alias to project", plog.Args(
|
||||
"project", project.String(),
|
||||
"alias", a,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func promptAliasesForProject(p *gitlab.Project) []string {
|
||||
aliases := cache.GetProjectAliases(p)
|
||||
if len(aliases) > 0 {
|
||||
plog.Info("Adding aliases to project", plog.Args(
|
||||
"project", p.String(),
|
||||
"existingAliases", projects.ProjectAliasesString(aliases),
|
||||
))
|
||||
} else {
|
||||
pterm.Info.Printfln("Adding aliases to %s", p.Name)
|
||||
}
|
||||
|
||||
response, _ := pterm.DefaultInteractiveTextInput.
|
||||
WithMultiLine(false).
|
||||
WithDefaultValue(p.Path + " ").
|
||||
Show("Enter aliases separated by space")
|
||||
|
||||
return strings.Split(response, " ")
|
||||
}
|
||||
|
||||
func init() {
|
||||
aliasCmd.AddCommand(aliasAddCmd)
|
||||
aliasAddCmd.PersistentFlags().Int("projectID", 0, "Specify a project by ID")
|
||||
viper.BindPFlag("alias.add.projectID", aliasAddCmd.Flag("projectID"))
|
||||
}
|
95
cmd/alias_delete.go
Normal file
95
cmd/alias_delete.go
Normal file
@ -0,0 +1,95 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/pterm/pterm"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/gitlab"
|
||||
"gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/projects"
|
||||
)
|
||||
|
||||
var aliasAddCmd = &cobra.Command{
|
||||
Use: "add",
|
||||
Short: "Add a project alias",
|
||||
Long: aliasAddCmdLong,
|
||||
Run: runAddAliasCmd,
|
||||
}
|
||||
|
||||
func runAddAliasCmd(cmd *cobra.Command, args []string) {
|
||||
var project *gitlab.Project
|
||||
|
||||
// Check by flag
|
||||
if projectID := viper.GetInt("alias.add.projectid"); projectID > 0 {
|
||||
plog.Debug(fmt.Sprintf("Adding for inbound project ID %d", projectID))
|
||||
project = cache.GetProjectByID(projectID)
|
||||
}
|
||||
|
||||
// Collect by fzf
|
||||
if project == nil {
|
||||
var err error
|
||||
project, err = fzfProject(cmd.Context())
|
||||
if err != nil || project == nil {
|
||||
plog.Fatal("No project to alias, nothing to do", plog.Args("error", err))
|
||||
}
|
||||
}
|
||||
|
||||
addNewAliases(project.ID)
|
||||
}
|
||||
|
||||
func addNewAliases(projectID int) {
|
||||
project := cache.GetProjectByID(projectID)
|
||||
if project == nil {
|
||||
plog.Error("Failed to find project to alias", plog.Args("projectID", projectID))
|
||||
return
|
||||
}
|
||||
|
||||
// Collect the aliases
|
||||
aliases := promptAliasesForProject(project)
|
||||
|
||||
// Add aliases
|
||||
for _, a := range aliases {
|
||||
a = strings.Trim(a, " '\"%")
|
||||
if a == "" {
|
||||
continue
|
||||
}
|
||||
if err := cache.AddAlias(a, project.ID); err != nil {
|
||||
plog.Debug("Skipping alias add", plog.Args(
|
||||
"error", err,
|
||||
"alias", a,
|
||||
))
|
||||
} else {
|
||||
plog.Info("Successfully added alias to project", plog.Args(
|
||||
"project", project.String(),
|
||||
"alias", a,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func promptAliasesForProject(p *gitlab.Project) []string {
|
||||
aliases := cache.GetProjectAliases(p)
|
||||
if len(aliases) > 0 {
|
||||
plog.Info("Adding aliases to project", plog.Args(
|
||||
"project", p.String(),
|
||||
"existingAliases", projects.ProjectAliasesString(aliases),
|
||||
))
|
||||
} else {
|
||||
pterm.Info.Printfln("Adding aliases to %s", p.Name)
|
||||
}
|
||||
|
||||
response, _ := pterm.DefaultInteractiveTextInput.
|
||||
WithMultiLine(false).
|
||||
WithDefaultValue(p.Path + " ").
|
||||
Show("Enter aliases separated by space")
|
||||
|
||||
return strings.Split(response, " ")
|
||||
}
|
||||
|
||||
func init() {
|
||||
aliasCmd.AddCommand(aliasAddCmd)
|
||||
aliasAddCmd.PersistentFlags().Int("projectID", 0, "Specify a project by ID")
|
||||
viper.BindPFlag("alias.add.projectID", aliasAddCmd.Flag("projectID"))
|
||||
}
|
@ -8,16 +8,12 @@ import (
|
||||
"gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/projects"
|
||||
)
|
||||
|
||||
const desc = `Contains sub-commands for managing project cache.
|
||||
The project cache keeps this speedy, without smashing against the GitLab
|
||||
API every time a new project is added / searched for`
|
||||
|
||||
var cache *projects.Cache
|
||||
|
||||
var cacheCmd = &cobra.Command{
|
||||
Use: "cache",
|
||||
Short: "Manage GitLab project cache",
|
||||
Long: desc,
|
||||
Long: cacheCmdLong,
|
||||
PersistentPreRun: initCacheCmd,
|
||||
PersistentPostRun: postCacheCmd,
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var dumpCmd = &cobra.Command{
|
||||
@ -11,10 +12,16 @@ var dumpCmd = &cobra.Command{
|
||||
Short: "Dump GitLab project cache",
|
||||
Long: `Dumps cache to display`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println(cache.String())
|
||||
if conf.Dump.Full {
|
||||
fmt.Println(cache.DumpString())
|
||||
} else {
|
||||
plog.Info(cache.String())
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cacheCmd.AddCommand(dumpCmd)
|
||||
dumpCmd.PersistentFlags().BoolP("full", "f", false, "Dumps entire cache")
|
||||
viper.BindPFlag("dump.full", dumpCmd.LocalFlags().Lookup("full"))
|
||||
}
|
48
cmd/project.go
Normal file
48
cmd/project.go
Normal file
@ -0,0 +1,48 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/projects"
|
||||
)
|
||||
|
||||
var projectCmd = &cobra.Command{
|
||||
Use: "project [fuzzy alias search]",
|
||||
Short: "Use a GitLab project",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
ArgAliases: []string{"alias"},
|
||||
ValidArgsFunction: validAliasesFunc,
|
||||
Long: projCmdLong,
|
||||
PersistentPreRun: initProjectCmd,
|
||||
PersistentPostRun: postProjectCmd,
|
||||
Run: projectCmdRun,
|
||||
}
|
||||
|
||||
func projectCmdRun(cmd *cobra.Command, args []string) {
|
||||
var searchString string
|
||||
if len(args) > 0 {
|
||||
searchString = args[0]
|
||||
}
|
||||
project := fzfFindProject(searchString)
|
||||
|
||||
if project == nil {
|
||||
plog.Fatal("Failed to find a project, nothing to do")
|
||||
} else {
|
||||
plog.Info("Houston, we have a project", plog.Args(
|
||||
"project", project.String(),
|
||||
"aliases", projects.ProjectAliasesString(
|
||||
cache.GetProjectAliases(project)),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
func initProjectCmd(cmd *cobra.Command, args []string) {
|
||||
initProjectCache(cmd, args)
|
||||
}
|
||||
|
||||
func postProjectCmd(cmd *cobra.Command, args []string) {
|
||||
postProjectCache(cmd, args)
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(projectCmd)
|
||||
}
|
44
cmd/project_show.go
Normal file
44
cmd/project_show.go
Normal file
@ -0,0 +1,44 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pterm/pterm"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var projectShowCmd = &cobra.Command{
|
||||
Use: "show [fuzzy alias search]",
|
||||
Short: "Show detail for a GitLab project",
|
||||
Args: cobra.ArbitraryArgs,
|
||||
ValidArgsFunction: validProjectsOrAliasesFunc,
|
||||
Long: projShowCmdLong,
|
||||
Run: projectShowCmdRun,
|
||||
}
|
||||
|
||||
func projectShowCmdRun(cmd *cobra.Command, args []string) {
|
||||
var searchString string
|
||||
if len(args) > 0 {
|
||||
searchString = args[0]
|
||||
}
|
||||
|
||||
project := fzfFindProject(searchString)
|
||||
|
||||
if project == nil {
|
||||
var err error
|
||||
project, err = fzfProject(cmd.Context())
|
||||
if err != nil || project == nil {
|
||||
plog.Fatal("Failed to find project, nothing to show", plog.Args(
|
||||
"error", err,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(pterm.LightGreen("\n--------------"))
|
||||
fmt.Println(cache.ProjectString(project))
|
||||
fmt.Println(pterm.LightGreen("--------------\n"))
|
||||
}
|
||||
|
||||
func init() {
|
||||
projectCmd.AddCommand(projectShowCmd)
|
||||
}
|
14
cmd/root.go
14
cmd/root.go
@ -12,21 +12,13 @@ import (
|
||||
"gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/config"
|
||||
)
|
||||
|
||||
const (
|
||||
defGitlabHost = "gitlab.sweetwater.com"
|
||||
defProjectsPath = "~/work/projects"
|
||||
defLogLevel = "info"
|
||||
)
|
||||
|
||||
var conf config.Config
|
||||
var plog *pterm.Logger
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "gitlab-project-manager",
|
||||
Short: "Find and use GitLab projects locally",
|
||||
Long: `Finds GitLab projects using fuzzy-find, remembering
|
||||
your chosen term for the project as an alias, and offers helpful
|
||||
shortcuts for moving around in projects and opening your code`,
|
||||
Use: "gitlab-project-manager",
|
||||
Short: "Find and use GitLab projects locally",
|
||||
Long: rootCmdLong,
|
||||
PersistentPreRun: initRootCmd,
|
||||
}
|
||||
|
||||
|
20
cmd/util_completion.go
Normal file
20
cmd/util_completion.go
Normal file
@ -0,0 +1,20 @@
|
||||
package cmd
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
|
||||
func validProjectsFunc(cmd *cobra.Command, args []string, toComplete string) (
|
||||
[]string, cobra.ShellCompDirective) {
|
||||
return cache.ProjectStrings(), cobra.ShellCompDirectiveDefault
|
||||
}
|
||||
|
||||
func validAliasesFunc(cmd *cobra.Command, args []string, toComplete string) (
|
||||
[]string, cobra.ShellCompDirective) {
|
||||
return cache.AliasStrings(), cobra.ShellCompDirectiveDefault
|
||||
}
|
||||
|
||||
func validProjectsOrAliasesFunc(cmd *cobra.Command, args []string, toComplete string) (
|
||||
[]string, cobra.ShellCompDirective) {
|
||||
projectStrings, _ := validAliasesFunc(cmd, args, toComplete)
|
||||
aliasStrings, _ := validProjectsFunc(cmd, args, toComplete)
|
||||
return append(projectStrings, aliasStrings...), cobra.ShellCompDirectiveDefault
|
||||
}
|
28
cmd/util_constants.go
Normal file
28
cmd/util_constants.go
Normal file
@ -0,0 +1,28 @@
|
||||
package cmd
|
||||
|
||||
const (
|
||||
defGitlabHost = "gitlab.sweetwater.com"
|
||||
defProjectsPath = "~/work/projects"
|
||||
defLogLevel = "info"
|
||||
)
|
||||
|
||||
const aliasCmdLong = `Manages project aliases, with options for
|
||||
listing, adding, and deleting.`
|
||||
|
||||
const aliasAddCmdLong = `Adds a project alias to a project
|
||||
project ID can be provided, or will otherwise use fuzzy find`
|
||||
|
||||
const cacheCmdLong = `Contains sub-commands for managing project cache.
|
||||
The project cache keeps this speedy, without smashing against the GitLab
|
||||
API every time a new project is added / searched for`
|
||||
|
||||
const rootCmdLong = `Finds GitLab projects using fuzzy-find, remembering
|
||||
your chosen term for the project as an alias, and offers helpful
|
||||
shortcuts for moving around in projects and opening your code`
|
||||
|
||||
const projCmdLong = `Switches to a GitLab project by name or alias
|
||||
If not found, will enter fzf mode. If not cloned, will clone
|
||||
the project locally.`
|
||||
|
||||
const projShowCmdLong = `Shows detail for a particular project
|
||||
Will always fuzzy find`
|
83
cmd/util_fzf.go
Normal file
83
cmd/util_fzf.go
Normal file
@ -0,0 +1,83 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
fzf "github.com/ktr0731/go-fuzzyfinder"
|
||||
"gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/gitlab"
|
||||
"gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/projects"
|
||||
)
|
||||
|
||||
func fzfFindProject(searchString string) *gitlab.Project {
|
||||
var project *gitlab.Project
|
||||
|
||||
if searchString != "" {
|
||||
project = fzfSearchProjectAliases(searchString)
|
||||
} else {
|
||||
var err error
|
||||
project, err = fzfProject(rootCmd.Context())
|
||||
if project == nil || err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return project
|
||||
}
|
||||
|
||||
func fzfSearchProjectAliases(searchString string) *gitlab.Project {
|
||||
var project *gitlab.Project
|
||||
var alias *projects.ProjectAlias
|
||||
if alias = cache.GetAliasByName(searchString); alias != nil {
|
||||
project = cache.GetProjectByAlias(alias)
|
||||
plog.Info("Perfect alias match... flawless")
|
||||
} else {
|
||||
// Get fuzzy if we don't have an exact match
|
||||
aliases := cache.FuzzyFindAlias(searchString)
|
||||
if len(aliases) > 1 {
|
||||
alias = fzfAliasFromAliases(aliasAddCmd.Context(), aliases)
|
||||
} else if len(aliases) == 1 {
|
||||
alias = aliases[0]
|
||||
}
|
||||
project = cache.GetProjectByAlias(alias)
|
||||
}
|
||||
return project
|
||||
}
|
||||
|
||||
func fzfAliasFromAliases(ctx context.Context, aliases []*projects.ProjectAlias) *projects.ProjectAlias {
|
||||
var alias *projects.ProjectAlias
|
||||
i, err := fzf.Find(
|
||||
aliases,
|
||||
func(i int) string {
|
||||
return aliases[i].Alias + " -> " + cache.GetProjectByAlias(aliases[i]).PathWithNamespace
|
||||
},
|
||||
fzf.WithContext(ctx),
|
||||
fzf.WithHeader("Choose an Alias"),
|
||||
)
|
||||
if err != nil {
|
||||
plog.Error("Failed to fzf alias slice", plog.Args("error", err))
|
||||
} else {
|
||||
alias = aliases[i]
|
||||
}
|
||||
return alias
|
||||
}
|
||||
|
||||
func fzfProject(ctx context.Context) (*gitlab.Project, error) {
|
||||
i, err := fzf.Find(cache.Projects, fzfProjectString,
|
||||
fzf.WithPreviewWindow(fzfPreviewWindow),
|
||||
fzf.WithContext(ctx),
|
||||
fzf.WithHeader("Fuzzy find yourself a project"),
|
||||
)
|
||||
if err != nil || i < 0 {
|
||||
return nil, err
|
||||
}
|
||||
return cache.Projects[i], nil
|
||||
}
|
||||
|
||||
func fzfPreviewWindow(i, w, h int) string {
|
||||
p := cache.Projects[i]
|
||||
return cache.ProjectString(p)
|
||||
}
|
||||
|
||||
func fzfProjectString(i int) string {
|
||||
return cache.Projects[i].String()
|
||||
}
|
@ -18,8 +18,8 @@ import (
|
||||
// func from their PersistentPreRun commands
|
||||
|
||||
func initProjectCache(cmd *cobra.Command, args []string) {
|
||||
plog.Debug("Running persistent pre-run for cacheCmd")
|
||||
conf.Cache.File = conf.ProjectPath + "/.cache.json"
|
||||
plog.Debug("Running pre-run for cacheCmd")
|
||||
conf.Cache.File = conf.ProjectPath + "/.cache.yaml"
|
||||
|
||||
gitlabClient, err := gitlab.NewGitlabClient(cmd.Context(), conf.GitlabHost, conf.GitlabToken)
|
||||
if err != nil {
|
||||
@ -38,7 +38,7 @@ func initProjectCache(cmd *cobra.Command, args []string) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := cache.Load(); err != nil {
|
||||
if err := cache.Read(); err != nil {
|
||||
plog.Error("Cache load failed", plog.Args("error", err))
|
||||
os.Exit(1)
|
||||
}
|
Reference in New Issue
Block a user