Skip to content

feat: add resource github_repository_pre_receive_hook #2663

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ export GITHUB_BASE_URL=
export GITHUB_TEST_OWNER=
export GITHUB_TEST_ORGANIZATION=
export GITHUB_TEST_USER_TOKEN=
export GITHUB_TEST_PRE_RECEIVE_HOOK=
```

See [this project](https://github.com/terraformtesting/acceptance-tests) for more information on our old system for automated testing.
Expand Down
1 change: 1 addition & 0 deletions github/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ func Provider() *schema.Provider {
"github_repository_milestone": resourceGithubRepositoryMilestone(),
"github_repository_project": resourceGithubRepositoryProject(),
"github_repository_pull_request": resourceGithubRepositoryPullRequest(),
"github_repository_pre_receive_hook": resourceGithubRepositoryPreReceiveHook(),
"github_repository_ruleset": resourceGithubRepositoryRuleset(),
"github_repository_topics": resourceGithubRepositoryTopics(),
"github_repository_webhook": resourceGithubRepositoryWebhook(),
Expand Down
13 changes: 13 additions & 0 deletions github/provider_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ func skipUnlessMode(t *testing.T, providerMode string) {
t.Skipf("Skipping %s which requires %s mode", t.Name(), providerMode)
}

func skipUnlessEnterpriseServer(t *testing.T) {
if os.Getenv("GITHUB_BASE_URL") == "" || os.Getenv("GITHUB_BASE_URL") == "https://api.github.com/" {
t.Skipf("Skipping %s which requires GitHub Enterprise Server", t.Name())
}
}

func testAccCheckOrganization() error {

baseURL := os.Getenv("GITHUB_BASE_URL")
Expand Down Expand Up @@ -130,6 +136,13 @@ func testOwnerFunc() string {
return owner
}

func testPreReceiveHookFunc() string {
hookName := os.Getenv("GITHUB_TEST_PRE_RECEIVE_HOOK")
log.Printf("[INFO] Selecting pre-receive hook name '%s' from GITHUB_TEST_PRE_RECEIVE_HOOK environment variable", hookName)

return hookName
}

const anonymous = "anonymous"
const individual = "individual"
const organization = "organization"
Expand Down
178 changes: 178 additions & 0 deletions github/resource_github_repository_pre_receive_hook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package github

import (
"context"
"fmt"
"strconv"

"github.com/google/go-github/v66/github"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

func resourceGithubRepositoryPreReceiveHook() *schema.Resource {
return &schema.Resource{
Create: resourceGithubRepositoryPreReceiveHookCreate,
Read: resourceGithubRepositoryPreReceiveHookRead,
Update: resourceGithubRepositoryPreReceiveHookUpdate,
Delete: resourceGithubRepositoryPreReceiveHookDelete,
Schema: map[string]*schema.Schema{
"repository": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "The repository of the pre-receive hook.",
},
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "The name of the pre-receive hook.",
},
"enforcement": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{"enabled", "disabled", "testing"}, false),
Description: "The state of enforcement for the hook on the repository. Possible values for enforcement are 'enabled', 'disabled' and 'testing'. 'disabled' indicates the pre-receive hook will not run. 'enabled' indicates it will run and reject any pushes that result in a non-zero status. 'testing' means the script will run but will not cause any pushes to be rejected.",
},
"configuration_url": {
Type: schema.TypeString,
Computed: true,
Description: "The URL for the endpoint where enforcement is set.",
},
},
}
}

func resourceGithubRepositoryPreReceiveHookCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Owner).v3client

owner := meta.(*Owner).name
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

ctx := context.WithValue(context.Background(), ctxId, d.Id())

_, _, err = client.Repositories.UpdatePreReceiveHook(ctx, owner, repoName, hook.GetID(), hook)
if err != nil {
return err
}
d.SetId(strconv.FormatInt(hook.GetID(), 10))

return resourceGithubRepositoryPreReceiveHookRead(d, meta)
}

func resourceGithubRepositoryPreReceiveHookRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Owner).v3client
ctx := context.WithValue(context.Background(), ctxId, d.Id())

owner := meta.(*Owner).name
repoName := d.Get("repository").(string)

hookID, err := strconv.ParseInt(d.Id(), 10, 64)
if err != nil {
return unconvertibleIdErr(d.Id(), err)
}

hook, _, err := client.Repositories.GetPreReceiveHook(ctx, owner, repoName, hookID)
if err != nil {
return err
}
if err = d.Set("enforcement", hook.Enforcement); err != nil {
return err
}
if err = d.Set("configuration_url", hook.ConfigURL); err != nil {
return err
}

return nil
}

func resourceGithubRepositoryPreReceiveHookUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Owner).v3client
ctx := context.WithValue(context.Background(), ctxId, d.Id())

owner := meta.(*Owner).name
repoName := d.Get("repository").(string)
enforcement := d.Get("enforcement").(string)

hookID, err := strconv.ParseInt(d.Id(), 10, 64)
if err != nil {
return unconvertibleIdErr(d.Id(), err)
}

hook := &github.PreReceiveHook{
Enforcement: &enforcement,
}
_, _, err = client.Repositories.UpdatePreReceiveHook(ctx, owner, repoName, hookID, hook)
if err != nil {
return err
}

return resourceGithubRepositoryPreReceiveHookRead(d, meta)
}

func resourceGithubRepositoryPreReceiveHookDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Owner).v3client
ctx := context.WithValue(context.Background(), ctxId, d.Id())

owner := meta.(*Owner).name
repoName := d.Get("repository").(string)

hookID, err := strconv.ParseInt(d.Id(), 10, 64)
if err != nil {
return unconvertibleIdErr(d.Id(), err)
}
_, err = client.Repositories.DeletePreReceiveHook(ctx, owner, repoName, hookID)
if err != nil {
return err
}

return nil
}

func fetchGitHubRepositoryPreReceiveHookByName(meta interface{}, repoName, hookName string) (*github.PreReceiveHook, error) {
ctx := context.Background()
client := meta.(*Owner).v3client
owner := meta.(*Owner).name

opt := &github.ListOptions{
PerPage: 100,
}

var hook *github.PreReceiveHook

for {
hooks, resp, err := client.Repositories.ListPreReceiveHooks(ctx, owner, 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, owner, repoName)
}

return hook, nil
}
59 changes: 59 additions & 0 deletions github/resource_github_repository_pre_receive_hook_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package github

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccGithubRepositoryPreReceiveHook_basic(t *testing.T) {
skipUnlessEnterpriseServer(t)

randString := acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccGithubRepositoryPreReceiveHookConfig(randString, "enabled"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"github_repository_pre_receive_hook.test", "enforcement", "enabled"),
resource.TestCheckResourceAttrSet(
"github_repository_pre_receive_hook.test", "id"),
resource.TestCheckResourceAttrSet(
"github_repository_pre_receive_hook.test", "configuration_url"),
),
},
{
Config: testAccGithubRepositoryPreReceiveHookConfig(randString, "disabled"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"github_repository_pre_receive_hook.test", "enforcement", "disabled"),
resource.TestCheckResourceAttrSet(
"github_repository_pre_receive_hook.test", "id"),
resource.TestCheckResourceAttrSet(
"github_repository_pre_receive_hook.test", "configuration_url"),
),
},
},
})
}

func testAccGithubRepositoryPreReceiveHookConfig(randString string, enforcement string) string {
return fmt.Sprintf(`
resource "github_repository" "test" {
name = "foo-%[1]s"
description = "Terraform acceptance tests"
}

resource "github_repository_pre_receive_hook" "test" {
name = "%[2]s"
repository = github_repository.test.name
enforcement = "%[3]s"
}
`, randString, testPreReceiveHookFunc(), enforcement)
}
38 changes: 38 additions & 0 deletions website/docs/r/repository_pre_receive_hook.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
layout: "github"
page_title: "GitHub: github_repository_pre_receive_hook"
description: |-
Creates and manages repository pre-receive hooks.
---

# github_repository_pre_receive_hook

This resource allows you to create and manage pre-receive hooks for repositories.

~> **Note** Repository pre-receive hooks are currently only available in GitHub Enterprise Server.

## Example usage

```
resource "github_repository_pre_receive_hook" "example" {
repository = "test-repo"
name = "ensure-conventional-commits"
enforcement = "enabled"
}
```

## Argument Reference

The following arguments are supported:

* `repository` - (Required) The repository of the pre-receive hook.

* `name` - (Required) The name of the pre-receive hook.

* `enforcement` - (Required) The state of enforcement for the hook on the repository. Possible values for enforcement are `enabled`, `disabled` and `testing`. `disabled` indicates the pre-receive hook will not run. `enabled` indicates it will run and reject any pushes that result in a non-zero status. `testing` means the script will run but will not cause any pushes to be rejected.

## Attributes Reference

The following additional attributes are exported:

* `configuration_url` - The URL for the endpoint where enforcement is set.
3 changes: 3 additions & 0 deletions website/github.erb
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,9 @@
<li>
<a href="/docs/providers/github/r/repository_milestone.html">github_repository_milestone</a>
</li>
<li>
<a href="/docs/providers/github/r/repository_pre_receive_hook.html">github_repository_pre_receive_hook</a>
</li>
<li>
<a href="/docs/providers/github/r/repository_project.html">github_repository_project</a>
</li>
Expand Down
Loading