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

Allow updating git_repository without recreating project #171

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
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
59 changes: 59 additions & 0 deletions client/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,3 +323,62 @@ func (c *Client) UpdateProductionBranch(ctx context.Context, request UpdateProdu
r.TeamID = c.teamID(c.teamID(request.TeamID))
return r, err
}

func (c *Client) UnlinkGitRepoFromProject(ctx context.Context, projectID, teamID string) (r ProjectResponse, err error) {
url := fmt.Sprintf("%s/v9/projects/%s/link", c.baseURL, projectID)
if c.teamID(teamID) != "" {
url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID))
}
tflog.Info(ctx, "unlinking project git repo", map[string]interface{}{
"url": url,
})
err = c.doRequest(clientRequest{
ctx: ctx,
method: "DELETE",
url: url,
}, &r)
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
}

type LinkGitRepoToProjectRequest struct {
ProjectID string `json:"-"`
TeamID string `json:"-"`
Type string `json:"type"`
Repo string `json:"repo"`
}

func (c *Client) LinkGitRepoToProject(ctx context.Context, request LinkGitRepoToProjectRequest) (r ProjectResponse, err error) {
url := fmt.Sprintf("%s/v9/projects/%s/link", c.baseURL, request.ProjectID)
if c.teamID(request.TeamID) != "" {
url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(request.TeamID))
}
tflog.Info(ctx, "linking project git repo", map[string]interface{}{
"url": url,
})
payload := string(mustMarshal(request))
err = c.doRequest(clientRequest{
ctx: ctx,
method: "POST",
url: url,
body: payload,
}, &r)
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
}
96 changes: 86 additions & 10 deletions vercel/resource_project.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/objectdefault"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
Expand Down Expand Up @@ -156,20 +157,18 @@ At this time you cannot use a Vercel Project resource with in-line ` + "`environ
"git_repository": schema.SingleNestedAttribute{
Description: "The Git Repository that will be connected to the project. When this is defined, any pushes to the specified connected Git Repository will be automatically deployed. This requires the corresponding Vercel for [Github](https://vercel.com/docs/concepts/git/vercel-for-github), [Gitlab](https://vercel.com/docs/concepts/git/vercel-for-gitlab) or [Bitbucket](https://vercel.com/docs/concepts/git/vercel-for-bitbucket) plugins to be installed.",
Optional: true,
PlanModifiers: []planmodifier.Object{objectplanmodifier.RequiresReplace(), objectplanmodifier.UseStateForUnknown()},
PlanModifiers: []planmodifier.Object{objectplanmodifier.UseStateForUnknown()},
Attributes: map[string]schema.Attribute{
"type": schema.StringAttribute{
Description: "The git provider of the repository. Must be either `github`, `gitlab`, or `bitbucket`.",
Required: true,
Validators: []validator.String{
stringOneOf("github", "gitlab", "bitbucket"),
},
PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()},
},
"repo": schema.StringAttribute{
Description: "The name of the git repository. For example: `vercel/next.js`.",
Required: true,
PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()},
Description: "The name of the git repository. For example: `vercel/next.js`.",
Required: true,
},
"production_branch": schema.StringAttribute{
Description: "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.",
Expand Down Expand Up @@ -298,9 +297,10 @@ At this time you cannot use a Vercel Project resource with in-line ` + "`environ
Description: "If `protection_bypass_for_automation` is enabled, use this value in the `x-vercel-protection-bypass` header to bypass Vercel Authentication and Password Protection for both Preview and Production Deployments.",
},
"automatically_expose_system_environment_variables": schema.BoolAttribute{
Optional: true,
Computed: true,
Description: "Vercel provides a set of Environment Variables that are automatically populated by the System, such as the URL of the Deployment or the name of the Git branch deployed. To expose them to your Deployments, enable this field",
Optional: true,
Computed: true,
Description: "Vercel provides a set of Environment Variables that are automatically populated by the System, such as the URL of the Deployment or the name of the Git branch deployed. To expose them to your Deployments, enable this field",
PlanModifiers: []planmodifier.Bool{boolplanmodifier.UseStateForUnknown()},
},
},
}
Expand Down Expand Up @@ -462,6 +462,18 @@ type GitRepository struct {
ProductionBranch types.String `tfsdk:"production_branch"`
}

func (g *GitRepository) isDifferentRepo(other *GitRepository) bool {
if g == nil && other == nil {
return false
}

if g == nil || other == nil {
return true
}

return g.Repo.ValueString() != other.Repo.ValueString() || g.Type.ValueString() != other.Type.ValueString()
}

func (g *GitRepository) toCreateProjectRequest() *client.GitRepository {
if g == nil {
return nil
Expand Down Expand Up @@ -1114,7 +1126,70 @@ func (r *projectResource) Update(ctx context.Context, req resource.UpdateRequest
return
}

if plan.GitRepository != nil && !plan.GitRepository.ProductionBranch.IsNull() && (state.GitRepository == nil || state.GitRepository.ProductionBranch.ValueString() != plan.GitRepository.ProductionBranch.ValueString()) {
if plan.GitRepository == nil && state.GitRepository != nil {
out, err = r.client.UnlinkGitRepoFromProject(ctx, plan.ID.ValueString(), plan.TeamID.ValueString())
if err != nil {
resp.Diagnostics.AddError(
"Error updating project",
fmt.Sprintf(
"Could not update project %s %s, unexpected error: %s",
state.TeamID.ValueString(),
state.ID.ValueString(),
err,
),
)
return
}
}

wasUnlinked := false
if plan.GitRepository.isDifferentRepo(state.GitRepository) {
if state.GitRepository != nil {
_, err = r.client.UnlinkGitRepoFromProject(ctx, plan.ID.ValueString(), plan.TeamID.ValueString())
wasUnlinked = true
if err != nil {
resp.Diagnostics.AddError(
"Error updating project",
fmt.Sprintf(
"Could not update project unlinking git repo %s %s, unexpected error: %s",
state.TeamID.ValueString(),
state.ID.ValueString(),
err,
),
)
return
}
}

if plan.GitRepository != nil {
out, err = r.client.LinkGitRepoToProject(ctx, client.LinkGitRepoToProjectRequest{
ProjectID: plan.ID.ValueString(),
TeamID: plan.TeamID.ValueString(),
Repo: plan.GitRepository.Repo.ValueString(),
Type: plan.GitRepository.Type.ValueString(),
})
if err != nil {
resp.Diagnostics.AddError(
"Error updating project",
fmt.Sprintf(
"Could not update project git repo %s %s, unexpected error: %s",
state.TeamID.ValueString(),
state.ID.ValueString(),
err,
),
)
return
}
}
}

if plan.GitRepository != nil && !plan.GitRepository.ProductionBranch.IsUnknown() &&
!plan.GitRepository.ProductionBranch.IsNull() && // we know the value the production branch _should_ be
(wasUnlinked || // and we either unlinked the repo,
(state.GitRepository == nil || // or the production branch was never set
// or the production branch was/is something else
state.GitRepository.ProductionBranch.ValueString() != plan.GitRepository.ProductionBranch.ValueString())) {

out, err = r.client.UpdateProductionBranch(ctx, client.UpdateProductionBranchRequest{
ProjectID: plan.ID.ValueString(),
TeamID: plan.TeamID.ValueString(),
Expand All @@ -1124,9 +1199,10 @@ func (r *projectResource) Update(ctx context.Context, req resource.UpdateRequest
resp.Diagnostics.AddError(
"Error updating project",
fmt.Sprintf(
"Could not update project %s %s, unexpected error: %s",
"Could not update project production branch %s %s to '%s', unexpected error: %s",
state.TeamID.ValueString(),
state.ID.ValueString(),
plan.GitRepository.ProductionBranch.ValueString(),
err,
),
)
Expand Down