diff --git a/github/provider.go b/github/provider.go index b363e82687..c1aa42cc99 100644 --- a/github/provider.go +++ b/github/provider.go @@ -66,6 +66,7 @@ func Provider() terraform.ResourceProvider { "github_user_gpg_key": resourceGithubUserGpgKey(), "github_user_invitation_accepter": resourceGithubUserInvitationAccepter(), "github_user_ssh_key": resourceGithubUserSshKey(), + "github_branch_default": resourceGithubBranchDefault(), }, DataSourcesMap: map[string]*schema.Resource{ diff --git a/github/resource_github_branch_default.go b/github/resource_github_branch_default.go new file mode 100644 index 0000000000..893209796b --- /dev/null +++ b/github/resource_github_branch_default.go @@ -0,0 +1,126 @@ +package github + +import ( + "context" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func resourceGithubBranchDefault() *schema.Resource { + return &schema.Resource{ + Create: resourceGithubBranchDefaultCreate, + Read: resourceGithubBranchDefaultRead, + Delete: resourceGithubBranchDefaultDelete, + Update: resourceGithubBranchDefaultUpdate, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "branch": { + Type: schema.TypeString, + Required: true, + }, + "repository": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceGithubBranchDefaultCreate(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*Owner).v3client + owner := meta.(*Owner).name + repoName := d.Get("repository").(string) + defaultBranch := d.Get("branch").(string) + + ctx := context.Background() + + repository, _, err := client.Repositories.Get(ctx, owner, repoName) + if err != nil { + return err + } + + repository.DefaultBranch = &defaultBranch + + log.Printf("[DEBUG] Creating branch default: %s (%s/%s)", defaultBranch, owner, repoName) + if _, _, err := client.Repositories.Edit(ctx, owner, repoName, repository); err != nil { + return err + } + + d.SetId(repoName) + + return resourceGithubBranchDefaultRead(d, meta) +} + +func resourceGithubBranchDefaultRead(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*Owner).v3client + owner := meta.(*Owner).name + repoName := d.Id() + + ctx := context.WithValue(context.Background(), ctxId, d.Id()) + + repository, _, err := client.Repositories.Get(ctx, owner, repoName) + if err != nil { + return err + } + + if repository.DefaultBranch == nil { + d.SetId("") + return nil + } + + d.Set("branch", *repository.DefaultBranch) + d.Set("repository", *repository.Name) + return nil +} + +func resourceGithubBranchDefaultDelete(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*Owner).v3client + owner := meta.(*Owner).name + repoName := d.Id() + defaultBranch := d.Get("branch").(string) + + ctx := context.Background() + + repository, _, err := client.Repositories.Get(ctx, owner, repoName) + if err != nil { + return err + } + + repository.DefaultBranch = nil + + log.Printf("[DEBUG] Removing branch default: %s (%s/%s)", defaultBranch, owner, repoName) + _, _, err = client.Repositories.Edit(ctx, owner, repoName, repository) + return err +} + +func resourceGithubBranchDefaultUpdate(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*Owner).v3client + owner := meta.(*Owner).name + repoName := d.Id() + defaultBranch := d.Get("branch").(string) + + ctx := context.Background() + + repository, _, err := client.Repositories.Get(ctx, owner, repoName) + if err != nil { + return err + } + + repository.DefaultBranch = &defaultBranch + + log.Printf("[DEBUG] Updating branch default: %s (%s/%s)", defaultBranch, owner, repoName) + if _, _, err := client.Repositories.Edit(ctx, owner, repoName, repository); err != nil { + return err + } + + return resourceGithubBranchDefaultRead(d, meta) +} diff --git a/github/resource_github_branch_default_test.go b/github/resource_github_branch_default_test.go new file mode 100644 index 0000000000..f01b67c87b --- /dev/null +++ b/github/resource_github_branch_default_test.go @@ -0,0 +1,121 @@ +package github + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccGithubBranchDefault(t *testing.T) { + + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + + t.Run("creates and manages branch defaults", func(t *testing.T) { + + config := fmt.Sprintf(` + + resource "github_repository" "test" { + name = "tf-acc-test-%s" + auto_init = true + } + + resource "github_branch_default" "test" { + repository = github_repository.test.name + branch = "main" + } + `, randomID) + + check := resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "github_branch_default.test", "branch", + "main", + ), + resource.TestCheckResourceAttr( + "github_branch_default.test", "repository", + fmt.Sprintf("tf-acc-test-%s", randomID), + ), + ) + + testCase := func(t *testing.T, mode string) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, mode) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: check, + }, + }, + }) + } + + 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) + }) + + }) + + t.Run("can be configured to override the default_branch of the repository", func(t *testing.T) { + + config := fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-test-%s" + default_branch = "main" + auto_init = true + } + + resource "github_branch_default" "test" { + repository = github_repository.test.name + branch = "override" + } + + `, randomID) + + check := resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "github_branch_default.test", "branch", + "override", + ), + resource.TestCheckResourceAttr( + "github_branch_default.test", "repository", + fmt.Sprintf("tf-acc-test-%s", randomID), + ), + ) + + testCase := func(t *testing.T, mode string) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, mode) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: check, + }, + }, + }) + } + + 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) + }) + + }) +} diff --git a/github/resource_github_repository.go b/github/resource_github_repository.go index d6cdc095ff..8f06df695d 100644 --- a/github/resource_github_repository.go +++ b/github/resource_github_repository.go @@ -102,6 +102,7 @@ func resourceGithubRepository() *schema.Resource { Optional: true, Computed: true, Description: "Can only be set after initial repository creation, and only if the target branch exists", + Deprecated: "Use the github_branch_default resource instead", }, "license_template": { Type: schema.TypeString, diff --git a/website/docs/r/branch_default.html.markdown b/website/docs/r/branch_default.html.markdown new file mode 100644 index 0000000000..d9885c1b6b --- /dev/null +++ b/website/docs/r/branch_default.html.markdown @@ -0,0 +1,53 @@ +--- +layout: "github" +page_title: "GitHub: github_branch_default" +description: |- + Provides a GitHub branch default for a given repository. +--- + +# github_branch_default + +Provides a GitHub branch default resource. + +This resource allows you to set the default branch for a given repository. + +## Example Usage + +```hcl +resource "github_repository" "example" { + name = "example" + description = "My awesome codebase" + + private = true + + template { + owner = "github" + repository = "terraform-module-template" + } +} + +resource "github_branch" "development" { + repository = github_repository.example.name + branch = "development" +} + +resource "github_branch_default" "default"{ + repository = github_repository.example.name + branch = github_branch.development.branch +} +``` + +## Argument Reference + +The following arguments are supported: + +* `repository` - (Required) The GitHub repository +* `branch` - (Required) The branch (e.g. `main`) + +## Import + +GitHub Branch Defaults can be imported using an ID made up of `repository`, e.g. + +``` +$ terraform import github_branch_default.branch_default my-repo +``` diff --git a/website/docs/r/repository.html.markdown b/website/docs/r/repository.html.markdown index 8ec527b616..de1f26629b 100644 --- a/website/docs/r/repository.html.markdown +++ b/website/docs/r/repository.html.markdown @@ -67,7 +67,7 @@ The following arguments are supported: * `license_template` - (Optional) Use the [name of the template](https://github.com/github/choosealicense.com/tree/gh-pages/_licenses) without the extension. For example, "mit" or "mpl-2.0". -* `default_branch` - (Optional) The name of the default branch of the repository. **NOTE:** This can only be set after a repository has already been created, +* `default_branch` - (Optional) (Deprecated: Use `github_branch_default` resource instead) The name of the default branch of the repository. **NOTE:** This can only be set after a repository has already been created, and after a correct reference has been created for the target branch inside the repository. This means a user will have to omit this parameter from the initial repository creation and create the target branch inside of the repository prior to setting this attribute.