Skip to content

Commit efeb9d4

Browse files
committed
refactor: move pull_request_creation_policy to a dedicated resource
- refactor: extract pull_request_creation_policy into github_repository_pull_request_creation_policy resource - refactor: revert pull_request_creation_policy field and GraphQL calls from github_repository - refactor: remove isUnsupportedPullRequestCreationPolicyError; dedicated resource fails loudly on missing API field - feat: add github_repository_pull_request_creation_policy resource with GraphQL CRUD and import - test: add acceptance tests for github_repository_pull_request_creation_policy - docs: add documentation for github_repository_pull_request_creation_policy
1 parent 3004840 commit efeb9d4

9 files changed

Lines changed: 261 additions & 125 deletions

github/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ func Provider() *schema.Provider {
200200
"github_repository_milestone": resourceGithubRepositoryMilestone(),
201201
"github_repository_project": resourceGithubRepositoryProject(),
202202
"github_repository_pull_request": resourceGithubRepositoryPullRequest(),
203+
"github_repository_pull_request_creation_policy": resourceGithubRepositoryPullRequestCreationPolicy(),
203204
"github_repository_ruleset": resourceGithubRepositoryRuleset(),
204205
"github_repository_topics": resourceGithubRepositoryTopics(),
205206
"github_repository_webhook": resourceGithubRepositoryWebhook(),

github/resource_github_repository.go

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -284,13 +284,6 @@ func resourceGithubRepository() *schema.Resource {
284284
Default: false,
285285
Description: "Automatically delete head branch after a pull request is merged. Defaults to 'false'.",
286286
},
287-
"pull_request_creation_policy": {
288-
Type: schema.TypeString,
289-
Optional: true,
290-
Computed: true,
291-
ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice([]string{"all", "collaborators_only"}, false)),
292-
Description: "Controls who can create pull requests in the repository. Can be `all` or `collaborators_only`.",
293-
},
294287
"web_commit_signoff_required": {
295288
Type: schema.TypeBool,
296289
Optional: true,
@@ -877,19 +870,6 @@ func resourceGithubRepositoryRead(ctx context.Context, d *schema.ResourceData, m
877870
}
878871
}
879872

880-
pullRequestCreationPolicy, err := getRepositoryPullRequestCreationPolicy(ctx, owner, repoName, meta)
881-
if err != nil {
882-
if isUnsupportedPullRequestCreationPolicyError(err) {
883-
log.Printf("[DEBUG] Skipping pull_request_creation_policy read for %s/%s: %s", owner, repoName, err)
884-
} else {
885-
return diag.Errorf("error reading repository pull request creation policy: %s", err.Error())
886-
}
887-
} else {
888-
if err = d.Set("pull_request_creation_policy", pullRequestCreationPolicy); err != nil {
889-
return diag.FromErr(err)
890-
}
891-
}
892-
893873
// Set fork information if this is a fork
894874
if repo.GetFork() {
895875
_ = d.Set("fork", "true")
@@ -1015,19 +995,6 @@ func resourceGithubRepositoryUpdate(ctx context.Context, d *schema.ResourceData,
1015995
}
1016996
}
1017997

1018-
if d.IsNewResource() || d.HasChange("pull_request_creation_policy") {
1019-
if v, ok := d.GetOk("pull_request_creation_policy"); ok {
1020-
repositoryID, err := getRepositoryID(repo.GetName(), meta)
1021-
if err != nil {
1022-
return diag.Errorf("error resolving repository id for pull request creation policy update: %s", err.Error())
1023-
}
1024-
1025-
if err := updateRepositoryPullRequestCreationPolicy(ctx, repositoryID, v.(string), meta); err != nil {
1026-
return diag.Errorf("error updating repository pull request creation policy: %s", err.Error())
1027-
}
1028-
}
1029-
}
1030-
1031998
if d.IsNewResource() || d.HasChange("vulnerability_alerts") {
1032999
if v, ok := d.GetOkExists("vulnerability_alerts"); ok { //nolint:staticcheck // SA1019 // We sometimes need to use GetOkExists for booleans
10331000
if val, ok := v.(bool); ok {
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package github
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
10+
)
11+
12+
func resourceGithubRepositoryPullRequestCreationPolicy() *schema.Resource {
13+
return &schema.Resource{
14+
Description: "Manages the pull request creation policy for a repository.",
15+
CreateContext: resourceGithubRepositoryPullRequestCreationPolicyCreate,
16+
ReadContext: resourceGithubRepositoryPullRequestCreationPolicyRead,
17+
UpdateContext: resourceGithubRepositoryPullRequestCreationPolicyUpdate,
18+
DeleteContext: resourceGithubRepositoryPullRequestCreationPolicyDelete,
19+
Importer: &schema.ResourceImporter{
20+
StateContext: resourceGithubRepositoryPullRequestCreationPolicyImport,
21+
},
22+
23+
Schema: map[string]*schema.Schema{
24+
"repository": {
25+
Type: schema.TypeString,
26+
Required: true,
27+
ForceNew: true,
28+
Description: "The name of the GitHub repository.",
29+
},
30+
"policy": {
31+
Type: schema.TypeString,
32+
Required: true,
33+
Description: "Controls who can create pull requests for the repository. Can be `all` or `collaborators_only`.",
34+
ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice([]string{"all", "collaborators_only"}, false)),
35+
},
36+
},
37+
}
38+
}
39+
40+
func resourceGithubRepositoryPullRequestCreationPolicyCreate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
41+
repoName := d.Get("repository").(string)
42+
policy := d.Get("policy").(string)
43+
44+
nodeID, err := getRepositoryID(repoName, meta)
45+
if err != nil {
46+
return diag.Errorf("error resolving repository node ID for %s: %s", repoName, err)
47+
}
48+
49+
if err := updateRepositoryPullRequestCreationPolicy(ctx, nodeID, policy, meta); err != nil {
50+
return diag.Errorf("error setting pull request creation policy for %s: %s", repoName, err)
51+
}
52+
53+
d.SetId(repoName)
54+
return resourceGithubRepositoryPullRequestCreationPolicyRead(ctx, d, meta)
55+
}
56+
57+
func resourceGithubRepositoryPullRequestCreationPolicyRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
58+
owner := meta.(*Owner).name
59+
repoName := d.Id()
60+
61+
policy, err := getRepositoryPullRequestCreationPolicy(ctx, owner, repoName, meta)
62+
if err != nil {
63+
return diag.Errorf("error reading pull request creation policy for %s/%s: %s", owner, repoName, err)
64+
}
65+
66+
if err := d.Set("policy", policy); err != nil {
67+
return diag.FromErr(err)
68+
}
69+
if err := d.Set("repository", repoName); err != nil {
70+
return diag.FromErr(err)
71+
}
72+
73+
return nil
74+
}
75+
76+
func resourceGithubRepositoryPullRequestCreationPolicyUpdate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
77+
repoName := d.Id()
78+
policy := d.Get("policy").(string)
79+
80+
nodeID, err := getRepositoryID(repoName, meta)
81+
if err != nil {
82+
return diag.Errorf("error resolving repository node ID for %s: %s", repoName, err)
83+
}
84+
85+
if err := updateRepositoryPullRequestCreationPolicy(ctx, nodeID, policy, meta); err != nil {
86+
return diag.Errorf("error updating pull request creation policy for %s: %s", repoName, err)
87+
}
88+
89+
return resourceGithubRepositoryPullRequestCreationPolicyRead(ctx, d, meta)
90+
}
91+
92+
func resourceGithubRepositoryPullRequestCreationPolicyDelete(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
93+
repoName := d.Id()
94+
95+
nodeID, err := getRepositoryID(repoName, meta)
96+
if err != nil {
97+
return diag.Errorf("error resolving repository node ID for %s: %s", repoName, err)
98+
}
99+
100+
if err := updateRepositoryPullRequestCreationPolicy(ctx, nodeID, "all", meta); err != nil {
101+
return diag.Errorf("error resetting pull request creation policy for %s: %s", repoName, err)
102+
}
103+
104+
return nil
105+
}
106+
107+
func resourceGithubRepositoryPullRequestCreationPolicyImport(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) {
108+
repoName := d.Id()
109+
110+
if err := d.Set("repository", repoName); err != nil {
111+
return nil, err
112+
}
113+
114+
diags := resourceGithubRepositoryPullRequestCreationPolicyRead(ctx, d, meta)
115+
if diags.HasError() {
116+
return nil, fmt.Errorf("%s", diags[0].Summary)
117+
}
118+
119+
return []*schema.ResourceData{d}, nil
120+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package github
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
8+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
9+
)
10+
11+
func TestAccGithubRepositoryPullRequestCreationPolicy(t *testing.T) {
12+
t.Run("sets policy without error", func(t *testing.T) {
13+
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
14+
repoName := fmt.Sprintf("%srepo-pr-policy-%s", testResourcePrefix, randomID)
15+
initial := `policy = "collaborators_only"`
16+
updated := `policy = "all"`
17+
18+
config := fmt.Sprintf(`
19+
resource "github_repository" "test" {
20+
name = "%s"
21+
visibility = "private"
22+
auto_init = true
23+
}
24+
25+
resource "github_repository_pull_request_creation_policy" "test" {
26+
repository = github_repository.test.name
27+
%%s
28+
}
29+
`, repoName)
30+
31+
checks := map[string]resource.TestCheckFunc{
32+
"before": resource.ComposeTestCheckFunc(
33+
resource.TestCheckResourceAttr(
34+
"github_repository_pull_request_creation_policy.test", "policy",
35+
"collaborators_only",
36+
),
37+
),
38+
"after": resource.ComposeTestCheckFunc(
39+
resource.TestCheckResourceAttr(
40+
"github_repository_pull_request_creation_policy.test", "policy",
41+
"all",
42+
),
43+
),
44+
}
45+
46+
resource.Test(t, resource.TestCase{
47+
PreCheck: func() { skipUnauthenticated(t) },
48+
ProviderFactories: providerFactories,
49+
Steps: []resource.TestStep{
50+
{
51+
Config: fmt.Sprintf(config, initial),
52+
Check: checks["before"],
53+
},
54+
{
55+
Config: fmt.Sprintf(config, updated),
56+
Check: checks["after"],
57+
},
58+
},
59+
})
60+
})
61+
62+
t.Run("imports without error", func(t *testing.T) {
63+
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
64+
repoName := fmt.Sprintf("%srepo-pr-policy-%s", testResourcePrefix, randomID)
65+
66+
config := fmt.Sprintf(`
67+
resource "github_repository" "test" {
68+
name = "%s"
69+
visibility = "private"
70+
auto_init = true
71+
}
72+
73+
resource "github_repository_pull_request_creation_policy" "test" {
74+
repository = github_repository.test.name
75+
policy = "collaborators_only"
76+
}
77+
`, repoName)
78+
79+
check := resource.ComposeTestCheckFunc(
80+
resource.TestCheckResourceAttrSet("github_repository_pull_request_creation_policy.test", "repository"),
81+
resource.TestCheckResourceAttr("github_repository_pull_request_creation_policy.test", "policy", "collaborators_only"),
82+
)
83+
84+
resource.Test(t, resource.TestCase{
85+
PreCheck: func() { skipUnauthenticated(t) },
86+
ProviderFactories: providerFactories,
87+
Steps: []resource.TestStep{
88+
{
89+
Config: config,
90+
Check: check,
91+
},
92+
{
93+
ResourceName: "github_repository_pull_request_creation_policy.test",
94+
ImportState: true,
95+
ImportStateVerify: true,
96+
},
97+
},
98+
})
99+
})
100+
}

github/resource_github_repository_test.go

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1334,43 +1334,6 @@ resource "github_repository" "test" {
13341334
})
13351335
})
13361336

1337-
t.Run("check_pull_request_creation_policy", func(t *testing.T) {
1338-
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
1339-
testRepoName := fmt.Sprintf("%spr-policy-%s", testResourcePrefix, randomID)
1340-
config := `
1341-
resource "github_repository" "test" {
1342-
name = "%s"
1343-
visibility = "%s"
1344-
pull_request_creation_policy = "%s"
1345-
}
1346-
`
1347-
1348-
resource.Test(t, resource.TestCase{
1349-
PreCheck: func() { skipUnauthenticated(t) },
1350-
ProviderFactories: providerFactories,
1351-
Steps: []resource.TestStep{
1352-
{
1353-
Config: fmt.Sprintf(config, testRepoName, testAccConf.testRepositoryVisibility, "collaborators_only"),
1354-
Check: resource.ComposeTestCheckFunc(
1355-
resource.TestCheckResourceAttr("github_repository.test", "pull_request_creation_policy", "collaborators_only"),
1356-
),
1357-
},
1358-
{
1359-
Config: fmt.Sprintf(config, testRepoName, testAccConf.testRepositoryVisibility, "all"),
1360-
Check: resource.ComposeTestCheckFunc(
1361-
resource.TestCheckResourceAttr("github_repository.test", "pull_request_creation_policy", "all"),
1362-
),
1363-
},
1364-
{
1365-
ResourceName: "github_repository.test",
1366-
ImportState: true,
1367-
ImportStateVerify: true,
1368-
ImportStateVerifyIgnore: []string{"auto_init", "vulnerability_alerts", "ignore_vulnerability_alerts_during_read"},
1369-
},
1370-
},
1371-
})
1372-
})
1373-
13741337
t.Run("check_web_commit_signoff_required_organization_enabled_but_not_set", func(t *testing.T) {
13751338
t.Skip("This test should be run manually after confirming that the test organization has 'Require contributors to sign off on web-based commits' enabled under Organizations -> Settings -> Repository -> Repository defaults.")
13761339

github/util_v4_repository.go

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"encoding/base64"
66
"errors"
77
"fmt"
8-
"strings"
98

109
"github.com/shurcooL/githubv4"
1110
)
@@ -110,21 +109,6 @@ func expandPullRequestCreationPolicy(policy string) (PullRequestCreationPolicy,
110109
}
111110
}
112111

113-
func isUnsupportedPullRequestCreationPolicyError(err error) bool {
114-
if err == nil {
115-
return false
116-
}
117-
118-
message := strings.ToLower(err.Error())
119-
120-
return strings.Contains(message, "pullrequestcreationpolicy") &&
121-
(strings.Contains(message, "doesn't exist") ||
122-
strings.Contains(message, "does not exist") ||
123-
strings.Contains(message, "undefined field") ||
124-
strings.Contains(message, "unknown argument") ||
125-
strings.Contains(message, "cannot query field"))
126-
}
127-
128112
func getRepositoryPullRequestCreationPolicy(ctx context.Context, owner, name string, meta any) (string, error) {
129113
var query struct {
130114
Repository struct {

0 commit comments

Comments
 (0)