Skip to content

Commit 850a2be

Browse files
feat: add resource github_repository_pre_receive_hook
Co-Authored-by: Joe Stump <[email protected]>
1 parent 6008909 commit 850a2be

7 files changed

+293
-0
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ export GITHUB_BASE_URL=
156156
export GITHUB_TEST_OWNER=
157157
export GITHUB_TEST_ORGANIZATION=
158158
export GITHUB_TEST_USER_TOKEN=
159+
export GITHUB_TEST_PRE_RECEIVE_HOOK=
159160
```
160161

161162
See [this project](https://github.com/terraformtesting/acceptance-tests) for more information on our old system for automated testing.

github/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ func Provider() *schema.Provider {
181181
"github_repository_milestone": resourceGithubRepositoryMilestone(),
182182
"github_repository_project": resourceGithubRepositoryProject(),
183183
"github_repository_pull_request": resourceGithubRepositoryPullRequest(),
184+
"github_repository_pre_receive_hook": resourceGithubRepositoryPreReceiveHook(),
184185
"github_repository_ruleset": resourceGithubRepositoryRuleset(),
185186
"github_repository_topics": resourceGithubRepositoryTopics(),
186187
"github_repository_webhook": resourceGithubRepositoryWebhook(),

github/provider_utils.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ func skipUnlessMode(t *testing.T, providerMode string) {
7474
t.Skipf("Skipping %s which requires %s mode", t.Name(), providerMode)
7575
}
7676

77+
func skipUnlessEnterpriseServer(t *testing.T) {
78+
if os.Getenv("GITHUB_BASE_URL") == "" || os.Getenv("GITHUB_BASE_URL") == "https://api.github.com/" {
79+
t.Skipf("Skipping %s which requires GitHub Enterprise Server", t.Name())
80+
}
81+
}
82+
7783
func testAccCheckOrganization() error {
7884

7985
baseURL := os.Getenv("GITHUB_BASE_URL")
@@ -130,6 +136,13 @@ func testOwnerFunc() string {
130136
return owner
131137
}
132138

139+
func testPreReceiveHookFunc() string {
140+
hookName := os.Getenv("GITHUB_TEST_PRE_RECEIVE_HOOK")
141+
log.Printf("[INFO] Selecting pre-receive hook name '%s' from GITHUB_TEST_PRE_RECEIVE_HOOK environment variable", hookName)
142+
143+
return hookName
144+
}
145+
133146
const anonymous = "anonymous"
134147
const individual = "individual"
135148
const organization = "organization"
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
package github
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"strconv"
7+
8+
"github.com/google/go-github/v66/github"
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
11+
)
12+
13+
func resourceGithubRepositoryPreReceiveHook() *schema.Resource {
14+
return &schema.Resource{
15+
Create: resourceGithubRepositoryPreReceiveHookCreate,
16+
Read: resourceGithubRepositoryPreReceiveHookRead,
17+
Update: resourceGithubRepositoryPreReceiveHookUpdate,
18+
Delete: resourceGithubRepositoryPreReceiveHookDelete,
19+
Schema: map[string]*schema.Schema{
20+
"repository": {
21+
Type: schema.TypeString,
22+
Required: true,
23+
ForceNew: true,
24+
Description: "The repository of the pre-receive hook.",
25+
},
26+
"name": {
27+
Type: schema.TypeString,
28+
Required: true,
29+
ForceNew: true,
30+
Description: "The name of the pre-receive hook.",
31+
},
32+
"enforcement": {
33+
Type: schema.TypeString,
34+
Required: true,
35+
ValidateFunc: validation.StringInSlice([]string{"enabled", "disabled", "testing"}, false),
36+
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.",
37+
},
38+
"configuration_url": {
39+
Type: schema.TypeString,
40+
Computed: true,
41+
Description: "The URL for the endpoint where enforcement is set.",
42+
},
43+
},
44+
}
45+
}
46+
47+
func resourceGithubRepositoryPreReceiveHookCreate(d *schema.ResourceData, meta interface{}) error {
48+
client := meta.(*Owner).v3client
49+
50+
owner := meta.(*Owner).name
51+
repoName := d.Get("repository").(string)
52+
hookName := d.Get("name").(string)
53+
54+
hook, err := fetchGitHubRepositoryPreReceiveHookByName(meta, repoName, hookName)
55+
if err != nil {
56+
return err
57+
}
58+
59+
enforcement := d.Get("enforcement").(string)
60+
hook.Enforcement = &enforcement
61+
62+
ctx := context.WithValue(context.Background(), ctxId, d.Id())
63+
64+
_, _, err = client.Repositories.UpdatePreReceiveHook(ctx, owner, repoName, hook.GetID(), hook)
65+
if err != nil {
66+
return err
67+
}
68+
d.SetId(strconv.FormatInt(hook.GetID(), 10))
69+
70+
return resourceGithubRepositoryPreReceiveHookRead(d, meta)
71+
}
72+
73+
func resourceGithubRepositoryPreReceiveHookRead(d *schema.ResourceData, meta interface{}) error {
74+
client := meta.(*Owner).v3client
75+
ctx := context.WithValue(context.Background(), ctxId, d.Id())
76+
77+
owner := meta.(*Owner).name
78+
repoName := d.Get("repository").(string)
79+
80+
hookID, err := strconv.ParseInt(d.Id(), 10, 64)
81+
if err != nil {
82+
return unconvertibleIdErr(d.Id(), err)
83+
}
84+
85+
hook, _, err := client.Repositories.GetPreReceiveHook(ctx, owner, repoName, hookID)
86+
if err != nil {
87+
return err
88+
}
89+
if err = d.Set("enforcement", hook.Enforcement); err != nil {
90+
return err
91+
}
92+
if err = d.Set("configuration_url", hook.ConfigURL); err != nil {
93+
return err
94+
}
95+
96+
return nil
97+
}
98+
99+
func resourceGithubRepositoryPreReceiveHookUpdate(d *schema.ResourceData, meta interface{}) error {
100+
client := meta.(*Owner).v3client
101+
ctx := context.WithValue(context.Background(), ctxId, d.Id())
102+
103+
owner := meta.(*Owner).name
104+
repoName := d.Get("repository").(string)
105+
enforcement := d.Get("enforcement").(string)
106+
107+
hookID, err := strconv.ParseInt(d.Id(), 10, 64)
108+
if err != nil {
109+
return unconvertibleIdErr(d.Id(), err)
110+
}
111+
112+
hook := &github.PreReceiveHook{
113+
Enforcement: &enforcement,
114+
}
115+
_, _, err = client.Repositories.UpdatePreReceiveHook(ctx, owner, repoName, hookID, hook)
116+
if err != nil {
117+
return err
118+
}
119+
120+
return resourceGithubRepositoryPreReceiveHookRead(d, meta)
121+
}
122+
123+
func resourceGithubRepositoryPreReceiveHookDelete(d *schema.ResourceData, meta interface{}) error {
124+
client := meta.(*Owner).v3client
125+
ctx := context.WithValue(context.Background(), ctxId, d.Id())
126+
127+
owner := meta.(*Owner).name
128+
repoName := d.Get("repository").(string)
129+
130+
hookID, err := strconv.ParseInt(d.Id(), 10, 64)
131+
if err != nil {
132+
return unconvertibleIdErr(d.Id(), err)
133+
}
134+
_, err = client.Repositories.DeletePreReceiveHook(ctx, owner, repoName, hookID)
135+
if err != nil {
136+
return err
137+
}
138+
139+
return nil
140+
}
141+
142+
func fetchGitHubRepositoryPreReceiveHookByName(meta interface{}, repoName, hookName string) (*github.PreReceiveHook, error) {
143+
ctx := context.Background()
144+
client := meta.(*Owner).v3client
145+
owner := meta.(*Owner).name
146+
147+
opt := &github.ListOptions{
148+
PerPage: 100,
149+
}
150+
151+
var hook *github.PreReceiveHook
152+
153+
for {
154+
hooks, resp, err := client.Repositories.ListPreReceiveHooks(ctx, owner, repoName, opt)
155+
if err != nil {
156+
return nil, err
157+
}
158+
159+
for _, h := range hooks {
160+
n := *h.Name
161+
if n == hookName {
162+
hook = h
163+
break
164+
}
165+
}
166+
167+
if resp.NextPage == 0 {
168+
break
169+
}
170+
opt.Page = resp.NextPage
171+
}
172+
173+
if *hook.ID <= 0 {
174+
return nil, fmt.Errorf("no pre-receive hook with name %s found on %s/%s", hookName, owner, repoName)
175+
}
176+
177+
return hook, nil
178+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package github
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
9+
)
10+
11+
func TestAccGithubRepositoryPreReceiveHook_basic(t *testing.T) {
12+
skipUnlessEnterpriseServer(t)
13+
14+
randString := acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)
15+
16+
resource.ParallelTest(t, resource.TestCase{
17+
PreCheck: func() { testAccPreCheck(t) },
18+
Providers: testAccProviders,
19+
Steps: []resource.TestStep{
20+
{
21+
Config: testAccGithubRepositoryPreReceiveHookConfig(randString, "enabled"),
22+
Check: resource.ComposeTestCheckFunc(
23+
resource.TestCheckResourceAttr(
24+
"github_repository_pre_receive_hook.test", "enforcement", "enabled"),
25+
resource.TestCheckResourceAttrSet(
26+
"github_repository_pre_receive_hook.test", "id"),
27+
resource.TestCheckResourceAttrSet(
28+
"github_repository_pre_receive_hook.test", "configuration_url"),
29+
),
30+
},
31+
{
32+
Config: testAccGithubRepositoryPreReceiveHookConfig(randString, "disabled"),
33+
Check: resource.ComposeTestCheckFunc(
34+
resource.TestCheckResourceAttr(
35+
"github_repository_pre_receive_hook.test", "enforcement", "disabled"),
36+
resource.TestCheckResourceAttrSet(
37+
"github_repository_pre_receive_hook.test", "id"),
38+
resource.TestCheckResourceAttrSet(
39+
"github_repository_pre_receive_hook.test", "configuration_url"),
40+
),
41+
},
42+
},
43+
})
44+
}
45+
46+
func testAccGithubRepositoryPreReceiveHookConfig(randString string, enforcement string) string {
47+
return fmt.Sprintf(`
48+
resource "github_repository" "test" {
49+
name = "foo-%[1]s"
50+
description = "Terraform acceptance tests"
51+
}
52+
53+
resource "github_repository_pre_receive_hook" "test" {
54+
name = "%[2]s"
55+
repository = github_repository.test.name
56+
enforcement = "%[3]s"
57+
}
58+
`, randString, testPreReceiveHookFunc(), enforcement)
59+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
layout: "github"
3+
page_title: "GitHub: github_repository_pre_receive_hook"
4+
description: |-
5+
Creates and manages repository pre-receive hooks.
6+
---
7+
8+
# github_repository_pre_receive_hook
9+
10+
This resource allows you to create and manage pre-receive hooks for repositories.
11+
12+
~> **Note** Repository pre-receive hooks are currently only available in GitHub Enterprise Server.
13+
14+
## Example usage
15+
16+
```
17+
resource "github_repository_pre_receive_hook" "example" {
18+
repository = "test-repo"
19+
name = "ensure-conventional-commits"
20+
enforcement = "enabled"
21+
}
22+
```
23+
24+
## Argument Reference
25+
26+
The following arguments are supported:
27+
28+
* `repository` - (Required) The repository of the pre-receive hook.
29+
30+
* `name` - (Required) The name of the pre-receive hook.
31+
32+
* `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.
33+
34+
## Attributes Reference
35+
36+
The following additional attributes are exported:
37+
38+
* `configuration_url` - The URL for the endpoint where enforcement is set.

website/github.erb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,9 @@
343343
<li>
344344
<a href="/docs/providers/github/r/repository_milestone.html">github_repository_milestone</a>
345345
</li>
346+
<li>
347+
<a href="/docs/providers/github/r/repository_pre_receive_hook.html">github_repository_pre_receive_hook</a>
348+
</li>
346349
<li>
347350
<a href="/docs/providers/github/r/repository_project.html">github_repository_project</a>
348351
</li>

0 commit comments

Comments
 (0)