diff --git a/cmd/cli/deployment_deploy.go b/cmd/cli/deployment_deploy.go index 960a72df..ec60383d 100644 --- a/cmd/cli/deployment_deploy.go +++ b/cmd/cli/deployment_deploy.go @@ -34,7 +34,7 @@ var deploymentDeployCommand = &cli.Command{ } c := buildClient(cli) - d, err := c.Deployment.Create(cli.Context, ns, n, api.DeploymentCreateRequest{ + d, err := c.Deployment.Create(cli.Context, ns, n, &api.DeploymentCreateRequest{ Type: cli.String("type"), Ref: cli.String("ref"), Env: cli.String("env"), diff --git a/cmd/cli/deployment_list.go b/cmd/cli/deployment_list.go index da2bd780..28b2cac1 100644 --- a/cmd/cli/deployment_list.go +++ b/cmd/cli/deployment_list.go @@ -40,7 +40,7 @@ var deploymentListCommand = &cli.Command{ return err } - ds, err := c.Deployment.List(cli.Context, ns, n, api.DeploymentListOptions{ + ds, err := c.Deployment.List(cli.Context, ns, n, &api.DeploymentListOptions{ ListOptions: api.ListOptions{Page: cli.Int("page"), PerPage: cli.Int("per-page")}, Env: cli.String("env"), Status: deployment.Status(cli.String("status")), diff --git a/cmd/cli/deploymentstatus.go b/cmd/cli/deploymentstatus.go new file mode 100644 index 00000000..8a4d550f --- /dev/null +++ b/cmd/cli/deploymentstatus.go @@ -0,0 +1,13 @@ +package main + +import "github.com/urfave/cli/v2" + +var deploymentStatusCommand = &cli.Command{ + Name: "deployment-status", + Aliases: []string{"ds"}, + Usage: "Manage deployment statuses.", + Subcommands: []*cli.Command{ + deploymentStatusListCommand, + deploymentStatusCreateCommand, + }, +} diff --git a/cmd/cli/deploymentstatus_create.go b/cmd/cli/deploymentstatus_create.go new file mode 100644 index 00000000..1077ec4a --- /dev/null +++ b/cmd/cli/deploymentstatus_create.go @@ -0,0 +1,67 @@ +package main + +import ( + "fmt" + "strconv" + + "github.com/urfave/cli/v2" + + "github.com/gitploy-io/gitploy/pkg/api" +) + +var deploymentStatusCreateCommand = &cli.Command{ + Name: "create", + Usage: "Create the remote deployment status under the deployment.", + ArgsUsage: "/ ", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "status", + Usage: "The remote deployment status. For GitHub, Can be one of error, failure, in_progress, queued, or success.", + Required: true, + }, + &cli.StringFlag{ + Name: "description", + Usage: "A short description of the status.", + DefaultText: "USER_LOGIN updated the status manually.", + }, + }, + Action: func(cli *cli.Context) error { + ns, n, err := splitFullName(cli.Args().First()) + if err != nil { + return err + } + + number, err := strconv.Atoi(cli.Args().Get(1)) + if err != nil { + return err + } + + // Build the request. + c := buildClient(cli) + req := &api.DeploymentStatusCreateRemoteRequest{ + Status: cli.String("status"), + Description: cli.String("description"), + } + if cli.String("description") == "" { + req.Description = buildDescription(cli) + } + + dss, err := c.DeploymentStatus.CreateRemote(cli.Context, ns, n, number, req) + if err != nil { + return err + } + + return printJson(cli, dss) + }, +} + +func buildDescription(cli *cli.Context) string { + c := buildClient(cli) + + me, err := c.User.GetMe(cli.Context) + if err != nil { + return "Updated the status manually." + } + + return fmt.Sprintf("%s updated the status manually.", me.Login) +} diff --git a/cmd/cli/deploymentstatus_list.go b/cmd/cli/deploymentstatus_list.go new file mode 100644 index 00000000..b3bd51ce --- /dev/null +++ b/cmd/cli/deploymentstatus_list.go @@ -0,0 +1,35 @@ +package main + +import ( + "strconv" + + "github.com/urfave/cli/v2" + + "github.com/gitploy-io/gitploy/pkg/api" +) + +var deploymentStatusListCommand = &cli.Command{ + Name: "list", + Aliases: []string{"ls"}, + Usage: "Show the deployment status under the deployment.", + ArgsUsage: "/ ", + Action: func(cli *cli.Context) error { + ns, n, err := splitFullName(cli.Args().First()) + if err != nil { + return err + } + + number, err := strconv.Atoi(cli.Args().Get(1)) + if err != nil { + return err + } + + c := buildClient(cli) + dss, err := c.DeploymentStatus.List(cli.Context, ns, n, number, &api.ListOptions{}) + if err != nil { + return err + } + + return printJson(cli, dss) + }, +} diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 0663c3ee..d8a76f3c 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -37,6 +37,7 @@ func main() { Commands: []*cli.Command{ repoCommand, deploymentCommand, + deploymentStatusCommand, configCommand, }, } diff --git a/cmd/cli/repo_list.go b/cmd/cli/repo_list.go index 19b1f45e..29d82ba0 100644 --- a/cmd/cli/repo_list.go +++ b/cmd/cli/repo_list.go @@ -44,7 +44,7 @@ var repoListCommand = &cli.Command{ return err } } else { - if repos, err = c.Repo.List(cli.Context, api.RepoListOptions{ + if repos, err = c.Repo.List(cli.Context, &api.RepoListOptions{ ListOptions: api.ListOptions{Page: cli.Int("page"), PerPage: cli.Int("per-page")}, }); err != nil { return err diff --git a/cmd/cli/repo_update.go b/cmd/cli/repo_update.go index df14d652..c55aa0b9 100644 --- a/cmd/cli/repo_update.go +++ b/cmd/cli/repo_update.go @@ -34,7 +34,7 @@ var repoUpdateCommand = &cli.Command{ } // Build the request body. - req := api.RepoUpdateRequest{} + req := &api.RepoUpdateRequest{} if config := cli.String("config"); config != "" { req.ConfigPath = pointer.ToString(config) } diff --git a/pkg/api/client.go b/pkg/api/client.go index 2d82c2c0..0fa1786f 100644 --- a/pkg/api/client.go +++ b/pkg/api/client.go @@ -19,10 +19,12 @@ type ( // Reuse a single struct instead of allocating one for each service on the heap. common *client - // Services used for talking to different parts of the Gitploy API. - Repo *RepoService - Deployment *DeploymentService - Config *ConfigService + // Services is used for talking to different parts of the Gitploy API. + Repo *RepoService + Deployment *DeploymentService + DeploymentStatus *DeploymentStatusService + Config *ConfigService + User *UserService } client struct { @@ -57,7 +59,9 @@ func NewClient(host string, httpClient *http.Client) *Client { c.Repo = &RepoService{client: c.common} c.Deployment = &DeploymentService{client: c.common} + c.DeploymentStatus = &DeploymentStatusService{client: c.common} c.Config = &ConfigService{client: c.common} + c.User = &UserService{client: c.common} return c } diff --git a/pkg/api/deployment.go b/pkg/api/deployment.go index 74286b5f..7efd47eb 100644 --- a/pkg/api/deployment.go +++ b/pkg/api/deployment.go @@ -29,7 +29,7 @@ type ( // List returns the deployment list. // It returns an error for a bad request. -func (s *DeploymentService) List(ctx context.Context, namespace, name string, options DeploymentListOptions) ([]*ent.Deployment, error) { +func (s *DeploymentService) List(ctx context.Context, namespace, name string, options *DeploymentListOptions) ([]*ent.Deployment, error) { // Build the query. vals := url.Values{} @@ -88,7 +88,7 @@ func (s *DeploymentService) Get(ctx context.Context, namespace, name string, num } // Create requests a server to deploy a specific ref(branch, SHA, tag). -func (s *DeploymentService) Create(ctx context.Context, namespace, name string, body DeploymentCreateRequest) (*ent.Deployment, error) { +func (s *DeploymentService) Create(ctx context.Context, namespace, name string, body *DeploymentCreateRequest) (*ent.Deployment, error) { req, err := s.client.NewRequest( "POST", fmt.Sprintf("api/v1/repos/%s/%s/deployments", namespace, name), diff --git a/pkg/api/deployment_status.go b/pkg/api/deployment_status.go new file mode 100644 index 00000000..29c7dc57 --- /dev/null +++ b/pkg/api/deployment_status.go @@ -0,0 +1,59 @@ +package api + +import ( + "context" + "fmt" + + "github.com/gitploy-io/gitploy/model/ent" + "github.com/gitploy-io/gitploy/model/extent" +) + +type ( + DeploymentStatusService service + + DeploymentStatusCreateRemoteRequest struct { + Status string `json:"status"` + Description string `json:"description"` + LogURL string `json:"log_url"` + } +) + +// List returns the list of deployment statuses. +// It returns an error for a bad request. +func (s *DeploymentStatusService) List(ctx context.Context, namespace, name string, number int, opt *ListOptions) ([]*ent.DeploymentStatus, error) { + req, err := s.client.NewRequest( + "GET", + fmt.Sprintf("api/v1/repos/%s/%s/deployments/%d/statuses", namespace, name, number), + nil, + ) + if err != nil { + return nil, err + } + + var dss []*ent.DeploymentStatus + if err := s.client.Do(ctx, req, &dss); err != nil { + return nil, err + } + + return dss, nil +} + +// CreateRemote returns the remote status. +// It returns an error for a bad request. +func (s *DeploymentStatusService) CreateRemote(ctx context.Context, namespace, name string, number int, body *DeploymentStatusCreateRemoteRequest) (*extent.RemoteDeploymentStatus, error) { + req, err := s.client.NewRequest( + "POST", + fmt.Sprintf("api/v1/repos/%s/%s/deployments/%d/remote-statuses", namespace, name, number), + body, + ) + if err != nil { + return nil, err + } + + var ds *extent.RemoteDeploymentStatus + if err := s.client.Do(ctx, req, &ds); err != nil { + return nil, err + } + + return ds, nil +} diff --git a/pkg/api/deployment_status_test.go b/pkg/api/deployment_status_test.go new file mode 100644 index 00000000..a54255ca --- /dev/null +++ b/pkg/api/deployment_status_test.go @@ -0,0 +1,46 @@ +package api + +import ( + "context" + "net/http" + "testing" + + "github.com/gitploy-io/gitploy/model/ent" + "github.com/gitploy-io/gitploy/model/extent" + "gopkg.in/h2non/gock.v1" +) + +func TestDeploymentStatus_List(t *testing.T) { + t.Run("Return the list of statuses.", func(t *testing.T) { + dss := []*ent.DeploymentStatus{ + {ID: 1}, + } + gock.New("https://cloud.gitploy.io"). + Get("/api/v1/repos/gitploy-io/gitploy/deployments/1/statuses"). + Reply(200). + JSON(dss) + + c := NewClient("https://cloud.gitploy.io/", http.DefaultClient) + + _, err := c.DeploymentStatus.List(context.Background(), "gitploy-io", "gitploy", 1, &ListOptions{}) + if err != nil { + t.Fatalf("Create returns an error: %s", err) + } + }) +} + +func TestDeploymentStatus_Create(t *testing.T) { + t.Run("Return the deployment statuses.", func(t *testing.T) { + gock.New("https://cloud.gitploy.io"). + Post("/api/v1/repos/gitploy-io/gitploy/deployments/1/remote-statuses"). + Reply(201). + JSON(extent.RemoteDeploymentStatus{ID: 1}) + + c := NewClient("https://cloud.gitploy.io/", http.DefaultClient) + + _, err := c.DeploymentStatus.CreateRemote(context.Background(), "gitploy-io", "gitploy", 1, &DeploymentStatusCreateRemoteRequest{}) + if err != nil { + t.Fatalf("Create returns an error: %s", err) + } + }) +} diff --git a/pkg/api/deployment_test.go b/pkg/api/deployment_test.go index bfff7f9d..df553bf2 100644 --- a/pkg/api/deployment_test.go +++ b/pkg/api/deployment_test.go @@ -27,7 +27,7 @@ func TestDeploymentService_List(t *testing.T) { c := NewClient("https://cloud.gitploy.io/", http.DefaultClient) - ret, err := c.Deployment.List(context.Background(), "gitploy-io", "gitploy", DeploymentListOptions{ + ret, err := c.Deployment.List(context.Background(), "gitploy-io", "gitploy", &DeploymentListOptions{ ListOptions: ListOptions{Page: 1, PerPage: 30}, Env: "production", Status: "waiting", @@ -68,7 +68,7 @@ func TestDeploymentService_Create(t *testing.T) { c := NewClient("https://cloud.gitploy.io/", http.DefaultClient) - _, err := c.Deployment.Create(context.Background(), "gitploy-io", "gitploy", DeploymentCreateRequest{ + _, err := c.Deployment.Create(context.Background(), "gitploy-io", "gitploy", &DeploymentCreateRequest{ Type: "branch", Env: "production", Ref: "main", diff --git a/pkg/api/repo.go b/pkg/api/repo.go index 997979f3..c09507bc 100644 --- a/pkg/api/repo.go +++ b/pkg/api/repo.go @@ -31,7 +31,7 @@ func (s *RepoService) ListAll(ctx context.Context) ([]*ent.Repo, error) { page := 1 for { - rs, err := s.List(ctx, RepoListOptions{ + rs, err := s.List(ctx, &RepoListOptions{ ListOptions{ Page: page, PerPage: perPage, @@ -55,7 +55,7 @@ func (s *RepoService) ListAll(ctx context.Context) ([]*ent.Repo, error) { } // List returns repositories which are on the page. -func (s *RepoService) List(ctx context.Context, options RepoListOptions) ([]*ent.Repo, error) { +func (s *RepoService) List(ctx context.Context, options *RepoListOptions) ([]*ent.Repo, error) { // Build the query. vals := url.Values{} vals.Add("page", strconv.Itoa(options.Page)) @@ -95,7 +95,7 @@ func (s *RepoService) Get(ctx context.Context, namespace, name string) (*ent.Rep return repo, nil } -func (s *RepoService) Update(ctx context.Context, namespace, name string, options RepoUpdateRequest) (*ent.Repo, error) { +func (s *RepoService) Update(ctx context.Context, namespace, name string, options *RepoUpdateRequest) (*ent.Repo, error) { req, err := s.client.NewRequest( "PATCH", fmt.Sprintf("api/v1/repos/%s/%s", namespace, name), diff --git a/pkg/api/repo_test.go b/pkg/api/repo_test.go index eddfa3a7..8e55ca56 100644 --- a/pkg/api/repo_test.go +++ b/pkg/api/repo_test.go @@ -36,7 +36,7 @@ func TestRepoService_List(t *testing.T) { c := NewClient("https://cloud.gitploy.io/", http.DefaultClient) - ret, err := c.Repo.List(context.Background(), RepoListOptions{ + ret, err := c.Repo.List(context.Background(), &RepoListOptions{ ListOptions: ListOptions{Page: 1, PerPage: 30}, }) if err != nil { @@ -100,7 +100,7 @@ func TestRepoService_Update(t *testing.T) { c := NewClient("https://cloud.gitploy.io/", http.DefaultClient) - _, err := c.Repo.Update(context.Background(), "gitploy-io", "gitploy", RepoUpdateRequest{ + _, err := c.Repo.Update(context.Background(), "gitploy-io", "gitploy", &RepoUpdateRequest{ ConfigPath: pointer.ToString("new_deploy.yml"), }) if err != nil { diff --git a/pkg/api/user.go b/pkg/api/user.go new file mode 100644 index 00000000..63e05029 --- /dev/null +++ b/pkg/api/user.go @@ -0,0 +1,31 @@ +package api + +import ( + "context" + + "github.com/gitploy-io/gitploy/model/ent" +) + +type ( + // UserService communicates with the server for users. + UserService service +) + +// GetMe returns the user information. +func (s *UserService) GetMe(ctx context.Context) (*ent.User, error) { + req, err := s.client.NewRequest( + "GET", + "/api/v1/user", + nil, + ) + if err != nil { + return nil, err + } + + var u *ent.User + if err := s.client.Do(ctx, req, &u); err != nil { + return nil, err + } + + return u, nil +}