From 4734816d85ef80aee0e4b1c7680f821eb9e720a4 Mon Sep 17 00:00:00 2001 From: Gorbunov Kirill Andreevich Date: Sat, 23 Mar 2024 18:52:13 +0300 Subject: [PATCH] #8 add cli, need fix parse yaml. --- Makefile | 4 + cmd/pkgdashcli/pkgdashcli.go | 7 ++ internal/cli/checkupdate.go | 94 ++++++++++++++++++++++++ internal/cli/delete.go | 75 +++++++++++++++++++ internal/cli/init.go | 121 +++++++++++++++++++++++++++++++ internal/cli/list.go | 73 +++++++++++++++++++ internal/cli/root.go | 42 +++++++++++ internal/cli/update.go | 97 +++++++++++++++++++++++++ internal/source/github/github.go | 3 + internal/source/gitlab/gitlab.go | 3 + internal/source/gogs/gogs.go | 3 + 11 files changed, 522 insertions(+) create mode 100644 cmd/pkgdashcli/pkgdashcli.go create mode 100644 internal/cli/checkupdate.go create mode 100644 internal/cli/delete.go create mode 100644 internal/cli/init.go create mode 100644 internal/cli/list.go create mode 100644 internal/cli/root.go create mode 100644 internal/cli/update.go diff --git a/Makefile b/Makefile index fb3315a..b359373 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,10 @@ cli: buildcli: CGO_ENABLED=0 go build -o bin/app -mod=readonly git.unistack.org/unistack-org/pkgdash/cmd/pkgdashcli +.PHONY: cli +cli: + go install git.unistack.org/unistack-org/pkgdash/cmd/pkgdashcli + .PHONY: test test: go test -v ./... -race -cover diff --git a/cmd/pkgdashcli/pkgdashcli.go b/cmd/pkgdashcli/pkgdashcli.go new file mode 100644 index 0000000..e080b7c --- /dev/null +++ b/cmd/pkgdashcli/pkgdashcli.go @@ -0,0 +1,7 @@ +package main + +import "git.unistack.org/unistack-org/pkgdash/internal/cli" + +func main() { + cli.Execute() +} diff --git a/internal/cli/checkupdate.go b/internal/cli/checkupdate.go new file mode 100644 index 0000000..1fe8e2a --- /dev/null +++ b/internal/cli/checkupdate.go @@ -0,0 +1,94 @@ +/* +Copyright © 2024 NAME HERE +*/ +package cli + +import ( + "context" + "fmt" + "os" + "strings" + + "git.unistack.org/unistack-org/pkgdash/internal/modules" + "github.com/spf13/cobra" + "go.unistack.org/micro/v4/logger" + "golang.org/x/mod/modfile" + "golang.org/x/mod/semver" +) + +// checkupdateCmd represents the checkupdate command +var checkupdateCmd = NewCheckUpdateCommand() + +var mvs = make(map[string]modules.Update) + +func init() { + rootCmd.AddCommand(checkupdateCmd) +} + +func NewCheckUpdateCommand() *cobra.Command { + ctx := context.Background() + + cmd := &cobra.Command{ + Use: "checkupdate", + Short: "CheckUpdate collects a list of dependencies with the latest updates.", + Long: `CheckUpdate collects a list of dependencies with the latest updates.`, + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Println("checkupdate called") + path := "." + if len(os.Args) > 1 { + path = os.Args[1] + } + + name, err := modules.FindModFile(path) + if err != nil { + panic(err) + } + buf, err := os.ReadFile(name) + if err != nil { + panic(err) + } + mfile, err := modfile.Parse(name, buf, nil) + if err != nil { + panic(err) + } + + mvs = make(map[string]modules.Update) + + updateOptions := modules.UpdateOptions{ + Pre: cfg.UpdateOpt.Pre, + Major: cfg.UpdateOpt.Major, + UpMajor: cfg.UpdateOpt.UpMajor, + Cached: cfg.UpdateOpt.Cached, + OnUpdate: func(u modules.Update) { + var modpath string // new mod path with major + if u.Err != nil { + logger.Error(ctx, fmt.Sprintf("%s: failed: %v", u.Module.Path, u.Err)) + return + } + modpath = u.Module.Path + v := semver.Major(u.Version) + p := modules.ModPrefix(modpath) + if !strings.HasPrefix(u.Module.Version, v) && v != "v1" && v != "v0" { + switch strings.HasPrefix(u.Module.Path, "gopkg.in") { + case true: + modpath = p + "." + v + case false: + modpath = p + "/" + v + } + } + mvs[modpath] = u + }, + } + + for _, req := range mfile.Require { + updateOptions.Modules = append(updateOptions.Modules, req.Mod) + } + + modules.Updates(updateOptions) + logger.Info(ctx, fmt.Sprintf("Modules get update: /n %s", mvs)) + return nil + }, + } + + return cmd +} diff --git a/internal/cli/delete.go b/internal/cli/delete.go new file mode 100644 index 0000000..b152c83 --- /dev/null +++ b/internal/cli/delete.go @@ -0,0 +1,75 @@ +/* +Copyright © 2024 NAME HERE +*/ +package cli + +import ( + "context" + "fmt" + + "github.com/spf13/cobra" +) + +// deleteCmd represents the delete command +var deleteCmd = NewDeleteCommand() + +type DeleteFlags struct { + all bool + mod string +} + +func init() { + rootCmd.AddCommand(deleteCmd) +} + +func NewDeleteCommand() *cobra.Command { + var flags UpdateFlags + + ctx := context.Background() + + cmd := &cobra.Command{ + Use: "delete", + Short: "Delete closes the merge requests created with a dependency update.", + Long: `Delete closes the merge requests created with a dependency update.`, + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Println("delete called") + var err error + + if gitsource == nil { + return errSourceNil + } + + if len(prList) == 0 { + return errPRNotExist + } + + if flags.all { + for _, branch := range cfg.Branches { + rMap := prList[branch] + for path, _ := range rMap { + err = gitsource.RequestClose(ctx, branch, path) + if err != nil { + return err + } + } + } + } + + if flags.mod != "" { + for _, branch := range cfg.Branches { + err = gitsource.RequestClose(ctx, branch, flags.mod) + if err != nil { + return err + } + } + } + + return errFlagsNotExist + }, + } + + deleteCmd.Flags().BoolVarP(&flags.all, "all", "a", false, "Deletes everything depending") + deleteCmd.Flags().StringVarP(&flags.mod, "mod", "m", "", "Deletes one dependency") + + return cmd +} diff --git a/internal/cli/init.go b/internal/cli/init.go new file mode 100644 index 0000000..0a6af9d --- /dev/null +++ b/internal/cli/init.go @@ -0,0 +1,121 @@ +/* +Copyright © 2024 NAME HERE +*/ +package cli + +import ( + "context" + "fmt" + "path/filepath" + + "git.unistack.org/unistack-org/pkgdash/internal/configcli" + "git.unistack.org/unistack-org/pkgdash/internal/source" + "github.com/spf13/cobra" + yamlcodec "go.unistack.org/micro-codec-yaml/v4" + envconfig "go.unistack.org/micro-config-env/v4" + fileconfig "go.unistack.org/micro-config-file/v4" + "go.unistack.org/micro/v4/config" + "go.unistack.org/micro/v4/logger" + "go.unistack.org/micro/v4/logger/slog" + "go.unistack.org/micro/v4/options" +) + +// initCmd represents the init command +var initCmd = NewInitCommand() + +var gitsource = source.SourceControl(nil) + +var cfg = configcli.NewConfig() + +var ( + DefaultPullRequestTitle = `Bump {{.Name}} from {{.VersionOld}} to {{.VersionNew}}` + DefaultPullRequestBody = `Bumps {{.Name}} from {{.VersionOld}} to {{.VersionNew}}` +) + +var ( + configFiles = []string{ + "dependabot.yml", + "pkgdashcli.yml", + "pkgdashcli.yaml", + } + configDirs = []string{ + ".gitea", + ".github", + ".gitlab", + } +) + +func init() { + rootCmd.AddCommand(initCmd) +} + +func NewInitCommand() *cobra.Command { + ctx := context.Background() + + logger.DefaultLogger = slog.NewLogger() + + if err := logger.DefaultLogger.Init(logger.WithCallerSkipCount(3), logger.WithLevel(logger.DebugLevel)); err != nil { + logger.Error(ctx, fmt.Sprintf("logger init error: %v", err)) + } + + cmd := &cobra.Command{ + Use: "init", + Short: "Init fills the config with data from the configuration file.", + Long: `Init fills the config with data from the configuration file.`, + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Println("init called") + var err error + + if err = config.Load(ctx, + []config.Config{ + config.NewConfig( + config.Struct(cfg), + ), + envconfig.NewConfig( + config.Struct(cfg), + ), + }, + config.LoadOverride(true), + ); err != nil { + logger.Fatal(ctx, fmt.Sprintf("failed to load config: %v", err)) + } + + for _, configDir := range configDirs { + for _, configFile := range configFiles { + logger.Info(ctx, fmt.Sprintf("path: %s", filepath.Join(configDir, configFile))) + c := fileconfig.NewConfig( + config.AllowFail(false), + config.Struct(cfg), + options.Codec(yamlcodec.NewCodec()), + fileconfig.Path(".gitea/pkgdashcli.yaml"), + ) + err = c.Init(options.Context(ctx)) + if err != nil { + logger.Error(ctx, fmt.Sprintf("failed to init config: %v", err)) + return err + } + if err = c.Load(ctx, config.LoadOverride(true)); err != nil { + logger.Error(ctx, fmt.Sprintf("failed to load config: %v", err)) + return err + } + } + } + + logger.Info(ctx, fmt.Sprintf("Load config... %s", cfg.Source.Repository)) + + if cfg.PullRequestBody == "" { + cfg.PullRequestBody = DefaultPullRequestBody + } + + if cfg.PullRequestTitle == "" { + cfg.PullRequestTitle = DefaultPullRequestTitle + } + + gitsource = source.NewSourceControl(*cfg) + + return nil + }, + } + + return cmd +} diff --git a/internal/cli/list.go b/internal/cli/list.go new file mode 100644 index 0000000..a440079 --- /dev/null +++ b/internal/cli/list.go @@ -0,0 +1,73 @@ +/* +Copyright © 2024 NAME HERE +*/ +package cli + +import ( + "context" + "fmt" + + "github.com/spf13/cobra" + "go.unistack.org/micro/v4/logger" +) + +// updateCmd represents the update command +var listCmd = NewListCommand() + +var prList = map[string]map[string]string{} + +/* [ + "master": + [ + "go.unistack.org/micro/v4" : "Bump go.unistack.org/micro/v4 from v4.0.0 to v4.0.1", + "go.unistack.org/micro-client-http/v4":"Bump go.unistack.org/micro-client-http/v4 from v4.0.0 to v4.0.2", + ], + "v3": + [ + "go.unistack.org/micro/v3" : "Bump go.unistack.org/micro/v4 from v3.0.0 to v3.0.1", + ], +] */ + +func init() { + rootCmd.AddCommand(listCmd) +} + +func NewListCommand() *cobra.Command { + ctx := context.Background() + + logger.Info(ctx, "RequestList start") + + cmd := &cobra.Command{ + Use: "list", + Short: "Update allows you to start the process of creating a PR in the repository", + Long: ` +Update allows you to start the process of creating a PR in the repository. +Use the -a flag to update all dependencies or -m flag a specific module. +`, + RunE: func(cmd *cobra.Command, args []string) error { + if gitsource == nil { + return errSourceNil + } + + if cfg == nil { + return errCfgNil + } + + prList = make(map[string]map[string]string) + + for _, branch := range cfg.Branches { + rMap, err := gitsource.RequestList(ctx, branch) + if err != nil { + return err + } + prList[branch] = rMap + + logger.Info(ctx, fmt.Sprintf("for %s:\n%s", branch, rMap)) + } + + return nil + }, + } + + return cmd +} diff --git a/internal/cli/root.go b/internal/cli/root.go new file mode 100644 index 0000000..b700289 --- /dev/null +++ b/internal/cli/root.go @@ -0,0 +1,42 @@ +/* +Copyright © 2024 NAME HERE +*/ +package cli + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" +) + +var ( + errUpdateNotExist = fmt.Errorf("mod not in list of update. Call init") + errFlagsNotExist = fmt.Errorf("empty flags") + errSourceNil = fmt.Errorf("source nil. Call init") + errCfgNil = fmt.Errorf("cfg nil. Call init") + errPRNotExist = fmt.Errorf("pr not exist. Call list") +) + +// rootCmd represents the base command when called without any subcommands +var rootCmd = &cobra.Command{ + Use: "pkgdash [flags]", + Short: "Pkgdash application to update dependencies.", + Long: `Pkgdash allows you to define a version update for a dependency and start merge requests in version control systems.`, + // Uncomment the following line if your bare application + // has an action associated with it: + // Run: func(cmd *cobra.Command, args []string) { }, +} + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute() { + err := rootCmd.Execute() + if err != nil { + os.Exit(1) + } +} + +func init() { + +} diff --git a/internal/cli/update.go b/internal/cli/update.go new file mode 100644 index 0000000..279db50 --- /dev/null +++ b/internal/cli/update.go @@ -0,0 +1,97 @@ +/* +Copyright © 2024 NAME HERE +*/ +package cli + +import ( + "context" + "fmt" + + "github.com/spf13/cobra" + "go.unistack.org/micro/v4/logger" +) + +// updateCmd represents the update command +var updateCmd = NewUpdateCommand() + +type UpdateFlags struct { + all bool + mod string +} + +func init() { + rootCmd.AddCommand(updateCmd) +} + +func NewUpdateCommand() *cobra.Command { + var flags UpdateFlags + + ctx := context.Background() + + logger.Info(ctx, "update start") + cmd := &cobra.Command{ + Use: "update", + Short: "Update allows you to start the process of creating a PR in the repository", + Long: ` +Update allows you to start the process of creating a PR in the repository. +Use the -a flag to update all dependencies or -m flag a specific module. +`, + RunE: func(cmd *cobra.Command, args []string) error { + var err error + + if gitsource == nil { + return errSourceNil + } + if len(mvs) == 0 { + return errUpdateNotExist + } + + if flags.all { + logger.Info(ctx, "update all") + for pathMod, m := range mvs { + for _, branch := range cfg.Branches { + logger.Info(ctx, fmt.Sprintf("update mod: %s", m)) + + if err = gitsource.RequestOpen(ctx, branch, pathMod, m); err != nil { + logger.Error(ctx, fmt.Sprintf("PR open error: %s, base branch: %s", pathMod, branch)) + return err + } + + logger.Info(ctx, fmt.Sprintf("Successful update mod: %s", pathMod)) + } + + delete(mvs, pathMod) // todo возможно чисть не стоит и нужно просто пропускать + } + return nil + } + + if flags.mod != "" { + for _, branch := range cfg.Branches { + logger.Info(ctx, fmt.Sprintf("update mod: %s", flags.mod)) + + if _, ok := mvs[flags.mod]; !ok { + return errUpdateNotExist + } + + if err = gitsource.RequestOpen(ctx, branch, flags.mod, mvs[flags.mod]); err != nil { + logger.Error(ctx, fmt.Sprintf("PR open error: %s, base branch: %s", flags.mod, branch)) + return err + } + + logger.Info(ctx, fmt.Sprintf("Successful update mod: %s", flags.mod)) + } + + delete(mvs, flags.mod) + + return nil + } + + return errFlagsNotExist + }, + } + + updateCmd.Flags().BoolVarP(&flags.all, "all", "a", false, "Updates everything depending") + updateCmd.Flags().StringVarP(&flags.mod, "mod", "m", "", "Update one dependency") + + return cmd +} diff --git a/internal/source/github/github.go b/internal/source/github/github.go index fa04b84..b7ddd06 100644 --- a/internal/source/github/github.go +++ b/internal/source/github/github.go @@ -34,3 +34,6 @@ func (g *Github) RequestUpdate(ctx context.Context, branch string, path string, func (g *Github) RequestList(ctx context.Context, branch string) (map[string]string, error) { return nil, fmt.Errorf("implement me") } +func (g *Github) RequestList(ctx context.Context, branch string) (map[string]string, error) { + return nil, nil +} diff --git a/internal/source/gitlab/gitlab.go b/internal/source/gitlab/gitlab.go index ec33af4..7808051 100644 --- a/internal/source/gitlab/gitlab.go +++ b/internal/source/gitlab/gitlab.go @@ -34,3 +34,6 @@ func (g *Gitlab) RequestUpdate(ctx context.Context, branch string, path string, func (g *Gitlab) RequestList(ctx context.Context, branch string) (map[string]string, error) { return nil, fmt.Errorf("implement me") } +func (g *Gitlab) RequestList(ctx context.Context, branch string) (map[string]string, error) { + return nil, nil +} diff --git a/internal/source/gogs/gogs.go b/internal/source/gogs/gogs.go index 310a5e1..a1a6dbb 100644 --- a/internal/source/gogs/gogs.go +++ b/internal/source/gogs/gogs.go @@ -34,3 +34,6 @@ func (g *Gogs) RequestUpdate(ctx context.Context, branch string, path string, mo func (g *Gogs) RequestList(ctx context.Context, branch string) (map[string]string, error) { return nil, fmt.Errorf("implement me") } +func (g *Gogs) RequestList(ctx context.Context, branch string) (map[string]string, error) { + return nil, nil +}