Skip to content

Commit d0e914b

Browse files
committed
Support repository_property in push rulesets
1 parent 0888d58 commit d0e914b

6 files changed

Lines changed: 366 additions & 24 deletions

github/resource_github_organization_ruleset.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/google/go-github/v81/github"
1111
"github.com/hashicorp/terraform-plugin-log/tflog"
1212
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
13+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
1314
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1415
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
1516
)
@@ -26,6 +27,10 @@ func resourceGithubOrganizationRuleset() *schema.Resource {
2627

2728
SchemaVersion: 1,
2829

30+
CustomizeDiff: customdiff.All(
31+
validateOrganizationRulesetConditions,
32+
),
33+
2934
Schema: map[string]*schema.Schema{
3035
"name": {
3136
Type: schema.TypeString,
@@ -87,12 +92,12 @@ func resourceGithubOrganizationRuleset() *schema.Resource {
8792
Type: schema.TypeList,
8893
Optional: true,
8994
MaxItems: 1,
90-
Description: "Parameters for an organization ruleset condition. `ref_name` is required alongside one of `repository_name`, `repository_id`, or `repository_property`.",
95+
Description: "Parameters for an organization ruleset condition. Push rulesets require exactly one of: repository_id, repository_name, or repository_property. Branch/tag rulesets require ref_name AND exactly one repository targeting option.",
9196
Elem: &schema.Resource{
9297
Schema: map[string]*schema.Schema{
9398
"ref_name": {
9499
Type: schema.TypeList,
95-
Required: true,
100+
Optional: true,
96101
MaxItems: 1,
97102
Elem: &schema.Resource{
98103
Schema: map[string]*schema.Schema{
@@ -119,9 +124,7 @@ func resourceGithubOrganizationRuleset() *schema.Resource {
119124
Type: schema.TypeList,
120125
Optional: true,
121126
MaxItems: 1,
122-
ExactlyOneOf: []string{"conditions.0.repository_id", "conditions.0.repository_name"},
123-
AtLeastOneOf: []string{"conditions.0.repository_id", "conditions.0.repository_name"},
124-
Description: "Conditions to target repositories by property ",
127+
Description: "Conditions to target repositories by custom or system properties.",
125128
Elem: &schema.Resource{
126129
Schema: map[string]*schema.Schema{
127130
"include": {
@@ -189,8 +192,6 @@ func resourceGithubOrganizationRuleset() *schema.Resource {
189192
Type: schema.TypeList,
190193
Optional: true,
191194
MaxItems: 1,
192-
ExactlyOneOf: []string{"conditions.0.repository_id", "conditions.0.repository_property"},
193-
AtLeastOneOf: []string{"conditions.0.repository_id", "conditions.0.repository_property"},
194195
Elem: &schema.Resource{
195196
Schema: map[string]*schema.Schema{
196197
"include": {
@@ -221,8 +222,6 @@ func resourceGithubOrganizationRuleset() *schema.Resource {
221222
"repository_id": {
222223
Type: schema.TypeList,
223224
Optional: true,
224-
ExactlyOneOf: []string{"conditions.0.repository_name", "conditions.0.repository_property"},
225-
AtLeastOneOf: []string{"conditions.0.repository_name", "conditions.0.repository_property"},
226225
Description: "The repository IDs that the ruleset applies to. One of these IDs must match for the condition to pass.",
227226
Elem: &schema.Schema{
228227
Type: schema.TypeInt,

github/resource_github_organization_ruleset_test.go

Lines changed: 241 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package github
22

33
import (
44
"fmt"
5+
"regexp"
56
"testing"
67

78
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
@@ -267,11 +268,6 @@ resource "github_organization_ruleset" "test" {
267268
exclude = []
268269
}
269270
270-
ref_name {
271-
include = ["~ALL"]
272-
exclude = []
273-
}
274-
}
275271
276272
rules {
277273
file_path_restriction {
@@ -555,6 +551,246 @@ resource "github_organization_ruleset" "test" {
555551
},
556552
})
557553
})
554+
555+
t.Run("push_no_ref_name", func(t *testing.T) {
556+
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
557+
rulesetName := fmt.Sprintf("%s-push-no-ref-%s", testResourcePrefix, randomID)
558+
559+
config := fmt.Sprintf(`
560+
resource "github_organization_ruleset" "test" {
561+
name = "%s"
562+
target = "push"
563+
enforcement = "active"
564+
565+
conditions {
566+
repository_name {
567+
include = ["~ALL"]
568+
exclude = []
569+
}
570+
}
571+
572+
rules {
573+
max_file_size {
574+
max_file_size = 50
575+
}
576+
}
577+
}
578+
`, rulesetName)
579+
580+
resource.Test(t, resource.TestCase{
581+
PreCheck: func() { skipUnlessHasPaidOrgs(t) },
582+
ProviderFactories: providerFactories,
583+
Steps: []resource.TestStep{
584+
{
585+
Config: config,
586+
Check: resource.ComposeTestCheckFunc(
587+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "name", rulesetName),
588+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "target", "push"),
589+
),
590+
},
591+
},
592+
})
593+
})
594+
595+
t.Run("push_with_ref_name_should_fail", func(t *testing.T) {
596+
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
597+
rulesetName := fmt.Sprintf("%s-push-with-ref-%s", testResourcePrefix, randomID)
598+
599+
config := fmt.Sprintf(`
600+
resource "github_organization_ruleset" "test" {
601+
name = "%s"
602+
target = "push"
603+
enforcement = "active"
604+
605+
conditions {
606+
repository_name {
607+
include = ["~ALL"]
608+
exclude = []
609+
}
610+
611+
ref_name {
612+
include = ["~ALL"]
613+
exclude = []
614+
}
615+
}
616+
617+
rules {
618+
max_file_size {
619+
max_file_size = 50
620+
}
621+
}
622+
}
623+
`, rulesetName)
624+
625+
resource.Test(t, resource.TestCase{
626+
PreCheck: func() { skipUnlessHasPaidOrgs(t) },
627+
ProviderFactories: providerFactories,
628+
Steps: []resource.TestStep{
629+
{
630+
Config: config,
631+
ExpectError: regexp.MustCompile("ref_name cannot be set for push rulesets"),
632+
},
633+
},
634+
})
635+
})
636+
637+
t.Run("push_with_repository_property", func(t *testing.T) {
638+
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
639+
rulesetName := fmt.Sprintf("%s-push-repo-prop-%s", testResourcePrefix, randomID)
640+
641+
config := fmt.Sprintf(`
642+
resource "github_organization_ruleset" "test" {
643+
name = "%s"
644+
target = "push"
645+
enforcement = "active"
646+
647+
conditions {
648+
repository_property {
649+
include {
650+
name = "test-property"
651+
property_values = ["test-value"]
652+
}
653+
exclude {
654+
name = "exclude-property"
655+
property_values = ["exclude-value"]
656+
}
657+
}
658+
}
659+
660+
rules {
661+
max_file_size {
662+
max_file_size = 50
663+
}
664+
}
665+
}
666+
`, rulesetName)
667+
668+
resource.Test(t, resource.TestCase{
669+
PreCheck: func() { skipUnlessHasPaidOrgs(t) },
670+
ProviderFactories: providerFactories,
671+
Steps: []resource.TestStep{
672+
{
673+
Config: config,
674+
Check: resource.ComposeTestCheckFunc(
675+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "name", rulesetName),
676+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "target", "push"),
677+
),
678+
},
679+
},
680+
})
681+
})
682+
683+
t.Run("branch_requires_ref_name", func(t *testing.T) {
684+
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
685+
rulesetName := fmt.Sprintf("%s-branch-missing-ref-%s", testResourcePrefix, randomID)
686+
687+
config := fmt.Sprintf(`
688+
resource "github_organization_ruleset" "test" {
689+
name = "%s"
690+
target = "branch"
691+
enforcement = "active"
692+
693+
conditions {
694+
repository_name {
695+
include = ["~ALL"]
696+
exclude = []
697+
}
698+
}
699+
700+
rules {
701+
creation = true
702+
}
703+
}
704+
`, rulesetName)
705+
706+
resource.Test(t, resource.TestCase{
707+
PreCheck: func() { skipUnlessHasPaidOrgs(t) },
708+
ProviderFactories: providerFactories,
709+
Steps: []resource.TestStep{
710+
{
711+
Config: config,
712+
ExpectError: regexp.MustCompile("branch rulesets require ref_name to be set"),
713+
},
714+
},
715+
})
716+
})
717+
718+
t.Run("branch_missing_repo_targeting", func(t *testing.T) {
719+
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
720+
rulesetName := fmt.Sprintf("%s-branch-no-repo-%s", testResourcePrefix, randomID)
721+
722+
config := fmt.Sprintf(`
723+
resource "github_organization_ruleset" "test" {
724+
name = "%s"
725+
target = "branch"
726+
enforcement = "active"
727+
728+
conditions {
729+
ref_name {
730+
include = ["~ALL"]
731+
exclude = []
732+
}
733+
}
734+
735+
rules {
736+
creation = true
737+
}
738+
}
739+
`, rulesetName)
740+
741+
resource.Test(t, resource.TestCase{
742+
PreCheck: func() { skipUnlessHasPaidOrgs(t) },
743+
ProviderFactories: providerFactories,
744+
Steps: []resource.TestStep{
745+
{
746+
Config: config,
747+
ExpectError: regexp.MustCompile("branch rulesets require exactly one of repository_id, repository_name, or repository_property"),
748+
},
749+
},
750+
})
751+
})
752+
753+
t.Run("multiple_repo_targeting_should_fail", func(t *testing.T) {
754+
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
755+
rulesetName := fmt.Sprintf("%s-multi-target-%s", testResourcePrefix, randomID)
756+
757+
config := fmt.Sprintf(`
758+
resource "github_organization_ruleset" "test" {
759+
name = "%s"
760+
target = "branch"
761+
enforcement = "active"
762+
763+
conditions {
764+
ref_name {
765+
include = ["~ALL"]
766+
exclude = []
767+
}
768+
769+
repository_id = [123, 456]
770+
771+
repository_name {
772+
include = ["~ALL"]
773+
exclude = []
774+
}
775+
}
776+
777+
rules {
778+
creation = true
779+
}
780+
}
781+
`, rulesetName)
782+
783+
resource.Test(t, resource.TestCase{
784+
PreCheck: func() { skipUnlessHasPaidOrgs(t) },
785+
ProviderFactories: providerFactories,
786+
Steps: []resource.TestStep{
787+
{
788+
Config: config,
789+
ExpectError: regexp.MustCompile("require exactly one of repository_id, repository_name, or repository_property"),
790+
},
791+
},
792+
})
793+
})
558794
}
559795

560796
func TestOrganizationPushRulesetSupport(t *testing.T) {

github/resource_github_repository_ruleset.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"github.com/google/go-github/v81/github"
1313
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
14+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
1415
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1516
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
1617
)
@@ -27,6 +28,10 @@ func resourceGithubRepositoryRuleset() *schema.Resource {
2728

2829
SchemaVersion: 1,
2930

31+
CustomizeDiff: customdiff.All(
32+
validateRepositoryRulesetConditions,
33+
),
34+
3035
Schema: map[string]*schema.Schema{
3136
"name": {
3237
Type: schema.TypeString,
@@ -99,7 +104,7 @@ func resourceGithubRepositoryRuleset() *schema.Resource {
99104
Schema: map[string]*schema.Schema{
100105
"ref_name": {
101106
Type: schema.TypeList,
102-
Required: true,
107+
Optional: true,
103108
MaxItems: 1,
104109
Elem: &schema.Resource{
105110
Schema: map[string]*schema.Schema{

0 commit comments

Comments
 (0)