Skip to content

Commit e8cf32e

Browse files
authored
feat: Add repository rulesets (#12)
1 parent 78699f8 commit e8cf32e

File tree

4 files changed

+364
-4
lines changed

4 files changed

+364
-4
lines changed

README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ No modules.
157157
| [github_repository_deploy_key.deploy_key_computed](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository_deploy_key) | resource |
158158
| [github_repository_deployment_branch_policy.this](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository_deployment_branch_policy) | resource |
159159
| [github_repository_environment.this](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository_environment) | resource |
160-
| [github_repository_project.repository_project](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository_project) | resource |
160+
| [github_repository_ruleset.ruleset](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository_ruleset) | resource |
161161
| [github_repository_webhook.repository_webhook](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository_webhook) | resource |
162162
| [github_team_repository.team_repository](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team_repository) | resource |
163163
| [github_team_repository.team_repository_by_slug](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team_repository) | resource |
@@ -196,7 +196,6 @@ No modules.
196196
| <a name="input_gitignore_template"></a> [gitignore\_template](#input\_gitignore\_template) | (Optional) Use the name of the template without the extension. For example, Haskell. Available templates: https://github.com/github/gitignore | `string` | `null` | no |
197197
| <a name="input_has_downloads"></a> [has\_downloads](#input\_has\_downloads) | (Optional) Set to true to enable the (deprecated) downloads features on the repository. (Default: false) | `bool` | `null` | no |
198198
| <a name="input_has_issues"></a> [has\_issues](#input\_has\_issues) | (Optional) Set to true to enable the GitHub Issues features on the repository. (Default: false) | `bool` | `null` | no |
199-
| <a name="input_has_projects"></a> [has\_projects](#input\_has\_projects) | (Optional) Set to true to enable the GitHub Projects features on the repository. Per the github documentation when in an organization that has disabled repository projects it will default to false and will otherwise default to true. If you specify true when it has been disabled it will return an error. (Default: false) | `bool` | `null` | no |
200199
| <a name="input_has_wiki"></a> [has\_wiki](#input\_has\_wiki) | (Optional) Set to true to enable the GitHub Wiki features on the repository. (Default: false) | `bool` | `null` | no |
201200
| <a name="input_homepage_url"></a> [homepage\_url](#input\_homepage\_url) | (Optional) The website of the repository. | `string` | `null` | no |
202201
| <a name="input_is_template"></a> [is\_template](#input\_is\_template) | (Optional) Whether or not to tell GitHub that this is a template repository. ( Default: false) | `bool` | `null` | no |
@@ -214,13 +213,13 @@ No modules.
214213
| <a name="input_pages"></a> [pages](#input\_pages) | (Optional) The repository's GitHub Pages configuration. (Default: {}) | <pre>object({<br/> branch = optional(string)<br/> path = optional(string, null)<br/> cname = optional(string)<br/> build_type = optional(string, "legacy") # requires branch and optional path<br/> })</pre> | `null` | no |
215214
| <a name="input_plaintext_secrets"></a> [plaintext\_secrets](#input\_plaintext\_secrets) | (Optional) Configuring actions secrets. For details please check: https://www.terraform.io/docs/providers/github/r/actions_secret | `map(string)` | `{}` | no |
216215
| <a name="input_private"></a> [private](#input\_private) | (Optional) (DEPRECATED: use visibility) | `bool` | `null` | no |
217-
| <a name="input_projects"></a> [projects](#input\_projects) | (Optional) Create and manage projects for GitHub repository. | <pre>list(object({<br/> name = string<br/> body = string<br/> }))</pre> | `[]` | no |
218216
| <a name="input_pull_collaborators"></a> [pull\_collaborators](#input\_pull\_collaborators) | (Optional) A list of users to add as collaborators granting them pull (read-only) permission. | `list(string)` | `[]` | no |
219217
| <a name="input_pull_team_ids"></a> [pull\_team\_ids](#input\_pull\_team\_ids) | (Optional) A list of teams (by id) to grant pull (read-only) permission to. | `list(string)` | `[]` | no |
220218
| <a name="input_pull_teams"></a> [pull\_teams](#input\_pull\_teams) | (Optional) A list of teams (by name/slug) to grant pull (read-only) permission to. | `list(string)` | `[]` | no |
221219
| <a name="input_push_collaborators"></a> [push\_collaborators](#input\_push\_collaborators) | (Optional) A list of users to add as collaborators granting them push (read-write) permission. | `list(string)` | `[]` | no |
222220
| <a name="input_push_team_ids"></a> [push\_team\_ids](#input\_push\_team\_ids) | (Optional) A list of teams (by id) to grant push (read-write) permission to. | `list(string)` | `[]` | no |
223221
| <a name="input_push_teams"></a> [push\_teams](#input\_push\_teams) | (Optional) A list of teams (by name/slug) to grant push (read-write) permission to. | `list(string)` | `[]` | no |
222+
| <a name="input_rulesets"></a> [rulesets](#input\_rulesets) | (Optional) A list of branch rulesets to apply to the repository. Default is [].<br/><br/>It is very likely removal of any section will require setting it to an empty list/map.<br/>This is due to limitations in the API whereby components are not destroyed upon removal. | <pre>list(<br/> object({<br/> enforcement = string<br/> name = string<br/> target = string<br/><br/> rules = list(<br/> object({<br/> creation = optional(bool)<br/> deletion = optional(bool)<br/> non_fast_forward = optional(bool)<br/> required_signatures = optional(bool)<br/> required_linear_history = optional(bool)<br/> update = optional(bool)<br/> update_allows_fetch_and_merge = optional(bool)<br/><br/> branch_name_pattern = optional(<br/> object({<br/> operator = string<br/> pattern = string<br/> name = optional(string)<br/> negate = optional(bool)<br/> })<br/> )<br/><br/> commit_author_email_pattern = optional(<br/> object({<br/> operator = string<br/> pattern = string<br/> name = optional(string)<br/> negate = optional(bool)<br/> })<br/> )<br/><br/> commit_message_pattern = optional(<br/> object({<br/> operator = string<br/> pattern = string<br/> name = optional(string)<br/> negate = optional(bool)<br/> })<br/> )<br/><br/> committer_email_pattern = optional(<br/> object({<br/> operator = string<br/> pattern = string<br/> name = optional(string)<br/> negate = optional(bool)<br/> })<br/> )<br/><br/> tag_name_pattern = optional(<br/> object({<br/> operator = string<br/> pattern = string<br/> name = optional(string)<br/> negate = optional(bool)<br/> })<br/> )<br/><br/> required_status_checks = optional(<br/> object({<br/> strict_required_status_checks_policy = optional(bool)<br/> do_not_enforce_on_create = optional(bool)<br/> required_check = list(<br/> object({<br/> context = string<br/> integration_id = optional(number)<br/> })<br/> )<br/> })<br/> )<br/><br/> pull_request = optional(<br/> object({<br/> dismiss_stale_reviews_on_push = optional(bool)<br/> require_code_owner_review = optional(bool)<br/> require_last_push_approval = optional(bool)<br/> required_approving_review_count = optional(number)<br/> required_review_thread_resolution = optional(bool)<br/> })<br/> )<br/><br/> required_workflows = optional(<br/> object({<br/> required_workflow = list(<br/> object({<br/> repository_id = number<br/> ref = string<br/> path = string<br/> })<br/> )<br/> })<br/> )<br/><br/> required_deployments = optional(<br/> object({<br/> required_deployment_environments = list(string)<br/> })<br/> )<br/><br/> required_code_scanning = optional(<br/> object({<br/> required_code_scanning_tool = list(<br/> object({<br/> tool = string<br/> alerts_threshold = string<br/> security_alerts_threshold = string<br/> })<br/> )<br/> })<br/> )<br/><br/> merge_queue = optional(<br/> object({<br/> check_response_timeout_minutes = optional(number)<br/> grouping_strategy = optional(string)<br/> max_entries_to_build = optional(number)<br/> max_entries_to_merge = optional(number)<br/> merge_method = optional(string)<br/> min_entries_to_merge = optional(number)<br/> min_entries_to_merge_wait_minutes = optional(number)<br/> })<br/> )<br/> })<br/> )<br/><br/> bypass_actors = optional(<br/> list(<br/> object({<br/> actor_id = optional(number)<br/> actor_type = string<br/> bypass_mode = optional(string)<br/> })<br/> )<br/> )<br/><br/> conditions = optional(<br/> object({<br/> ref_name = object({<br/> include = list(string)<br/> exclude = list(string)<br/> })<br/> })<br/> )<br/> })<br/> )</pre> | `[]` | no |
224223
| <a name="input_squash_merge_commit_message"></a> [squash\_merge\_commit\_message](#input\_squash\_merge\_commit\_message) | (Optional) Can be `PR_BODY`, `COMMIT_MESSAGES`, or `BLANK` for a default squash merge commit message. | `string` | `"COMMIT_MESSAGES"` | no |
225224
| <a name="input_squash_merge_commit_title"></a> [squash\_merge\_commit\_title](#input\_squash\_merge\_commit\_title) | (Optional) Can be `PR_BODY`, `COMMIT_MESSAGES`, or `BLANK` for a default squash merge commit message. | `string` | `"COMMIT_OR_PR_TITLE"` | no |
226225
| <a name="input_template"></a> [template](#input\_template) | (Optional) Template repository to use. (Default: {}) | <pre>object({<br/> owner = string<br/> repository = string<br/> })</pre> | `null` | no |
@@ -247,7 +246,6 @@ No modules.
247246
| <a name="output_html_url"></a> [html\_url](#output\_html\_url) | URL to the repository on the web. |
248247
| <a name="output_http_clone_url"></a> [http\_clone\_url](#output\_http\_clone\_url) | URL that can be provided to git clone to clone the repository via HTTPS. |
249248
| <a name="output_issue_labels"></a> [issue\_labels](#output\_issue\_labels) | A map of issue labels keyed by label input id or name. |
250-
| <a name="output_projects"></a> [projects](#output\_projects) | A map of projects keyed by project input id. |
251249
| <a name="output_repository"></a> [repository](#output\_repository) | All attributes and arguments as returned by the github\_repository resource. |
252250
| <a name="output_secrets"></a> [secrets](#output\_secrets) | List of secrets available. |
253251
| <a name="output_ssh_clone_url"></a> [ssh\_clone\_url](#output\_ssh\_clone\_url) | URL that can be provided to git clone to clone the repository via SSH. |

locals.tf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
locals {
2+
rulesets_by_name = {
3+
for rs in var.rulesets : rs.name => rs
4+
}
5+
}

rulesets_resources.tf

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
resource "github_repository_ruleset" "ruleset" {
2+
for_each = local.rulesets_by_name
3+
4+
name = each.value.name
5+
enforcement = each.value.enforcement
6+
target = each.value.target
7+
repository = github_repository.repository.name
8+
9+
dynamic "rules" {
10+
for_each = each.value.rules
11+
content {
12+
creation = lookup(rules.value, "creation", null)
13+
deletion = lookup(rules.value, "deletion", null)
14+
non_fast_forward = lookup(rules.value, "non_fast_forward", null)
15+
required_signatures = lookup(rules.value, "required_signatures", null)
16+
required_linear_history = lookup(rules.value, "required_linear_history", null)
17+
update = lookup(rules.value, "update", null)
18+
update_allows_fetch_and_merge = lookup(rules.value, "update_allows_fetch_and_merge", null)
19+
20+
# -----------------------------------------------------------------------
21+
# Metadata Patterns
22+
# -----------------------------------------------------------------------
23+
24+
dynamic "branch_name_pattern" {
25+
for_each = rules.value.branch_name_pattern != null ? [rules.value.branch_name_pattern] : []
26+
content {
27+
operator = lookup(branch_name_pattern.value, "operator", null)
28+
pattern = lookup(branch_name_pattern.value, "pattern", null)
29+
name = lookup(branch_name_pattern.value, "name", null)
30+
negate = lookup(branch_name_pattern.value, "negate", null)
31+
}
32+
}
33+
34+
dynamic "tag_name_pattern" {
35+
for_each = rules.value.tag_name_pattern != null ? [rules.value.tag_name_pattern] : []
36+
content {
37+
operator = lookup(tag_name_pattern.value, "operator", null)
38+
pattern = lookup(tag_name_pattern.value, "pattern", null)
39+
name = lookup(tag_name_pattern.value, "name", null)
40+
negate = lookup(tag_name_pattern.value, "negate", null)
41+
}
42+
}
43+
44+
dynamic "commit_author_email_pattern" {
45+
for_each = rules.value.commit_author_email_pattern != null ? [rules.value.commit_author_email_pattern] : []
46+
content {
47+
operator = lookup(commit_author_email_pattern.value, "operator", null)
48+
pattern = lookup(commit_author_email_pattern.value, "pattern", null)
49+
name = lookup(commit_author_email_pattern.value, "name", null)
50+
negate = lookup(commit_author_email_pattern.value, "negate", null)
51+
}
52+
}
53+
54+
dynamic "commit_message_pattern" {
55+
for_each = rules.value.commit_message_pattern != null ? [rules.value.commit_message_pattern] : []
56+
content {
57+
operator = lookup(commit_message_pattern.value, "operator", null)
58+
pattern = lookup(commit_message_pattern.value, "pattern", null)
59+
name = lookup(commit_message_pattern.value, "name", null)
60+
negate = lookup(commit_message_pattern.value, "negate", null)
61+
}
62+
}
63+
64+
dynamic "committer_email_pattern" {
65+
for_each = rules.value.committer_email_pattern != null ? [rules.value.committer_email_pattern] : []
66+
content {
67+
operator = lookup(committer_email_pattern.value, "operator", null)
68+
pattern = lookup(committer_email_pattern.value, "pattern", null)
69+
name = lookup(committer_email_pattern.value, "name", null)
70+
negate = lookup(committer_email_pattern.value, "negate", null)
71+
}
72+
}
73+
74+
# -----------------------------------------------------------------------
75+
# Pull Requests & Checks
76+
# -----------------------------------------------------------------------
77+
78+
dynamic "pull_request" {
79+
for_each = rules.value.pull_request != null ? [rules.value.pull_request] : []
80+
content {
81+
dismiss_stale_reviews_on_push = lookup(pull_request.value, "dismiss_stale_reviews_on_push", null)
82+
require_code_owner_review = lookup(pull_request.value, "require_code_owner_review", null)
83+
require_last_push_approval = lookup(pull_request.value, "require_last_push_approval", null)
84+
required_approving_review_count = lookup(pull_request.value, "required_approving_review_count", null)
85+
required_review_thread_resolution = lookup(pull_request.value, "required_review_thread_resolution", null)
86+
}
87+
}
88+
89+
dynamic "required_status_checks" {
90+
for_each = rules.value.required_status_checks != null ? [rules.value.required_status_checks] : []
91+
content {
92+
strict_required_status_checks_policy = lookup(required_status_checks.value, "strict_required_status_checks_policy", null)
93+
do_not_enforce_on_create = lookup(required_status_checks.value, "do_not_enforce_on_create", null)
94+
95+
dynamic "required_check" {
96+
for_each = coalesce(required_status_checks.value.required_check, [])
97+
content {
98+
context = required_check.value.context
99+
integration_id = required_check.value.integration_id
100+
}
101+
}
102+
}
103+
}
104+
105+
dynamic "merge_queue" {
106+
for_each = rules.value.merge_queue != null ? [rules.value.merge_queue] : []
107+
content {
108+
check_response_timeout_minutes = lookup(merge_queue.value, "check_response_timeout_minutes", null)
109+
grouping_strategy = lookup(merge_queue.value, "grouping_strategy", null)
110+
max_entries_to_build = lookup(merge_queue.value, "max_entries_to_build", null)
111+
max_entries_to_merge = lookup(merge_queue.value, "max_entries_to_merge", null)
112+
merge_method = lookup(merge_queue.value, "merge_method", null)
113+
min_entries_to_merge = lookup(merge_queue.value, "min_entries_to_merge", null)
114+
min_entries_to_merge_wait_minutes = lookup(merge_queue.value, "min_entries_to_merge_wait_minutes", null)
115+
}
116+
}
117+
}
118+
}
119+
120+
dynamic "bypass_actors" {
121+
for_each = coalesce(each.value.bypass_actors, [])
122+
content {
123+
actor_id = bypass_actors.value.actor_id
124+
actor_type = bypass_actors.value.actor_type
125+
bypass_mode = lookup(bypass_actors.value, "bypass_mode", null)
126+
}
127+
}
128+
129+
dynamic "conditions" {
130+
for_each = each.value.conditions != null ? [each.value.conditions] : []
131+
content {
132+
ref_name {
133+
include = conditions.value.ref_name.include
134+
exclude = conditions.value.ref_name.exclude
135+
}
136+
}
137+
}
138+
}

0 commit comments

Comments
 (0)