From 998c86b6d25a8764c420715b97c0fa95e952b3a2 Mon Sep 17 00:00:00 2001 From: Joe Stump Date: Tue, 2 Jul 2019 14:18:41 -0700 Subject: [PATCH 1/7] Introduce a working github_repository_prereceive_hook resource. --- github/provider.go | 37 ++-- ...ource_github_repository_prereceive_hook.go | 160 ++++++++++++++++++ 2 files changed, 179 insertions(+), 18 deletions(-) create mode 100644 github/resource_github_repository_prereceive_hook.go diff --git a/github/provider.go b/github/provider.go index 9eb9d0086c..b30078fe56 100644 --- a/github/provider.go +++ b/github/provider.go @@ -42,24 +42,25 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ - "github_branch_protection": resourceGithubBranchProtection(), - "github_issue_label": resourceGithubIssueLabel(), - "github_membership": resourceGithubMembership(), - "github_organization_block": resourceOrganizationBlock(), - "github_organization_project": resourceGithubOrganizationProject(), - "github_organization_webhook": resourceGithubOrganizationWebhook(), - "github_project_column": resourceGithubProjectColumn(), - "github_repository_collaborator": resourceGithubRepositoryCollaborator(), - "github_repository_deploy_key": resourceGithubRepositoryDeployKey(), - "github_repository_project": resourceGithubRepositoryProject(), - "github_repository_webhook": resourceGithubRepositoryWebhook(), - "github_repository": resourceGithubRepository(), - "github_team_membership": resourceGithubTeamMembership(), - "github_team_repository": resourceGithubTeamRepository(), - "github_team": resourceGithubTeam(), - "github_user_gpg_key": resourceGithubUserGpgKey(), - "github_user_invitation_accepter": resourceGithubUserInvitationAccepter(), - "github_user_ssh_key": resourceGithubUserSshKey(), + "github_branch_protection": resourceGithubBranchProtection(), + "github_issue_label": resourceGithubIssueLabel(), + "github_membership": resourceGithubMembership(), + "github_organization_block": resourceOrganizationBlock(), + "github_organization_project": resourceGithubOrganizationProject(), + "github_organization_webhook": resourceGithubOrganizationWebhook(), + "github_project_column": resourceGithubProjectColumn(), + "github_repository_collaborator": resourceGithubRepositoryCollaborator(), + "github_repository_deploy_key": resourceGithubRepositoryDeployKey(), + "github_repository_project": resourceGithubRepositoryProject(), + "github_repository_webhook": resourceGithubRepositoryWebhook(), + "github_repository_prereceive_hook": resourceGithubRepositoryPreReceiveHook(), + "github_repository": resourceGithubRepository(), + "github_team_membership": resourceGithubTeamMembership(), + "github_team_repository": resourceGithubTeamRepository(), + "github_team": resourceGithubTeam(), + "github_user_gpg_key": resourceGithubUserGpgKey(), + "github_user_invitation_accepter": resourceGithubUserInvitationAccepter(), + "github_user_ssh_key": resourceGithubUserSshKey(), }, DataSourcesMap: map[string]*schema.Resource{ diff --git a/github/resource_github_repository_prereceive_hook.go b/github/resource_github_repository_prereceive_hook.go new file mode 100644 index 0000000000..9b15701f3e --- /dev/null +++ b/github/resource_github_repository_prereceive_hook.go @@ -0,0 +1,160 @@ +package github + +import ( + "context" + "fmt" + "strconv" + + "github.com/google/go-github/v25/github" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" +) + +func resourceGithubRepositoryPreReceiveHook() *schema.Resource { + return &schema.Resource{ + Create: resourceGithubRepositoryPreReceiveHookCreate, + Read: resourceGithubRepositoryPreReceiveHookRead, + Update: resourceGithubRepositoryPreReceiveHookCreate, + Delete: resourceGithubRepositoryPreReceiveHookDelete, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "repository": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "enforcement": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"enabled", "disabled", "testing"}, false), + }, + "config_url": { + Type: schema.TypeString, + Optional: true, + }, + }, + } +} + +func fetchGitHubRepositoryPreReceiveHookByName(meta interface{}, repoName, hookName string) (*github.PreReceiveHook, error) { + ctx := context.Background() + client := meta.(*Organization).client + orgName := meta.(*Organization).name + + opt := &github.ListOptions{ + PerPage: 100, + } + + var hook *github.PreReceiveHook + + for { + hooks, resp, err := client.Repositories.ListPreReceiveHooks(ctx, orgName, repoName, opt) + if err != nil { + return nil, err + } + + for _, h := range hooks { + n := *h.Name + if n == hookName { + hook = h + break + } + } + + if resp.NextPage == 0 { + break + } + opt.Page = resp.NextPage + } + + if *hook.ID <= 0 { + return nil, fmt.Errorf("No pre-receive hook with name %s found on %s/%s", hookName, orgName, repoName) + } + + return hook, nil +} + +func resourceGithubRepositoryPreReceiveHookCreate(d *schema.ResourceData, meta interface{}) error { + err := checkOrganization(meta) + if err != nil { + return err + } + + repoName := d.Get("repository").(string) + hookName := d.Get("name").(string) + + hook, err := fetchGitHubRepositoryPreReceiveHookByName(meta, repoName, hookName) + if err != nil { + return err + } + + enforcement := d.Get("enforcement").(string) + hook.Enforcement = &enforcement + + configURL := d.Get("config_url").(string) + hook.ConfigURL = &configURL + + ctx := context.Background() + client := meta.(*Organization).client + orgName := meta.(*Organization).name + _, _, err = client.Repositories.UpdatePreReceiveHook(ctx, orgName, repoName, *hook.ID, hook) + if err != nil { + return err + } + + d.SetId(fmt.Sprintf("%s-%s-%s", orgName, repoName, strconv.FormatInt(*hook.ID, 10))) + + return resourceGithubRepositoryPreReceiveHookRead(d, meta) +} + +func resourceGithubRepositoryPreReceiveHookRead(d *schema.ResourceData, meta interface{}) error { + err := checkOrganization(meta) + if err != nil { + return err + } + + repoName := d.Get("repository").(string) + hookName := d.Get("name").(string) + + hook, err := fetchGitHubRepositoryPreReceiveHookByName(meta, repoName, hookName) + if err != nil { + return err + } + + d.Set("enforcement", hook.Enforcement) + d.Set("config_url", hook.ConfigURL) + + return nil +} + +func resourceGithubRepositoryPreReceiveHookDelete(d *schema.ResourceData, meta interface{}) error { + err := checkOrganization(meta) + if err != nil { + return err + } + + repoName := d.Get("repository").(string) + hookName := d.Get("name").(string) + + hook, err := fetchGitHubRepositoryPreReceiveHookByName(meta, repoName, hookName) + if err != nil { + return err + } + + disabled := "disabled" + hook.Enforcement = &disabled + + ctx := context.Background() + client := meta.(*Organization).client + orgName := meta.(*Organization).name + _, _, err = client.Repositories.UpdatePreReceiveHook(ctx, orgName, repoName, *hook.ID, hook) + if err != nil { + return err + } + + return nil +} From ad7456efaa5648a93224a5f5c43c55207417098a Mon Sep 17 00:00:00 2001 From: Joe Stump Date: Wed, 3 Jul 2019 10:02:32 -0700 Subject: [PATCH 2/7] Add an acceptance test and address a dirty plan bug. --- ...ource_github_repository_prereceive_hook.go | 13 ++- ..._github_repository_prereceive_hook_test.go | 96 +++++++++++++++++++ 2 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 github/resource_github_repository_prereceive_hook_test.go diff --git a/github/resource_github_repository_prereceive_hook.go b/github/resource_github_repository_prereceive_hook.go index 9b15701f3e..44062270f2 100644 --- a/github/resource_github_repository_prereceive_hook.go +++ b/github/resource_github_repository_prereceive_hook.go @@ -95,8 +95,10 @@ func resourceGithubRepositoryPreReceiveHookCreate(d *schema.ResourceData, meta i enforcement := d.Get("enforcement").(string) hook.Enforcement = &enforcement - configURL := d.Get("config_url").(string) - hook.ConfigURL = &configURL + if v, ok := d.GetOk("config_url"); ok { + configURL := v.(string) + hook.ConfigURL = &configURL + } ctx := context.Background() client := meta.(*Organization).client @@ -106,7 +108,7 @@ func resourceGithubRepositoryPreReceiveHookCreate(d *schema.ResourceData, meta i return err } - d.SetId(fmt.Sprintf("%s-%s-%s", orgName, repoName, strconv.FormatInt(*hook.ID, 10))) + d.SetId(fmt.Sprintf("%s/%s/%s", orgName, repoName, strconv.FormatInt(*hook.ID, 10))) return resourceGithubRepositoryPreReceiveHookRead(d, meta) } @@ -126,7 +128,10 @@ func resourceGithubRepositoryPreReceiveHookRead(d *schema.ResourceData, meta int } d.Set("enforcement", hook.Enforcement) - d.Set("config_url", hook.ConfigURL) + + if _, ok := d.GetOk("config_url"); ok { + d.Set("config_url", hook.ConfigURL) + } return nil } diff --git a/github/resource_github_repository_prereceive_hook_test.go b/github/resource_github_repository_prereceive_hook_test.go new file mode 100644 index 0000000000..5fdebc21e6 --- /dev/null +++ b/github/resource_github_repository_prereceive_hook_test.go @@ -0,0 +1,96 @@ +package github + +import ( + "context" + "fmt" + "strconv" + "strings" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccGithubRepositoryPreReceiveHook_basic(t *testing.T) { + randString := acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum) + enforcement := "enabled" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccGithubRepositoryPreReceiveHookConfig_basic(randString), + Check: resource.ComposeTestCheckFunc( + testAccCheckGitHubPreReceiveHookEnforcement("github_repository_prereceive_hook.foo", enforcement), + resource.TestCheckResourceAttr( + "github_repository_prereceive_hook.foo", "enforcement", enforcement), + resource.TestCheckResourceAttrSet( + "github_repository_prereceive_hook.foo", "id"), + ), + }, + }, + }) +} + +func testAccCheckGitHubPreReceiveHookEnforcement(n string, enforcement string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No hook ID is set") + } + + id := strings.Split(rs.Primary.ID, "/") + orgName, repoName, i := id[0], id[1], id[2] + + hookID, err := strconv.ParseInt(i, 10, 64) + if err != nil { + return err + } + + org := testAccProvider.Meta().(*Organization) + client := org.client + + hook, _, err := client.Repositories.GetPreReceiveHook(context.TODO(), orgName, repoName, hookID) + + if err != nil { + return err + } + + if *hook.Enforcement != enforcement { + return fmt.Errorf("Enforcement set to %s instead of %s", *hook.Enforcement, enforcement) + } + + return nil + } +} + +func testAccGithubRepositoryPreReceiveHookConfig_basic(randString string) string { + return fmt.Sprintf(` +resource "github_repository" "foo" { + name = "foo-%s" + description = "Terraform acceptance tests" + homepage_url = "http://example.com/" + + # So that acceptance tests can be run in a github organization + # with no billing + private = false + + has_issues = true + has_wiki = true + has_downloads = true +} + +resource "github_repository_prereceive_hook" "foo" { + name = "require-code-review" + repository = "${github_repository.foo.name}" + enforcement = "enabled" +} +`, randString) +} From 2a9f524de6fc73d5f039061548df2b25c0a4a8c3 Mon Sep 17 00:00:00 2001 From: Joe Stump Date: Wed, 3 Jul 2019 10:14:48 -0700 Subject: [PATCH 3/7] Add docs for the new resource --- .../docs/r/repository_prereceive_hook.html | 41 +++++++++++++++++++ website/github.erb | 3 ++ 2 files changed, 44 insertions(+) create mode 100644 website/docs/r/repository_prereceive_hook.html diff --git a/website/docs/r/repository_prereceive_hook.html b/website/docs/r/repository_prereceive_hook.html new file mode 100644 index 0000000000..d18530d535 --- /dev/null +++ b/website/docs/r/repository_prereceive_hook.html @@ -0,0 +1,41 @@ +--- +layout: "github" +page_title: "GitHub: github_repository_prereceive_hook" +sidebar_current: "docs-github-resource-repository-prereceive-hook" +description: |- + Creates and manages repository pre-recieve hooks within a GitHub Enterprise organization +--- + +# github_repository_prereceive_hook + +This resource allows you to create and manage [pre-receive hooks](https://developer.github.com/enterprise/2.17/v3/enterprise-admin/repo_pre_receive_hooks/) for +repositories within your GitHub Enterprise organizations. + +~> **NOTE** Pre-recieve hooks are only available on GitHub Enterprise. + +## Example Usage + +```hcl +resource "github_repository" "repo" { + name = "foo" + description = "Terraform acceptance tests" +} + +resource "github_repository_prereceive_hook" "reviews" { + name = "require-code-review" + repository = "${github_repository.repo.name}" + enforcement = "enabled" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the pre-receive hook. + +* `repository` - (Required) The repository of the pre-receive hook. + +* `enforcement` - (Required) The state of enforcement for the hook on this repository. Available keys are `enabled`, `disabled`, and `testing`. + +* `config_url` - (Optional) URL for the endpoint where enforcement is set. diff --git a/website/github.erb b/website/github.erb index 7421b267b7..60fd3e4745 100644 --- a/website/github.erb +++ b/website/github.erb @@ -67,6 +67,9 @@ > github_repository_deploy_key + > + github_repository_prereceive_hook + > github_repository_project From 3f53e386ff2f9f5571ca1564f212761eed1a27f0 Mon Sep 17 00:00:00 2001 From: Joe Stump Date: Wed, 3 Jul 2019 14:38:39 -0700 Subject: [PATCH 4/7] Update CHANGELOG --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87d45f9198..a60d75d23d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ ## 2.3.0 (Unreleased) + +FEATURES: + +* **New Resource:** `github_repository_prereceive_hook` ([#252](https://github.com/terraform-providers/terraform-provider-github/issues/252)) + ## 2.2.0 (June 28, 2019) FEATURES: From 07752d6235035ea31148b180b633bb9acf488de9 Mon Sep 17 00:00:00 2001 From: Joe Stump Date: Thu, 18 Jul 2019 17:21:50 -0400 Subject: [PATCH 5/7] Update github/resource_github_repository_prereceive_hook.go Co-Authored-By: Tracy P Holmes <12778804+tracypholmes@users.noreply.github.com> --- github/resource_github_repository_prereceive_hook.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/github/resource_github_repository_prereceive_hook.go b/github/resource_github_repository_prereceive_hook.go index 44062270f2..c25902fafc 100644 --- a/github/resource_github_repository_prereceive_hook.go +++ b/github/resource_github_repository_prereceive_hook.go @@ -157,7 +157,7 @@ func resourceGithubRepositoryPreReceiveHookDelete(d *schema.ResourceData, meta i client := meta.(*Organization).client orgName := meta.(*Organization).name _, _, err = client.Repositories.UpdatePreReceiveHook(ctx, orgName, repoName, *hook.ID, hook) - if err != nil { + if _, _, err = client.Repositories.UpdatePreReceiveHook(ctx, orgName, repoName, *hook.ID, hook); err != nil { return err } From 75175896d38fe82127651c0902b845389f919f61 Mon Sep 17 00:00:00 2001 From: Joe Stump Date: Thu, 18 Jul 2019 14:23:10 -0700 Subject: [PATCH 6/7] Remove CHANGELOG edit --- CHANGELOG.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a60d75d23d..f7bae73b4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,5 @@ ## 2.3.0 (Unreleased) -FEATURES: - -* **New Resource:** `github_repository_prereceive_hook` ([#252](https://github.com/terraform-providers/terraform-provider-github/issues/252)) - ## 2.2.0 (June 28, 2019) FEATURES: From 4d222eeab7938a04522fdfce6a80c2551aeecd2f Mon Sep 17 00:00:00 2001 From: Joe Stump Date: Thu, 18 Jul 2019 14:26:32 -0700 Subject: [PATCH 7/7] Clarify function is used for both create and update. --- github/resource_github_repository_prereceive_hook.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/github/resource_github_repository_prereceive_hook.go b/github/resource_github_repository_prereceive_hook.go index 44062270f2..fc1b4b1d5a 100644 --- a/github/resource_github_repository_prereceive_hook.go +++ b/github/resource_github_repository_prereceive_hook.go @@ -12,9 +12,9 @@ import ( func resourceGithubRepositoryPreReceiveHook() *schema.Resource { return &schema.Resource{ - Create: resourceGithubRepositoryPreReceiveHookCreate, + Create: resourceGithubRepositoryPreReceiveHookCreateUpdate, Read: resourceGithubRepositoryPreReceiveHookRead, - Update: resourceGithubRepositoryPreReceiveHookCreate, + Update: resourceGithubRepositoryPreReceiveHookCreateUpdate, Delete: resourceGithubRepositoryPreReceiveHookDelete, Schema: map[string]*schema.Schema{ "name": { @@ -78,7 +78,7 @@ func fetchGitHubRepositoryPreReceiveHookByName(meta interface{}, repoName, hookN return hook, nil } -func resourceGithubRepositoryPreReceiveHookCreate(d *schema.ResourceData, meta interface{}) error { +func resourceGithubRepositoryPreReceiveHookCreateUpdate(d *schema.ResourceData, meta interface{}) error { err := checkOrganization(meta) if err != nil { return err