From 42744f2bf9fbd4c677cf36d87ad6f5a56c28e558 Mon Sep 17 00:00:00 2001 From: Maxwell Brown Date: Mon, 22 Jan 2024 20:53:26 -0500 Subject: [PATCH] Add support for arbitrary branch protection rules (#32) --- terraform/github/repository.tf | 28 +++---- terraform/github/terraform.tfvars | 20 +++-- terraform/github/variables.tf | 25 ++++++ terraform/modules/github_repository/branch.tf | 84 ++++++++++++------- .../modules/github_repository/repository.tf | 1 - .../modules/github_repository/variables.tf | 50 ++++++++++- 6 files changed, 149 insertions(+), 59 deletions(-) diff --git a/terraform/github/repository.tf b/terraform/github/repository.tf index e89adf1ed..6f9a57c8c 100644 --- a/terraform/github/repository.tf +++ b/terraform/github/repository.tf @@ -1,16 +1,16 @@ module "github_repository" { - source = "../modules/github_repository" - for_each = var.repositories - name = each.key - description = each.value.description - topics = each.value.topics - homepage_url = each.value.homepage_url - visibility = each.value.visibility - collaborators = each.value.collaborators - pages = each.value.pages - has_discussions = each.value.has_discussions - is_archived = each.value.is_archived - allow_squash_merge = each.value.allow_squash_merge - allow_rebase_merge = each.value.allow_rebase_merge - has_release_branches = each.value.has_release_branches + source = "../modules/github_repository" + for_each = var.repositories + name = each.key + description = each.value.description + topics = each.value.topics + homepage_url = each.value.homepage_url + visibility = each.value.visibility + collaborators = each.value.collaborators + pages = each.value.pages + has_discussions = each.value.has_discussions + is_archived = each.value.is_archived + allow_squash_merge = each.value.allow_squash_merge + allow_rebase_merge = each.value.allow_rebase_merge + branch_protection_rules = each.value.branch_protection_rules } diff --git a/terraform/github/terraform.tfvars b/terraform/github/terraform.tfvars index a4f6adef5..769a3d5eb 100755 --- a/terraform/github/terraform.tfvars +++ b/terraform/github/terraform.tfvars @@ -38,9 +38,6 @@ repositories = { pages = { build_type = "legacy" } is_archived = true } - discord-bot = { - description = "The Effect Community's custom Discord bot, built with Effect" - } docgen = { description = "An opinionated documentation generator for Effect projects" homepage_url = "https://effect-ts.github.io/docgen" @@ -61,6 +58,9 @@ repositories = { } effect = { description = "A fully-fledged functional effect system for TypeScript with a rich standard library" + homepage_url = "https://www.effect.website" + topics = ["effect-system", "fp", "framework", "stack-safe", "typescript", "zio"] + pages = { build_type = "workflow" } allow_rebase_merge = true collaborators = [ { username = "DenisFrezzato", permission = "push" }, @@ -68,10 +68,14 @@ repositories = { { username = "remiguittaut", permission = "push" }, { username = "rzeigler", permission = "push" }, ] - pages = { build_type = "workflow" } - homepage_url = "https://www.effect.website" - topics = ["effect-system", "fp", "framework", "stack-safe", "typescript", "zio"] - has_release_branches = true + branch_protection_rules = { + "next-*" = { + allows_force_pushes = true + required_status_checks = { + strict = true + } + } + } } eslint-plugin = { description = "A set of ESlint and TypeScript rules to work with Effect" @@ -112,7 +116,7 @@ repositories = { is_archived = true } monorepo-testing = { - collaborators = [{ username = "fubhy", permission = "push" }] + collaborators = [{ username = "fubhy", permission = "admin" }] } opentelemetry = { description = "OpenTelemetry integration with Effect" diff --git a/terraform/github/variables.tf b/terraform/github/variables.tf index 95169014f..7a04eeb1c 100644 --- a/terraform/github/variables.tf +++ b/terraform/github/variables.tf @@ -30,6 +30,31 @@ variable "repositories" { source_branch = optional(string) source_path = optional(string) })) + branch_protection_rules = optional(map(object({ + allows_deletions = optional(bool) + allows_force_pushes = optional(bool) + blocks_creations = optional(bool) + enforce_admins = optional(bool) + lock_branch = optional(bool) + require_signed_commits = optional(bool) + require_conversation_resolution = optional(bool) + push_restrictions = optional(list(string)) + force_push_bypassers = optional(list(string)) + required_linear_history = optional(bool) + required_pull_request_reviews = optional(object({ + dismiss_stale_reviews = optional(bool) + dismissal_restrictions = optional(list(string)) + restrict_dismissals = optional(bool) + pull_request_bypassers = optional(list(string)) + require_code_owner_reviews = optional(bool) + required_approving_review_count = optional(number) + require_last_push_approval = optional(bool) + })) + required_status_checks = optional(object({ + strict = optional(bool) + contexts = optional(list(string)) + })) + }))) })) } diff --git a/terraform/modules/github_repository/branch.tf b/terraform/modules/github_repository/branch.tf index 6fe016dea..86ca39fee 100644 --- a/terraform/modules/github_repository/branch.tf +++ b/terraform/modules/github_repository/branch.tf @@ -8,46 +8,66 @@ resource "github_branch_default" "main" { repository = github_repository.repository.name } -resource "github_branch_protection" "main" { +resource "github_branch_protection" "rules" { # Branch protection can only be enabled on private repositories if using a # paid GitHub plan - count = var.visibility == "public" ? 1 : 0 + for_each = var.visibility == "public" ? local.branch_protection_rules : tomap({}) - repository_id = github_repository.repository.node_id - pattern = var.default_branch - enforce_admins = true - required_linear_history = true + repository_id = github_repository.repository.node_id + pattern = each.key + allows_deletions = try(each.value.allows_deletions, false) + allows_force_pushes = try(each.value.allows_force_pushes, false) + blocks_creations = try(each.value.blocks_creations, false) + enforce_admins = try(each.value.enforce_admins, false) + lock_branch = try(each.value.lock_branch, false) + require_signed_commits = try(each.value.require_signed_commits, false) + require_conversation_resolution = try(each.value.require_conversation_resolution, false) + push_restrictions = try(each.value.push_restrictions, []) + force_push_bypassers = try(each.value.force_push_bypassers, []) + required_linear_history = try(each.value.required_linear_history, false) - required_status_checks { - strict = true - contexts = null + dynamic "required_pull_request_reviews" { + for_each = each.value.required_pull_request_reviews != null ? [1] : [] + content { + dismiss_stale_reviews = try(each.value.required_pull_request_reviews.dismiss_stale_reviews, false) + dismissal_restrictions = try(each.value.required_pull_request_reviews.dismissal_restrictions, []) + restrict_dismissals = try(each.value.required_pull_request_reviews.restrict_dismissals, false) + pull_request_bypassers = try(each.value.required_pull_request_reviews.pull_request_bypassers, []) + require_code_owner_reviews = try(each.value.required_pull_request_reviews.require_code_owner_reviews, false) + required_approving_review_count = try(each.value.required_pull_request_reviews.required_approving_review_count, null) + require_last_push_approval = try(each.value.required_pull_request_reviews.require_last_push_approval, false) + } } - required_pull_request_reviews { - required_approving_review_count = 0 + dynamic "required_status_checks" { + for_each = each.value.required_status_checks != null ? [1] : [] + content { + strict = try(each.value.required_status_checks.strict, false) + contexts = try(each.value.required_status_checks.contexts, []) + } } } -resource "github_branch_protection" "next-release" { - # Branch protection can only be enabled on private repositories if using a - # paid GitHub plan - count = var.visibility == "public" && var.has_release_branches ? 1 : 0 - - repository_id = github_repository.repository.node_id - pattern = "next-*" - enforce_admins = true - required_linear_history = false - allows_deletions = false - allows_force_pushes = true - blocks_creations = false - - required_status_checks { - strict = true - contexts = null - } +# resource "github_branch_protection" "next-release" { +# # Branch protection can only be enabled on private repositories if using a +# # paid GitHub plan +# count = var.visibility == "public" && var.has_release_branches ? 1 : 0 - required_pull_request_reviews { - required_approving_review_count = 0 - } -} +# repository_id = github_repository.repository.node_id +# pattern = "next-*" +# enforce_admins = true +# required_linear_history = false +# allows_deletions = false +# allows_force_pushes = true +# blocks_creations = false + +# required_status_checks { +# strict = true +# contexts = null +# } + +# required_pull_request_reviews { +# required_approving_review_count = 0 +# } +# } diff --git a/terraform/modules/github_repository/repository.tf b/terraform/modules/github_repository/repository.tf index 98e18c1db..159aab18e 100644 --- a/terraform/modules/github_repository/repository.tf +++ b/terraform/modules/github_repository/repository.tf @@ -19,7 +19,6 @@ resource "github_repository" "repository" { has_projects = var.has_projects has_wiki = var.visibility == "public" - dynamic "pages" { for_each = local.pages != null ? [1] : [] diff --git a/terraform/modules/github_repository/variables.tf b/terraform/modules/github_repository/variables.tf index df2f10bb5..8dabe8df8 100644 --- a/terraform/modules/github_repository/variables.tf +++ b/terraform/modules/github_repository/variables.tf @@ -133,8 +133,50 @@ variable "delete_branch_on_merge" { default = true } -variable "has_release_branches" { - type = bool - description = "Has next-* branches for releases" - default = false +variable "branch_protection_rules" { + description = "The branch protection rules to apply to the repository (the key of the map is the branch pattern to use)" + type = map(object({ + allows_deletions = optional(bool) + allows_force_pushes = optional(bool) + blocks_creations = optional(bool) + enforce_admins = optional(bool) + lock_branch = optional(bool) + require_signed_commits = optional(bool) + require_conversation_resolution = optional(bool) + push_restrictions = optional(list(string)) + force_push_bypassers = optional(list(string)) + required_linear_history = optional(bool) + required_pull_request_reviews = optional(object({ + dismiss_stale_reviews = optional(bool) + dismissal_restrictions = optional(list(string)) + restrict_dismissals = optional(bool) + pull_request_bypassers = optional(list(string)) + require_code_owner_reviews = optional(bool) + required_approving_review_count = optional(number) + require_last_push_approval = optional(bool) + })) + required_status_checks = optional(object({ + strict = optional(bool) + contexts = optional(list(string)) + })) + })) + default = {} +} +locals { + default_branch_protection_rule = { + "${var.default_branch}" = { + enforce_admins = true + required_linear_history = true + required_status_checks = { + strict = true + } + required_pull_request_reviews = { + required_approving_review_count = 0 + } + } + } + branch_protection_rules = merge( + local.default_branch_protection_rule, + var.branch_protection_rules + ) }