Skip to content

Commit ce9a54a

Browse files
authored
Feature github_repository_tag_protection closes#1250 (#1283)
* Adding github_tag_protection to provider. * adding loop for tag_protection call to set properties if pattern matches input * changing resource name from resourceGithubTagProtection to resourceGithubRepositoryTagProtection * Adding tests for import and delete * adding documentation * adding doc lin to github.erb and removing commented out code
1 parent 0ba3d4b commit ce9a54a

File tree

5 files changed

+314
-0
lines changed

5 files changed

+314
-0
lines changed

github/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ func Provider() terraform.ResourceProvider {
121121
"github_repository_milestone": resourceGithubRepositoryMilestone(),
122122
"github_repository_project": resourceGithubRepositoryProject(),
123123
"github_repository_pull_request": resourceGithubRepositoryPullRequest(),
124+
"github_repository_tag_protection": resourceGithubRepositoryTagProtection(),
124125
"github_repository_webhook": resourceGithubRepositoryWebhook(),
125126
"github_team": resourceGithubTeam(),
126127
"github_team_members": resourceGithubTeamMembers(),
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package github
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log"
7+
"net/http"
8+
"strconv"
9+
"strings"
10+
11+
"github.com/google/go-github/v47/github"
12+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
13+
)
14+
15+
func resourceGithubRepositoryTagProtection() *schema.Resource {
16+
return &schema.Resource{
17+
Create: resourceGithubRepositoryTagProtectionCreateOrUpdate,
18+
Read: resourceGithubRepositoryTagProtectionRead,
19+
Delete: resourceGithubRepositoryTagProtectionDelete,
20+
Importer: &schema.ResourceImporter{
21+
State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
22+
parts := strings.Split(d.Id(), "/")
23+
if len(parts) != 2 {
24+
return nil, fmt.Errorf("Invalid ID specified. Supplied ID must be written as <repository>/<webhook_id>")
25+
}
26+
d.Set("repository", parts[0])
27+
d.Set("tag_protection_id", parts[1])
28+
d.SetId(parts[1])
29+
return []*schema.ResourceData{d}, nil
30+
},
31+
},
32+
33+
Schema: map[string]*schema.Schema{
34+
"repository": {
35+
Type: schema.TypeString,
36+
Required: true,
37+
ForceNew: true,
38+
},
39+
"pattern": {
40+
Type: schema.TypeString,
41+
Required: true,
42+
ForceNew: true,
43+
},
44+
"tag_protection_id": {
45+
Type: schema.TypeInt,
46+
Computed: true,
47+
},
48+
},
49+
}
50+
}
51+
52+
func resourceGithubRepositoryTagProtectionCreateOrUpdate(d *schema.ResourceData, meta interface{}) error {
53+
client := meta.(*Owner).v3client
54+
ctx := context.Background()
55+
owner := meta.(*Owner).name
56+
repo := d.Get("repository").(string)
57+
pattern := d.Get("pattern").(string)
58+
log.Printf("[DEBUG] Creating tag protection for %s/%s with pattern %s", owner, repo, pattern)
59+
tagProtection, _, err := client.Repositories.CreateTagProtection(ctx, owner, repo, pattern)
60+
if err != nil {
61+
return err
62+
}
63+
d.SetId(strconv.FormatInt(tagProtection.GetID(), 10))
64+
65+
return resourceGithubRepositoryTagProtectionRead(d, meta)
66+
}
67+
68+
func resourceGithubRepositoryTagProtectionRead(d *schema.ResourceData, meta interface{}) error {
69+
70+
ctx := context.WithValue(context.Background(), ctxId, d.Id())
71+
72+
client := meta.(*Owner).v3client
73+
owner := meta.(*Owner).name
74+
repo := d.Get("repository").(string)
75+
76+
id, err := strconv.ParseInt(d.Id(), 10, 64)
77+
if err != nil {
78+
return err
79+
}
80+
81+
tag_protection, _, err := client.Repositories.ListTagProtection(ctx, owner, repo)
82+
if err != nil {
83+
if ghErr, ok := err.(*github.ErrorResponse); ok {
84+
if ghErr.Response.StatusCode == http.StatusNotModified {
85+
return nil
86+
}
87+
if ghErr.Response.StatusCode == http.StatusNotFound && d.IsNewResource() {
88+
return nil
89+
}
90+
return err
91+
}
92+
return err
93+
}
94+
for _, tag := range tag_protection {
95+
if tag.GetID() == id {
96+
d.Set("pattern", tag.GetPattern())
97+
}
98+
}
99+
100+
return nil
101+
}
102+
103+
func resourceGithubRepositoryTagProtectionDelete(d *schema.ResourceData, meta interface{}) error {
104+
client := meta.(*Owner).v3client
105+
ctx := context.WithValue(context.Background(), ctxId, d.Id())
106+
owner := meta.(*Owner).name
107+
repo := d.Get("repository").(string)
108+
tag_protection_id, err := strconv.ParseInt(d.Id(), 10, 64)
109+
if err != nil {
110+
return err
111+
}
112+
113+
log.Printf("[DEBUG] Deleting tag protection for %s/%s with id %d", owner, repo, tag_protection_id)
114+
_, error := client.Repositories.DeleteTagProtection(ctx, owner, repo, tag_protection_id)
115+
116+
return error
117+
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
package github
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
8+
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
9+
)
10+
11+
func TestAccGithubRepositoryTagProtection(t *testing.T) {
12+
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
13+
t.Run("creates tag protection without error", func(t *testing.T) {
14+
15+
config := fmt.Sprintf(`
16+
resource "github_repository" "test" {
17+
name = "tf-acc-test-%s"
18+
auto_init = true
19+
}
20+
21+
resource "github_repository_tag_protection" "test" {
22+
depends_on = ["github_repository.test"]
23+
repository = github_repository.test.name
24+
pattern = "v*"
25+
}
26+
`, randomID)
27+
28+
check := resource.ComposeTestCheckFunc(
29+
resource.TestCheckResourceAttr(
30+
"github_repository_tag_protection.test", "pattern", "v*",
31+
),
32+
)
33+
34+
testCase := func(t *testing.T, mode string) {
35+
resource.Test(t, resource.TestCase{
36+
PreCheck: func() { skipUnlessMode(t, mode) },
37+
Providers: testAccProviders,
38+
Steps: []resource.TestStep{
39+
{
40+
Config: config,
41+
Check: check,
42+
},
43+
},
44+
})
45+
}
46+
t.Run("run with an anonymous account", func(t *testing.T) {
47+
t.Skip("anonymous account not supported for this operation")
48+
})
49+
t.Run("run with an individual account", func(t *testing.T) {
50+
testCase(t, individual)
51+
})
52+
t.Run("run with an organization account", func(t *testing.T) {
53+
testCase(t, organization)
54+
})
55+
56+
})
57+
t.Run("imports tag protection without error", func(t *testing.T) {
58+
59+
config := fmt.Sprintf(`
60+
resource "github_repository" "test" {
61+
name = "tf-acc-test-%s"
62+
auto_init = true
63+
}
64+
65+
resource "github_repository_tag_protection" "test" {
66+
depends_on = ["github_repository.test"]
67+
repository = github_repository.test.name
68+
pattern = "v*"
69+
}
70+
`, randomID)
71+
72+
check := resource.ComposeTestCheckFunc(
73+
resource.TestCheckResourceAttr(
74+
"github_repository_tag_protection.test", "pattern", "v*",
75+
),
76+
resource.TestCheckResourceAttrSet(
77+
"github_repository_tag_protection.test", "id",
78+
),
79+
)
80+
81+
testCase := func(t *testing.T, mode string) {
82+
resource.Test(t, resource.TestCase{
83+
PreCheck: func() { skipUnlessMode(t, mode) },
84+
Providers: testAccProviders,
85+
Steps: []resource.TestStep{
86+
{
87+
Config: config,
88+
Check: check,
89+
},
90+
{
91+
ResourceName: "github_repository_tag_protection.test",
92+
ImportState: true,
93+
ImportStateIdPrefix: fmt.Sprintf("tf-acc-test-%s/", randomID),
94+
ImportStateVerify: true,
95+
},
96+
},
97+
})
98+
}
99+
t.Run("run with an anonymous account", func(t *testing.T) {
100+
t.Skip("anonymous account not supported for this operation")
101+
})
102+
t.Run("run with an individual account", func(t *testing.T) {
103+
testCase(t, individual)
104+
})
105+
t.Run("run with an organization account", func(t *testing.T) {
106+
testCase(t, organization)
107+
})
108+
})
109+
t.Run("deletes tag protection without error", func(t *testing.T) {
110+
111+
config := fmt.Sprintf(`
112+
resource "github_repository" "test" {
113+
name = "tf-acc-test-%s"
114+
auto_init = true
115+
}
116+
117+
resource "github_repository_tag_protection" "test" {
118+
depends_on = ["github_repository.test"]
119+
repository = github_repository.test.name
120+
pattern = "v*"
121+
}
122+
`, randomID)
123+
124+
testCase := func(t *testing.T, mode string) {
125+
resource.Test(t, resource.TestCase{
126+
PreCheck: func() { skipUnlessMode(t, mode) },
127+
Providers: testAccProviders,
128+
Steps: []resource.TestStep{
129+
{
130+
Config: config,
131+
Destroy: true,
132+
},
133+
},
134+
})
135+
}
136+
137+
t.Run("run with an anonymous account", func(t *testing.T) {
138+
t.Skip("anonymous account not supported for this operation")
139+
})
140+
141+
t.Run("run with an individual account", func(t *testing.T) {
142+
testCase(t, individual)
143+
})
144+
145+
t.Run("run with an organization account", func(t *testing.T) {
146+
testCase(t, organization)
147+
})
148+
149+
})
150+
151+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
layout: "github"
3+
page_title: "GitHub: repository_tag_protection"
4+
description: |-
5+
Creates and manages repository tag protection within GitHub organizations or personal accounts
6+
---
7+
8+
# github_repository_tag_protection
9+
10+
This resource allows you to create and manage a repository tag protection for repositories within your GitHub organization or personal account.
11+
12+
## Example Usage
13+
14+
```hcl
15+
resource "github_repository_tag_protection" "example" {
16+
repository = "example-repository"
17+
pattern = "v*"
18+
}
19+
```
20+
21+
## Argument Reference
22+
23+
* `repository` - (Required) Name of the repository to add the tag protection to.
24+
25+
* `pattern` - (Required) The pattern of the tag to protect.
26+
27+
## Attributes Reference
28+
29+
The following additional attributes are exported:
30+
31+
* `id` - The ID of the tag protection.
32+
33+
## Import
34+
35+
Repository tag protections can be imported using the `name` of the repository, combined with the `id` of the tag protection, separated by a `/` character.
36+
The `id` of the tag protection can be found using the [GitHub API](https://docs.github.com/en/rest/repos/tags#list-tag-protection-states-for-a-repository).
37+
38+
Importing uses the name of the repository, as well as the ID of the webhook, e.g.
39+
40+
```
41+
$ terraform import github_repository_tag_protection.terraform my-repo/31077
42+
```

website/github.erb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@
160160
<li>
161161
<a href="/docs/providers/github/r/repository_project.html">github_repository_project</a>
162162
</li>
163+
<li>
164+
<a href="/docs/providers/github/r/repository_tag_protection.html">github_repository_tag_protection</a>
165+
</li>
163166
<li>
164167
<a href="/docs/providers/github/r/repository_webhook.html">github_repository_webhook</a>
165168
</li>

0 commit comments

Comments
 (0)