diff --git a/github/resource_github_branch.go b/github/resource_github_branch.go index 47314dd9ed..298ae3e2e7 100644 --- a/github/resource_github_branch.go +++ b/github/resource_github_branch.go @@ -16,6 +16,7 @@ func resourceGithubBranch() *schema.Resource { return &schema.Resource{ Create: resourceGithubBranchCreate, Read: resourceGithubBranchRead, + Update: resourceGithubBranchUpdate, Delete: resourceGithubBranchDelete, Importer: &schema.ResourceImporter{ State: resourceGithubBranchImport, @@ -31,7 +32,6 @@ func resourceGithubBranch() *schema.Resource { "branch": { Type: schema.TypeString, Required: true, - ForceNew: true, Description: "The repository branch to create.", }, "source_branch": { @@ -186,6 +186,29 @@ func resourceGithubBranchDelete(d *schema.ResourceData, meta any) error { return nil } +func resourceGithubBranchUpdate(d *schema.ResourceData, meta any) error { + if !d.HasChange("branch") { + return resourceGithubBranchRead(d, meta) + } + + ctx := context.WithValue(context.Background(), ctxId, d.Id()) + client := meta.(*Owner).v3client + orgName := meta.(*Owner).name + repoName, oldBranchName, err := parseTwoPartID(d.Id(), "repository", "branch") + if err != nil { + return err + } + newBranchName := d.Get("branch").(string) + + if _, _, err := client.Repositories.RenameBranch(ctx, orgName, repoName, oldBranchName, newBranchName); err != nil { + return fmt.Errorf("error renaming GitHub branch %s/%s (%s -> %s): %w", orgName, repoName, oldBranchName, newBranchName, err) + } + + d.SetId(buildTwoPartID(repoName, newBranchName)) + + return resourceGithubBranchRead(d, meta) +} + func resourceGithubBranchImport(d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) { repoName, branchName, err := parseTwoPartID(d.Id(), "repository", "branch") if err != nil { diff --git a/github/resource_github_branch_test.go b/github/resource_github_branch_test.go index bf75cf7d0a..7de895f1cb 100644 --- a/github/resource_github_branch_test.go +++ b/github/resource_github_branch_test.go @@ -201,4 +201,62 @@ func TestAccGithubBranch(t *testing.T) { testCase(t, organization) }) }) + + t.Run("renames a branch without replacement", func(t *testing.T) { + initialConfig := fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-test-%[1]s" + auto_init = true + } + + resource "github_branch" "test" { + repository = github_repository.test.id + branch = "initial" + } + `, randomID) + + renamedConfig := fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-test-%[1]s" + auto_init = true + } + + resource "github_branch" "test" { + repository = github_repository.test.id + branch = "renamed" + } + `, randomID) + + testCase := func(t *testing.T, mode string) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, mode) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: initialConfig, + }, + { + Config: renamedConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "github_branch.test", "branch", "renamed", + ), + ), + }, + }, + }) + } + + t.Run("with an anonymous account", func(t *testing.T) { + t.Skip("anonymous account not supported for this operation") + }) + + t.Run("with an individual account", func(t *testing.T) { + testCase(t, individual) + }) + + t.Run("with an organization account", func(t *testing.T) { + testCase(t, organization) + }) + }) }