internal/provider/sdk/resource_gitlab_branch.go (246 lines of code) (raw):

package sdk import ( "context" "fmt" "time" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" gitlab "gitlab.com/gitlab-org/api/client-go" "gitlab.com/gitlab-org/terraform-provider-gitlab/internal/provider/api" "gitlab.com/gitlab-org/terraform-provider-gitlab/internal/provider/utils" ) var _ = registerResource("gitlab_branch", func() *schema.Resource { return &schema.Resource{ Description: `The ` + "`gitlab_branch`" + ` resource allows to manage the lifecycle of a repository branch. **Upstream API**: [GitLab REST API docs](https://docs.gitlab.com/api/branches/)`, CreateContext: resourceGitlabBranchCreate, ReadContext: resourceGitlabBranchRead, UpdateContext: resourceGitlabBranchUpdate, DeleteContext: resourceGitlabBranchDelete, Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: map[string]*schema.Schema{ "name": { Description: "The name for this branch.", Type: schema.TypeString, ForceNew: true, Required: true, }, "project": { Description: "The ID or full path of the project which the branch is created against.", Type: schema.TypeString, ForceNew: true, Required: true, }, "ref": { Description: "The ref which the branch is created from.", Type: schema.TypeString, ForceNew: true, Required: true, }, "keep_on_destroy": { Description: "Indicates whether the branch is kept once the resource destroyed (must be applied before a destroy).", Type: schema.TypeBool, Optional: true, Default: false, }, "web_url": { Description: "The url of the created branch (https).", Type: schema.TypeString, Computed: true, }, "default": { Description: "Bool, true if branch is the default branch for the project.", Type: schema.TypeBool, Computed: true, }, "can_push": { Description: "Bool, true if you can push to the branch.", Type: schema.TypeBool, Computed: true, }, "merged": { Description: "Bool, true if the branch has been merged into its parent.", Type: schema.TypeBool, Computed: true, }, "protected": { Description: "Bool, true if branch has branch protection.", Type: schema.TypeBool, Computed: true, }, "developer_can_merge": { Description: "Bool, true if developer level access allows to merge branch.", Type: schema.TypeBool, Computed: true, }, "developer_can_push": { Description: "Bool, true if developer level access allows git push.", Type: schema.TypeBool, Computed: true, }, "commit": { Description: "The commit associated with the branch ref.", Type: schema.TypeSet, Computed: true, Set: schema.HashResource(commitSchema), Elem: commitSchema, }, }, } }) var commitSchema = &schema.Resource{ Schema: map[string]*schema.Schema{ "id": { Description: "The unique id assigned to the commit by Gitlab.", Type: schema.TypeString, Computed: true, }, "author_email": { Description: "The email of the author.", Type: schema.TypeString, Computed: true, }, "author_name": { Description: "The name of the author.", Type: schema.TypeString, Computed: true, }, "authored_date": { Description: "The date which the commit was authored (format: yyyy-MM-ddTHH:mm:ssZ).", Type: schema.TypeString, Computed: true, }, "committed_date": { Description: "The date at which the commit was pushed (format: yyyy-MM-ddTHH:mm:ssZ).", Type: schema.TypeString, Computed: true, }, "committer_email": { Description: "The email of the user that committed.", Type: schema.TypeString, Computed: true, }, "committer_name": { Description: "The name of the user that committed.", Type: schema.TypeString, Computed: true, }, "short_id": { Description: "The short id assigned to the commit by Gitlab.", Type: schema.TypeString, Computed: true, }, "title": { Description: "The title of the commit", Type: schema.TypeString, Computed: true, }, "message": { Description: "The commit message", Type: schema.TypeString, Computed: true, }, "parent_ids": { Description: "The id of the parents of the commit", Type: schema.TypeSet, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, Set: schema.HashString, }, }, } func resourceGitlabBranchCreate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*gitlab.Client) name := d.Get("name").(string) project := d.Get("project").(string) ref := d.Get("ref").(string) branchOptions := &gitlab.CreateBranchOptions{ Branch: &name, Ref: &ref, } tflog.Debug(ctx, fmt.Sprintf("[DEBUG] create gitlab branch %s for project %s with ref %s", name, project, ref)) branch, resp, err := client.Branches.CreateBranch(project, branchOptions, gitlab.WithContext(ctx)) if err != nil { tflog.Debug(ctx, fmt.Sprintf("[DEBUG] failed to create gitlab branch %v response %v", branch, resp)) return diag.FromErr(err) } d.Set("ref", ref) d.SetId(utils.BuildTwoPartID(&project, &name)) return resourceGitlabBranchRead(ctx, d, meta) } func resourceGitlabBranchRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*gitlab.Client) project, name, err := utils.ParseTwoPartID(d.Id()) if err != nil { return diag.FromErr(err) } tflog.Debug(ctx, fmt.Sprintf("[DEBUG] read gitlab branch %s", name)) branch, resp, err := client.Branches.GetBranch(project, name, gitlab.WithContext(ctx)) if err != nil { if api.Is404(err) { tflog.Debug(ctx, fmt.Sprintf("[DEBUG] recieved 404 for gitlab branch %s, removing from state", name)) d.SetId("") return nil } tflog.Debug(ctx, fmt.Sprintf("[DEBUG] failed to read gitlab branch %s response %v", name, resp)) return diag.FromErr(err) } d.SetId(utils.BuildTwoPartID(&project, &name)) d.Set("name", branch.Name) d.Set("project", project) d.Set("web_url", branch.WebURL) d.Set("default", branch.Default) d.Set("can_push", branch.CanPush) d.Set("merged", branch.Merged) d.Set("developer_can_merge", branch.DevelopersCanMerge) d.Set("developer_can_push", branch.DevelopersCanPush) d.Set("protected", branch.Protected) commit := flattenCommit(branch.Commit) if err := d.Set("commit", commit); err != nil { return diag.FromErr(err) } return nil } func resourceGitlabBranchUpdate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { // This function exists only to update the `keep_on_destroy` in state. No action is necessary, because all important attributes // force re-creation of the resource. return resourceGitlabBranchRead(ctx, d, meta) } func resourceGitlabBranchDelete(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*gitlab.Client) project, name, err := utils.ParseTwoPartID(d.Id()) if err != nil { return diag.FromErr(err) } tflog.Debug(ctx, fmt.Sprintf("[DEBUG] delete gitlab branch %s", name)) if d.Get("keep_on_destroy").(bool) { tflog.Info(ctx, fmt.Sprintf("[INFO] skipping deletion of branch %s, 'keep_on_destroy' enabled", name)) return nil } resp, err := client.Branches.DeleteBranch(project, name, gitlab.WithContext(ctx)) if err != nil { tflog.Debug(ctx, fmt.Sprintf("[DEBUG] failed to delete gitlab branch %s response %v", name, resp)) return diag.FromErr(err) } return nil } func flattenCommit(commit *gitlab.Commit) (values []map[string]any) { if commit == nil { return []map[string]any{} } return []map[string]any{ { "id": commit.ID, "short_id": commit.ShortID, "title": commit.Title, "author_name": commit.AuthorName, "author_email": commit.AuthorEmail, "authored_date": commit.AuthoredDate.Format(time.RFC3339), "committed_date": commit.CommittedDate.Format(time.RFC3339), "committer_email": commit.CommitterEmail, "committer_name": commit.CommitterName, "message": commit.Message, "parent_ids": commit.ParentIDs, }, } }