Skip to content

Commit 73dfb5d

Browse files
andrewesweetclaude
andcommitted
fix: add Computed and use GetOkExists for fork field
Add Computed: true to members_can_fork_private_repositories schema so Terraform accepts API values when users omit the field, preventing phantom diffs that trigger 422 errors under enterprise policy. Replace shouldInclude with d.GetOkExists for this field since GetOk cannot distinguish "set to false" from "not set" on Computed+Optional bools. Follows allow_forking pattern in resource_github_repository.go. Add unit tests for buildOrganizationSettings and acceptance test proving no phantom diff after apply. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 21524a1 commit 73dfb5d

3 files changed

Lines changed: 142 additions & 1 deletion

github/resource_github_organization_settings.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ func resourceGithubOrganizationSettings() *schema.Resource {
119119
"members_can_fork_private_repositories": {
120120
Type: schema.TypeBool,
121121
Optional: true,
122+
Computed: true,
122123
Description: "Whether or not organization members can fork private repositories. When an enterprise policy controls this setting, omit this attribute to avoid API validation errors.",
123124
},
124125
"web_commit_signoff_required": {
@@ -265,7 +266,11 @@ func buildOrganizationSettings(d *schema.ResourceData, isEnterprise bool) *githu
265266
if shouldInclude("members_can_create_private_pages") {
266267
settings.MembersCanCreatePrivatePages = new(d.Get("members_can_create_private_pages").(bool))
267268
}
268-
if shouldInclude("members_can_fork_private_repositories") {
269+
if !isUpdate {
270+
if _, ok := d.GetOkExists("members_can_fork_private_repositories"); ok { //nolint:staticcheck // SA1019 // GetOkExists needed for Computed+Optional bool fields
271+
settings.MembersCanForkPrivateRepos = new(d.Get("members_can_fork_private_repositories").(bool))
272+
}
273+
} else if d.HasChange("members_can_fork_private_repositories") {
269274
settings.MembersCanForkPrivateRepos = new(d.Get("members_can_fork_private_repositories").(bool))
270275
}
271276
if shouldInclude("web_commit_signoff_required") {

github/resource_github_organization_settings_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,3 +611,35 @@ func TestAccGithubOrganizationSettings(t *testing.T) {
611611
})
612612
})
613613
}
614+
615+
func TestAccGithubOrganizationSettings_omittedForkFieldProducesCleanPlan(t *testing.T) {
616+
config := `
617+
resource "github_organization_settings" "test" {
618+
billing_email = "test@example.com"
619+
}`
620+
621+
resource.Test(t, resource.TestCase{
622+
PreCheck: func() { skipUnlessHasOrgs(t) },
623+
ProviderFactories: providerFactories,
624+
Steps: []resource.TestStep{
625+
{
626+
Config: config,
627+
Check: resource.ComposeTestCheckFunc(
628+
resource.TestCheckResourceAttr(
629+
"github_organization_settings.test",
630+
"billing_email", "test@example.com",
631+
),
632+
resource.TestCheckResourceAttrSet(
633+
"github_organization_settings.test",
634+
"members_can_fork_private_repositories",
635+
),
636+
),
637+
},
638+
{
639+
Config: config,
640+
PlanOnly: true,
641+
ExpectNonEmptyPlan: false,
642+
},
643+
},
644+
})
645+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package github
2+
3+
import (
4+
"testing"
5+
6+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
7+
)
8+
9+
func TestBuildOrganizationSettings_OmittedFieldsNotSent(t *testing.T) {
10+
resource := resourceGithubOrganizationSettings()
11+
d := schema.TestResourceDataRaw(t, resource.Schema, map[string]any{
12+
"billing_email": "test@example.com",
13+
})
14+
15+
settings := buildOrganizationSettings(d, false)
16+
17+
if settings.MembersCanForkPrivateRepos != nil {
18+
t.Error("MembersCanForkPrivateRepos should be nil when not configured")
19+
}
20+
if settings.BillingEmail == nil {
21+
t.Error("BillingEmail should be set when configured")
22+
}
23+
}
24+
25+
func TestBuildOrganizationSettings_ExplicitTrueFieldsSent(t *testing.T) {
26+
resource := resourceGithubOrganizationSettings()
27+
d := schema.TestResourceDataRaw(t, resource.Schema, map[string]any{
28+
"billing_email": "test@example.com",
29+
"members_can_fork_private_repositories": true,
30+
})
31+
32+
settings := buildOrganizationSettings(d, false)
33+
34+
if settings.MembersCanForkPrivateRepos == nil {
35+
t.Fatal("MembersCanForkPrivateRepos should not be nil when explicitly set to true")
36+
}
37+
if *settings.MembersCanForkPrivateRepos != true {
38+
t.Errorf("MembersCanForkPrivateRepos = %v, want true", *settings.MembersCanForkPrivateRepos)
39+
}
40+
}
41+
42+
func TestBuildOrganizationSettings_ExplicitFalseFieldsSent(t *testing.T) {
43+
resource := resourceGithubOrganizationSettings()
44+
d := schema.TestResourceDataRaw(t, resource.Schema, map[string]any{
45+
"billing_email": "test@example.com",
46+
"members_can_fork_private_repositories": false,
47+
})
48+
49+
settings := buildOrganizationSettings(d, false)
50+
51+
if settings.MembersCanForkPrivateRepos == nil {
52+
t.Fatal("MembersCanForkPrivateRepos should not be nil when explicitly set to false")
53+
}
54+
if *settings.MembersCanForkPrivateRepos != false {
55+
t.Errorf("MembersCanForkPrivateRepos = %v, want false", *settings.MembersCanForkPrivateRepos)
56+
}
57+
}
58+
59+
func TestBuildOrganizationSettings_UpdateOmittedFieldsNotSent(t *testing.T) {
60+
resource := resourceGithubOrganizationSettings()
61+
d := schema.TestResourceDataRaw(t, resource.Schema, map[string]any{
62+
"billing_email": "test@example.com",
63+
})
64+
d.SetId("test-org")
65+
66+
settings := buildOrganizationSettings(d, false)
67+
68+
if settings.MembersCanForkPrivateRepos != nil {
69+
t.Error("MembersCanForkPrivateRepos should be nil on update when field has not changed")
70+
}
71+
}
72+
73+
func TestBuildOrganizationSettings_NonEnterpriseExcludesInternalRepos(t *testing.T) {
74+
resource := resourceGithubOrganizationSettings()
75+
d := schema.TestResourceDataRaw(t, resource.Schema, map[string]any{
76+
"billing_email": "test@example.com",
77+
"members_can_create_internal_repositories": true,
78+
})
79+
80+
settings := buildOrganizationSettings(d, false)
81+
82+
if settings.MembersCanCreateInternalRepos != nil {
83+
t.Error("MembersCanCreateInternalRepos should be nil when not enterprise")
84+
}
85+
}
86+
87+
func TestOrganizationSettingsSchemaProperties(t *testing.T) {
88+
resource := resourceGithubOrganizationSettings()
89+
90+
field := resource.Schema["members_can_fork_private_repositories"]
91+
if field == nil {
92+
t.Fatal("members_can_fork_private_repositories not found in schema")
93+
}
94+
95+
if !field.Optional {
96+
t.Error("members_can_fork_private_repositories should be Optional")
97+
}
98+
if !field.Computed {
99+
t.Error("members_can_fork_private_repositories should be Computed")
100+
}
101+
if field.Default != nil {
102+
t.Errorf("members_can_fork_private_repositories should have no Default, got %v", field.Default)
103+
}
104+
}

0 commit comments

Comments
 (0)