diff --git a/modules/organization/README.md b/modules/organization/README.md index 7166a13..6cee7f2 100644 --- a/modules/organization/README.md +++ b/modules/organization/README.md @@ -3,41 +3,51 @@ | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.3 | -| [github](#requirement\_github) | 5.42.0 | +| [github](#requirement\_github) | 6.1.0 | ## Providers | Name | Version | |------|---------| -| [github](#provider\_github) | 5.42.0 | +| [github](#provider\_github) | 6.1.0 | ## Modules -No modules. +| Name | Source | Version | +|------|--------|---------| +| [base\_default\_branch\_protection](#module\_base\_default\_branch\_protection) | ../ruleset | n/a | +| [dismiss\_stale\_reviews](#module\_dismiss\_stale\_reviews) | ../ruleset | n/a | +| [minimum\_approvals](#module\_minimum\_approvals) | ../ruleset | n/a | +| [require\_signatures](#module\_require\_signatures) | ../ruleset | n/a | +| [ruleset](#module\_ruleset) | ../ruleset | n/a | ## Resources | Name | Type | |------|------| -| [github_actions_organization_secret.action_secret](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/actions_organization_secret) | resource | -| [github_codespaces_organization_secret.codespace_secret](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/codespaces_organization_secret) | resource | -| [github_dependabot_organization_secret.dependabot_secret](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/dependabot_organization_secret) | resource | -| [github_membership.membership_for_user](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/membership) | resource | -| [github_organization_block.blocked_user](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/organization_block) | resource | -| [github_organization_custom_role.community_manager_role](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/organization_custom_role) | resource | -| [github_organization_custom_role.contractor_role](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/organization_custom_role) | resource | -| [github_organization_custom_role.custom_repository_role](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/organization_custom_role) | resource | -| [github_organization_custom_role.security_engineer_role](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/organization_custom_role) | resource | -| [github_organization_settings.organization_settings](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/organization_settings) | resource | +| [github_actions_organization_secret.action_secret](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/actions_organization_secret) | resource | +| [github_codespaces_organization_secret.codespace_secret](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/codespaces_organization_secret) | resource | +| [github_dependabot_organization_secret.dependabot_secret](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/dependabot_organization_secret) | resource | +| [github_membership.membership_for_user](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/membership) | resource | +| [github_organization_block.blocked_user](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/organization_block) | resource | +| [github_organization_custom_role.community_manager_role](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/organization_custom_role) | resource | +| [github_organization_custom_role.contractor_role](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/organization_custom_role) | resource | +| [github_organization_custom_role.custom_repository_role](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/organization_custom_role) | resource | +| [github_organization_custom_role.security_engineer_role](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/organization_custom_role) | resource | +| [github_organization_settings.organization_settings](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/organization_settings) | resource | +| [github_organization_custom_role.branch_ruleset_bypasser](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/data-sources/organization_custom_role) | data source | +| [github_team.branch_ruleset_bypasser](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/data-sources/team) | data source | +| [github_user.branch_ruleset_bypasser](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/data-sources/user) | data source | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [actions\_secrets](#input\_actions\_secrets) | A map of organization-level GitHub Actions secrets to create. The key is the name of the secret and the value is an object describing how to create the secret. |
map(object({
encrypted_value = string
visibility = string
})) | `{}` | no |
-| [codespaces\_secrets](#input\_codespaces\_secrets) | A map of organization-level GitHub Codespaces secrets to create. The key is the name of the secret and the value is an object describing how to create the secret. | map(object({
encrypted_value = string
visibility = string
})) | `{}` | no |
+| [actions\_secrets](#input\_actions\_secrets) | A map of organization-level GitHub Actions secrets to create. The key is the name of the secret and the value is an object describing how to create the secret. | map(object({
encrypted_value = string
visibility = string
})) | `{}` | no |
+| [codespaces\_secrets](#input\_codespaces\_secrets) | A map of organization-level GitHub Codespaces secrets to create. The key is the name of the secret and the value is an object describing how to create the secret. | map(object({
encrypted_value = string
visibility = string
})) | `{}` | no |
| [custom\_repository\_roles](#input\_custom\_repository\_roles) | A map of custom repository roles to create. The key is the name of the role and the value is the role configurations. | map(object({
description = string
base_role = string
permissions = list(string)
})) | n/a | yes |
-| [dependabot\_secrets](#input\_dependabot\_secrets) | A map of organization-level Dependabot secrets to create. The key is the name of the secret and the value is an object describing how to create the secret. | map(object({
encrypted_value = string
visibility = string
})) | `{}` | no |
+| [default\_branch\_protection\_rulesets](#input\_default\_branch\_protection\_rulesets) | n/a | object({
base_protection = optional(object({
enforcement = string
}))
minimum_approvals = optional(object({
enforcement = string
approvals_required = number
}))
dismiss_stale_reviews = optional(object({
enforcement = string
}))
require_signatures = optional(object({
enforcement = string
}))
bypass_actors = optional(object({
repository_roles = optional(list(object({
role = string
always_bypass = optional(bool)
})))
teams = optional(list(object({
team = string
always_bypass = optional(bool)
})))
integrations = optional(list(object({
installation_id = number
always_bypass = optional(bool)
})))
organization_admins = optional(list(object({
user = string
always_bypass = optional(bool)
})))
}))
}) | `{}` | no |
+| [dependabot\_secrets](#input\_dependabot\_secrets) | A map of organization-level Dependabot secrets to create. The key is the name of the secret and the value is an object describing how to create the secret. | map(object({
encrypted_value = string
visibility = string
})) | `{}` | no |
| [enable\_community\_manager\_role](#input\_enable\_community\_manager\_role) | If `true` will create a custom repository role for community managers. Defaults to `false`. If `true` the maximum number of `custom_repository_roles` that can be defined will be reduced by one. | `bool` | `false` | no |
| [enable\_contractor\_role](#input\_enable\_contractor\_role) | If `true` will create a custom repository role for contractors. Defaults to `false`. If `true` the maximum number of `custom_repository_roles` that can be defined will be reduced by one. | `bool` | `false` | no |
| [enable\_security\_engineer\_role](#input\_enable\_security\_engineer\_role) | If `true` will create a custom repository role for security engineers. Defaults to `false`. If `true` the maximum number of `custom_repository_roles` that can be defined will be reduced by one. | `bool` | `false` | no |
@@ -57,6 +67,7 @@ No modules.
| [github\_organization\_pages\_settings](#input\_github\_organization\_pages\_settings) | Settings for organization page creation. The default setting does not allow members to create public and private pages. | object({
members_can_create_public = bool,
members_can_create_private = bool
}) | {
"members_can_create_private": false,
"members_can_create_public": false
} | no |
| [github\_organization\_repository\_settings](#input\_github\_organization\_repository\_settings) | Settings for organization repository creation. The default setting allows members to create internal and private repositories but not public. | object({
members_can_create_public = bool,
members_can_create_internal = bool,
members_can_create_private = bool
}) | {
"members_can_create_internal": true,
"members_can_create_private": true,
"members_can_create_public": false
} | no |
| [github\_organization\_requires\_web\_commit\_signing](#input\_github\_organization\_requires\_web\_commit\_signing) | If set commit signatures are required for commits to the organization. Defaults to `false`. | `bool` | `false` | no |
+| [rulesets](#input\_rulesets) | n/a | map(object({
bypass_actors = optional(object({
repository_roles = optional(list(object({
role = string
always_bypass = optional(bool)
})))
teams = optional(list(object({
team = string
always_bypass = optional(bool)
})))
integrations = optional(list(object({
installation_id = number
always_bypass = optional(bool)
})))
organization_admins = optional(list(object({
user = string
always_bypass = optional(bool)
})))
}))
conditions = optional(object({
ref_name = object({
include = list(string)
exclude = list(string)
})
repository_name = object({
include = list(string)
exclude = list(string)
})
}))
rules = object({
branch_name_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
tag_name_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
commit_author_email_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
commit_message_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
committer_email_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
creation = optional(bool)
deletion = optional(bool)
update = optional(bool)
non_fast_forward = optional(bool)
required_linear_history = optional(bool)
required_signatures = optional(bool)
pull_request = optional(object({
dismiss_stale_reviews_on_push = optional(bool)
require_code_owner_review = optional(bool)
require_last_push_approval = optional(bool)
required_approving_review_count = optional(number)
required_review_thread_resolution = optional(bool)
}))
required_status_checks = optional(object({
required_check = list(object({
context = string
integration_id = optional(number)
}))
strict_required_status_check_policy = optional(bool)
}))
required_workflows = optional(object({
required_workflows = list(object({
repository_id = number
path = string
ref = optional(string)
}))
}))
})
target = string
enforcement = string
})) | `{}` | no |
## Outputs
diff --git a/modules/organization/rulesets.tf b/modules/organization/rulesets.tf
new file mode 100644
index 0000000..a51af05
--- /dev/null
+++ b/modules/organization/rulesets.tf
@@ -0,0 +1,249 @@
+locals {
+ all_team_bypassers = toset(concat(
+ coalesce(try(var.default_branch_protection_rulesets.bypass_actors.teams, []), []),
+ [
+ for _, ruleset_config in var.rulesets : coalesce(try(ruleset_config.bypass_actors.teams, []), [])
+ ]...
+ ))
+
+ all_admin_bypassers = toset(concat(
+ coalesce(try(var.default_branch_protection_rulesets.bypass_actors.organization_admins, []), []),
+ [
+ for _, ruleset_config in var.rulesets : coalesce(try(ruleset_config.bypass_actors.organization_admins, []), [])
+ ]...
+ ))
+
+ all_repository_roles_bypassers = toset(concat(
+ coalesce(try(var.default_branch_protection_rulesets.bypass_actors.repository_roles, []), []),
+ [
+ for _, ruleset_config in var.rulesets : coalesce(try(ruleset_config.bypass_actors.repository_roles, []), [])
+ ]...
+ ))
+
+ github_base_role_ids = {
+ "maintain" = 2
+ "write" = 4
+ "admin" = 5
+ }
+}
+
+
+data "github_team" "branch_ruleset_bypasser" {
+ for_each = {
+ for bypasser in local.all_team_bypassers : bypasser.team => bypasser.team
+ }
+
+ slug = each.value
+ summary_only = true
+}
+
+data "github_user" "branch_ruleset_bypasser" {
+ for_each = {
+ for bypasser in local.all_admin_bypassers : bypasser.user => bypasser.user
+ }
+
+ username = each.value
+}
+
+data "github_organization_custom_role" "branch_ruleset_bypasser" {
+ for_each = {
+ for bypasser in local.all_repository_roles_bypassers : bypasser.role => bypasser.role
+ }
+
+ name = each.value
+}
+
+module "ruleset" {
+ source = "../ruleset"
+
+ for_each = var.rulesets
+
+ name = each.key
+ target = each.value.target
+ enforcement = each.value.enforcement
+
+ ruleset_type = "organization"
+
+ rules = {
+ creation = each.value.rules.creation
+ update = each.value.rules.update
+ deletion = each.value.rules.deletion
+ non_fast_forward = each.value.rules.non_fast_forward
+ required_linear_history = each.value.rules.required_linear_history
+ required_signatures = each.value.rules.required_signatures
+
+ branch_name_pattern = each.value.rules.branch_name_pattern
+ tag_name_pattern = each.value.rules.tag_name_pattern
+ commit_author_email_pattern = each.value.rules.commit_author_email_pattern
+ commit_message_pattern = each.value.rules.commit_message_pattern
+ committer_email_pattern = each.value.rules.committer_email_pattern
+ pull_request = each.value.rules.pull_request
+ required_status_checks = each.value.rules.required_status_checks
+ required_workflows = each.value.rules.required_workflows
+ }
+
+ bypass_actors = {
+ repository_roles = [for bypasser in try(toset(coalesce(each.value.bypass_actors.repository_roles, [])), []) : {
+ role_id = lookup(local.github_base_role_ids, bypasser.role, data.github_organization_custom_role.branch_ruleset_bypasser["${bypasser.role}"].id)
+ always_bypass = bypasser.always_bypass
+ }]
+ teams = [for bypasser in try(toset(coalesce(each.value.bypass_actors.teams, [])), []) : {
+ team_id = data.github_team.branch_ruleset_bypasser["${bypasser.team}"].id
+ always_bypass = bypasser.always_bypass
+ }]
+ organization_admins = [for bypasser in try(toset(coalesce(each.value.bypass_actors.organization_admins, [])), []) : {
+ user_id = data.github_user.branch_ruleset_bypasser["${bypasser.user}"].id
+ always_bypass = bypasser.always_bypass
+ }]
+ integrations = try(each.value.bypass_actors.integrations, [])
+ }
+
+ ref_name_inclusions = each.value.conditions.ref_name.include
+ ref_name_exclusions = each.value.conditions.ref_name.exclude
+ repository_name_inclusions = each.value.conditions.repository_name.include
+ repository_name_exclusions = each.value.conditions.repository_name.exclude
+}
+
+module "base_default_branch_protection" {
+ source = "../ruleset"
+ count = var.default_branch_protection_rulesets.base_protection != null ? 1 : 0
+
+ name = "base_default_branch_protection"
+ target = "branch"
+ enforcement = var.default_branch_protection_rulesets.base_protection.enforcement
+
+ ruleset_type = "organization"
+
+ ref_name_inclusions = ["~DEFAULT_BRANCH"]
+ repository_name_inclusions = ["~ALL"]
+
+ rules = {
+ deletion = true
+ non_fast_forward = true
+ pull_request = {}
+ }
+
+ bypass_actors = {
+ repository_roles = [for bypasser in try(toset(coalesce(var.default_branch_protection_rulesets.bypass_actors.repository_roles, [])), []) : {
+ role_id = lookup(local.github_base_role_ids, bypasser.role, data.github_organization_custom_role.branch_ruleset_bypasser["${bypasser.role}"].id)
+ always_bypass = bypasser.always_bypass
+ }]
+ teams = [for bypasser in try(toset(coalesce(var.default_branch_protection_rulesets.bypass_actors.teams, [])), []) : {
+ team_id = data.github_team.branch_ruleset_bypasser["${bypasser.team}"].id
+ always_bypass = bypasser.always_bypass
+ }]
+ organization_admins = [for bypasser in try(toset(coalesce(var.default_branch_protection_rulesets.bypass_actors.organization_admins, [])), []) : {
+ user_id = data.github_user.branch_ruleset_bypasser["${bypasser.user}"].id
+ always_bypass = bypasser.always_bypass
+ }]
+ integrations = try(var.default_branch_protection_rulesets.bypass_actors.integrations, [])
+ }
+}
+
+module "minimum_approvals" {
+ source = "../ruleset"
+ count = var.default_branch_protection_rulesets.minimum_approvals != null ? 1 : 0
+
+ name = "minimum_approvals"
+ target = "branch"
+ enforcement = var.default_branch_protection_rulesets.minimum_approvals.enforcement
+
+ ruleset_type = "organization"
+
+ ref_name_inclusions = ["~DEFAULT_BRANCH"]
+ repository_name_inclusions = ["~ALL"]
+
+ rules = {
+ pull_request = {
+ require_last_push_approval = true
+ required_approving_review_count = var.default_branch_protection_rulesets.minimum_approvals.approvals_required
+ }
+ }
+
+ bypass_actors = {
+ repository_roles = [for bypasser in try(toset(coalesce(var.default_branch_protection_rulesets.bypass_actors.repository_roles, [])), []) : {
+ role_id = lookup(local.github_base_role_ids, bypasser.role, data.github_organization_custom_role.branch_ruleset_bypasser["${bypasser.role}"].id)
+ always_bypass = bypasser.always_bypass
+ }]
+ teams = [for bypasser in try(toset(coalesce(var.default_branch_protection_rulesets.bypass_actors.teams, [])), []) : {
+ team_id = data.github_team.branch_ruleset_bypasser["${bypasser.team}"].id
+ always_bypass = bypasser.always_bypass
+ }]
+ organization_admins = [for bypasser in try(toset(coalesce(var.default_branch_protection_rulesets.bypass_actors.organization_admins, [])), []) : {
+ user_id = data.github_user.branch_ruleset_bypasser["${bypasser.user}"].id
+ always_bypass = bypasser.always_bypass
+ }]
+ integrations = try(var.default_branch_protection_rulesets.bypass_actors.integrations, [])
+ }
+}
+
+
+module "dismiss_stale_reviews" {
+ source = "../ruleset"
+ count = var.default_branch_protection_rulesets.dismiss_stale_reviews != null ? 1 : 0
+
+ name = "dismiss_stale_reviews"
+ target = "branch"
+ enforcement = var.default_branch_protection_rulesets.dismiss_stale_reviews.enforcement
+
+ ruleset_type = "organization"
+
+ ref_name_inclusions = ["~DEFAULT_BRANCH"]
+ repository_name_inclusions = ["~ALL"]
+
+ rules = {
+ pull_request = {
+ dismiss_stale_reviews_on_push = true
+ }
+ }
+
+ bypass_actors = {
+ repository_roles = [for bypasser in try(toset(coalesce(var.default_branch_protection_rulesets.bypass_actors.repository_roles, [])), []) : {
+ role_id = lookup(local.github_base_role_ids, bypasser.role, data.github_organization_custom_role.branch_ruleset_bypasser["${bypasser.role}"].id)
+ always_bypass = bypasser.always_bypass
+ }]
+ teams = [for bypasser in try(toset(coalesce(var.default_branch_protection_rulesets.bypass_actors.teams, [])), []) : {
+ team_id = data.github_team.branch_ruleset_bypasser["${bypasser.team}"].id
+ always_bypass = bypasser.always_bypass
+ }]
+ organization_admins = [for bypasser in try(toset(coalesce(var.default_branch_protection_rulesets.bypass_actors.organization_admins, [])), []) : {
+ user_id = data.github_user.branch_ruleset_bypasser["${bypasser.user}"].id
+ always_bypass = bypasser.always_bypass
+ }]
+ integrations = try(var.default_branch_protection_rulesets.bypass_actors.integrations, [])
+ }
+}
+
+module "require_signatures" {
+ source = "../ruleset"
+ count = var.default_branch_protection_rulesets.require_signatures != null ? 1 : 0
+
+ name = "require_signatures"
+ target = "branch"
+ enforcement = var.default_branch_protection_rulesets.require_signatures.enforcement
+
+ ruleset_type = "organization"
+
+ ref_name_inclusions = ["~DEFAULT_BRANCH"]
+ repository_name_inclusions = ["~ALL"]
+
+ rules = {
+ required_signatures = true
+ }
+
+ bypass_actors = {
+ repository_roles = [for bypasser in try(toset(coalesce(var.default_branch_protection_rulesets.bypass_actors.repository_roles, [])), []) : {
+ role_id = lookup(local.github_base_role_ids, bypasser.role, data.github_organization_custom_role.branch_ruleset_bypasser["${bypasser.role}"].id)
+ always_bypass = bypasser.always_bypass
+ }]
+ teams = [for bypasser in try(toset(coalesce(var.default_branch_protection_rulesets.bypass_actors.teams, [])), []) : {
+ team_id = data.github_team.branch_ruleset_bypasser["${bypasser.team}"].id
+ always_bypass = bypasser.always_bypass
+ }]
+ organization_admins = [for bypasser in try(toset(coalesce(var.default_branch_protection_rulesets.bypass_actors.organization_admins, [])), []) : {
+ user_id = data.github_user.branch_ruleset_bypasser["${bypasser.user}"].id
+ always_bypass = bypasser.always_bypass
+ }]
+ integrations = try(var.default_branch_protection_rulesets.bypass_actors.integrations, [])
+ }
+}
\ No newline at end of file
diff --git a/modules/organization/variables.tf b/modules/organization/variables.tf
index 271739b..3708ac1 100644
--- a/modules/organization/variables.tf
+++ b/modules/organization/variables.tf
@@ -135,8 +135,8 @@ variable "custom_repository_roles" {
variable "actions_secrets" {
type = map(object({
- encrypted_value = string
- visibility = string
+ encrypted_value = string
+ visibility = string
}))
description = "A map of organization-level GitHub Actions secrets to create. The key is the name of the secret and the value is an object describing how to create the secret."
default = {}
@@ -144,8 +144,8 @@ variable "actions_secrets" {
variable "codespaces_secrets" {
type = map(object({
- encrypted_value = string
- visibility = string
+ encrypted_value = string
+ visibility = string
}))
description = "A map of organization-level GitHub Codespaces secrets to create. The key is the name of the secret and the value is an object describing how to create the secret."
default = {}
@@ -153,10 +153,141 @@ variable "codespaces_secrets" {
variable "dependabot_secrets" {
type = map(object({
- encrypted_value = string
- visibility = string
+ encrypted_value = string
+ visibility = string
}))
description = "A map of organization-level Dependabot secrets to create. The key is the name of the secret and the value is an object describing how to create the secret."
default = {}
}
+variable "default_branch_protection_rulesets" {
+ type = object({
+ base_protection = optional(object({
+ enforcement = string
+ }))
+ minimum_approvals = optional(object({
+ enforcement = string
+ approvals_required = number
+ }))
+ dismiss_stale_reviews = optional(object({
+ enforcement = string
+ }))
+ require_signatures = optional(object({
+ enforcement = string
+ }))
+ bypass_actors = optional(object({
+ repository_roles = optional(list(object({
+ role = string
+ always_bypass = optional(bool)
+ })))
+ teams = optional(list(object({
+ team = string
+ always_bypass = optional(bool)
+ })))
+ integrations = optional(list(object({
+ installation_id = number
+ always_bypass = optional(bool)
+ })))
+ organization_admins = optional(list(object({
+ user = string
+ always_bypass = optional(bool)
+ })))
+ }))
+ })
+ default = {}
+}
+
+variable "rulesets" {
+ type = map(object({
+ bypass_actors = optional(object({
+ repository_roles = optional(list(object({
+ role = string
+ always_bypass = optional(bool)
+ })))
+ teams = optional(list(object({
+ team = string
+ always_bypass = optional(bool)
+ })))
+ integrations = optional(list(object({
+ installation_id = number
+ always_bypass = optional(bool)
+ })))
+ organization_admins = optional(list(object({
+ user = string
+ always_bypass = optional(bool)
+ })))
+ }))
+ conditions = optional(object({
+ ref_name = object({
+ include = list(string)
+ exclude = list(string)
+ })
+ repository_name = object({
+ include = list(string)
+ exclude = list(string)
+ })
+ }))
+ rules = object({
+ branch_name_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ tag_name_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ commit_author_email_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ commit_message_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ committer_email_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ creation = optional(bool)
+ deletion = optional(bool)
+ update = optional(bool)
+ non_fast_forward = optional(bool)
+ required_linear_history = optional(bool)
+ required_signatures = optional(bool)
+ pull_request = optional(object({
+ dismiss_stale_reviews_on_push = optional(bool)
+ require_code_owner_review = optional(bool)
+ require_last_push_approval = optional(bool)
+ required_approving_review_count = optional(number)
+ required_review_thread_resolution = optional(bool)
+ }))
+ required_status_checks = optional(object({
+ required_check = list(object({
+ context = string
+ integration_id = optional(number)
+ }))
+ strict_required_status_check_policy = optional(bool)
+ }))
+ required_workflows = optional(object({
+ required_workflows = list(object({
+ repository_id = number
+ path = string
+ ref = optional(string)
+ }))
+ }))
+ })
+ target = string
+ enforcement = string
+ }))
+ default = {}
+}
\ No newline at end of file
diff --git a/modules/organization/versions.tf b/modules/organization/versions.tf
index 99d757e..d32b1bb 100644
--- a/modules/organization/versions.tf
+++ b/modules/organization/versions.tf
@@ -3,7 +3,7 @@ terraform {
required_providers {
github = {
source = "integrations/github"
- version = "5.42.0"
+ version = "6.1.0"
}
}
}
\ No newline at end of file
diff --git a/modules/private_repository/README.md b/modules/private_repository/README.md
index f516dee..f318c8a 100644
--- a/modules/private_repository/README.md
+++ b/modules/private_repository/README.md
@@ -2,8 +2,8 @@
| Name | Version |
|------|---------|
-| [terraform](#requirement\_terraform) | >= 1.7.1 |
-| [github](#requirement\_github) | 5.42.0 |
+| [terraform](#requirement\_terraform) | >= 1.3 |
+| [github](#requirement\_github) | 6.1.0 |
## Providers
@@ -38,6 +38,7 @@ No resources.
| [name](#input\_name) | The name of the repository to create/import. | `string` | n/a | yes |
| [protected\_branches](#input\_protected\_branches) | A list of ref names or patterns that should be protected. Defaults `["main"]` | `list(string)` | [| no | | [repository\_team\_permissions](#input\_repository\_team\_permissions) | A map where the keys are github team slugs and the value is the permissions the team should have in the repository | `map(string)` | n/a | yes | +| [rulesets](#input\_rulesets) | n/a |
"main"
]
map(object({
bypass_actors = optional(object({
repository_roles = optional(list(object({
role = string
always_bypass = optional(bool)
})))
teams = optional(list(object({
team = string
always_bypass = optional(bool)
})))
integrations = optional(list(object({
installation_id = number
always_bypass = optional(bool)
})))
organization_admins = optional(list(object({
user = string
always_bypass = optional(bool)
})))
}))
conditions = optional(object({
ref_name = object({
include = list(string)
exclude = list(string)
})
}))
rules = object({
branch_name_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
tag_name_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
commit_author_email_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
commit_message_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
committer_email_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
creation = optional(bool)
deletion = optional(bool)
update = optional(bool)
non_fast_forward = optional(bool)
required_linear_history = optional(bool)
required_signatures = optional(bool)
update_allows_fetch_and_merge = optional(bool)
pull_request = optional(object({
dismiss_stale_reviews_on_push = optional(bool)
require_code_owner_review = optional(bool)
require_last_push_approval = optional(bool)
required_approving_review_count = optional(number)
required_review_thread_resolution = optional(bool)
}))
required_status_checks = optional(object({
required_check = list(object({
context = string
integration_id = optional(number)
}))
strict_required_status_check_policy = optional(bool)
}))
required_deployment_environments = optional(list(string))
})
target = string
enforcement = string
})) | `{}` | no |
| [template\_repository](#input\_template\_repository) | A (Optional) list of template repositories to use for the repository | object({
owner = string
repository = string
include_all_branches = bool
}) | `null` | no |
| [topics](#input\_topics) | The topics to apply to the repository | `list(string)` | `[]` | no |
diff --git a/modules/private_repository/outputs.tf b/modules/private_repository/outputs.tf
index 0d1a523..c3adac2 100644
--- a/modules/private_repository/outputs.tf
+++ b/modules/private_repository/outputs.tf
@@ -1,4 +1,4 @@
output "id" {
- value = module.repository_base.id
+ value = module.repository_base.id
description = "The ID of the repository"
}
\ No newline at end of file
diff --git a/modules/private_repository/repository.tf b/modules/private_repository/repository.tf
index e95445b..45e1cc1 100644
--- a/modules/private_repository/repository.tf
+++ b/modules/private_repository/repository.tf
@@ -33,4 +33,6 @@ module "repository_base" {
template_repository = var.template_repository
license_template = var.license_template
+
+ rulesets = var.rulesets
}
diff --git a/modules/private_repository/variables.tf b/modules/private_repository/variables.tf
index b9a111a..ec040b5 100644
--- a/modules/private_repository/variables.tf
+++ b/modules/private_repository/variables.tf
@@ -65,19 +65,19 @@ variable "advance_security" {
variable "action_secrets" {
description = "An (Optional) map of GitHub Actions secrets to create for this repository. The key is the name of the secret and the value is the encrypted value."
type = map(string)
- default = {}
+ default = {}
}
variable "codespace_secrets" {
description = "An (Optional) map of Github Codespace secrets to create for this repository. The key is the name of the secret and the value is the encrypted value."
type = map(string)
- default = {}
+ default = {}
}
variable "dependabot_secrets" {
description = "An (Optional) map of Dependabot secrets to create for this repository. The key is the name of the secret and the value is the encrypted value."
type = map(string)
- default = {}
+ default = {}
}
variable "environments" {
@@ -90,7 +90,7 @@ variable "environments" {
variable "template_repository" {
description = "A (Optional) list of template repositories to use for the repository"
- type = object({
+ type = object({
owner = string
repository = string
include_all_branches = bool
@@ -102,4 +102,90 @@ variable "license_template" {
description = "The (Optional) license template to use for the repository"
type = string
default = null
+}
+
+variable "rulesets" {
+ type = map(object({
+ bypass_actors = optional(object({
+ repository_roles = optional(list(object({
+ role = string
+ always_bypass = optional(bool)
+ })))
+ teams = optional(list(object({
+ team = string
+ always_bypass = optional(bool)
+ })))
+ integrations = optional(list(object({
+ installation_id = number
+ always_bypass = optional(bool)
+ })))
+ organization_admins = optional(list(object({
+ user = string
+ always_bypass = optional(bool)
+ })))
+ }))
+ conditions = optional(object({
+ ref_name = object({
+ include = list(string)
+ exclude = list(string)
+ })
+ }))
+ rules = object({
+ branch_name_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ tag_name_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ commit_author_email_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ commit_message_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ committer_email_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ creation = optional(bool)
+ deletion = optional(bool)
+ update = optional(bool)
+ non_fast_forward = optional(bool)
+ required_linear_history = optional(bool)
+ required_signatures = optional(bool)
+ update_allows_fetch_and_merge = optional(bool)
+ pull_request = optional(object({
+ dismiss_stale_reviews_on_push = optional(bool)
+ require_code_owner_review = optional(bool)
+ require_last_push_approval = optional(bool)
+ required_approving_review_count = optional(number)
+ required_review_thread_resolution = optional(bool)
+ }))
+ required_status_checks = optional(object({
+ required_check = list(object({
+ context = string
+ integration_id = optional(number)
+ }))
+ strict_required_status_check_policy = optional(bool)
+ }))
+ required_deployment_environments = optional(list(string))
+ })
+ target = string
+ enforcement = string
+ }))
+ default = {}
}
\ No newline at end of file
diff --git a/modules/private_repository/versions.tf b/modules/private_repository/versions.tf
index d6e8a27..d32b1bb 100644
--- a/modules/private_repository/versions.tf
+++ b/modules/private_repository/versions.tf
@@ -1,9 +1,9 @@
terraform {
- required_version = ">= 1.7.1"
+ required_version = ">= 1.3"
required_providers {
github = {
source = "integrations/github"
- version = "5.42.0"
+ version = "6.1.0"
}
}
}
\ No newline at end of file
diff --git a/modules/public_repository/README.md b/modules/public_repository/README.md
index 038a54e..77eb62a 100644
--- a/modules/public_repository/README.md
+++ b/modules/public_repository/README.md
@@ -2,8 +2,8 @@
| Name | Version |
|------|---------|
-| [terraform](#requirement\_terraform) | >= 1.7.1 |
-| [github](#requirement\_github) | 5.42.0 |
+| [terraform](#requirement\_terraform) | >= 1.3 |
+| [github](#requirement\_github) | 6.1.0 |
## Providers
@@ -38,6 +38,7 @@ No resources.
| [name](#input\_name) | The name of the repository to create/import. | `string` | n/a | yes |
| [protected\_branches](#input\_protected\_branches) | A list of ref names or patterns that should be protected. Defaults `["main"]` | `list(string)` | [| no | | [repository\_team\_permissions](#input\_repository\_team\_permissions) | A map where the keys are github team slugs and the value is the permissions the team should have in the repository | `map(string)` | n/a | yes | +| [rulesets](#input\_rulesets) | n/a |
"main"
]
map(object({
bypass_actors = optional(object({
repository_roles = optional(list(object({
role = string
always_bypass = optional(bool)
})))
teams = optional(list(object({
team = string
always_bypass = optional(bool)
})))
integrations = optional(list(object({
installation_id = number
always_bypass = optional(bool)
})))
organization_admins = optional(list(object({
user = string
always_bypass = optional(bool)
})))
}))
conditions = optional(object({
ref_name = object({
include = list(string)
exclude = list(string)
})
}))
rules = object({
branch_name_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
tag_name_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
commit_author_email_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
commit_message_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
committer_email_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
creation = optional(bool)
deletion = optional(bool)
update = optional(bool)
non_fast_forward = optional(bool)
required_linear_history = optional(bool)
required_signatures = optional(bool)
update_allows_fetch_and_merge = optional(bool)
pull_request = optional(object({
dismiss_stale_reviews_on_push = optional(bool)
require_code_owner_review = optional(bool)
require_last_push_approval = optional(bool)
required_approving_review_count = optional(number)
required_review_thread_resolution = optional(bool)
}))
required_status_checks = optional(object({
required_check = list(object({
context = string
integration_id = optional(number)
}))
strict_required_status_check_policy = optional(bool)
}))
required_deployment_environments = optional(list(string))
})
target = string
enforcement = string
})) | `{}` | no |
| [template\_repository](#input\_template\_repository) | A (Optional) list of template repositories to use for the repository | object({
owner = string
repository = string
include_all_branches = bool
}) | `null` | no |
| [topics](#input\_topics) | The topics to apply to the repository | `list(string)` | `[]` | no |
diff --git a/modules/public_repository/outputs.tf b/modules/public_repository/outputs.tf
index 0d1a523..c3adac2 100644
--- a/modules/public_repository/outputs.tf
+++ b/modules/public_repository/outputs.tf
@@ -1,4 +1,4 @@
output "id" {
- value = module.repository_base.id
+ value = module.repository_base.id
description = "The ID of the repository"
}
\ No newline at end of file
diff --git a/modules/public_repository/repository.tf b/modules/public_repository/repository.tf
index 7eb7e5e..7acc30e 100644
--- a/modules/public_repository/repository.tf
+++ b/modules/public_repository/repository.tf
@@ -30,7 +30,9 @@ module "repository_base" {
action_secrets = var.action_secrets
environments = var.environments
-
+
template_repository = var.template_repository
license_template = var.license_template
+
+ rulesets = var.rulesets
}
\ No newline at end of file
diff --git a/modules/public_repository/variables.tf b/modules/public_repository/variables.tf
index 3e84e32..ba047b8 100644
--- a/modules/public_repository/variables.tf
+++ b/modules/public_repository/variables.tf
@@ -65,19 +65,19 @@ variable "advance_security" {
variable "action_secrets" {
description = "An (Optional) map of GitHub Actions secrets to create for this repository. The key is the name of the secret and the value is the encrypted value."
type = map(string)
- default = {}
+ default = {}
}
variable "codespace_secrets" {
description = "An (Optional) map of GitHub Codespace secrets to create for this repository. The key is the name of the secret and the value is the encrypted value."
type = map(string)
- default = {}
+ default = {}
}
variable "dependabot_secrets" {
description = "An (Optional) map of Dependabot secrets to create for this repository. The key is the name of the secret and the value is the encrypted value."
type = map(string)
- default = {}
+ default = {}
}
variable "environments" {
@@ -90,7 +90,7 @@ variable "environments" {
variable "template_repository" {
description = "A (Optional) list of template repositories to use for the repository"
- type = object({
+ type = object({
owner = string
repository = string
include_all_branches = bool
@@ -102,4 +102,90 @@ variable "license_template" {
description = "The (Optional) license template to apply to the repository"
type = string
default = null
+}
+
+variable "rulesets" {
+ type = map(object({
+ bypass_actors = optional(object({
+ repository_roles = optional(list(object({
+ role = string
+ always_bypass = optional(bool)
+ })))
+ teams = optional(list(object({
+ team = string
+ always_bypass = optional(bool)
+ })))
+ integrations = optional(list(object({
+ installation_id = number
+ always_bypass = optional(bool)
+ })))
+ organization_admins = optional(list(object({
+ user = string
+ always_bypass = optional(bool)
+ })))
+ }))
+ conditions = optional(object({
+ ref_name = object({
+ include = list(string)
+ exclude = list(string)
+ })
+ }))
+ rules = object({
+ branch_name_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ tag_name_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ commit_author_email_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ commit_message_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ committer_email_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ creation = optional(bool)
+ deletion = optional(bool)
+ update = optional(bool)
+ non_fast_forward = optional(bool)
+ required_linear_history = optional(bool)
+ required_signatures = optional(bool)
+ update_allows_fetch_and_merge = optional(bool)
+ pull_request = optional(object({
+ dismiss_stale_reviews_on_push = optional(bool)
+ require_code_owner_review = optional(bool)
+ require_last_push_approval = optional(bool)
+ required_approving_review_count = optional(number)
+ required_review_thread_resolution = optional(bool)
+ }))
+ required_status_checks = optional(object({
+ required_check = list(object({
+ context = string
+ integration_id = optional(number)
+ }))
+ strict_required_status_check_policy = optional(bool)
+ }))
+ required_deployment_environments = optional(list(string))
+ })
+ target = string
+ enforcement = string
+ }))
+ default = {}
}
\ No newline at end of file
diff --git a/modules/public_repository/versions.tf b/modules/public_repository/versions.tf
index d6e8a27..d32b1bb 100644
--- a/modules/public_repository/versions.tf
+++ b/modules/public_repository/versions.tf
@@ -1,9 +1,9 @@
terraform {
- required_version = ">= 1.7.1"
+ required_version = ">= 1.3"
required_providers {
github = {
source = "integrations/github"
- version = "5.42.0"
+ version = "6.1.0"
}
}
}
\ No newline at end of file
diff --git a/modules/repository_base/README.md b/modules/repository_base/README.md
index f357a5d..d05a568 100644
--- a/modules/repository_base/README.md
+++ b/modules/repository_base/README.md
@@ -2,33 +2,38 @@
| Name | Version |
|------|---------|
-| [terraform](#requirement\_terraform) | >= 1.7.1 |
-| [github](#requirement\_github) | 5.42.0 |
+| [terraform](#requirement\_terraform) | >= 1.3 |
+| [github](#requirement\_github) | 6.1.0 |
## Providers
| Name | Version |
|------|---------|
-| [github](#provider\_github) | 5.42.0 |
+| [github](#provider\_github) | 6.1.0 |
## Modules
-No modules.
+| Name | Source | Version |
+|------|--------|---------|
+| [ruleset](#module\_ruleset) | ../ruleset | n/a |
## Resources
| Name | Type |
|------|------|
-| [github_actions_environment_secret.environment_secret](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/actions_environment_secret) | resource |
-| [github_actions_secret.actions_secret](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/actions_secret) | resource |
-| [github_branch_default.default_branch](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/branch_default) | resource |
-| [github_codespaces_secret.codespaces_secret](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/codespaces_secret) | resource |
-| [github_dependabot_secret.dependabot_secret](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/dependabot_secret) | resource |
-| [github_repository.repository](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/repository) | resource |
-| [github_repository_collaborators.collaborators](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/repository_collaborators) | resource |
-| [github_repository_dependabot_security_updates.automated_security_fixes](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/repository_dependabot_security_updates) | resource |
-| [github_repository_environment.environment](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/repository_environment) | resource |
-| [github_repository_ruleset.protected_branch_base_rules](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/repository_ruleset) | resource |
+| [github_actions_environment_secret.environment_secret](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/actions_environment_secret) | resource |
+| [github_actions_secret.actions_secret](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/actions_secret) | resource |
+| [github_branch_default.default_branch](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/branch_default) | resource |
+| [github_codespaces_secret.codespaces_secret](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/codespaces_secret) | resource |
+| [github_dependabot_secret.dependabot_secret](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/dependabot_secret) | resource |
+| [github_repository.repository](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/repository) | resource |
+| [github_repository_collaborators.collaborators](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/repository_collaborators) | resource |
+| [github_repository_dependabot_security_updates.automated_security_fixes](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/repository_dependabot_security_updates) | resource |
+| [github_repository_environment.environment](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/repository_environment) | resource |
+| [github_repository_ruleset.protected_branch_base_rules](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/repository_ruleset) | resource |
+| [github_organization_custom_role.branch_ruleset_bypasser](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/data-sources/organization_custom_role) | data source |
+| [github_team.branch_ruleset_bypasser](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/data-sources/team) | data source |
+| [github_user.branch_ruleset_bypasser](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/data-sources/user) | data source |
## Inputs
@@ -55,6 +60,7 @@ No modules.
| [name](#input\_name) | The name of the repository to create/import. | `string` | n/a | yes |
| [protected\_branches](#input\_protected\_branches) | A list of ref names or patterns that should be protected. Setting to `[]` means no protection. Defaults `["~DEFAULT_BRANCH"]` | `list(string)` | [| no | | [repository\_team\_permissions](#input\_repository\_team\_permissions) | A map where the keys are github team slugs and the value is the permissions the team should have in the repository | `map(string)` | n/a | yes | +| [rulesets](#input\_rulesets) | n/a |
"~DEFAULT_BRANCH"
]
map(object({
bypass_actors = optional(object({
repository_roles = optional(list(object({
role = string
always_bypass = optional(bool)
})))
teams = optional(list(object({
team = string
always_bypass = optional(bool)
})))
integrations = optional(list(object({
installation_id = number
always_bypass = optional(bool)
})))
organization_admins = optional(list(object({
user = string
always_bypass = optional(bool)
})))
}))
conditions = optional(object({
ref_name = object({
include = list(string)
exclude = list(string)
})
}))
rules = object({
branch_name_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
tag_name_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
commit_author_email_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
commit_message_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
committer_email_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
creation = optional(bool)
deletion = optional(bool)
update = optional(bool)
non_fast_forward = optional(bool)
required_linear_history = optional(bool)
required_signatures = optional(bool)
update_allows_fetch_and_merge = optional(bool)
pull_request = optional(object({
dismiss_stale_reviews_on_push = optional(bool)
require_code_owner_review = optional(bool)
require_last_push_approval = optional(bool)
required_approving_review_count = optional(number)
required_review_thread_resolution = optional(bool)
}))
required_status_checks = optional(object({
required_check = list(object({
context = string
integration_id = optional(number)
}))
strict_required_status_check_policy = optional(bool)
}))
required_deployment_environments = optional(list(string))
})
target = string
enforcement = string
})) | `{}` | no |
| [secret\_scanning](#input\_secret\_scanning) | Enables secret scanning for the repository. If repository is private `advance_security` must also be enabled. | `bool` | `true` | no |
| [secret\_scanning\_on\_push](#input\_secret\_scanning\_on\_push) | Enables secret scanning push protection for the repository. If repository is private `advance_security` must also be enabled. | `bool` | `true` | no |
| [template\_repository](#input\_template\_repository) | A (Optional) list of template repositories to use for the repository | object({
owner = string
repository = string
include_all_branches = bool
}) | `null` | no |
diff --git a/modules/repository_base/outputs.tf b/modules/repository_base/outputs.tf
index 33078a8..1d91b91 100644
--- a/modules/repository_base/outputs.tf
+++ b/modules/repository_base/outputs.tf
@@ -1,4 +1,4 @@
output "id" {
- value = github_repository.repository.repo_id
+ value = github_repository.repository.repo_id
description = "The ID of the repository"
}
\ No newline at end of file
diff --git a/modules/repository_base/rulesets.tf b/modules/repository_base/rulesets.tf
new file mode 100644
index 0000000..a199f5d
--- /dev/null
+++ b/modules/repository_base/rulesets.tf
@@ -0,0 +1,101 @@
+locals {
+ all_team_bypassers = toset(flatten(
+ [
+ for _, ruleset_config in var.rulesets : coalesce(try(ruleset_config.bypass_actors.teams, []), [])
+ ]
+ ))
+
+ all_admin_bypassers = toset(flatten(
+ [
+ for _, ruleset_config in var.rulesets : coalesce(try(ruleset_config.bypass_actors.organization_admins, []), [])
+ ]
+ ))
+
+ all_repository_roles_bypassers = toset(flatten(
+ [
+ for _, ruleset_config in var.rulesets : coalesce(try(ruleset_config.bypass_actors.repository_roles, []), [])
+ ]
+ ))
+
+ github_base_role_ids = {
+ "maintain" = 2
+ "write" = 4
+ "admin" = 5
+ }
+}
+
+
+data "github_team" "branch_ruleset_bypasser" {
+ for_each = {
+ for bypasser in local.all_team_bypassers : bypasser.team => bypasser.team
+ }
+
+ slug = each.value
+ summary_only = true
+}
+
+data "github_user" "branch_ruleset_bypasser" {
+ for_each = {
+ for bypasser in local.all_admin_bypassers : bypasser.user => bypasser.user
+ }
+
+ username = each.value
+}
+
+data "github_organization_custom_role" "branch_ruleset_bypasser" {
+ for_each = {
+ for bypasser in local.all_repository_roles_bypassers : bypasser.role => bypasser.role
+ }
+
+ name = each.value
+}
+
+module "ruleset" {
+ source = "../ruleset"
+
+ for_each = var.rulesets
+
+ name = each.key
+ target = each.value.target
+ enforcement = each.value.enforcement
+
+ ruleset_type = "repository"
+
+ rules = {
+ creation = each.value.rules.creation
+ update = each.value.rules.update
+ deletion = each.value.rules.deletion
+ non_fast_forward = each.value.rules.non_fast_forward
+ required_linear_history = each.value.rules.required_linear_history
+ required_signatures = each.value.rules.required_signatures
+ update_allows_fetch_and_merge = each.value.rules.update_allows_fetch_and_merge
+
+ branch_name_pattern = each.value.rules.branch_name_pattern
+ tag_name_pattern = each.value.rules.tag_name_pattern
+ commit_author_email_pattern = each.value.rules.commit_author_email_pattern
+ commit_message_pattern = each.value.rules.commit_message_pattern
+ committer_email_pattern = each.value.rules.committer_email_pattern
+ pull_request = each.value.rules.pull_request
+ required_status_checks = each.value.rules.required_status_checks
+ required_deployment_environments = each.value.rules.required_deployment_environments
+ }
+
+ bypass_actors = {
+ repository_roles = [for bypasser in try(toset(coalesce(each.value.bypass_actors.repository_roles, [])), []) : {
+ role_id = lookup(local.github_base_role_ids, bypasser.role, data.github_organization_custom_role.branch_ruleset_bypasser["${bypasser.role}"].id)
+ always_bypass = bypasser.always_bypass
+ }]
+ teams = [for bypasser in try(toset(coalesce(each.value.bypass_actors.teams, [])), []) : {
+ team_id = data.github_team.branch_ruleset_bypasser["${bypasser.team}"].id
+ always_bypass = bypasser.always_bypass
+ }]
+ organization_admins = [for bypasser in try(toset(coalesce(each.value.bypass_actors.organization_admins, [])), []) : {
+ user_id = data.github_user.branch_ruleset_bypasser["${bypasser.user}"].id
+ always_bypass = bypasser.always_bypass
+ }]
+ integrations = try(each.value.bypass_actors.repository_roles, [])
+ }
+
+ ref_name_inclusions = each.value.conditions.ref_name.include
+ ref_name_exclusions = each.value.conditions.ref_name.exclude
+}
\ No newline at end of file
diff --git a/modules/repository_base/variables.tf b/modules/repository_base/variables.tf
index a9a2f0d..9a49ac2 100644
--- a/modules/repository_base/variables.tf
+++ b/modules/repository_base/variables.tf
@@ -144,7 +144,7 @@ variable "environments" {
variable "template_repository" {
description = "A (Optional) list of template repositories to use for the repository"
- type = object({
+ type = object({
owner = string
repository = string
include_all_branches = bool
@@ -156,4 +156,90 @@ variable "license_template" {
description = "The (Optional) license template to use for the repository"
type = string
default = null
+}
+
+variable "rulesets" {
+ type = map(object({
+ bypass_actors = optional(object({
+ repository_roles = optional(list(object({
+ role = string
+ always_bypass = optional(bool)
+ })))
+ teams = optional(list(object({
+ team = string
+ always_bypass = optional(bool)
+ })))
+ integrations = optional(list(object({
+ installation_id = number
+ always_bypass = optional(bool)
+ })))
+ organization_admins = optional(list(object({
+ user = string
+ always_bypass = optional(bool)
+ })))
+ }))
+ conditions = optional(object({
+ ref_name = object({
+ include = list(string)
+ exclude = list(string)
+ })
+ }))
+ rules = object({
+ branch_name_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ tag_name_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ commit_author_email_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ commit_message_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ committer_email_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ creation = optional(bool)
+ deletion = optional(bool)
+ update = optional(bool)
+ non_fast_forward = optional(bool)
+ required_linear_history = optional(bool)
+ required_signatures = optional(bool)
+ update_allows_fetch_and_merge = optional(bool)
+ pull_request = optional(object({
+ dismiss_stale_reviews_on_push = optional(bool)
+ require_code_owner_review = optional(bool)
+ require_last_push_approval = optional(bool)
+ required_approving_review_count = optional(number)
+ required_review_thread_resolution = optional(bool)
+ }))
+ required_status_checks = optional(object({
+ required_check = list(object({
+ context = string
+ integration_id = optional(number)
+ }))
+ strict_required_status_check_policy = optional(bool)
+ }))
+ required_deployment_environments = optional(list(string))
+ })
+ target = string
+ enforcement = string
+ }))
+ default = {}
}
\ No newline at end of file
diff --git a/modules/repository_base/versions.tf b/modules/repository_base/versions.tf
index d6e8a27..d32b1bb 100644
--- a/modules/repository_base/versions.tf
+++ b/modules/repository_base/versions.tf
@@ -1,9 +1,9 @@
terraform {
- required_version = ">= 1.7.1"
+ required_version = ">= 1.3"
required_providers {
github = {
source = "integrations/github"
- version = "5.42.0"
+ version = "6.1.0"
}
}
}
\ No newline at end of file
diff --git a/modules/repository_set/README.md b/modules/repository_set/README.md
index 534a87f..6f09872 100644
--- a/modules/repository_set/README.md
+++ b/modules/repository_set/README.md
@@ -2,14 +2,14 @@
| Name | Version |
|------|---------|
-| [terraform](#requirement\_terraform) | >= 1.7.1 |
-| [github](#requirement\_github) | 5.42.0 |
+| [terraform](#requirement\_terraform) | >= 1.3 |
+| [github](#requirement\_github) | 6.1.0 |
## Providers
| Name | Version |
|------|---------|
-| [github](#provider\_github) | 5.42.0 |
+| [github](#provider\_github) | 6.1.0 |
## Modules
@@ -22,9 +22,9 @@
| Name | Type |
|------|------|
-| [github_actions_organization_secret_repositories.org__action_secret_repo_access](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/actions_organization_secret_repositories) | resource |
-| [github_codespaces_organization_secret_repositories.org__codespace_secret_repo_access](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/codespaces_organization_secret_repositories) | resource |
-| [github_dependabot_organization_secret_repositories.org__dependabot_secret_repo_access](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/dependabot_organization_secret_repositories) | resource |
+| [github_actions_organization_secret_repositories.org__action_secret_repo_access](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/actions_organization_secret_repositories) | resource |
+| [github_codespaces_organization_secret_repositories.org__codespace_secret_repo_access](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/codespaces_organization_secret_repositories) | resource |
+| [github_dependabot_organization_secret_repositories.org__dependabot_secret_repo_access](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/dependabot_organization_secret_repositories) | resource |
## Inputs
@@ -33,6 +33,7 @@
| [default\_repository\_team\_permissions](#input\_default\_repository\_team\_permissions) | A map where the keys are github team slugs and the value is the permissions the team should have by default for every repository. If an entry exists in `repository_team_permissions_override` for a repository then that will take precedence over this default. | `map(string)` | n/a | yes |
| [private\_repositories](#input\_private\_repositories) | A map of private repositories where the key is the repository name and the value is the configuration | map(object({
description = string
default_branch = string
repository_team_permissions_override = map(string)
protected_branches = list(string)
advance_security = bool
has_vulnerability_alerts = bool
topics = list(string)
homepage = string
delete_head_on_merge = bool
allow_auto_merge = bool
dependabot_security_updates = bool
organization_action_secrets = optional(list(string))
organization_codespace_secrets = optional(list(string))
organization_dependabot_secrets = optional(list(string))
action_secrets = optional(map(string))
codespace_secrets = optional(map(string))
dependabot_secrets = optional(map(string))
environments = optional(map(object({
action_secrets = optional(map(string))
})))
template_repository = optional(object({
owner = string
repository = string
include_all_branches = bool
}))
license_template = optional(string)
})) | n/a | yes |
| [public\_repositories](#input\_public\_repositories) | A map of public repositories where the key is the repository name and the value is the configuration | map(object({
description = string
default_branch = string
repository_team_permissions_override = map(string)
protected_branches = list(string)
advance_security = bool
topics = list(string)
homepage = string
delete_head_on_merge = bool
allow_auto_merge = bool
dependabot_security_updates = bool
organization_action_secrets = optional(list(string))
organization_codespace_secrets = optional(list(string))
organization_dependabot_secrets = optional(list(string))
action_secrets = optional(map(string))
codespace_secrets = optional(map(string))
dependabot_secrets = optional(map(string))
environments = optional(map(object({
action_secrets = optional(map(string))
})))
template_repository = optional(object({
owner = string
repository = string
include_all_branches = bool
}))
license_template = optional(string)
})) | n/a | yes |
+| [rulesets](#input\_rulesets) | n/a | map(object({
bypass_actors = optional(object({
repository_roles = optional(list(object({
role = string
always_bypass = optional(bool)
})))
teams = optional(list(object({
team = string
always_bypass = optional(bool)
})))
integrations = optional(list(object({
installation_id = number
always_bypass = optional(bool)
})))
organization_admins = optional(list(object({
user = string
always_bypass = optional(bool)
})))
}))
conditions = optional(object({
ref_name = object({
include = list(string)
exclude = list(string)
})
}))
rules = object({
branch_name_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
tag_name_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
commit_author_email_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
commit_message_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
committer_email_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
creation = optional(bool)
deletion = optional(bool)
update = optional(bool)
non_fast_forward = optional(bool)
required_linear_history = optional(bool)
required_signatures = optional(bool)
update_allows_fetch_and_merge = optional(bool)
pull_request = optional(object({
dismiss_stale_reviews_on_push = optional(bool)
require_code_owner_review = optional(bool)
require_last_push_approval = optional(bool)
required_approving_review_count = optional(number)
required_review_thread_resolution = optional(bool)
}))
required_status_checks = optional(object({
required_check = list(object({
context = string
integration_id = optional(number)
}))
strict_required_status_check_policy = optional(bool)
}))
required_deployment_environments = optional(list(string))
})
target = string
enforcement = string
repositories = list(string)
})) | `{}` | no |
## Outputs
diff --git a/modules/repository_set/organization-secrets.tf b/modules/repository_set/organization-secrets.tf
index 12c0127..599a08c 100644
--- a/modules/repository_set/organization-secrets.tf
+++ b/modules/repository_set/organization-secrets.tf
@@ -1,5 +1,5 @@
locals {
- coalesced_public_repositories = coalesce(var.public_repositories, {})
+ coalesced_public_repositories = coalesce(var.public_repositories, {})
coalesced_private_repositories = coalesce(var.private_repositories, {})
organization_action_secrets = distinct(flatten(concat(
@@ -42,20 +42,20 @@ locals {
resource "github_actions_organization_secret_repositories" "org__action_secret_repo_access" {
for_each = local.organization_action_secrets_repository_id_list
- secret_name = each.key
+ secret_name = each.key
selected_repository_ids = each.value
}
resource "github_codespaces_organization_secret_repositories" "org__codespace_secret_repo_access" {
for_each = local.codespace_secrets_repository_id_list
- secret_name = each.key
+ secret_name = each.key
selected_repository_ids = each.value
}
resource "github_dependabot_organization_secret_repositories" "org__dependabot_secret_repo_access" {
for_each = local.dependabot_secrets_id_list
- secret_name = each.key
+ secret_name = each.key
selected_repository_ids = each.value
}
\ No newline at end of file
diff --git a/modules/repository_set/repositories.tf b/modules/repository_set/repositories.tf
index af80a83..4a390b9 100644
--- a/modules/repository_set/repositories.tf
+++ b/modules/repository_set/repositories.tf
@@ -1,3 +1,16 @@
+locals {
+ rulesets_by_public_repository = {
+ for repo_name, repo_config in var.public_repositories : repo_name => {
+ for ruleset_name, ruleset_config in var.rulesets : ruleset_name => ruleset_config if contains(ruleset_config.repositories, repo_name)
+ }
+ }
+ rulesets_by_private_repository = {
+ for repo_name, repo_config in var.private_repositories : repo_name => {
+ for ruleset_name, ruleset_config in var.rulesets : ruleset_name => ruleset_config if contains(ruleset_config.repositories, repo_name)
+ }
+ }
+}
+
module "public_repositories" {
source = "../public_repository"
@@ -20,6 +33,7 @@ module "public_repositories" {
environments = each.value.environments
template_repository = each.value.template_repository
license_template = each.value.license_template
+ rulesets = lookup(local.rulesets_by_public_repository, each.key, {})
}
module "private_repositories" {
@@ -44,4 +58,6 @@ module "private_repositories" {
environments = each.value.environments
template_repository = each.value.template_repository
license_template = each.value.license_template
+ rulesets = lookup(local.rulesets_by_private_repository, each.key, {})
+
}
diff --git a/modules/repository_set/variables.tf b/modules/repository_set/variables.tf
index aac97f7..5d7a6ab 100644
--- a/modules/repository_set/variables.tf
+++ b/modules/repository_set/variables.tf
@@ -65,3 +65,90 @@ variable "default_repository_team_permissions" {
type = map(string)
description = "A map where the keys are github team slugs and the value is the permissions the team should have by default for every repository. If an entry exists in `repository_team_permissions_override` for a repository then that will take precedence over this default."
}
+
+variable "rulesets" {
+ type = map(object({
+ bypass_actors = optional(object({
+ repository_roles = optional(list(object({
+ role = string
+ always_bypass = optional(bool)
+ })))
+ teams = optional(list(object({
+ team = string
+ always_bypass = optional(bool)
+ })))
+ integrations = optional(list(object({
+ installation_id = number
+ always_bypass = optional(bool)
+ })))
+ organization_admins = optional(list(object({
+ user = string
+ always_bypass = optional(bool)
+ })))
+ }))
+ conditions = optional(object({
+ ref_name = object({
+ include = list(string)
+ exclude = list(string)
+ })
+ }))
+ rules = object({
+ branch_name_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ tag_name_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ commit_author_email_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ commit_message_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ committer_email_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ creation = optional(bool)
+ deletion = optional(bool)
+ update = optional(bool)
+ non_fast_forward = optional(bool)
+ required_linear_history = optional(bool)
+ required_signatures = optional(bool)
+ update_allows_fetch_and_merge = optional(bool)
+ pull_request = optional(object({
+ dismiss_stale_reviews_on_push = optional(bool)
+ require_code_owner_review = optional(bool)
+ require_last_push_approval = optional(bool)
+ required_approving_review_count = optional(number)
+ required_review_thread_resolution = optional(bool)
+ }))
+ required_status_checks = optional(object({
+ required_check = list(object({
+ context = string
+ integration_id = optional(number)
+ }))
+ strict_required_status_check_policy = optional(bool)
+ }))
+ required_deployment_environments = optional(list(string))
+ })
+ target = string
+ enforcement = string
+ repositories = list(string)
+ }))
+ default = {}
+}
\ No newline at end of file
diff --git a/modules/repository_set/versions.tf b/modules/repository_set/versions.tf
index d6e8a27..d32b1bb 100644
--- a/modules/repository_set/versions.tf
+++ b/modules/repository_set/versions.tf
@@ -1,9 +1,9 @@
terraform {
- required_version = ">= 1.7.1"
+ required_version = ">= 1.3"
required_providers {
github = {
source = "integrations/github"
- version = "5.42.0"
+ version = "6.1.0"
}
}
}
\ No newline at end of file
diff --git a/modules/ruleset/README.md b/modules/ruleset/README.md
new file mode 100644
index 0000000..1809db2
--- /dev/null
+++ b/modules/ruleset/README.md
@@ -0,0 +1,44 @@
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 1.3 |
+| [github](#requirement\_github) | 6.1.0 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [github](#provider\_github) | 6.1.0 |
+
+## Modules
+
+No modules.
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [github_organization_ruleset.ruleset](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/organization_ruleset) | resource |
+| [github_repository_ruleset.ruleset](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/repository_ruleset) | resource |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [bypass\_actors](#input\_bypass\_actors) | An object containing fields for role, team, organization admin, and integration bypass actors. Defaults to `{}` | object({
repository_roles = optional(list(object({
role_id = string
always_bypass = optional(bool)
})))
teams = optional(list(object({
team_id = string
always_bypass = optional(bool)
})))
integrations = optional(list(object({
installation_id = number
always_bypass = optional(bool)
})))
organization_admins = optional(list(object({
user_id = string
always_bypass = optional(bool)
})))
}) | `{}` | no |
+| [conditions](#input\_conditions) | An object that describes what branches and repositories the ruleset should apply to. If `ruleset_type` is set to `repository` then this field is optional and repository\_name is ignored. | object({
ref_name = optional(object({
include = list(string)
exclude = list(string)
}))
repository_name = optional(object({
include = list(string)
exclude = list(string)
}))
}) | `{}` | no |
+| [enforcement](#input\_enforcement) | The enforcement level of the ruleset. Should be one of either `active`, `evaluate` or `disabled`. Defaults to `active` | `string` | `"active"` | no |
+| [name](#input\_name) | The name of the ruleset. | `string` | n/a | yes |
+| [ref\_name\_exclusions](#input\_ref\_name\_exclusions) | A list of ref names or patterns to exclude. Defaults to an empty list. If set and `ruleset_type` is set to `organization` then either `repository_name_inclusions` or `repository_name_exclusions` must be set to a list of atleast 1 string. | `list(string)` | `[]` | no |
+| [ref\_name\_inclusions](#input\_ref\_name\_inclusions) | A list of ref names or patterns to include. Defaults to an empty list. If set and `ruleset_type` is set to `organization` then either `repository_name_inclusions` or `repository_name_exclusions` must be set to a list of atleast 1 string. | `list(string)` | `[]` | no |
+| [repository](#input\_repository) | The repository to create the ruleset under. Only applicable if `ruleset_type` is set to `repository`. Defaults to "" | `string` | `""` | no |
+| [repository\_name\_exclusions](#input\_repository\_name\_exclusions) | A list of repository names or patterns to exclude. If `ruleset_type` is set to `repository` then this field is ignored. | `list(string)` | `[]` | no |
+| [repository\_name\_inclusions](#input\_repository\_name\_inclusions) | A list of repository names or patterns to include. If `ruleset_type` is set to `repository` then this field is ignored. | `list(string)` | `[]` | no |
+| [rules](#input\_rules) | An object containing fields for all the rule definitions the ruleset should enforce. | object({
branch_name_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
tag_name_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
commit_author_email_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
commit_message_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
committer_email_pattern = optional(object({
operator = string
pattern = string
name = optional(string)
negate = optional(bool)
}))
creation = optional(bool)
deletion = optional(bool)
update = optional(bool)
non_fast_forward = optional(bool)
required_linear_history = optional(bool)
required_signatures = optional(bool)
update_allows_fetch_and_merge = optional(bool)
pull_request = optional(object({
dismiss_stale_reviews_on_push = optional(bool)
require_code_owner_review = optional(bool)
require_last_push_approval = optional(bool)
required_approving_review_count = optional(number)
required_review_thread_resolution = optional(bool)
}))
required_status_checks = optional(object({
required_check = list(object({
context = string
integration_id = optional(number)
}))
strict_required_status_check_policy = optional(bool)
}))
required_workflows = optional(object({
required_workflows = list(object({
repository_id = number
path = string
ref = optional(string)
}))
}))
required_deployment_environments = optional(list(string))
}) | n/a | yes |
+| [ruleset\_type](#input\_ruleset\_type) | The type of rulset to make. Should be one of ether `organization` or `repository`. | `string` | n/a | yes |
+| [target](#input\_target) | The target of the ruleset. Should be one of either `branch` or `tag`. | `string` | n/a | yes |
+
+## Outputs
+
+No outputs.
\ No newline at end of file
diff --git a/modules/ruleset/organization_ruleset.tf b/modules/ruleset/organization_ruleset.tf
new file mode 100644
index 0000000..b2b531f
--- /dev/null
+++ b/modules/ruleset/organization_ruleset.tf
@@ -0,0 +1,172 @@
+resource "github_organization_ruleset" "ruleset" {
+ count = var.ruleset_type == "organization" ? 1 : 0
+
+ name = var.name
+ target = var.target
+ enforcement = var.enforcement
+
+ dynamic "conditions" {
+ for_each = length(concat(var.ref_name_inclusions, var.ref_name_exclusions)) > 0 ? [1] : []
+ content {
+ ref_name {
+ include = var.ref_name_inclusions
+ exclude = var.ref_name_exclusions
+ }
+
+ repository_name {
+ include = var.repository_name_inclusions
+ exclude = var.repository_name_exclusions
+ }
+ }
+ }
+
+ rules {
+ creation = var.rules.creation
+ update = var.rules.update
+ deletion = var.rules.deletion
+ non_fast_forward = var.rules.non_fast_forward
+ required_linear_history = var.rules.required_linear_history
+ required_signatures = var.rules.required_signatures
+
+ dynamic "branch_name_pattern" {
+ for_each = var.rules.branch_name_pattern != null ? [var.rules.branch_name_pattern] : []
+
+ content {
+ operator = branch_name_pattern.value.operator
+ pattern = branch_name_pattern.value.pattern
+ name = branch_name_pattern.value.name
+ negate = coalesce(branch_name_pattern.value.negate, false)
+ }
+ }
+
+ dynamic "tag_name_pattern" {
+ for_each = var.rules.tag_name_pattern != null ? [var.rules.tag_name_pattern] : []
+
+ content {
+ operator = tag_name_pattern.value.operator
+ pattern = tag_name_pattern.value.pattern
+ name = tag_name_pattern.value.name
+ negate = coalesce(tag_name_pattern.value.negate, false)
+ }
+ }
+
+ dynamic "commit_author_email_pattern" {
+ for_each = var.rules.commit_author_email_pattern != null ? [var.rules.commit_author_email_pattern] : []
+
+ content {
+ operator = commit_author_email_pattern.value.operator
+ pattern = commit_author_email_pattern.value.pattern
+ name = commit_author_email_pattern.value.name
+ negate = coalesce(commit_author_email_pattern.value.negate, false)
+ }
+ }
+
+ dynamic "commit_message_pattern" {
+ for_each = var.rules.commit_message_pattern != null ? [var.rules.commit_message_pattern] : []
+
+ content {
+ operator = commit_message_pattern.value.operator
+ pattern = commit_message_pattern.value.pattern
+ name = commit_message_pattern.value.name
+ negate = coalesce(commit_message_pattern.value.negate, false)
+ }
+ }
+
+ dynamic "committer_email_pattern" {
+ for_each = var.rules.committer_email_pattern != null ? [var.rules.committer_email_pattern] : []
+
+ content {
+ operator = committer_email_pattern.value.operator
+ pattern = committer_email_pattern.value.pattern
+ name = committer_email_pattern.value.name
+ negate = coalesce(committer_email_pattern.value.negate, false)
+ }
+ }
+
+ dynamic "pull_request" {
+ for_each = var.rules.pull_request != null ? [var.rules.pull_request] : []
+
+ content {
+ dismiss_stale_reviews_on_push = coalesce(pull_request.value.dismiss_stale_reviews_on_push, false)
+ require_code_owner_review = coalesce(pull_request.value.require_code_owner_review, false)
+ require_last_push_approval = coalesce(pull_request.value.require_last_push_approval, false)
+ required_approving_review_count = coalesce(pull_request.value.required_approving_review_count, 0)
+ required_review_thread_resolution = coalesce(pull_request.value.required_review_thread_resolution, false)
+ }
+ }
+
+ dynamic "required_status_checks" {
+ for_each = var.rules.required_status_checks != null ? [var.rules.required_status_checks] : []
+
+ content {
+ dynamic "required_check" {
+ for_each = required_status_checks.value.required_check
+
+ content {
+ context = required_check.value.context
+ integration_id = required_check.value.integration_id
+ }
+ }
+
+ strict_required_status_checks_policy = required_status_checks.value.strict_required_status_check_policy
+ }
+ }
+
+ dynamic "required_workflows" {
+ for_each = var.rules.required_workflows != null ? [var.rules.required_workflows] : []
+
+ content {
+
+ dynamic "required_workflow" {
+ for_each = required_workflows.value.required_workflows
+
+ content {
+ repository_id = required_workflow.value.repository_id
+ path = required_workflow.value.path
+ ref = coalesce(required_workflow.value.ref, "main")
+ }
+ }
+ }
+ }
+ }
+
+ dynamic "bypass_actors" {
+ for_each = var.bypass_actors != null ? toset(coalesce(var.bypass_actors.repository_roles, [])) : []
+
+ content {
+ actor_id = bypass_actors.value.role_id
+ actor_type = "RepositoryRole"
+ bypass_mode = coalesce(bypass_actors.value.always_bypass, false) ? "always" : "pull_request"
+ }
+ }
+
+ dynamic "bypass_actors" {
+ for_each = var.bypass_actors != null ? toset(coalesce(var.bypass_actors.teams, [])) : []
+
+ content {
+ actor_id = bypass_actors.value.team_id
+ actor_type = "Team"
+ bypass_mode = coalesce(bypass_actors.value.always_bypass, false) ? "always" : "pull_request"
+ }
+ }
+
+ dynamic "bypass_actors" {
+ for_each = var.bypass_actors != null ? toset(coalesce(var.bypass_actors.integrations, [])) : []
+
+ content {
+ actor_id = bypass_actors.value.installation_id
+ actor_type = "Integration"
+ bypass_mode = coalesce(bypass_actors.value.always_bypass, false) ? "always" : "pull_request"
+ }
+ }
+
+ dynamic "bypass_actors" {
+ for_each = var.bypass_actors != null ? toset(coalesce(var.bypass_actors.organization_admins, [])) : []
+
+ content {
+ actor_id = bypass_actors.value.user_id
+ actor_type = "OrganizationAdmin"
+ bypass_mode = coalesce(bypass_actors.value.always_bypass, false) ? "always" : "pull_request"
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/ruleset/repository_ruleset.tf b/modules/ruleset/repository_ruleset.tf
new file mode 100644
index 0000000..bcaa162
--- /dev/null
+++ b/modules/ruleset/repository_ruleset.tf
@@ -0,0 +1,157 @@
+resource "github_repository_ruleset" "ruleset" {
+ count = var.ruleset_type == "repository" ? 1 : 0
+ name = var.name
+ target = var.target
+ enforcement = var.enforcement
+
+ dynamic "conditions" {
+ for_each = length(concat(var.ref_name_inclusions, var.ref_name_exclusions)) > 0 ? [1] : []
+ content {
+ ref_name {
+ include = var.ref_name_inclusions
+ exclude = var.ref_name_exclusions
+ }
+ }
+ }
+
+ rules {
+ creation = var.rules.creation
+ update = var.rules.update
+ deletion = var.rules.deletion
+ non_fast_forward = var.rules.non_fast_forward
+ required_linear_history = var.rules.required_linear_history
+ required_signatures = var.rules.required_signatures
+ update_allows_fetch_and_merge = var.rules.update_allows_fetch_and_merge
+
+ dynamic "branch_name_pattern" {
+ for_each = var.rules.branch_name_pattern != null ? [var.rules.branch_name_pattern] : []
+
+ content {
+ operator = branch_name_pattern.value.operator
+ pattern = branch_name_pattern.value.pattern
+ name = branch_name_pattern.value.name
+ negate = coalesce(branch_name_pattern.value.negate, false)
+ }
+ }
+
+ dynamic "tag_name_pattern" {
+ for_each = var.rules.tag_name_pattern != null ? [var.rules.tag_name_pattern] : []
+
+ content {
+ operator = tag_name_pattern.value.operator
+ pattern = tag_name_pattern.value.pattern
+ name = tag_name_pattern.value.name
+ negate = coalesce(tag_name_pattern.value.negate, false)
+ }
+ }
+
+ dynamic "commit_author_email_pattern" {
+ for_each = var.rules.commit_author_email_pattern != null ? [var.rules.commit_author_email_pattern] : []
+
+ content {
+ operator = commit_author_email_pattern.value.operator
+ pattern = commit_author_email_pattern.value.pattern
+ name = commit_author_email_pattern.value.name
+ negate = coalesce(commit_author_email_pattern.value.negate, false)
+ }
+ }
+
+ dynamic "commit_message_pattern" {
+ for_each = var.rules.commit_message_pattern != null ? [var.rules.commit_message_pattern] : []
+
+ content {
+ operator = commit_message_pattern.value.operator
+ pattern = commit_message_pattern.value.pattern
+ name = commit_message_pattern.value.name
+ negate = coalesce(commit_message_pattern.value.negate, false)
+ }
+ }
+
+ dynamic "committer_email_pattern" {
+ for_each = var.rules.committer_email_pattern != null ? [var.rules.committer_email_pattern] : []
+
+ content {
+ operator = committer_email_pattern.value.operator
+ pattern = committer_email_pattern.value.pattern
+ name = committer_email_pattern.value.name
+ negate = coalesce(committer_email_pattern.value.negate, false)
+ }
+ }
+
+ dynamic "pull_request" {
+ for_each = var.rules.pull_request != null ? [var.rules.pull_request] : []
+
+ content {
+ dismiss_stale_reviews_on_push = coalesce(pull_request.value.dismiss_stale_reviews_on_push, false)
+ require_code_owner_review = coalesce(pull_request.value.require_code_owner_review, false)
+ require_last_push_approval = coalesce(pull_request.value.require_last_push_approval, false)
+ required_approving_review_count = coalesce(pull_request.value.required_approving_review_count, 0)
+ required_review_thread_resolution = coalesce(pull_request.value.required_review_thread_resolution, false)
+ }
+ }
+
+ dynamic "required_status_checks" {
+ for_each = var.rules.required_status_checks != null ? [var.rules.required_status_checks] : []
+
+ content {
+ dynamic "required_check" {
+ for_each = required_status_checks.value.required_check
+
+ content {
+ context = required_check.value.context
+ integration_id = required_check.value.integration_id
+ }
+ }
+
+ strict_required_status_checks_policy = required_status_checks.value.strict_required_status_check_policy
+ }
+ }
+
+ dynamic "required_deployments" {
+ for_each = var.rules.required_deployment_environments != null ? [var.rules.required_deployment_environments] : []
+
+ content {
+ required_deployment_environments = required_deployments.value
+ }
+ }
+ }
+
+ dynamic "bypass_actors" {
+ for_each = var.bypass_actors.repository_roles != null ? toset(coalesce(var.bypass_actors.repository_roles, [])) : []
+
+ content {
+ actor_id = bypass_actors.value.role_id
+ actor_type = "RepositoryRole"
+ bypass_mode = coalesce(bypass_actors.value.always_bypass, false) ? "always" : "pull_request"
+ }
+ }
+ dynamic "bypass_actors" {
+ for_each = var.bypass_actors != null ? toset(coalesce(var.bypass_actors.teams, [])) : []
+
+ content {
+ actor_id = bypass_actors.value.team_id
+ actor_type = "Team"
+ bypass_mode = coalesce(bypass_actors.value.always_bypass, false) ? "always" : "pull_request"
+ }
+ }
+
+ dynamic "bypass_actors" {
+ for_each = var.bypass_actors != null ? toset(coalesce(var.bypass_actors.integrations, [])) : []
+
+ content {
+ actor_id = bypass_actors.value.installation_id
+ actor_type = "Integration"
+ bypass_mode = coalesce(bypass_actors.value.always_bypass, false) ? "always" : "pull_request"
+ }
+ }
+
+ dynamic "bypass_actors" {
+ for_each = var.bypass_actors != null ? toset(coalesce(var.bypass_actors.organization_admins, [])) : []
+
+ content {
+ actor_id = bypass_actors.value.user_id
+ actor_type = "OrganizationAdmin"
+ bypass_mode = coalesce(bypass_actors.value.always_bypass, false) ? "always" : "pull_request"
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/ruleset/variables.tf b/modules/ruleset/variables.tf
new file mode 100644
index 0000000..1c245d9
--- /dev/null
+++ b/modules/ruleset/variables.tf
@@ -0,0 +1,165 @@
+variable "name" {
+ type = string
+ description = "The name of the ruleset."
+}
+
+variable "bypass_actors" {
+ type = object({
+ repository_roles = optional(list(object({
+ role_id = string
+ always_bypass = optional(bool)
+ })))
+ teams = optional(list(object({
+ team_id = string
+ always_bypass = optional(bool)
+ })))
+ integrations = optional(list(object({
+ installation_id = number
+ always_bypass = optional(bool)
+ })))
+ organization_admins = optional(list(object({
+ user_id = string
+ always_bypass = optional(bool)
+ })))
+ })
+ default = {}
+ description = "An object containing fields for role, team, organization admin, and integration bypass actors. Defaults to `{}`"
+}
+
+variable "rules" {
+ type = object({
+ branch_name_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ tag_name_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ commit_author_email_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ commit_message_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ committer_email_pattern = optional(object({
+ operator = string
+ pattern = string
+ name = optional(string)
+ negate = optional(bool)
+ }))
+ creation = optional(bool)
+ deletion = optional(bool)
+ update = optional(bool)
+ non_fast_forward = optional(bool)
+ required_linear_history = optional(bool)
+ required_signatures = optional(bool)
+ update_allows_fetch_and_merge = optional(bool)
+ pull_request = optional(object({
+ dismiss_stale_reviews_on_push = optional(bool)
+ require_code_owner_review = optional(bool)
+ require_last_push_approval = optional(bool)
+ required_approving_review_count = optional(number)
+ required_review_thread_resolution = optional(bool)
+ }))
+ required_status_checks = optional(object({
+ required_check = list(object({
+ context = string
+ integration_id = optional(number)
+ }))
+ strict_required_status_check_policy = optional(bool)
+ }))
+ required_workflows = optional(object({
+ required_workflows = list(object({
+ repository_id = number
+ path = string
+ ref = optional(string)
+ }))
+ }))
+ required_deployment_environments = optional(list(string))
+ })
+ description = "An object containing fields for all the rule definitions the ruleset should enforce."
+}
+
+variable "ref_name_inclusions" {
+ type = list(string)
+ description = "A list of ref names or patterns to include. Defaults to an empty list. If set and `ruleset_type` is set to `organization` then either `repository_name_inclusions` or `repository_name_exclusions` must be set to a list of atleast 1 string."
+ default = []
+}
+
+variable "ref_name_exclusions" {
+ type = list(string)
+ description = "A list of ref names or patterns to exclude. Defaults to an empty list. If set and `ruleset_type` is set to `organization` then either `repository_name_inclusions` or `repository_name_exclusions` must be set to a list of atleast 1 string."
+ default = []
+}
+
+variable "repository_name_inclusions" {
+ type = list(string)
+ description = "A list of repository names or patterns to include. If `ruleset_type` is set to `repository` then this field is ignored."
+ default = []
+}
+
+variable "repository_name_exclusions" {
+ type = list(string)
+ description = "A list of repository names or patterns to exclude. If `ruleset_type` is set to `repository` then this field is ignored."
+ default = []
+}
+
+variable "conditions" {
+ type = object({
+ ref_name = optional(object({
+ include = list(string)
+ exclude = list(string)
+ }))
+ repository_name = optional(object({
+ include = list(string)
+ exclude = list(string)
+ }))
+ })
+ description = "An object that describes what branches and repositories the ruleset should apply to. If `ruleset_type` is set to `repository` then this field is optional and repository_name is ignored."
+ default = {}
+}
+
+variable "target" {
+ type = string
+ description = "The target of the ruleset. Should be one of either `branch` or `tag`."
+ validation {
+ condition = can(regex("branch|tag", var.target))
+ error_message = "The target must be either `branch` or `tag`."
+ }
+}
+
+variable "ruleset_type" {
+ type = string
+ description = "The type of rulset to make. Should be one of ether `organization` or `repository`."
+ validation {
+ condition = can(regex("organization|repository", var.ruleset_type))
+ error_message = "The ruleset type must be either `organization` or `repository`."
+ }
+}
+
+variable "enforcement" {
+ type = string
+ description = "The enforcement level of the ruleset. Should be one of either `active`, `evaluate` or `disabled`. Defaults to `active`"
+ default = "active"
+ validation {
+ condition = can(regex("active|evaluate|disabled", var.enforcement))
+ error_message = "The enforcement level must be either `active`, `evaluate` or `disabled`."
+ }
+}
+
+variable "repository" {
+ type = string
+ description = "The repository to create the ruleset under. Only applicable if `ruleset_type` is set to `repository`. Defaults to \"\""
+ default = ""
+}
\ No newline at end of file
diff --git a/modules/ruleset/versions.tf b/modules/ruleset/versions.tf
new file mode 100644
index 0000000..d32b1bb
--- /dev/null
+++ b/modules/ruleset/versions.tf
@@ -0,0 +1,9 @@
+terraform {
+ required_version = ">= 1.3"
+ required_providers {
+ github = {
+ source = "integrations/github"
+ version = "6.1.0"
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/team/README.md b/modules/team/README.md
index ff3387c..0991dfc 100644
--- a/modules/team/README.md
+++ b/modules/team/README.md
@@ -3,13 +3,13 @@
| Name | Version |
|------|---------|
| [terraform](#requirement\_terraform) | >= 1.3 |
-| [github](#requirement\_github) | 5.42.0 |
+| [github](#requirement\_github) | 6.1.0 |
## Providers
| Name | Version |
|------|---------|
-| [github](#provider\_github) | 5.42.0 |
+| [github](#provider\_github) | 6.1.0 |
## Modules
@@ -19,9 +19,9 @@ No modules.
| Name | Type |
|------|------|
-| [github_team.team](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/team) | resource |
-| [github_team_membership.maintainers](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/team_membership) | resource |
-| [github_team_membership.members](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/team_membership) | resource |
+| [github_team.team](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/team) | resource |
+| [github_team_membership.maintainers](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/team_membership) | resource |
+| [github_team_membership.members](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/team_membership) | resource |
## Inputs
diff --git a/modules/team/versions.tf b/modules/team/versions.tf
index 99d757e..d32b1bb 100644
--- a/modules/team/versions.tf
+++ b/modules/team/versions.tf
@@ -3,7 +3,7 @@ terraform {
required_providers {
github = {
source = "integrations/github"
- version = "5.42.0"
+ version = "6.1.0"
}
}
}
\ No newline at end of file
diff --git a/modules/team_set/README.md b/modules/team_set/README.md
index e209b1f..468ce69 100644
--- a/modules/team_set/README.md
+++ b/modules/team_set/README.md
@@ -3,7 +3,7 @@
| Name | Version |
|------|---------|
| [terraform](#requirement\_terraform) | >= 1.3 |
-| [github](#requirement\_github) | 5.42.0 |
+| [github](#requirement\_github) | 6.1.0 |
## Providers
diff --git a/modules/team_set/versions.tf b/modules/team_set/versions.tf
index a369b06..95d7d87 100644
--- a/modules/team_set/versions.tf
+++ b/modules/team_set/versions.tf
@@ -3,7 +3,7 @@ terraform {
required_providers {
github = {
source = "integrations/github"
- version = "5.42.0"
+ version = "6.1.0"
}
}
}