这是indexloc提供的服务,不要输入任何密码
Skip to content

Add support for Deploy Hooks #173

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions client/deploy_hooks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package client

import (
"context"
"fmt"
"slices"

"github.com/hashicorp/terraform-plugin-log/tflog"
)

type DeployHook struct {
Name string `json:"name"`
Ref string `json:"ref"`
URL string `json:"url"`
ID string `json:"id"`
}

type CreateDeployHookRequest struct {
ProjectID string `json:"-"`
TeamID string `json:"-"`
Name string `json:"name"`
Ref string `json:"ref"`
}

func (c *Client) CreateDeployHook(ctx context.Context, request CreateDeployHookRequest) (h DeployHook, err error) {
url := fmt.Sprintf("%s/v2/projects/%s/deploy-hooks", c.baseURL, request.ProjectID)
if c.teamID(request.TeamID) != "" {
url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(request.TeamID))
}
payload := string(mustMarshal(request))
tflog.Info(ctx, "creating deploy hook", map[string]interface{}{
"url": url,
"payload": payload,
})

var r ProjectResponse
err = c.doRequest(clientRequest{
ctx: ctx,
method: "POST",
url: url,
body: payload,
}, &r)
if err != nil {
return h, fmt.Errorf("error creating deploy hook: %w", err)
}

// Reverse the list as newest created are at the end
slices.Reverse(r.Link.DeployHooks)
for _, hook := range r.Link.DeployHooks {
if hook.Name == request.Name && hook.Ref == request.Ref {
return hook, nil
}
}

return h, fmt.Errorf("deploy hook was created successfully, but could not be found")
}

type DeleteDeployHookRequest struct {
ProjectID string
TeamID string
ID string
}

func (c *Client) DeleteDeployHook(ctx context.Context, request DeleteDeployHookRequest) error {
url := fmt.Sprintf("%s/v2/projects/%s/deploy-hooks/%s", c.baseURL, request.ProjectID, request.ID)
if c.teamID(request.TeamID) != "" {
url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(request.TeamID))
}
payload := string(mustMarshal(request))
tflog.Info(ctx, "creating deploy hook", map[string]interface{}{
"url": url,
"payload": payload,
})

err := c.doRequest(clientRequest{
ctx: ctx,
method: "DELETE",
url: url,
}, nil)
if err != nil {
return fmt.Errorf("error deleting deploy hook: %w", err)
}
return nil
}
2 changes: 1 addition & 1 deletion client/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ func (e MissingFilesError) Error() string {
}

func (c *Client) getGitSource(ctx context.Context, projectID, ref, teamID string) (gs gitSource, err error) {
project, err := c.GetProject(ctx, projectID, teamID, false)
project, err := c.GetProject(ctx, projectID, teamID)
if err != nil {
return gs, fmt.Errorf("error getting project: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion client/environment_variable.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func (c *Client) DeleteEnvironmentVariable(ctx context.Context, projectID, teamI
}, nil)
}

func (c *Client) getEnvironmentVariables(ctx context.Context, projectID, teamID string) ([]EnvironmentVariable, error) {
func (c *Client) GetEnvironmentVariables(ctx context.Context, projectID, teamID string) ([]EnvironmentVariable, error) {
url := fmt.Sprintf("%s/v8/projects/%s/env?decrypt=true", c.baseURL, projectID)
if c.teamID(teamID) != "" {
url = fmt.Sprintf("%s&teamId=%s", url, c.teamID(teamID))
Expand Down
72 changes: 18 additions & 54 deletions client/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,6 @@ func (c *Client) CreateProject(ctx context.Context, teamID string, request Creat
if err != nil {
return r, err
}
env, err := c.getEnvironmentVariables(ctx, r.ID, teamID)
if err != nil {
return r, fmt.Errorf("error getting environment variables: %w", err)
}
r.EnvironmentVariables = env
r.TeamID = c.teamID(teamID)
return r, err
}
Expand Down Expand Up @@ -96,6 +91,7 @@ type Repository struct {
Type string
Repo string
ProductionBranch *string
DeployHooks []DeployHook
}

// getRepoNameFromURL is a helper method to extract the repo name from a GitLab URL.
Expand All @@ -120,33 +116,35 @@ func (r *ProjectResponse) Repository() *Repository {
Type: "github",
Repo: fmt.Sprintf("%s/%s", r.Link.Org, r.Link.Repo),
ProductionBranch: r.Link.ProductionBranch,
DeployHooks: r.Link.DeployHooks,
}
case "gitlab":
return &Repository{
Type: "gitlab",
Repo: fmt.Sprintf("%s/%s", r.Link.ProjectNamespace, getRepoNameFromURL(r.Link.ProjectURL)),
ProductionBranch: r.Link.ProductionBranch,
DeployHooks: r.Link.DeployHooks,
}
case "bitbucket":
return &Repository{
Type: "bitbucket",
Repo: fmt.Sprintf("%s/%s", r.Link.Owner, r.Link.Slug),
ProductionBranch: r.Link.ProductionBranch,
DeployHooks: r.Link.DeployHooks,
}
}
return nil
}

// ProjectResponse defines the information Vercel returns about a project.
type ProjectResponse struct {
BuildCommand *string `json:"buildCommand"`
CommandForIgnoringBuildStep *string `json:"commandForIgnoringBuildStep"`
DevCommand *string `json:"devCommand"`
EnvironmentVariables []EnvironmentVariable `json:"env"`
Framework *string `json:"framework"`
ID string `json:"id"`
TeamID string `json:"-"`
InstallCommand *string `json:"installCommand"`
BuildCommand *string `json:"buildCommand"`
CommandForIgnoringBuildStep *string `json:"commandForIgnoringBuildStep"`
DevCommand *string `json:"devCommand"`
Framework *string `json:"framework"`
ID string `json:"id"`
TeamID string `json:"-"`
InstallCommand *string `json:"installCommand"`
Link *struct {
Type string `json:"type"`
// github
Expand All @@ -160,7 +158,8 @@ type ProjectResponse struct {
ProjectURL string `json:"projectUrl"`
ProjectID int64 `json:"projectId,string"`
// production branch
ProductionBranch *string `json:"productionBranch"`
ProductionBranch *string `json:"productionBranch"`
DeployHooks []DeployHook `json:"deployHooks"`
} `json:"link"`
Name string `json:"name"`
OutputDirectory *string `json:"outputDirectory"`
Expand All @@ -175,14 +174,13 @@ type ProjectResponse struct {
}

// GetProject retrieves information about an existing project from Vercel.
func (c *Client) GetProject(ctx context.Context, projectID, teamID string, shouldFetchEnvironmentVariables bool) (r ProjectResponse, err error) {
func (c *Client) GetProject(ctx context.Context, projectID, teamID string) (r ProjectResponse, err error) {
url := fmt.Sprintf("%s/v10/projects/%s", c.baseURL, projectID)
if c.teamID(teamID) != "" {
url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID))
}
tflog.Info(ctx, "getting project", map[string]interface{}{
"url": url,
"shouldFetchEnvironment": shouldFetchEnvironmentVariables,
"url": url,
})
err = c.doRequest(clientRequest{
ctx: ctx,
Expand All @@ -194,16 +192,6 @@ func (c *Client) GetProject(ctx context.Context, projectID, teamID string, shoul
return r, fmt.Errorf("unable to get project: %w", err)
}

if shouldFetchEnvironmentVariables {
r.EnvironmentVariables, err = c.getEnvironmentVariables(ctx, projectID, teamID)
if err != nil {
return r, fmt.Errorf("error getting environment variables for project: %w", err)
}
} else {
// The get project endpoint returns environment variables, but returns them fully
// encrypted. This isn't useful, so we just remove them.
r.EnvironmentVariables = nil
}
r.TeamID = c.teamID(teamID)
return r, err
}
Expand Down Expand Up @@ -257,16 +245,15 @@ type UpdateProjectRequest struct {
}

// UpdateProject updates an existing projects configuration within Vercel.
func (c *Client) UpdateProject(ctx context.Context, projectID, teamID string, request UpdateProjectRequest, shouldFetchEnvironmentVariables bool) (r ProjectResponse, err error) {
func (c *Client) UpdateProject(ctx context.Context, projectID, teamID string, request UpdateProjectRequest) (r ProjectResponse, err error) {
url := fmt.Sprintf("%s/v9/projects/%s", c.baseURL, projectID)
if c.teamID(teamID) != "" {
url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID))
}
payload := string(mustMarshal(request))
tflog.Info(ctx, "updating project", map[string]interface{}{
"url": url,
"payload": payload,
"shouldFetchEnvironmentVariables": shouldFetchEnvironmentVariables,
"url": url,
"payload": payload,
})
err = c.doRequest(clientRequest{
ctx: ctx,
Expand All @@ -277,14 +264,6 @@ func (c *Client) UpdateProject(ctx context.Context, projectID, teamID string, re
if err != nil {
return r, err
}
if shouldFetchEnvironmentVariables {
r.EnvironmentVariables, err = c.getEnvironmentVariables(ctx, r.ID, teamID)
if err != nil {
return r, fmt.Errorf("error getting environment variables for project: %w", err)
}
} else {
r.EnvironmentVariables = nil
}

r.TeamID = c.teamID(teamID)
return r, err
Expand Down Expand Up @@ -315,11 +294,6 @@ func (c *Client) UpdateProductionBranch(ctx context.Context, request UpdateProdu
if err != nil {
return r, err
}
env, err := c.getEnvironmentVariables(ctx, r.ID, request.TeamID)
if err != nil {
return r, fmt.Errorf("error getting environment variables: %w", err)
}
r.EnvironmentVariables = env
r.TeamID = c.teamID(c.teamID(request.TeamID))
return r, err
}
Expand All @@ -340,11 +314,6 @@ func (c *Client) UnlinkGitRepoFromProject(ctx context.Context, projectID, teamID
if err != nil {
return r, fmt.Errorf("error unlinking git repo: %w", err)
}
env, err := c.getEnvironmentVariables(ctx, r.ID, teamID)
if err != nil {
return r, fmt.Errorf("error getting environment variables: %w", err)
}
r.EnvironmentVariables = env
r.TeamID = c.teamID(teamID)
return r, err
}
Expand Down Expand Up @@ -374,11 +343,6 @@ func (c *Client) LinkGitRepoToProject(ctx context.Context, request LinkGitRepoTo
if err != nil {
return r, fmt.Errorf("error linking git repo: %w", err)
}
env, err := c.getEnvironmentVariables(ctx, r.ID, request.TeamID)
if err != nil {
return r, fmt.Errorf("error getting environment variables: %w", err)
}
r.EnvironmentVariables = env
r.TeamID = c.teamID(c.teamID(request.TeamID))
return r, err
}
15 changes: 15 additions & 0 deletions docs/resources/project.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,23 @@ Required:

Optional:

- `deploy_hooks` (Attributes Set) Deploy hooks are unique URLs that allow you to trigger a deployment of a given branch. See https://vercel.com/docs/deployments/deploy-hooks for full information. (see [below for nested schema](#nestedatt--git_repository--deploy_hooks))
- `production_branch` (String) By default, every commit pushed to the main branch will trigger a Production Deployment instead of the usual Preview Deployment. You can switch to a different branch here.

<a id="nestedatt--git_repository--deploy_hooks"></a>
### Nested Schema for `git_repository.deploy_hooks`

Required:

- `name` (String) The name of the deploy hook.
- `ref` (String) The branch or commit hash that should be deployed.

Read-Only:

- `id` (String) The ID of the deploy hook.
- `url` (String, Sensitive) A URL that, when a POST request is made to, will trigger a new deployment.



<a id="nestedatt--password_protection"></a>
### Nested Schema for `password_protection`
Expand Down
17 changes: 13 additions & 4 deletions vercel/data_source_project.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,8 +243,9 @@ type ProjectDataSource struct {
AutoExposeSystemEnvVars types.Bool `tfsdk:"automatically_expose_system_environment_variables"`
}

func convertResponseToProjectDataSource(ctx context.Context, response client.ProjectResponse, plan Project) (ProjectDataSource, error) {
project, err := convertResponseToProject(ctx, response, plan)
func convertResponseToProjectDataSource(ctx context.Context, response client.ProjectResponse, plan Project, environmentVariables []client.EnvironmentVariable) (ProjectDataSource, error) {
plan.Environment = types.SetValueMust(envVariableElemType, []attr.Value{})
project, err := convertResponseToProject(ctx, response, plan, environmentVariables)
if err != nil {
return ProjectDataSource{}, err
}
Expand Down Expand Up @@ -288,7 +289,7 @@ func (d *projectDataSource) Read(ctx context.Context, req datasource.ReadRequest
return
}

out, err := d.client.GetProject(ctx, config.Name.ValueString(), config.TeamID.ValueString(), true)
out, err := d.client.GetProject(ctx, config.Name.ValueString(), config.TeamID.ValueString())
if err != nil {
resp.Diagnostics.AddError(
"Error reading project",
Expand All @@ -301,7 +302,15 @@ func (d *projectDataSource) Read(ctx context.Context, req datasource.ReadRequest
return
}

result, err := convertResponseToProjectDataSource(ctx, out, nullProject)
environmentVariables, err := d.client.GetEnvironmentVariables(ctx, out.ID, out.TeamID)
if err != nil {
resp.Diagnostics.AddError(
"Error reading project environment variables",
"Could not read project, unexpected error: "+err.Error(),
)
return
}
result, err := convertResponseToProjectDataSource(ctx, out, nullProject, environmentVariables)
if err != nil {
resp.Diagnostics.AddError(
"Error converting project response to model",
Expand Down
2 changes: 1 addition & 1 deletion vercel/resource_deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ func (r *deploymentResource) Create(ctx context.Context, req resource.CreateRequ
Ref: plan.Ref.ValueString(),
}

_, err = r.client.GetProject(ctx, plan.ProjectID.ValueString(), plan.TeamID.ValueString(), false)
_, err = r.client.GetProject(ctx, plan.ProjectID.ValueString(), plan.TeamID.ValueString())
if client.NotFound(err) {
resp.Diagnostics.AddError(
"Error creating deployment",
Expand Down
Loading