diff --git a/README.md b/README.md index 503b39a..3a0d115 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,41 @@ You can delete a project with the `bb project delete` command: bb project delete myproject --workspace myworkspace ``` +#### Project Default Reviewers + +You can list the default reviewers of a project with the `bb project reviewer list` command: + +```bash +bb project reviewer list --workspace myworkspace --project myproject +``` + +You can add a default reviewer to a project with the `bb project reviewer add` command: + +```bash +bb project reviewer add \ + --workspace myworkspace \ + --project myproject \ + userUUID +``` + +You can remove a default reviewer from a project with the `bb project reviewer remove` command: + +```bash +bb project reviewer remove \ + --workspace myworkspace \ + --project myproject \ + userUUID +``` + + You can get the details of a default reviewer with the `bb project reviewer get` or `bb project reviewer show` command: + +```bash +bb project reviewer get \ + --workspace myworkspace \ + --project myproject \ + userUUID +``` + ### Pull Requests You can list pull requests with the `bb pullrequest list` command: @@ -201,6 +236,38 @@ You can `merge` a pull request with the `bb pullrequest merge` command: bb pullrequest merge 1 ``` +### Artifacts (Downloads) + +You can list artifacts with the `bb artifact list` command: + +```bash +bb artifact list +``` + +By default the current repository is used, you can specify a repository with the `--repository` flag. + +You can also upload an artifact with the `bb artifact upload` command: + +```bash +bb artifact upload myartifact.zip +``` + +At the moment, only one file at a time is supported (no folders or stdin). The artifact name is the file name. + +You can download an artifact with the `bb artifact download` command: + +```bash +bb artifact download myartifact.zip +``` + +You can provide a `--destination` flag to specify the destination folder. If the folder does not exist, it will be created. + +Finally, you can delete an artifact with the `bb artifact delete` command: + +```bash +bb artifact delete myartifact.zip +``` + ### Completion `bb` supports completion for Bash, fish, Powershell, and zsh. @@ -266,5 +333,3 @@ bb completion zsh > "$(brew --prefix)/share/zsh/site-functions/_bb" ### TODO We will add more commands in the future. If you have any suggestions, please open an issue. - -At the moment all outputs are in JSON. We will add more output formats in the future. diff --git a/cmd/artifact/artifact.go b/cmd/artifact/artifact.go new file mode 100644 index 0000000..59185c9 --- /dev/null +++ b/cmd/artifact/artifact.go @@ -0,0 +1,47 @@ +package artifact + +import ( + "fmt" + + "bitbucket.org/gildas_cherruel/bb/cmd/link" + "bitbucket.org/gildas_cherruel/bb/cmd/user" + "github.com/spf13/cobra" +) + +type Artifact struct { + Name string `json:"name" mapstructure:"name"` + Size uint64 `json:"size" mapstructure:"size"` + Downloads uint64 `json:"downloads" mapstructure:"downloads"` + User user.User `json:"user" mapstructure:"user"` + Links link.Links `json:"links" mapstructure:"links"` +} + +var Command = &cobra.Command{ + Use: "artifact", + Short: "Manage artifacts", + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("Artifact requires a subcommand:") + for _, command := range cmd.Commands() { + fmt.Println(command.Name()) + } + }, +} + +// GetHeader gets the header for a table +// +// implements common.Tableable +func (artifact Artifact) GetHeader(short bool) []string { + return []string{"Name", "Size", "Downloads", "Owner"} +} + +// GetRow gets the row for a table +// +// implements common.Tableable +func (artifact Artifact) GetRow(headers []string) []string { + return []string{ + artifact.Name, + fmt.Sprintf("%d", artifact.Size), + fmt.Sprintf("%d", artifact.Downloads), + artifact.User.Name, + } +} diff --git a/cmd/artifact/artifacts.go b/cmd/artifact/artifacts.go new file mode 100644 index 0000000..2fa7d5c --- /dev/null +++ b/cmd/artifact/artifacts.go @@ -0,0 +1,27 @@ +package artifact + +type Artifacts []Artifact + +// GetHeader gets the headers for the list command +// +// implements common.Tableables +func (artifacts Artifacts) GetHeader() []string { + return Artifact{}.GetHeader(false) +} + +// GetRowAt gets the row for the list command +// +// implements common.Tableables +func (artifacts Artifacts) GetRowAt(index int) []string { + if index < 0 || index >= len(artifacts) { + return []string{} + } + return artifacts[index].GetRow(nil) +} + +// Size gets the number of elements +// +// implements common.Tableables +func (artifacts Artifacts) Size() int { + return len(artifacts) +} diff --git a/cmd/artifact/delete.go b/cmd/artifact/delete.go new file mode 100644 index 0000000..ee76a85 --- /dev/null +++ b/cmd/artifact/delete.go @@ -0,0 +1,49 @@ +package artifact + +import ( + "fmt" + "os" + + "bitbucket.org/gildas_cherruel/bb/cmd/profile" + "github.com/gildas/go-errors" + "github.com/gildas/go-logger" + "github.com/spf13/cobra" +) + +var deleteCmd = &cobra.Command{ + Use: "delete", + Short: "delete an artifact by its filename", + Args: cobra.ExactArgs(1), + RunE: deleteProcess, +} + +var deleteOptions struct { + Repository string +} + +func init() { + Command.AddCommand(deleteCmd) + + deleteCmd.Flags().StringVar(&deleteOptions.Repository, "repository", "", "Repository to delete artifacts from. Defaults to the current repository") +} + +func deleteProcess(cmd *cobra.Command, args []string) error { + log := logger.Must(logger.FromContext(cmd.Context())).Child(cmd.Parent().Name(), "delete") + + if profile.Current == nil { + return errors.ArgumentMissing.With("profile") + } + + log.Infof("Deleting artifact %s from repository %s with profile %s", args[0], listOptions.Repository, profile.Current) + err := profile.Current.Delete( + log.ToContext(cmd.Context()), + deleteOptions.Repository, + fmt.Sprintf("downloads/%s", args[0]), + nil, + ) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to delete artifact %s: %s\n", args[0], err) + os.Exit(1) + } + return nil +} diff --git a/cmd/artifact/download.go b/cmd/artifact/download.go new file mode 100644 index 0000000..75080c1 --- /dev/null +++ b/cmd/artifact/download.go @@ -0,0 +1,54 @@ +package artifact + +import ( + "fmt" + "os" + + "bitbucket.org/gildas_cherruel/bb/cmd/profile" + "github.com/gildas/go-errors" + "github.com/gildas/go-logger" + "github.com/spf13/cobra" +) + +var downloadCmd = &cobra.Command{ + Use: "download", + Aliases: []string{"get", "fetch"}, + Short: "download an artifact", + Args: cobra.ExactArgs(1), + RunE: getProcess, +} + +var downloadOptions struct { + Repository string + Destination string +} + +func init() { + Command.AddCommand(downloadCmd) + + downloadCmd.Flags().StringVar(&downloadOptions.Repository, "repository", "", "Repository to download artifacts from. Defaults to the current repository") + downloadCmd.Flags().StringVar(&downloadOptions.Destination, "destination", "", "Destination folder to download the artifact to. Defaults to the current folder") + _ = downloadCmd.MarkFlagDirname("destination") +} + +func getProcess(cmd *cobra.Command, args []string) error { + log := logger.Must(logger.FromContext(cmd.Context())).Child(cmd.Parent().Name(), "download") + + if profile.Current == nil { + return errors.ArgumentMissing.With("profile") + } + + log.Infof("Downloading artifact %s", args[0]) + + err := profile.Current.Download( + log.ToContext(cmd.Context()), + downloadOptions.Repository, + fmt.Sprintf("downloads/%s", args[0]), + downloadOptions.Destination, + ) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to download artifact %s: %s\n", args[0], err) + os.Exit(1) + } + return nil +} diff --git a/cmd/artifact/list.go b/cmd/artifact/list.go new file mode 100644 index 0000000..70eaa95 --- /dev/null +++ b/cmd/artifact/list.go @@ -0,0 +1,49 @@ +package artifact + +import ( + "bitbucket.org/gildas_cherruel/bb/cmd/profile" + "github.com/gildas/go-errors" + "github.com/gildas/go-logger" + "github.com/spf13/cobra" +) + +var listCmd = &cobra.Command{ + Use: "list", + Short: "list all projects", + Args: cobra.NoArgs, + RunE: listProcess, +} + +var listOptions struct { + Repository string +} + +func init() { + Command.AddCommand(listCmd) + + listCmd.Flags().StringVar(&listOptions.Repository, "repository", "", "Repository to list artifacts from. Defaults to the current repository") +} + +func listProcess(cmd *cobra.Command, args []string) (err error) { + log := logger.Must(logger.FromContext(cmd.Context())).Child(cmd.Parent().Name(), "list") + + if profile.Current == nil { + return errors.ArgumentMissing.With("profile") + } + + log.Infof("Listing all projects from repository %s with profile %s", listOptions.Repository, profile.Current) + artifacts, err := profile.GetAll[Artifact]( + cmd.Context(), + profile.Current, + listOptions.Repository, + "downloads", + ) + if err != nil { + return err + } + if len(artifacts) == 0 { + log.Infof("No artifact found") + return nil + } + return profile.Current.Print(cmd.Context(), Artifacts(artifacts)) +} diff --git a/cmd/artifact/upload.go b/cmd/artifact/upload.go new file mode 100644 index 0000000..51b94bf --- /dev/null +++ b/cmd/artifact/upload.go @@ -0,0 +1,50 @@ +package artifact + +import ( + "fmt" + "os" + + "bitbucket.org/gildas_cherruel/bb/cmd/profile" + "github.com/gildas/go-errors" + "github.com/gildas/go-logger" + "github.com/spf13/cobra" +) + +var uploadCmd = &cobra.Command{ + Use: "upload", + Short: "upload an artifact", + Args: cobra.ExactArgs(1), + RunE: uploadProcess, +} + +var uploadOptions struct { + Repository string +} + +func init() { + Command.AddCommand(uploadCmd) + + uploadCmd.Flags().StringVar(&uploadOptions.Repository, "repository", "", "Repository to upload artifacts to. Defaults to the current repository") +} + +func uploadProcess(cmd *cobra.Command, args []string) error { + log := logger.Must(logger.FromContext(cmd.Context())).Child(cmd.Parent().Name(), "upload") + + if profile.Current == nil { + return errors.ArgumentMissing.With("profile") + } + + log.Infof("Uploading artifact %s", args[0]) + + err := profile.Current.Upload( + log.ToContext(cmd.Context()), + uploadOptions.Repository, + "downloads", + args[0], + ) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to upload artifact %s: %s\n", args[0], err) + os.Exit(1) + } + return nil +} diff --git a/cmd/branch/branch.go b/cmd/branch/branch.go index 1784080..79f919a 100644 --- a/cmd/branch/branch.go +++ b/cmd/branch/branch.go @@ -30,6 +30,20 @@ var Command = &cobra.Command{ }, } +// GetHeader gets the header for a table +// +// implements common.Tableable +func (branch Branch) GetHeader(short bool) []string { + return []string{"Name"} +} + +// GetRow gets the row for a table +// +// implements common.Tableable +func (branch Branch) GetRow(headers []string) []string { + return []string{branch.Name} +} + // Validate validates a Branch func (branch *Branch) Validate() error { var merr errors.MultiError diff --git a/cmd/branch/branches.go b/cmd/branch/branches.go new file mode 100644 index 0000000..a90296d --- /dev/null +++ b/cmd/branch/branches.go @@ -0,0 +1,27 @@ +package branch + +type Branches []Branch + +// GetHeader gets the header for a table +// +// implements common.Tableables +func (branches Branches) GetHeader() []string { + return Branch{}.GetHeader(false) +} + +// GetRowAt gets the row for a table +// +// implements common.Tableables +func (branches Branches) GetRowAt(index int) []string { + if index < 0 || index >= len(branches) { + return []string{} + } + return branches[index].GetRow(nil) +} + +// Size gets the number of elements +// +// implements common.Tableables +func (branches Branches) Size() int { + return len(branches) +} diff --git a/cmd/branch/list.go b/cmd/branch/list.go index c82ef70..c8c990c 100644 --- a/cmd/branch/list.go +++ b/cmd/branch/list.go @@ -1,9 +1,6 @@ package branch import ( - "encoding/json" - "fmt" - "bitbucket.org/gildas_cherruel/bb/cmd/profile" "github.com/gildas/go-errors" "github.com/gildas/go-logger" @@ -48,7 +45,5 @@ func listProcess(cmd *cobra.Command, args []string) (err error) { log.Infof("No branch found") return } - payload, _ := json.MarshalIndent(branches, "", " ") - fmt.Println(string(payload)) - return nil + return profile.Current.Print(cmd.Context(), Branches(branches)) } diff --git a/cmd/commit/commit.go b/cmd/commit/commit.go index 18ca8c7..9fc917b 100644 --- a/cmd/commit/commit.go +++ b/cmd/commit/commit.go @@ -48,6 +48,24 @@ var Command = &cobra.Command{ }, } +// GetHeader gets the header for a table +// +// implements common.Tableable +func (commit Commit) GetHeader(short bool) []string { + return []string{"Hash", "Author", "Message"} +} + +// GetRow gets the row for a table +// +// implements common.Tableable +func (commit Commit) GetRow(headers []string) []string { + return []string{ + commit.Hash[:7], + commit.Author.User.Name, + commit.Message, + } +} + // Validate validates a Commit func (commit *Commit) Validate() error { var merr errors.MultiError diff --git a/cmd/commit/commits.go b/cmd/commit/commits.go new file mode 100644 index 0000000..896a7e7 --- /dev/null +++ b/cmd/commit/commits.go @@ -0,0 +1,27 @@ +package commit + +type Commits []Commit + +// GetHeader gets the header for a table +// +// implements common.Tableables +func (commits Commits) GetHeader() []string { + return Commit{}.GetHeader(false) +} + +// GetRowAt gets the row for a table +// +// implements common.Tableables +func (commits Commits) GetRowAt(index int) []string { + if index < 0 || index >= len(commits) { + return []string{} + } + return commits[index].GetRow(nil) +} + +// Size gets the number of elements +// +// implements common.Tableables +func (commits Commits) Size() int { + return len(commits) +} diff --git a/cmd/commit/list.go b/cmd/commit/list.go index 54c04d6..ffe520a 100644 --- a/cmd/commit/list.go +++ b/cmd/commit/list.go @@ -1,9 +1,6 @@ package commit import ( - "encoding/json" - "fmt" - "bitbucket.org/gildas_cherruel/bb/cmd/profile" "github.com/gildas/go-errors" "github.com/gildas/go-logger" @@ -48,7 +45,5 @@ func listProcess(cmd *cobra.Command, args []string) (err error) { log.Infof("No branch found") return } - payload, _ := json.MarshalIndent(commits, "", " ") - fmt.Println(string(payload)) - return nil + return profile.Current.Print(cmd.Context(), Commits(commits)) } diff --git a/cmd/common/app_user.go b/cmd/common/app_user.go deleted file mode 100644 index 36bbf37..0000000 --- a/cmd/common/app_user.go +++ /dev/null @@ -1,34 +0,0 @@ -package common - -import ( - "encoding/json" - "time" - - "bitbucket.org/gildas_cherruel/bb/cmd/link" - "github.com/gildas/go-errors" -) - -type AppUser struct { - Type string `json:"type" mapstructure:"type"` - ID string `json:"uuid" mapstructure:"uuid"` - Name string `json:"display_name" mapstructure:"display_name"` - AccountID string `json:"account_id" mapstructure:"account_id"` - AccountStatus string `json:"account_status" mapstructure:"account_status"` - Kind string `json:"kind" mapstructure:"kind"` - Links link.Links `json:"links" mapstructure:"links"` - CreatedOn time.Time `json:"created_on" mapstructure:"created_on"` -} - -// MarshalJSON implements the json.Marshaler interface. -func (user AppUser) MarshalJSON() (data []byte, err error) { - type surrogate AppUser - - data, err = json.Marshal(struct { - surrogate - CreatedOn string `json:"created_on"` - }{ - surrogate: surrogate(user), - CreatedOn: user.CreatedOn.Format("2006-01-02T15:04:05.999999999-07:00"), - }) - return data, errors.JSONMarshalError.Wrap(err) -} diff --git a/cmd/common/common_test.go b/cmd/common/common_test.go new file mode 100644 index 0000000..b81dc62 --- /dev/null +++ b/cmd/common/common_test.go @@ -0,0 +1,83 @@ +package common_test + +import ( + "encoding/json" + "fmt" + "os" + "reflect" + "strings" + "testing" + "time" + + "github.com/gildas/go-logger" + "github.com/joho/godotenv" + "github.com/stretchr/testify/suite" +) + +type CommonSuite struct { + suite.Suite + Name string + Logger *logger.Logger + Start time.Time +} + +func TestPullRequestSuite(t *testing.T) { + suite.Run(t, new(CommonSuite)) +} + +// ***************************************************************************** +// Suite Tools + +func (suite *CommonSuite) SetupSuite() { + _ = godotenv.Load() + suite.Name = strings.TrimSuffix(reflect.TypeOf(suite).Elem().Name(), "Suite") + suite.Logger = logger.Create("test", + &logger.FileStream{ + Path: fmt.Sprintf("./log/test-%s.log", strings.ToLower(suite.Name)), + Unbuffered: true, + SourceInfo: true, + FilterLevels: logger.NewLevelSet(logger.TRACE), + }, + ).Child("test", "test") + suite.Logger.Infof("Suite Start: %s %s", suite.Name, strings.Repeat("=", 80-14-len(suite.Name))) +} + +func (suite *CommonSuite) TearDownSuite() { + suite.Logger.Debugf("Tearing down") + if suite.T().Failed() { + suite.Logger.Warnf("At least one test failed, we are not cleaning") + suite.T().Log("At least one test failed, we are not cleaning") + } else { + suite.Logger.Infof("All tests succeeded, we are cleaning") + } + suite.Logger.Infof("Suite End: %s %s", suite.Name, strings.Repeat("=", 80-12-len(suite.Name))) +} + +func (suite *CommonSuite) BeforeTest(suiteName, testName string) { + suite.Logger.Infof("Test Start: %s %s", testName, strings.Repeat("-", 80-13-len(testName))) + suite.Start = time.Now() +} + +func (suite *CommonSuite) AfterTest(suiteName, testName string) { + duration := time.Since(suite.Start) + if suite.T().Failed() { + suite.Logger.Errorf("Test %s failed", testName) + } + suite.Logger.Record("duration", duration.String()).Infof("Test End: %s %s", testName, strings.Repeat("-", 80-11-len(testName))) +} + +func (suite *CommonSuite) LoadTestData(filename string) []byte { + data, err := os.ReadFile(fmt.Sprintf("../../testdata/%s", filename)) + if err != nil { + suite.T().Fatal(err) + } + return data +} + +func (suite *CommonSuite) UnmarshalData(filename string, v interface{}) error { + data := suite.LoadTestData(filename) + suite.Logger.Infof("Loaded %s: %s", filename, string(data)) + return json.Unmarshal(data, v) +} + +// ***************************************************************************** diff --git a/cmd/common/uuid.go b/cmd/common/uuid.go new file mode 100644 index 0000000..bcfaafe --- /dev/null +++ b/cmd/common/uuid.go @@ -0,0 +1,46 @@ +package common + +import ( + "github.com/gildas/go-errors" + "github.com/google/uuid" +) + +type UUID uuid.UUID + +func NewUUID() UUID { + return UUID(uuid.New()) +} + +func ParseUUID(s string) (UUID, error) { + u, err := uuid.Parse(s) + return UUID(u), err +} + +func (u UUID) IsNil() bool { + return uuid.UUID(u) == uuid.Nil +} + +func (u UUID) String() string { + return "{" + uuid.UUID(u).String() + "}" +} + +func (u UUID) MarshalJSON() ([]byte, error) { + return []byte(`"` + u.String() + `"`), nil +} + +func (u *UUID) UnmarshalJSON(payload []byte) error { + if len(payload) < 2 { + return errors.JSONUnmarshalError.Wrap(errors.Errorf("unexpected end of JSON input")) + } + value := string(payload[1 : len(payload)-1]) + if len(value) == 0 { + *u = UUID(uuid.Nil) + return nil + } + parsed, err := ParseUUID(value) + if err != nil { + return errors.JSONUnmarshalError.Wrap(err) + } + *u = parsed + return nil +} diff --git a/cmd/common/uuid_test.go b/cmd/common/uuid_test.go new file mode 100644 index 0000000..24c3c67 --- /dev/null +++ b/cmd/common/uuid_test.go @@ -0,0 +1,39 @@ +package common_test + +import ( + "encoding/json" + + "bitbucket.org/gildas_cherruel/bb/cmd/common" +) + +func (suite *CommonSuite) TestCanCreateUUID() { + uuid := common.NewUUID() + suite.Require().NotNil(uuid) +} + +func (suite *CommonSuite) TestCanMarshalUUID() { + expected := "{c32f719b-6c8a-4c87-93e2-9ba8f5cd90dd}" // a Bitbucket String for UUIDs + uuid, err := common.ParseUUID(expected) + suite.Require().NoError(err) + suite.Require().NotNil(uuid) + suite.False(uuid.IsNil()) + payload, err := json.Marshal(uuid) + suite.Require().NoError(err) + suite.Require().NotNil(payload) + suite.Equal(`"`+expected+`"`, string(payload)) +} + +func (suite *CommonSuite) TestCanUnmarshalUUID() { + expected := "{c32f719b-6c8a-4c87-93e2-9ba8f5cd90dd}" // a Bitbucket String for UUIDs + var uuid common.UUID + err := json.Unmarshal([]byte(`"`+expected+`"`), &uuid) + suite.Require().NoError(err) + suite.Require().NotNil(uuid) + suite.False(uuid.IsNil()) + suite.Equal(expected, uuid.String()) + + err = json.Unmarshal([]byte(`""`), &uuid) + suite.Require().NoError(err) + suite.Require().NotNil(uuid) + suite.True(uuid.IsNil()) +} diff --git a/cmd/profile/create.go b/cmd/profile/create.go index f3c76a8..7fb9495 100644 --- a/cmd/profile/create.go +++ b/cmd/profile/create.go @@ -40,6 +40,7 @@ func init() { createCmd.MarkFlagsRequiredTogether("user", "password") createCmd.MarkFlagsRequiredTogether("client-id", "client-secret") createCmd.MarkFlagsMutuallyExclusive("user", "client-id", "access-token") + _ = updateCmd.RegisterFlagCompletionFunc("output", updateOptions.OutputFormat.CompletionFunc()) } func createProcess(cmd *cobra.Command, args []string) error { diff --git a/cmd/profile/profile_client.go b/cmd/profile/profile_client.go index ad55f11..74dcd82 100644 --- a/cmd/profile/profile_client.go +++ b/cmd/profile/profile_client.go @@ -5,6 +5,8 @@ import ( "fmt" "net/http" "net/url" + "os" + "path/filepath" "strings" "time" @@ -74,6 +76,128 @@ func GetAll[T any](context context.Context, profile *Profile, repository string, return resources, nil } +func (profile *Profile) Download(context context.Context, repository, uripath, destination string) (err error) { + log := logger.Must(logger.FromContext(context)).Child(nil, "download") + + if len(repository) == 0 { + remote, err := remote.GetFromGitConfig("origin") + if err != nil { + return err + } + repository = remote.Repository() + } + + var authorization string + + if len(profile.User) > 0 { + authorization = request.BasicAuthorization(profile.User, profile.Password) + } else if len(profile.AccessToken) > 0 { + authorization = request.BearerAuthorization(profile.AccessToken) + } else if authorization, err = profile.authorize(context); err != nil { + return err + } + + if strings.HasPrefix(uripath, "/") { + uripath = fmt.Sprintf("https://api.bitbucket.org/2.0%s", uripath) + } else if !strings.HasPrefix(uripath, "http") { + uripath = fmt.Sprintf("https://api.bitbucket.org/2.0/repositories/%s/%s", repository, uripath) + } + + if len(destination) == 0 { + destination = "." + } + if !strings.HasSuffix(destination, "/") { + destination += "/" + } + if err = os.MkdirAll(destination, 0755); err != nil { + return errors.RuntimeError.Wrap(err) + } + + log.Infof("Downloading artifact %s to repository %s with profile %s", uripath, repository, profile.Name) + options := &request.Options{ + Method: http.MethodGet, + URL: core.Must(url.Parse(uripath)), + Authorization: authorization, + Timeout: 30 * time.Second, + Logger: log, + } + result, err := request.Send(options, nil) + if err != nil { + if result != nil { + var bberr *BitBucketError + if jerr := result.UnmarshalContentJSON(&bberr); jerr == nil { + return bberr + } + } + return err + } + filename := result.Headers.Get("Content-Disposition") + if len(filename) == 0 { + filename = filepath.Base(uripath) + } else { + filename = strings.TrimPrefix(filename, "attachment; filename=\"") + filename = strings.TrimSuffix(filename, "\"") + } + return errors.RuntimeError.Wrap(os.WriteFile(filepath.Join(destination, filename), result.Data, 0644)) +} + +func (profile *Profile) Upload(context context.Context, repository, uripath, source string) (err error) { + log := logger.Must(logger.FromContext(context)).Child(nil, "upload") + + if len(repository) == 0 { + remote, err := remote.GetFromGitConfig("origin") + if err != nil { + return err + } + repository = remote.Repository() + } + + var authorization string + + if len(profile.User) > 0 { + authorization = request.BasicAuthorization(profile.User, profile.Password) + } else if len(profile.AccessToken) > 0 { + authorization = request.BearerAuthorization(profile.AccessToken) + } else if authorization, err = profile.authorize(context); err != nil { + return err + } + + if strings.HasPrefix(uripath, "/") { + uripath = fmt.Sprintf("https://api.bitbucket.org/2.0%s", uripath) + } else if !strings.HasPrefix(uripath, "http") { + uripath = fmt.Sprintf("https://api.bitbucket.org/2.0/repositories/%s/%s", repository, uripath) + } + + reader, err := os.Open(source) + if err != nil { + return errors.RuntimeError.Wrap(err) + } + defer reader.Close() + + log.Infof("Uploading artifact %s to repository %s with profile %s", source, repository, profile.Name) + options := &request.Options{ + Method: http.MethodPost, + URL: core.Must(url.Parse(uripath)), + Authorization: authorization, + Payload: map[string]string{ + ">files": filepath.Base(source), + }, + Attachment: reader, + Timeout: 30 * time.Second, + Logger: log, + } + result, err := request.Send(options, nil) + if err != nil { + if result != nil { + var bberr *BitBucketError + if jerr := result.UnmarshalContentJSON(&bberr); jerr == nil { + return bberr + } + } + } + return +} + func (profile *Profile) authorize(context context.Context) (authorization string, err error) { log := logger.Must(logger.FromContext(context)).Child(nil, "authorize") diff --git a/cmd/profile/update.go b/cmd/profile/update.go index 8e23266..d14246f 100644 --- a/cmd/profile/update.go +++ b/cmd/profile/update.go @@ -40,6 +40,7 @@ func init() { updateCmd.MarkFlagsRequiredTogether("user", "password") updateCmd.MarkFlagsRequiredTogether("client-id", "client-secret") updateCmd.MarkFlagsMutuallyExclusive("user", "client-id", "access-token") + _ = updateCmd.RegisterFlagCompletionFunc("output", updateOptions.OutputFormat.CompletionFunc()) } func updateProcess(cmd *cobra.Command, args []string) error { diff --git a/cmd/project/get.go b/cmd/project/get.go index 899f740..5cd11f0 100644 --- a/cmd/project/get.go +++ b/cmd/project/get.go @@ -1,7 +1,6 @@ package project import ( - "encoding/json" "fmt" "os" @@ -66,8 +65,5 @@ func getProcess(cmd *cobra.Command, args []string) error { fmt.Fprintf(os.Stderr, "Failed to get project %s: %s\n", args[0], err) os.Exit(1) } - - payload, _ := json.MarshalIndent(project, "", " ") - fmt.Println(string(payload)) - return nil + return profile.Current.Print(cmd.Context(), project) } diff --git a/cmd/project/list.go b/cmd/project/list.go index bb9a7b1..7c72016 100644 --- a/cmd/project/list.go +++ b/cmd/project/list.go @@ -1,7 +1,6 @@ package project import ( - "encoding/json" "fmt" "bitbucket.org/gildas_cherruel/bb/cmd/common" @@ -53,7 +52,5 @@ func listProcess(cmd *cobra.Command, args []string) (err error) { log.Infof("No project found") return nil } - payload, _ := json.MarshalIndent(projects, "", " ") - fmt.Println(string(payload)) - return nil + return profile.Current.Print(cmd.Context(), Projects(projects)) } diff --git a/cmd/project/project.go b/cmd/project/project.go index 02b9367..b2f086b 100644 --- a/cmd/project/project.go +++ b/cmd/project/project.go @@ -6,8 +6,10 @@ import ( "fmt" "time" + "bitbucket.org/gildas_cherruel/bb/cmd/common" "bitbucket.org/gildas_cherruel/bb/cmd/link" "bitbucket.org/gildas_cherruel/bb/cmd/profile" + "bitbucket.org/gildas_cherruel/bb/cmd/project/reviewer" "bitbucket.org/gildas_cherruel/bb/cmd/user" "bitbucket.org/gildas_cherruel/bb/cmd/workspace" "github.com/gildas/go-core" @@ -18,7 +20,7 @@ import ( type Project struct { Type string `json:"type" mapstructure:"type"` - ID string `json:"uuid" mapstructure:"uuid"` + ID common.UUID `json:"uuid" mapstructure:"uuid"` Name string `json:"name" mapstructure:"name"` Description string `json:"description,omitempty" mapstructure:"description"` Key string `json:"key" mapstructure:"key"` @@ -43,6 +45,24 @@ var Command = &cobra.Command{ }, } +func init() { + Command.AddCommand(reviewer.Command) +} + +// GetHeader gets the header for a table +// +// implements common.Tableable +func (project Project) GetHeader(short bool) []string { + return []string{"Key", "Name", "Description"} +} + +// GetRow gets the row for a table +// +// implements common.Tableable +func (project Project) GetRow(headers []string) []string { + return []string{project.Key, project.Name, project.Description} +} + // Validate validates a Project func (project *Project) Validate() error { var merr errors.MultiError diff --git a/cmd/project/projects.go b/cmd/project/projects.go new file mode 100644 index 0000000..e4e404f --- /dev/null +++ b/cmd/project/projects.go @@ -0,0 +1,27 @@ +package project + +type Projects []Project + +// GetHeader gets the headers for the list command +// +// implements common.Tableables +func (projects Projects) GetHeader() []string { + return Project{}.GetHeader(false) +} + +// GetRowAt gets the row for the list command +// +// implements common.Tableables +func (projects Projects) GetRowAt(index int) []string { + if index < 0 || index >= len(projects) { + return []string{} + } + return projects[index].GetRow(nil) +} + +// Size gets the number of elements +// +// implements common.Tableables +func (projects Projects) Size() int { + return len(projects) +} diff --git a/cmd/project/reviewer/add.go b/cmd/project/reviewer/add.go new file mode 100644 index 0000000..9cf3f82 --- /dev/null +++ b/cmd/project/reviewer/add.go @@ -0,0 +1,62 @@ +package reviewer + +import ( + "fmt" + "os" + + "bitbucket.org/gildas_cherruel/bb/cmd/common" + "bitbucket.org/gildas_cherruel/bb/cmd/profile" + "bitbucket.org/gildas_cherruel/bb/cmd/user" + "bitbucket.org/gildas_cherruel/bb/cmd/workspace" + "github.com/gildas/go-errors" + "github.com/gildas/go-logger" + "github.com/spf13/cobra" +) + +var addCmd = &cobra.Command{ + Use: "add", + Aliases: []string{"append"}, + Short: "add a reviewer", + Args: cobra.ExactArgs(1), + RunE: addProcess, +} + +var addOptions struct { + Workspace common.RemoteValueFlag + Project string +} + +func init() { + Command.AddCommand(addCmd) + + addOptions.Workspace = common.RemoteValueFlag{AllowedFunc: workspace.GetWorkspaceSlugs} + addCmd.Flags().Var(&addOptions.Workspace, "workspace", "Workspace to add reviewers to") + addCmd.Flags().StringVar(&addOptions.Project, "project", "", "Project Key to add reviewers to") + _ = addCmd.MarkFlagRequired("workspace") + _ = addCmd.MarkFlagRequired("project") + _ = addCmd.RegisterFlagCompletionFunc("workspace", addOptions.Workspace.CompletionFunc()) +} + +func addProcess(cmd *cobra.Command, args []string) error { + log := logger.Must(logger.FromContext(cmd.Context())).Child(cmd.Parent().Name(), "add") + + if profile.Current == nil { + return errors.ArgumentMissing.With("profile") + } + + log.Infof("Adding reviewer %s", args[0]) + var user user.User + + err := profile.Current.Put( + log.ToContext(cmd.Context()), + "", + fmt.Sprintf("/workspaces/%s/projects/%s/default-reviewers/%s", addOptions.Workspace, addOptions.Project, args[0]), + nil, + &user, + ) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to add reviewer: %s\n", err) + os.Exit(1) + } + return profile.Current.Print(cmd.Context(), user) +} diff --git a/cmd/project/reviewer/delete.go b/cmd/project/reviewer/delete.go new file mode 100644 index 0000000..7a86bca --- /dev/null +++ b/cmd/project/reviewer/delete.go @@ -0,0 +1,59 @@ +package reviewer + +import ( + "fmt" + "os" + + "bitbucket.org/gildas_cherruel/bb/cmd/common" + "bitbucket.org/gildas_cherruel/bb/cmd/profile" + "bitbucket.org/gildas_cherruel/bb/cmd/workspace" + "github.com/gildas/go-errors" + "github.com/gildas/go-logger" + "github.com/spf13/cobra" +) + +var deleteCmd = &cobra.Command{ + Use: "delete", + Aliases: []string{"remove"}, + Short: "delete a reviewer", + Args: cobra.ExactArgs(1), + RunE: deleteProcess, +} + +var deleteOptions struct { + Workspace common.RemoteValueFlag + Project string +} + +func init() { + Command.AddCommand(deleteCmd) + + deleteOptions.Workspace = common.RemoteValueFlag{AllowedFunc: workspace.GetWorkspaceSlugs} + deleteCmd.Flags().Var(&deleteOptions.Workspace, "workspace", "Workspace to delete reviewers from") + deleteCmd.Flags().StringVar(&deleteOptions.Project, "project", "", "Project Key to delete reviewers from") + _ = deleteCmd.MarkFlagRequired("workspace") + _ = deleteCmd.MarkFlagRequired("project") + _ = deleteCmd.RegisterFlagCompletionFunc("workspace", deleteOptions.Workspace.CompletionFunc()) +} + +func deleteProcess(cmd *cobra.Command, args []string) error { + log := logger.Must(logger.FromContext(cmd.Context())).Child(cmd.Parent().Name(), "delete") + + if profile.Current == nil { + return errors.ArgumentMissing.With("profile") + } + + log.Infof("deleteing reviewer %s", args[0]) + + err := profile.Current.Delete( + log.ToContext(cmd.Context()), + "", + fmt.Sprintf("/workspaces/%s/projects/%s/default-reviewers/%s", deleteOptions.Workspace, deleteOptions.Project, args[0]), + nil, + ) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to delete reviewer: %s\n", err) + os.Exit(1) + } + return nil +} diff --git a/cmd/project/reviewer/get.go b/cmd/project/reviewer/get.go new file mode 100644 index 0000000..3491314 --- /dev/null +++ b/cmd/project/reviewer/get.go @@ -0,0 +1,61 @@ +package reviewer + +import ( + "fmt" + "os" + + "bitbucket.org/gildas_cherruel/bb/cmd/common" + "bitbucket.org/gildas_cherruel/bb/cmd/profile" + "bitbucket.org/gildas_cherruel/bb/cmd/user" + "bitbucket.org/gildas_cherruel/bb/cmd/workspace" + "github.com/gildas/go-errors" + "github.com/gildas/go-logger" + "github.com/spf13/cobra" +) + +var getCmd = &cobra.Command{ + Use: "get", + Aliases: []string{"show", "info", "display"}, + Short: "get a reviewer", + Args: cobra.ExactArgs(1), + RunE: getProcess, +} + +var getOptions struct { + Workspace common.RemoteValueFlag + Project string +} + +func init() { + Command.AddCommand(getCmd) + + getOptions.Workspace = common.RemoteValueFlag{AllowedFunc: workspace.GetWorkspaceSlugs} + getCmd.Flags().Var(&getOptions.Workspace, "workspace", "Workspace to get reviewers from") + getCmd.Flags().StringVar(&getOptions.Project, "project", "", "Project Key to get reviewers from") + _ = getCmd.MarkFlagRequired("workspace") + _ = getCmd.MarkFlagRequired("project") + _ = getCmd.RegisterFlagCompletionFunc("workspace", getOptions.Workspace.CompletionFunc()) +} + +func getProcess(cmd *cobra.Command, args []string) error { + log := logger.Must(logger.FromContext(cmd.Context())).Child(cmd.Parent().Name(), "get") + + if profile.Current == nil { + return errors.ArgumentMissing.With("profile") + } + + log.Infof("Displaying reviewer %s", args[0]) + var user user.User + + err := profile.Current.Get( + log.ToContext(cmd.Context()), + "", + fmt.Sprintf("/workspaces/%s/projects/%s/default-reviewers/%s", getOptions.Workspace, getOptions.Project, args[0]), + &user, + ) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to get reviewer: %s\n", err) + os.Exit(1) + } + return profile.Current.Print(cmd.Context(), user) +} diff --git a/cmd/project/reviewer/list.go b/cmd/project/reviewer/list.go new file mode 100644 index 0000000..02ef75d --- /dev/null +++ b/cmd/project/reviewer/list.go @@ -0,0 +1,59 @@ +package reviewer + +import ( + "fmt" + + "bitbucket.org/gildas_cherruel/bb/cmd/common" + "bitbucket.org/gildas_cherruel/bb/cmd/profile" + "bitbucket.org/gildas_cherruel/bb/cmd/workspace" + "github.com/gildas/go-errors" + "github.com/gildas/go-logger" + "github.com/spf13/cobra" +) + +var listCmd = &cobra.Command{ + Use: "list", + Short: "list all reviewers", + Args: cobra.NoArgs, + RunE: listProcess, +} + +var listOptions struct { + Workspace common.RemoteValueFlag + Project string +} + +func init() { + Command.AddCommand(listCmd) + + listOptions.Workspace = common.RemoteValueFlag{AllowedFunc: workspace.GetWorkspaceSlugs} + listCmd.Flags().Var(&listOptions.Workspace, "workspace", "Workspace to list reviewers from") + listCmd.Flags().StringVar(&listOptions.Project, "project", "", "Project Key to list reviewers from") + _ = listCmd.MarkFlagRequired("workspace") + _ = listCmd.MarkFlagRequired("project") + _ = listCmd.RegisterFlagCompletionFunc("workspace", listOptions.Workspace.CompletionFunc()) +} + +func listProcess(cmd *cobra.Command, args []string) (err error) { + log := logger.Must(logger.FromContext(cmd.Context())).Child(cmd.Parent().Name(), "list") + + if profile.Current == nil { + return errors.ArgumentMissing.With("profile") + } + + log.Infof("Listing all reviewers") + reviewers, err := profile.GetAll[Reviewer]( + cmd.Context(), + profile.Current, + "", + fmt.Sprintf("/workspaces/%s/projects/%s/default-reviewers", listOptions.Workspace, listOptions.Project), + ) + if err != nil { + return err + } + if len(reviewers) == 0 { + log.Infof("No reviewer found") + return nil + } + return profile.Current.Print(cmd.Context(), Reviewers(reviewers)) +} diff --git a/cmd/project/reviewer/reviewer.go b/cmd/project/reviewer/reviewer.go new file mode 100644 index 0000000..109eb05 --- /dev/null +++ b/cmd/project/reviewer/reviewer.go @@ -0,0 +1,48 @@ +package reviewer + +import ( + "fmt" + + "bitbucket.org/gildas_cherruel/bb/cmd/user" + "github.com/gildas/go-errors" + "github.com/spf13/cobra" +) + +type Reviewer struct { + Type string `json:"type" mapstructure:"type"` + ReviewerType string `json:"reviewer_type" mapstructure:"reviewer_type"` + User user.User `json:"user" mapstructure:"user"` +} + +// Command represents this folder's command +var Command = &cobra.Command{ + Use: "reviewer", + Short: "Manage reviewers", + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("Reviewer requires a subcommand:") + for _, command := range cmd.Commands() { + fmt.Println(command.Name()) + } + }, +} + +// GetHeader gets the header for a table +// +// implements common.Tableable +func (reviewer Reviewer) GetHeader(short bool) []string { + return []string{"Type", "Reviewer Type", "User"} +} + +// GetRow gets the row for a table +// +// implements common.Tableable +func (reviewer Reviewer) GetRow(headers []string) []string { + return []string{reviewer.Type, reviewer.ReviewerType, reviewer.User.Name} +} + +// Validate validates a Reviewer +func (reviewer *Reviewer) Validate() error { + var merr errors.MultiError + + return merr.AsError() +} diff --git a/cmd/project/reviewer/reviewers.go b/cmd/project/reviewer/reviewers.go new file mode 100644 index 0000000..e1f2c20 --- /dev/null +++ b/cmd/project/reviewer/reviewers.go @@ -0,0 +1,27 @@ +package reviewer + +type Reviewers []Reviewer + +// GetHeader gets the headers for the list command +// +// implements common.Tableables +func (reviewers Reviewers) GetHeader() []string { + return Reviewer{}.GetHeader(false) +} + +// GetRowAt gets the row for the list command +// +// implements common.Tableables +func (reviewers Reviewers) GetRowAt(index int) []string { + if index < 0 || index >= len(reviewers) { + return []string{} + } + return reviewers[index].GetRow(nil) +} + +// Size gets the number of elements +// +// implements common.Tableables +func (reviewers Reviewers) Size() int { + return len(reviewers) +} diff --git a/cmd/project/update.go b/cmd/project/update.go index 8ebe1c3..b2a76c9 100644 --- a/cmd/project/update.go +++ b/cmd/project/update.go @@ -2,7 +2,6 @@ package project import ( "encoding/base64" - "encoding/json" "fmt" "net/url" "os" @@ -123,8 +122,5 @@ func updateProcess(cmd *cobra.Command, args []string) error { fmt.Fprintf(os.Stderr, "Failed to update project: %s\n", err) os.Exit(1) } - data, _ := json.MarshalIndent(project, "", " ") - fmt.Println(string(data)) - - return nil + return profile.Current.Print(cmd.Context(), project) } diff --git a/cmd/pullrequest/pullrequest.go b/cmd/pullrequest/pullrequest.go index 94c618d..50ca468 100644 --- a/cmd/pullrequest/pullrequest.go +++ b/cmd/pullrequest/pullrequest.go @@ -7,7 +7,6 @@ import ( "time" "bitbucket.org/gildas_cherruel/bb/cmd/commit" - "bitbucket.org/gildas_cherruel/bb/cmd/common" "bitbucket.org/gildas_cherruel/bb/cmd/link" "bitbucket.org/gildas_cherruel/bb/cmd/profile" "bitbucket.org/gildas_cherruel/bb/cmd/user" @@ -26,7 +25,7 @@ type PullRequest struct { MergeCommit *commit.Commit `json:"merge_commit,omitempty" mapstructure:"merge_commit"` CloseSourceBranch bool `json:"close_source_branch" mapstructure:"close_source_branch"` ClosedBy user.User `json:"closed_by" mapstructure:"closed_by"` - Author common.AppUser `json:"author" mapstructure:"author"` + Author user.AppUser `json:"author" mapstructure:"author"` Reason string `json:"reason" mapstructure:"reason"` Destination Endpoint `json:"destination" mapstructure:"destination"` Source Endpoint `json:"source" mapstructure:"source"` diff --git a/cmd/repository/repository.go b/cmd/repository/repository.go index 935581a..3fc71f7 100644 --- a/cmd/repository/repository.go +++ b/cmd/repository/repository.go @@ -1,13 +1,14 @@ package repository import ( + "bitbucket.org/gildas_cherruel/bb/cmd/common" "bitbucket.org/gildas_cherruel/bb/cmd/link" ) type Repository struct { - Type string `json:"type" mapstructure:"type"` - ID string `json:"uuid" mapstructure:"uuid"` - Name string `json:"name" mapstructure:"name"` - FullName string `json:"full_name" mapstructure:"full_name"` - Links link.Links `json:"links" mapstructure:"links"` + Type string `json:"type" mapstructure:"type"` + ID common.UUID `json:"uuid" mapstructure:"uuid"` + Name string `json:"name" mapstructure:"name"` + FullName string `json:"full_name" mapstructure:"full_name"` + Links link.Links `json:"links" mapstructure:"links"` } diff --git a/cmd/root.go b/cmd/root.go index e1748cb..84ceb48 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -8,6 +8,7 @@ import ( "runtime" "strings" + "bitbucket.org/gildas_cherruel/bb/cmd/artifact" "bitbucket.org/gildas_cherruel/bb/cmd/branch" "bitbucket.org/gildas_cherruel/bb/cmd/commit" "bitbucket.org/gildas_cherruel/bb/cmd/common" @@ -60,7 +61,10 @@ func init() { RootCmd.PersistentFlags().VarP(&CmdOptions.OutputFormat, "output", "o", "Output format (json, yaml, table). Overrides the default output format of the profile") _ = RootCmd.MarkFlagFilename("config") _ = RootCmd.MarkFlagFilename("log") + _ = RootCmd.RegisterFlagCompletionFunc("profile", profile.ValidProfileNames) + _ = RootCmd.RegisterFlagCompletionFunc("output", CmdOptions.OutputFormat.CompletionFunc()) + RootCmd.AddCommand(artifact.Command) RootCmd.AddCommand(profile.Command) RootCmd.AddCommand(project.Command) RootCmd.AddCommand(branch.Command) diff --git a/cmd/user/app_user.go b/cmd/user/app_user.go new file mode 100644 index 0000000..302b36c --- /dev/null +++ b/cmd/user/app_user.go @@ -0,0 +1,35 @@ +package user + +import ( + "encoding/json" + "time" + + "bitbucket.org/gildas_cherruel/bb/cmd/common" + "bitbucket.org/gildas_cherruel/bb/cmd/link" + "github.com/gildas/go-errors" +) + +type AppUser struct { + Type string `json:"type" mapstructure:"type"` + ID common.UUID `json:"uuid" mapstructure:"uuid"` + Name string `json:"display_name" mapstructure:"display_name"` + AccountID string `json:"account_id" mapstructure:"account_id"` + AccountStatus string `json:"account_status" mapstructure:"account_status"` + Kind string `json:"kind" mapstructure:"kind"` + Links link.Links `json:"links" mapstructure:"links"` + CreatedOn time.Time `json:"created_on" mapstructure:"created_on"` +} + +// MarshalJSON implements the json.Marshaler interface. +func (user AppUser) MarshalJSON() (data []byte, err error) { + type surrogate AppUser + + data, err = json.Marshal(struct { + surrogate + CreatedOn string `json:"created_on"` + }{ + surrogate: surrogate(user), + CreatedOn: user.CreatedOn.Format("2006-01-02T15:04:05.999999999-07:00"), + }) + return data, errors.JSONMarshalError.Wrap(err) +} diff --git a/cmd/user/participant.go b/cmd/user/participant.go index 53c4af9..ac00ff7 100644 --- a/cmd/user/participant.go +++ b/cmd/user/participant.go @@ -25,7 +25,7 @@ func (participant Participant) GetHeader(short bool) []string { // implements common.Tableable func (participant Participant) GetRow(headers []string) []string { return []string{ - participant.User.ID, + participant.User.ID.String(), participant.User.Name, participant.ParticipatedOn.Local().String(), participant.State, diff --git a/cmd/user/user.go b/cmd/user/user.go index 485ba07..9d18af4 100644 --- a/cmd/user/user.go +++ b/cmd/user/user.go @@ -1,13 +1,30 @@ package user -import "bitbucket.org/gildas_cherruel/bb/cmd/link" +import ( + "bitbucket.org/gildas_cherruel/bb/cmd/common" + "bitbucket.org/gildas_cherruel/bb/cmd/link" +) type User struct { - Type string `json:"type" mapstructure:"type"` - ID string `json:"uuid" mapstructure:"uuid"` - AccountID string `json:"account_id" mapstructure:"account_id"` - Name string `json:"display_name" mapstructure:"display_name"` - Nickname string `json:"nickname" mapstructure:"nickname"` - Raw string `json:"raw,omitempty" mapstructure:"raw"` - Links link.Links `json:"links" mapstructure:"links"` + Type string `json:"type" mapstructure:"type"` + ID common.UUID `json:"uuid" mapstructure:"uuid"` + AccountID string `json:"account_id" mapstructure:"account_id"` + Name string `json:"display_name" mapstructure:"display_name"` + Nickname string `json:"nickname" mapstructure:"nickname"` + Raw string `json:"raw,omitempty" mapstructure:"raw"` + Links link.Links `json:"links" mapstructure:"links"` +} + +// GetHeader gets the header for a table +// +// implements common.Tableable +func (user User) GetHeader(short bool) []string { + return []string{"ID", "Name", "Nickname"} +} + +// GetRow gets the row for a table +// +// implements common.Tableable +func (user User) GetRow(headers []string) []string { + return []string{user.ID.String(), user.Name, user.Nickname} } diff --git a/cmd/workspace/member.go b/cmd/workspace/member.go index 0970d88..566c1f6 100644 --- a/cmd/workspace/member.go +++ b/cmd/workspace/member.go @@ -24,7 +24,7 @@ func (member Member) GetHeader(short bool) []string { // implements common.Tableable func (member Member) GetRow(headers []string) []string { return []string{ - member.User.ID, + member.User.ID.String(), member.User.Name, member.Workspace.Name, } diff --git a/cmd/workspace/membership.go b/cmd/workspace/membership.go index b6d3c34..02c0798 100644 --- a/cmd/workspace/membership.go +++ b/cmd/workspace/membership.go @@ -25,7 +25,7 @@ func (membership Membership) GetHeader(short bool) []string { // implements common.Tableable func (membership Membership) GetRow(headers []string) []string { return []string{ - membership.User.ID, + membership.User.ID.String(), membership.User.Name, membership.Workspace.Name, membership.Permission, diff --git a/cmd/workspace/workspace.go b/cmd/workspace/workspace.go index 27962aa..bcc37ad 100644 --- a/cmd/workspace/workspace.go +++ b/cmd/workspace/workspace.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "bitbucket.org/gildas_cherruel/bb/cmd/common" "bitbucket.org/gildas_cherruel/bb/cmd/link" "bitbucket.org/gildas_cherruel/bb/cmd/profile" "github.com/gildas/go-core" @@ -12,11 +13,11 @@ import ( ) type Workspace struct { - Type string `json:"type" mapstructure:"type"` - ID string `json:"uuid" mapstructure:"uuid"` - Name string `json:"name" mapstructure:"name"` - Slug string `json:"slug" mapstructure:"slug"` - Links link.Links `json:"links" mapstructure:"links"` + Type string `json:"type" mapstructure:"type"` + ID common.UUID `json:"uuid" mapstructure:"uuid"` + Name string `json:"name" mapstructure:"name"` + Slug string `json:"slug" mapstructure:"slug"` + Links link.Links `json:"links" mapstructure:"links"` } // Command represents this folder's command @@ -43,7 +44,7 @@ func (workspace Workspace) GetHeader(short bool) []string { // implements common.Tableable func (workspace Workspace) GetRow(headers []string) []string { return []string{ - workspace.ID, + workspace.ID.String(), workspace.Name, workspace.Slug, } diff --git a/go.mod b/go.mod index 0084468..b8e0fd6 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/gildas/go-errors v0.3.6 github.com/gildas/go-logger v1.6.9 github.com/gildas/go-request v0.7.18 + github.com/google/uuid v1.5.0 github.com/joho/godotenv v1.5.1 github.com/kataras/tablewriter v0.0.0-20180708051242-e063d29b7c23 github.com/spf13/cobra v1.8.0 @@ -20,14 +21,16 @@ require ( cloud.google.com/go v0.111.0 // indirect cloud.google.com/go/compute v1.23.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/logging v1.8.1 // indirect + cloud.google.com/go/logging v1.9.0 // indirect cloud.google.com/go/longrunning v0.5.4 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-logr/logr v1.3.0 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/s2a-go v0.1.7 // indirect - github.com/google/uuid v1.4.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect @@ -46,20 +49,25 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.6.0 // indirect go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect + go.opentelemetry.io/otel v1.21.0 // indirect + go.opentelemetry.io/otel/metric v1.21.0 // indirect + go.opentelemetry.io/otel/trace v1.21.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.16.0 // indirect - golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb // indirect + golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611 // indirect golang.org/x/net v0.19.0 // indirect golang.org/x/oauth2 v0.15.0 // indirect golang.org/x/sync v0.5.0 // indirect golang.org/x/sys v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect - google.golang.org/api v0.153.0 // indirect + google.golang.org/api v0.154.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20231211222908-989df2bf70f3 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231211222908-989df2bf70f3 // indirect + google.golang.org/genproto v0.0.0-20231212172506-995d672761c0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect google.golang.org/grpc v1.60.0 // indirect google.golang.org/protobuf v1.31.0 // indirect ) diff --git a/go.sum b/go.sum index 0edf4dc..8ebb384 100644 --- a/go.sum +++ b/go.sum @@ -7,14 +7,16 @@ cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGB cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI= cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= -cloud.google.com/go/logging v1.8.1 h1:26skQWPeYhvIasWKm48+Eq7oUqdcdbwsCVwz5Ys0FvU= -cloud.google.com/go/logging v1.8.1/go.mod h1:TJjR+SimHwuC8MZ9cjByQulAMgni+RkXeI3wwctHJEI= +cloud.google.com/go/logging v1.9.0 h1:iEIOXFO9EmSiTjDmfpbRjOxECO7R8C7b8IXUGOj7xZw= +cloud.google.com/go/logging v1.9.0/go.mod h1:1Io0vnZv4onoUnsVUQY3HZ3Igb1nBchky0A0y7BBBhE= cloud.google.com/go/longrunning v0.5.4 h1:w8xEcbZodnA2BbW6sVirkkoC+1gP8wS57EUUgGS0GVg= cloud.google.com/go/longrunning v0.5.4/go.mod h1:zqNVncI0BOP8ST6XQD1+VcvuShMmq7+xFSzOL++V0dI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -24,6 +26,10 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= @@ -36,8 +42,9 @@ github.com/gildas/go-logger v1.6.9 h1:GdNY9SrGkO4zLFdKjAABzwirxubayrmKAOLIaFWbld github.com/gildas/go-logger v1.6.9/go.mod h1:R/tHixu5HBhFtlZoE6XAhsUqY1iYys8P2+Bq3XUGw8w= github.com/gildas/go-request v0.7.18 h1:ttxtbvPTxIgjHprt2lTM53numkAa/Nai72s2uRXrsuM= github.com/gildas/go-request v0.7.18/go.mod h1:YiiHsecBJAN9Yxe7FlicP4tfhOEWsPqoet8yBPzXVdk= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -70,8 +77,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= @@ -135,14 +142,18 @@ github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSW github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= -go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= -go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= -go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= +go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= +go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= +go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= +go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= -go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= -go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= +go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= +go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -151,8 +162,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb h1:c0vyKkb6yr3KR7jEfJaOSv4lG7xPkbN6r52aJz1d8a8= -golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= +golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611 h1:qCEDpW1G+vcj3Y7Fy52pEM1AWm3abj8WimGYejI3SC4= +golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -206,8 +217,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.153.0 h1:N1AwGhielyKFaUqH07/ZSIQR3uNPcV7NVw0vj+j4iR4= -google.golang.org/api v0.153.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY= +google.golang.org/api v0.154.0 h1:X7QkVKZBskztmpPKWQXgjJRPA2dJYrL6r+sYPRLj050= +google.golang.org/api v0.154.0/go.mod h1:qhSMkM85hgqiokIYsrRyKxrjfBeIhgl4Z2JmeRkYylc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= @@ -215,12 +226,12 @@ google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 h1:1hfbdAfFbkmpg41000wDVqr7jUpK/Yo+LPnIxxGzmkg= -google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3/go.mod h1:5RBcpGRxr25RbDzY5w+dmaqpSEvl8Gwl1x2CICf60ic= -google.golang.org/genproto/googleapis/api v0.0.0-20231211222908-989df2bf70f3 h1:EWIeHfGuUf00zrVZGEgYFxok7plSAXBGcH7NNdMAWvA= -google.golang.org/genproto/googleapis/api v0.0.0-20231211222908-989df2bf70f3/go.mod h1:k2dtGpRrbsSyKcNPKKI5sstZkrNCZwpU/ns96JoHbGg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231211222908-989df2bf70f3 h1:kzJAXnzZoFbe5bhZd4zjUuHos/I31yH4thfMb/13oVY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231211222908-989df2bf70f3/go.mod h1:eJVxU6o+4G1PSczBr85xmyvSNYAKvAYgkub40YGomFM= +google.golang.org/genproto v0.0.0-20231212172506-995d672761c0 h1:YJ5pD9rF8o9Qtta0Cmy9rdBwkSjrTCT6XTiUQVOtIos= +google.golang.org/genproto v0.0.0-20231212172506-995d672761c0/go.mod h1:l/k7rMz0vFTBPy+tFSGvXEd3z+BcoG1k7EHbqm+YBsY= +google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 h1:s1w3X6gQxwrLEpxnLd/qXTVLgQE2yXwaOaoa6IlY/+o= +google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0/go.mod h1:CAny0tYF+0/9rmDB9fahA9YLzX3+AEVl1qXbv5hhj6c= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 h1:/jFB8jK5R3Sq3i/lmeZO0cATSzFfZaJq1J2Euan3XKU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= diff --git a/version.go b/version.go index cdbc8e1..1b1bd9b 100644 --- a/version.go +++ b/version.go @@ -12,7 +12,7 @@ var branch string var stamp string // VERSION is the version of this application -var VERSION = "0.3.1" +var VERSION = "0.4.0" // APP is the name of the application const APP = "bb"