Skip to content

Commit 88008e3

Browse files
committed
Define schema for repository_property
Refactor expandConditions to reduce complexity Refactor logic to reduce the cognitive complexity and add logic to handle the repository_property field Flatten conditions for repository_property and fix schemas Add test case when ruleset use repository_property Refactor repository property conditions to make them optional Flatten update Target parameters to allow the detection of changes when remote resource is updated Update documentation
1 parent 8359c39 commit 88008e3

4 files changed

Lines changed: 225 additions & 8 deletions

File tree

github/resource_github_organization_ruleset.go

Lines changed: 79 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ func resourceGithubOrganizationRuleset() *schema.Resource {
8787
Type: schema.TypeList,
8888
Optional: true,
8989
MaxItems: 1,
90-
Description: "Parameters for an organization ruleset condition. `ref_name` is required alongside one of `repository_name` or `repository_id`.",
90+
Description: "Parameters for an organization ruleset condition. `ref_name` is required alongside one of `repository_name`, `repository_id`, or `repository_property`.",
9191
Elem: &schema.Resource{
9292
Schema: map[string]*schema.Schema{
9393
"ref_name": {
@@ -115,12 +115,83 @@ func resourceGithubOrganizationRuleset() *schema.Resource {
115115
},
116116
},
117117
},
118+
"repository_property": {
119+
Type: schema.TypeList,
120+
Optional: true,
121+
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 ",
125+
Elem: &schema.Resource{
126+
Schema: map[string]*schema.Schema{
127+
"include": {
128+
Type: schema.TypeList,
129+
Optional: true,
130+
Description: "The repository properties and values to include. All of these properties must match for the condition to pass.",
131+
Elem: &schema.Resource{
132+
Schema: map[string]*schema.Schema{
133+
134+
"name": {
135+
Type: schema.TypeString,
136+
Required: true,
137+
Description: "The name of the repository property to target.",
138+
},
139+
"property_values": {
140+
Type: schema.TypeList,
141+
Required: true,
142+
Description: "The values to match for the repository property.",
143+
Elem: &schema.Schema{
144+
Type: schema.TypeString,
145+
},
146+
},
147+
"source": {
148+
Type: schema.TypeString,
149+
Optional: true,
150+
Description: "The source of the repository property. Defaults to 'custom' if not specified. Can be one of: custom, system",
151+
Default: "custom",
152+
},
153+
},
154+
},
155+
},
156+
"exclude": {
157+
Type: schema.TypeList,
158+
Optional: true,
159+
Description: "The repository properties and values to exclude. The condition will not pass if any of these properties match.",
160+
Elem: &schema.Resource{
161+
Schema: map[string]*schema.Schema{
162+
163+
"name": {
164+
Type: schema.TypeString,
165+
Required: true,
166+
Description: "The name of the repository property to target.",
167+
},
168+
"property_values": {
169+
Type: schema.TypeList,
170+
Required: true,
171+
Description: "The values to match for the repository property.",
172+
Elem: &schema.Schema{
173+
Type: schema.TypeString,
174+
},
175+
},
176+
"source": {
177+
Type: schema.TypeString,
178+
Optional: true,
179+
Description: "The source of the repository property. Defaults to 'custom' if not specified. Can be one of: custom, system",
180+
Default: "custom",
181+
ValidateFunc: validation.StringInSlice([]string{"custom", "system"}, false),
182+
},
183+
},
184+
},
185+
},
186+
},
187+
},
188+
},
118189
"repository_name": {
119190
Type: schema.TypeList,
120191
Optional: true,
121192
MaxItems: 1,
122-
ExactlyOneOf: []string{"conditions.0.repository_id"},
123-
AtLeastOneOf: []string{"conditions.0.repository_id"},
193+
ExactlyOneOf: []string{"conditions.0.repository_id", "conditions.0.repository_property"},
194+
AtLeastOneOf: []string{"conditions.0.repository_id", "conditions.0.repository_property"},
124195
Elem: &schema.Resource{
125196
Schema: map[string]*schema.Schema{
126197
"include": {
@@ -149,9 +220,11 @@ func resourceGithubOrganizationRuleset() *schema.Resource {
149220
},
150221
},
151222
"repository_id": {
152-
Type: schema.TypeList,
153-
Optional: true,
154-
Description: "The repository IDs that the ruleset applies to. One of these IDs must match for the condition to pass.",
223+
Type: schema.TypeList,
224+
Optional: true,
225+
ExactlyOneOf: []string{"conditions.0.repository_name", "conditions.0.repository_property"},
226+
AtLeastOneOf: []string{"conditions.0.repository_name", "conditions.0.repository_property"},
227+
Description: "The repository IDs that the ruleset applies to. One of these IDs must match for the condition to pass.",
155228
Elem: &schema.Schema{
156229
Type: schema.TypeInt,
157230
},

github/resource_github_organization_ruleset_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,62 @@ resource "github_organization_ruleset" "test" {
178178
})
179179
})
180180

181+
t.Run("create_ruleset_with_repository_property", func(t *testing.T) {
182+
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
183+
rulesetName := fmt.Sprintf("%s-repo-prop-ruleset-%s", testResourcePrefix, randomID)
184+
185+
config := fmt.Sprintf(`
186+
resource "github_organization_ruleset" "test" {
187+
name = "%s"
188+
target = "branch"
189+
enforcement = "active"
190+
191+
conditions {
192+
repository_property {
193+
include = [{
194+
name = "team"
195+
source = "custom"
196+
property_values = ["blue"]
197+
}]
198+
exclude = []
199+
}
200+
201+
ref_name {
202+
include = ["~DEFAULT_BRANCH"]
203+
exclude = []
204+
}
205+
}
206+
207+
rules {
208+
creation = true
209+
update = true
210+
deletion = true
211+
required_linear_history = true
212+
}
213+
}
214+
`, rulesetName)
215+
216+
resource.Test(t, resource.TestCase{
217+
PreCheck: func() { skipUnlessHasPaidOrgs(t) },
218+
ProviderFactories: providerFactories,
219+
Steps: []resource.TestStep{
220+
{
221+
Config: config,
222+
Check: resource.ComposeTestCheckFunc(
223+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "name", rulesetName),
224+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "target", "branch"),
225+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "enforcement", "active"),
226+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "conditions.0.repository_property.0.include.#", "1"),
227+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "conditions.0.repository_property.0.include.0.name", "team"),
228+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "conditions.0.repository_property.0.include.0.source", "custom"),
229+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "conditions.0.repository_property.0.include.0.property_values.#", "1"),
230+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "conditions.0.repository_property.0.include.0.property_values.0", "blue"),
231+
),
232+
},
233+
},
234+
})
235+
})
236+
181237
t.Run("create_push_ruleset", func(t *testing.T) {
182238
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
183239
rulesetName := fmt.Sprintf("%s-push-ruleset-%s", testResourcePrefix, randomID)

github/util_rules.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,12 +214,61 @@ func expandConditions(input []any, org bool) *github.RepositoryRulesetConditions
214214
}
215215

216216
rulesetConditions.RepositoryID = &github.RepositoryRulesetRepositoryIDsConditionParameters{RepositoryIDs: repositoryIDs}
217+
} else if v, ok := inputConditions["repository_property"].([]any); ok && v != nil && len(v) != 0 {
218+
rulesetConditions.RepositoryProperty = expandRepositoryPropertyConditions(v)
217219
}
218220
}
219221

220222
return rulesetConditions
221223
}
222224

225+
func expandRepositoryPropertyConditions(v []any) *github.RepositoryRulesetRepositoryPropertyConditionParameters {
226+
repositoryProperties := v[0].(map[string]any)
227+
include := make([]*github.RepositoryRulesetRepositoryPropertyTargetParameters, 0)
228+
exclude := make([]*github.RepositoryRulesetRepositoryPropertyTargetParameters, 0)
229+
230+
for _, v := range repositoryProperties["include"].([]any) {
231+
if v != nil {
232+
propertyMap := v.(map[string]any)
233+
propertyValues := make([]string, 0)
234+
for _, pv := range propertyMap["property_values"].([]any) {
235+
if pv != nil {
236+
propertyValues = append(propertyValues, pv.(string))
237+
}
238+
}
239+
property := &github.RepositoryRulesetRepositoryPropertyTargetParameters{
240+
Name: propertyMap["name"].(string),
241+
Source: github.Ptr(propertyMap["source"].(string)),
242+
PropertyValues: propertyValues,
243+
}
244+
include = append(include, property)
245+
}
246+
}
247+
248+
for _, v := range repositoryProperties["exclude"].([]any) {
249+
if v != nil {
250+
propertyMap := v.(map[string]any)
251+
propertyValues := make([]string, 0)
252+
for _, pv := range propertyMap["property_values"].([]any) {
253+
if pv != nil {
254+
propertyValues = append(propertyValues, pv.(string))
255+
}
256+
}
257+
property := &github.RepositoryRulesetRepositoryPropertyTargetParameters{
258+
Name: propertyMap["name"].(string),
259+
Source: github.Ptr(propertyMap["source"].(string)),
260+
PropertyValues: propertyValues,
261+
}
262+
exclude = append(exclude, property)
263+
}
264+
}
265+
266+
return &github.RepositoryRulesetRepositoryPropertyConditionParameters{
267+
Include: include,
268+
Exclude: exclude,
269+
}
270+
}
271+
223272
func flattenConditions(conditions *github.RepositoryRulesetConditions, org bool) []any {
224273
if conditions == nil || conditions.RefName == nil {
225274
return []any{}
@@ -257,11 +306,35 @@ func flattenConditions(conditions *github.RepositoryRulesetConditions, org bool)
257306
if conditions.RepositoryID != nil {
258307
conditionsMap["repository_id"] = conditions.RepositoryID.RepositoryIDs
259308
}
309+
310+
if conditions.RepositoryProperty != nil {
311+
repositoryPropertySlice := make([]map[string]any, 0)
312+
313+
repositoryPropertySlice = append(repositoryPropertySlice, map[string]any{
314+
"include": flattenRulesetRepositoryPropertyTargetParameters(conditions.RepositoryProperty.Include),
315+
"exclude": flattenRulesetRepositoryPropertyTargetParameters(conditions.RepositoryProperty.Exclude),
316+
})
317+
conditionsMap["repository_property"] = repositoryPropertySlice
318+
}
260319
}
261320

262321
return []any{conditionsMap}
263322
}
264323

324+
func flattenRulesetRepositoryPropertyTargetParameters(input []*github.RepositoryRulesetRepositoryPropertyTargetParameters) []map[string]any {
325+
result := make([]map[string]any, 0)
326+
327+
for _, v := range input {
328+
propertyMap := make(map[string]any)
329+
propertyMap["name"] = v.Name
330+
propertyMap["source"] = v.GetSource()
331+
propertyMap["property_values"] = v.PropertyValues
332+
result = append(result, propertyMap)
333+
}
334+
335+
return result
336+
}
337+
265338
func expandRules(input []any, org bool) *github.RepositoryRulesetRules {
266339
if len(input) == 0 || input[0] == nil {
267340
return &github.RepositoryRulesetRules{}

website/docs/r/organization_ruleset.html.markdown

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,8 +306,9 @@ The `rules` block supports the following:
306306
#### conditions ####
307307

308308
- `ref_name` - (Required) (Block List, Min: 1, Max: 1) (see [below for nested schema](#conditions.ref_name))
309-
- `repository_id` (Optional) (List of Number) The repository IDs that the ruleset applies to. One of these IDs must match for the condition to pass. Conflicts with `repository_name`.
310-
- `repository_name` (Optional) (Block List, Max: 1) Conflicts with `repository_id`. (see [below for nested schema](#conditions.repository_name))
309+
- `repository_id` (Optional) (List of Number) The repository IDs that the ruleset applies to. One of these IDs must match for the condition to pass. Conflicts with `repository_name` and `repository_property`.
310+
- `repository_name` (Optional) (Block List, Max: 1) Conflicts with `repository_id` and `repository_property`. (see [below for nested schema](#conditions.repository_name))
311+
- `repository_property` (Optional) (Block List, Max: 1) Conflicts with `repository_id` and `repository_name`. (see [below for nested schema](#conditions.repository_property))
311312

312313
One of `repository_id` and `repository_name` must be set for the rule to target any repositories.
313314

@@ -323,6 +324,20 @@ One of `repository_id` and `repository_name` must be set for the rule to target
323324

324325
- `include` - (Required) (List of String) Array of repository names or patterns to include. One of these patterns must match for the condition to pass. Also accepts `~ALL` to include all repositories.
325326

327+
#### conditions.repository_property ####
328+
329+
- `include` - (Optional) (List of Repository Properties) The repository properties and values to include. All of these properties must match for the condition to pass. (see [below for nested schema](#conditions.repository_property.properties))
330+
331+
- `exclude` - (Optional) (List of Repository Properties) The repository properties and values to exclude. The condition will not pass if any of these properties match.(see [below for nested schema](#conditions.repository_property.properties))
332+
333+
#### conditions.repository_property.properties ####
334+
335+
- `name` (Required) (String) The name of the repository property to target.
336+
337+
- `property_values` (Required) (Array of String) The values to match for the repository property.
338+
339+
- `source` (String) The source of the repository property. Defaults to 'custom' if not specified. Can be one of: `custom`, `system`
340+
326341
## Attributes Reference
327342

328343
The following additional attributes are exported:

0 commit comments

Comments
 (0)