diff --git a/action_secrets.tf b/action_secrets.tf index bf012a1..07be87c 100644 --- a/action_secrets.tf +++ b/action_secrets.tf @@ -7,7 +7,7 @@ resource "github_actions_secret" "secret" { repository = local.github_repo.name secret_name = each.key encrypted_value = base64encode(each.value) - + depends_on = [data.github_actions_public_key.repo_key] } diff --git a/environment.tf b/environment.tf index b201cce..2a43c81 100644 --- a/environment.tf +++ b/environment.tf @@ -1,6 +1,6 @@ resource "github_repository_environment" "environments" { for_each = { for env in var.environments : env.name => env } - + environment = each.value.name repository = github_repository.repo[0].name reviewers { @@ -14,7 +14,7 @@ resource "github_repository_environment" "environments" { } resource "github_actions_environment_secret" "environment_secrets" { - for_each = { + for_each = { for pair in flatten([ for env in var.environments : [ for secret in coalesce(env.secrets, []) : { @@ -30,12 +30,12 @@ resource "github_actions_environment_secret" "environment_secrets" { environment = each.value.env_name secret_name = each.value.name plaintext_value = each.value.value - + depends_on = [github_repository_environment.environments] } resource "github_actions_environment_variable" "environment_variables" { - for_each = { + for_each = { for pair in flatten([ for env in var.environments : [ for _var in coalesce(env.vars, []) : { @@ -51,6 +51,6 @@ resource "github_actions_environment_variable" "environment_variables" { environment = each.value.env_name variable_name = each.value.name value = each.value.value - + depends_on = [github_repository_environment.environments] } \ No newline at end of file diff --git a/github_branch.tf b/github_branch.tf index c04d1bb..60751ec 100644 --- a/github_branch.tf +++ b/github_branch.tf @@ -36,14 +36,14 @@ resource "github_branch_protection" "main" { count = (var.enforce_prs && !var.github_is_private) || var.github_is_private ? 1 : 0 repository_id = local.github_repo.node_id - pattern = var.github_default_branch - enforce_admins = var.github_enforce_admins_branch_protection - allows_deletions = false - allows_force_pushes = false - require_signed_commits = true - required_linear_history = true + pattern = var.github_default_branch + enforce_admins = var.github_enforce_admins_branch_protection + allows_deletions = false + allows_force_pushes = false + require_signed_commits = true + required_linear_history = true require_conversation_resolution = true - lock_branch = false + lock_branch = false dynamic "required_status_checks" { for_each = var.required_status_checks != null ? ["true"] : [] @@ -57,10 +57,10 @@ resource "github_branch_protection" "main" { for_each = var.enforce_prs ? ["true"] : [] content { dismiss_stale_reviews = var.github_dismiss_stale_reviews - restrict_dismissals = true + restrict_dismissals = true require_code_owner_reviews = var.github_require_code_owner_reviews required_approving_review_count = var.github_required_approving_review_count - require_last_push_approval = true + require_last_push_approval = true } } diff --git a/github_repo.tf b/github_repo.tf index 5b6fd5f..7b9f754 100644 --- a/github_repo.tf +++ b/github_repo.tf @@ -1,39 +1,39 @@ locals { repo_name = var.force_name ? var.name : "${var.name}-${formatdate("YYYYMMDD", timestamp())}" - + github_repo = var.create_repo ? github_repository.repo[0] : data.github_repository.existing[0] validate_merge_options = ( - var.github_allow_merge_commit || - var.github_allow_squash_merge || + var.github_allow_merge_commit || + var.github_allow_squash_merge || var.github_allow_rebase_merge ) ? null : file("ERROR: At least one merge option must be enabled") } resource "github_repository" "repo" { - count = var.create_repo ? 1 : 0 - name = local.repo_name - description = var.github_repo_description - visibility = var.github_is_private ? "private" : "public" - has_issues = var.github_has_issues - has_projects = var.github_has_projects - has_wiki = var.github_has_wiki - has_downloads = var.github_has_downloads - auto_init = var.github_auto_init - archive_on_destroy = var.archive_on_destroy - archived = var.archived - vulnerability_alerts = var.vulnerability_alerts - topics = var.github_repo_topics - homepage_url = var.homepage_url - gitignore_template = var.gitignore_template - license_template = var.license_template - is_template = var.is_template - has_discussions = try(var.github_has_discussions, false) - merge_commit_title = try(var.github_merge_commit_title, "MERGE_MESSAGE") - merge_commit_message = try(var.github_merge_commit_message, "PR_TITLE") - squash_merge_commit_title = try(var.github_squash_merge_commit_title, "COMMIT_OR_PR_TITLE") + count = var.create_repo ? 1 : 0 + name = local.repo_name + description = var.github_repo_description + visibility = var.github_is_private ? "private" : "public" + has_issues = var.github_has_issues + has_projects = var.github_has_projects + has_wiki = var.github_has_wiki + has_downloads = var.github_has_downloads + auto_init = var.github_auto_init + archive_on_destroy = var.archive_on_destroy + archived = var.archived + vulnerability_alerts = var.vulnerability_alerts + topics = var.github_repo_topics + homepage_url = var.homepage_url + gitignore_template = var.gitignore_template + license_template = var.license_template + is_template = var.is_template + has_discussions = try(var.github_has_discussions, false) + merge_commit_title = try(var.github_merge_commit_title, "MERGE_MESSAGE") + merge_commit_message = try(var.github_merge_commit_message, "PR_TITLE") + squash_merge_commit_title = try(var.github_squash_merge_commit_title, "COMMIT_OR_PR_TITLE") squash_merge_commit_message = try(var.github_squash_merge_commit_message, "COMMIT_MESSAGES") - allow_update_branch = try(var.github_allow_update_branch, true) + allow_update_branch = try(var.github_allow_update_branch, true) allow_merge_commit = var.github_allow_merge_commit allow_squash_merge = var.github_allow_squash_merge diff --git a/github_repo.tftest.hcl b/github_repo.tftest.hcl index 97c39e6..72b8138 100644 --- a/github_repo.tftest.hcl +++ b/github_repo.tftest.hcl @@ -1,16 +1,16 @@ # valid_string_concat.tftest.hcl variables { - name = "github-repo-test" - repo_org = "HappyPathway" - force_name = true - github_is_private = true - enforce_prs = false - archive_on_destroy = false - github_org_teams = [] - admin_teams = ["test-team"] + name = "github-repo-test" + repo_org = "HappyPathway" + force_name = true + github_is_private = true + enforce_prs = false + archive_on_destroy = false + github_org_teams = [] + admin_teams = ["test-team"] github_repo_description = "Test repository" - github_repo_topics = ["test", "terraform"] - create_repo = true + github_repo_topics = ["test", "terraform"] + create_repo = true security_and_analysis = { advanced_security = { status = "enabled" @@ -55,32 +55,32 @@ run "create_new_repository" { command = plan assert { - condition = module.github_repo[0].name == var.name + condition = module.github_repo[0].name == var.name error_message = "Repository name does not match input" } assert { - condition = module.github_repo[0].visibility == "private" + condition = module.github_repo[0].visibility == "private" error_message = "Repository visibility should be private" } assert { - condition = contains(module.github_repo[0].topics, "test") + condition = contains(module.github_repo[0].topics, "test") error_message = "Repository topics should include 'test'" } assert { - condition = contains(module.github_repo[0].topics, "terraform") + condition = contains(module.github_repo[0].topics, "terraform") error_message = "Repository topics should include 'terraform'" } assert { - condition = module.github_repo[0].security_and_analysis.advanced_security.status == "enabled" + condition = module.github_repo[0].security_and_analysis.advanced_security.status == "enabled" error_message = "Advanced security should be enabled" } assert { - condition = module.github_repo[0].security_and_analysis.secret_scanning.status == "enabled" + condition = module.github_repo[0].security_and_analysis.secret_scanning.status == "enabled" error_message = "Secret scanning should be enabled" } } @@ -93,28 +93,28 @@ run "verify_data_source" { command = plan assert { - condition = data.github_repository.existing[0].name == var.name + condition = data.github_repository.existing[0].name == var.name error_message = "Data source repository name does not match input" } } run "verify_branch_protection" { variables { - github_default_branch = "main" - enforce_prs = true - github_is_private = false + github_default_branch = "main" + enforce_prs = true + github_is_private = false github_required_approving_review_count = 2 } command = plan assert { - condition = github_branch_protection.main[0].pattern == "main" + condition = github_branch_protection.main[0].pattern == "main" error_message = "Branch protection pattern should be main" } assert { - condition = github_branch_protection.main[0].required_pull_request_reviews[0].required_approving_review_count == 2 + condition = github_branch_protection.main[0].required_pull_request_reviews[0].required_approving_review_count == 2 error_message = "Should require 2 review approvals" } } @@ -123,12 +123,12 @@ run "verify_repository_files" { command = plan assert { - condition = github_repository_file.extra_files["test.md"].file == "test.md" + condition = github_repository_file.extra_files["test.md"].file == "test.md" error_message = "Extra file should be created" } assert { - condition = github_repository_file.extra_files["test.md"].content == "Test content" + condition = github_repository_file.extra_files["test.md"].content == "Test content" error_message = "Extra file content should match input" } } @@ -137,7 +137,7 @@ run "verify_team_access" { command = plan assert { - condition = github_team_repository.admin["test-team"].permission == "admin" + condition = github_team_repository.admin["test-team"].permission == "admin" error_message = "Team should have admin access" } } @@ -146,12 +146,12 @@ run "verify_action_secrets" { command = plan assert { - condition = github_actions_secret.secret["TEST_SECRET"].secret_name == "TEST_SECRET" + condition = github_actions_secret.secret["TEST_SECRET"].secret_name == "TEST_SECRET" error_message = "Action secret should be created" } assert { - condition = github_actions_variable.variable["TEST_VAR"].variable_name == "TEST_VAR" + condition = github_actions_variable.variable["TEST_VAR"].variable_name == "TEST_VAR" error_message = "Action variable should be created" } } @@ -160,57 +160,57 @@ run "verify_outputs" { command = plan assert { - condition = output.github_repo.name == var.name + condition = output.github_repo.name == var.name error_message = "Output repository name does not match input" } assert { - condition = output.ssh_clone_url != "" + condition = output.ssh_clone_url != "" error_message = "SSH clone URL should not be empty" } assert { - condition = output.node_id != "" + condition = output.node_id != "" error_message = "Node ID should not be empty" } assert { - condition = output.full_name != "" + condition = output.full_name != "" error_message = "Full name should not be empty" } assert { - condition = output.repo_id != null + condition = output.repo_id != null error_message = "Repository ID should not be null" } assert { - condition = output.html_url != "" + condition = output.html_url != "" error_message = "HTML URL should not be empty" } assert { - condition = output.http_clone_url != "" + condition = output.http_clone_url != "" error_message = "HTTP clone URL should not be empty" } assert { - condition = output.git_clone_url != "" + condition = output.git_clone_url != "" error_message = "Git clone URL should not be empty" } assert { - condition = output.visibility == "private" + condition = output.visibility == "private" error_message = "Visibility should be private" } assert { - condition = output.default_branch == "main" + condition = output.default_branch == "main" error_message = "Default branch should be 'main'" } assert { - condition = length(output.topics) == 2 + condition = length(output.topics) == 2 error_message = "Should have exactly 2 topics" } } diff --git a/variables.tf b/variables.tf index 5e3485e..86fc2fb 100644 --- a/variables.tf +++ b/variables.tf @@ -184,7 +184,7 @@ variable "secrets" { name = string value = string })) - default = [] + default = [] validation { condition = alltrue([for s in var.secrets : can(regex("^[A-Z0-9_]+$", s.name))]) error_message = "Secret names must contain only uppercase letters, numbers, and underscores." @@ -197,7 +197,7 @@ variable "vars" { name = string value = string })) - default = [] + default = [] validation { condition = alltrue([for v in var.vars : can(regex("^[A-Z0-9_]+$", v.name))]) error_message = "Variable names must contain only uppercase letters, numbers, and underscores." @@ -210,7 +210,7 @@ variable "extra_files" { path = string content = string })) - default = [] + default = [] } variable "managed_extra_files" { @@ -219,7 +219,7 @@ variable "managed_extra_files" { path = string content = string })) - default = [] + default = [] } variable "pull_request_bypassers" { @@ -296,7 +296,7 @@ variable "security_and_analysis" { }) default = null validation { - condition = var.security_and_analysis == null ? true : alltrue([ + condition = var.security_and_analysis == null ? true : alltrue([ try(contains(["enabled", "disabled"], var.security_and_analysis.advanced_security.status), true), try(contains(["enabled", "disabled"], var.security_and_analysis.secret_scanning.status), true), try(contains(["enabled", "disabled"], var.security_and_analysis.secret_scanning_push_protection.status), true)