Skip to content

Commit 9bc2646

Browse files
thisisprnickfloyddiofeher
authored
feat: Add functionality to update github_branch_default instead of forcing replacement (#2911)
* feat: Add update functionality to rename existing GitHub branches and update documentation * gofmt * fix: Update function signatures to use 'any' instead of 'interface{}' * test: Add validation tests for GitHub branch resource schema --------- Co-authored-by: Nick Floyd <139819+nickfloyd@users.noreply.github.com> Co-authored-by: Diógenes Fernandes <diofeher@gmail.com>
1 parent 895ae19 commit 9bc2646

File tree

3 files changed

+133
-1
lines changed

3 files changed

+133
-1
lines changed

github/resource_github_branch.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ func resourceGithubBranch() *schema.Resource {
1616
return &schema.Resource{
1717
Create: resourceGithubBranchCreate,
1818
Read: resourceGithubBranchRead,
19+
Update: resourceGithubBranchUpdate,
1920
Delete: resourceGithubBranchDelete,
2021
Importer: &schema.ResourceImporter{
2122
State: resourceGithubBranchImport,
@@ -31,7 +32,6 @@ func resourceGithubBranch() *schema.Resource {
3132
"branch": {
3233
Type: schema.TypeString,
3334
Required: true,
34-
ForceNew: true,
3535
Description: "The repository branch to create.",
3636
},
3737
"source_branch": {
@@ -186,6 +186,29 @@ func resourceGithubBranchDelete(d *schema.ResourceData, meta any) error {
186186
return nil
187187
}
188188

189+
func resourceGithubBranchUpdate(d *schema.ResourceData, meta any) error {
190+
if !d.HasChange("branch") {
191+
return resourceGithubBranchRead(d, meta)
192+
}
193+
194+
ctx := context.WithValue(context.Background(), ctxId, d.Id())
195+
client := meta.(*Owner).v3client
196+
orgName := meta.(*Owner).name
197+
repoName, oldBranchName, err := parseTwoPartID(d.Id(), "repository", "branch")
198+
if err != nil {
199+
return err
200+
}
201+
newBranchName := d.Get("branch").(string)
202+
203+
if _, _, err := client.Repositories.RenameBranch(ctx, orgName, repoName, oldBranchName, newBranchName); err != nil {
204+
return fmt.Errorf("error renaming GitHub branch %s/%s (%s -> %s): %w", orgName, repoName, oldBranchName, newBranchName, err)
205+
}
206+
207+
d.SetId(buildTwoPartID(repoName, newBranchName))
208+
209+
return resourceGithubBranchRead(d, meta)
210+
}
211+
189212
func resourceGithubBranchImport(d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) {
190213
repoName, branchName, err := parseTwoPartID(d.Id(), "repository", "branch")
191214
if err != nil {

github/resource_github_branch_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,4 +201,62 @@ func TestAccGithubBranch(t *testing.T) {
201201
testCase(t, organization)
202202
})
203203
})
204+
205+
t.Run("renames a branch without replacement", func(t *testing.T) {
206+
initialConfig := fmt.Sprintf(`
207+
resource "github_repository" "test" {
208+
name = "tf-acc-test-%[1]s"
209+
auto_init = true
210+
}
211+
212+
resource "github_branch" "test" {
213+
repository = github_repository.test.id
214+
branch = "initial"
215+
}
216+
`, randomID)
217+
218+
renamedConfig := fmt.Sprintf(`
219+
resource "github_repository" "test" {
220+
name = "tf-acc-test-%[1]s"
221+
auto_init = true
222+
}
223+
224+
resource "github_branch" "test" {
225+
repository = github_repository.test.id
226+
branch = "renamed"
227+
}
228+
`, randomID)
229+
230+
testCase := func(t *testing.T, mode string) {
231+
resource.Test(t, resource.TestCase{
232+
PreCheck: func() { skipUnlessMode(t, mode) },
233+
Providers: testAccProviders,
234+
Steps: []resource.TestStep{
235+
{
236+
Config: initialConfig,
237+
},
238+
{
239+
Config: renamedConfig,
240+
Check: resource.ComposeTestCheckFunc(
241+
resource.TestCheckResourceAttr(
242+
"github_branch.test", "branch", "renamed",
243+
),
244+
),
245+
},
246+
},
247+
})
248+
}
249+
250+
t.Run("with an anonymous account", func(t *testing.T) {
251+
t.Skip("anonymous account not supported for this operation")
252+
})
253+
254+
t.Run("with an individual account", func(t *testing.T) {
255+
testCase(t, individual)
256+
})
257+
258+
t.Run("with an organization account", func(t *testing.T) {
259+
testCase(t, organization)
260+
})
261+
})
204262
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package github
2+
3+
import "testing"
4+
5+
func TestGithubBranchIsUpdatedWhenBranchChanges(t *testing.T) {
6+
resource := resourceGithubBranch()
7+
8+
branchSchema := resource.Schema["branch"]
9+
if branchSchema == nil {
10+
t.Fatal("branch field should exist in schema")
11+
}
12+
if branchSchema.ForceNew {
13+
t.Error("branch field should not be ForceNew so renames are handled via update")
14+
}
15+
}
16+
17+
func TestGithubBranchIsRecreatedWhenRepositoryChanges(t *testing.T) {
18+
resource := resourceGithubBranch()
19+
20+
repositorySchema := resource.Schema["repository"]
21+
if repositorySchema == nil {
22+
t.Fatal("repository field should exist in schema")
23+
}
24+
if !repositorySchema.ForceNew {
25+
t.Error("repository field should be ForceNew so changes recreate the resource")
26+
}
27+
}
28+
29+
func TestGithubBranchIsRecreatedWhenSourceBranchChanges(t *testing.T) {
30+
resource := resourceGithubBranch()
31+
32+
sourceBranchSchema := resource.Schema["source_branch"]
33+
if sourceBranchSchema == nil {
34+
t.Fatal("source_branch field should exist in schema")
35+
}
36+
if !sourceBranchSchema.ForceNew {
37+
t.Error("source_branch field should be ForceNew so changes recreate the resource")
38+
}
39+
}
40+
41+
func TestGithubBranchIsRecreatedWhenSourceSHAChanges(t *testing.T) {
42+
resource := resourceGithubBranch()
43+
44+
sourceSHASchema := resource.Schema["source_sha"]
45+
if sourceSHASchema == nil {
46+
t.Fatal("source_sha field should exist in schema")
47+
}
48+
if !sourceSHASchema.ForceNew {
49+
t.Error("source_sha field should be ForceNew so changes recreate the resource")
50+
}
51+
}

0 commit comments

Comments
 (0)