From c5285e1f6dea9bce4a512d8502fda7727650b4b9 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Fri, 12 Jul 2024 12:09:26 -0400 Subject: [PATCH 01/39] terraform org mgmt --- terraform/backend.tf | 12 ++ terraform/locals.tf | 55 ++++++ terraform/main.tf | 290 +++++++++++++++++++++++++++++ terraform/markdown/SECURITY.md.tpl | 10 + terraform/tfvars/production.tfvars | 141 ++++++++++++++ terraform/variables.tf | 73 ++++++++ 6 files changed, 581 insertions(+) create mode 100644 terraform/backend.tf create mode 100644 terraform/locals.tf create mode 100644 terraform/main.tf create mode 100644 terraform/markdown/SECURITY.md.tpl create mode 100644 terraform/tfvars/production.tfvars create mode 100644 terraform/variables.tf diff --git a/terraform/backend.tf b/terraform/backend.tf new file mode 100644 index 0000000..c7d7ef1 --- /dev/null +++ b/terraform/backend.tf @@ -0,0 +1,12 @@ +# Backend Configuration +# https://www.terraform.io/language/settings/backends/configuration + +terraform { + + # Google Cloud Storage + # https://www.terraform.io/language/settings/backends/gcs +# +# backend "gcs" { +# prefix = "github-organization-management" +# } +} diff --git a/terraform/locals.tf b/terraform/locals.tf new file mode 100644 index 0000000..54ec649 --- /dev/null +++ b/terraform/locals.tf @@ -0,0 +1,55 @@ +# Local Values +# https://www.terraform.io/language/values/locals + +locals { + + admins = { + for user in var.admins : user => "admin" + } + + branch_protections = { + for repository_key, repository in var.repositories : repository_key => repository if repository.enable_branch_protection + } + + child_team_repositories = { + + for repository in flatten([ + for team_child_key, team_child in var.team_children : [ + for repository in team_child.repositories : { + team_child = team_child_key + repository = repository + permission = team_child.permission + } + ] + ]) : "${repository.team_child}-${repository.repository}" => repository } + + datadog_webhooks = { + for repository_key, repository in var.repositories : repository_key => repository if repository.enable_datadog_webhook + } + + discord_webhooks = { + for repository_key, repository in var.repositories : repository_key => repository if repository.enable_discord_webhook + } + + members = { + for user in var.members : user => "member" + } + + parent_team_repositories = { + + for repository in flatten([ + for team_parent_key, team_parent in var.team_parents : [ + for repository in team_parent.repositories : { + team_parent = team_parent_key + repository = repository + permission = team_parent.permission + } + ] + ]) : "${repository.team_parent}-${repository.repository}" => repository } + + review_request_delegations = { + for team_parent_key, team_parent in var.team_parents : team_parent_key => team_parent if team_parent.review_request_delegation + } + + users = merge(local.admins, local.members) +} diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 0000000..84981c3 --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,290 @@ +# Required Providers +# https://www.terraform.io/docs/language/providers/requirements.html#requiring-providers + +terraform { + required_providers { + github = { + source = "integrations/github" + } + + # Random Provider + # https://registry.terraform.io/providers/hashicorp/random/latest/docs + + random = { + source = "hashicorp/random" + } + + # Time Provider + # https://registry.terraform.io/providers/hashicorp/time/latest/docs + + time = { + source = "hashicorp/time" + } + } +} + +# Github Provider +# https://registry.terraform.io/providers/integrations/github/latest/docs + +provider "github" { + owner = "django-commons" + token = var.github_token +} + +# Github Actions Secret Resource +# https://registry.terraform.io/providers/integrations/github/latest/docs/resources/actions_organization_secret + +resource "github_actions_organization_secret" "this" { + + # Ensure GitHub Actions secrets are encrypted + # checkov:skip=CKV_GIT_4: We are passing the secret from the random_password resource which is sensitive by default + # and not checking in any plain text values. State is always secured. + + for_each = var.organization_secrets + + plaintext_value = random_password.this[each.key].result + secret_name = each.key + visibility = each.value.visibility +} + +# Github Branch Protection Resource +# https://registry.terraform.io/providers/integrations/github/latest/docs/resources/branch_protection + +resource "github_branch_protection" "this" { + + # GitHub pull requests should require at least 2 approvals + # checkov:skip=CKV_GIT_5: 1 approval is reasonable for a small team + for_each = local.branch_protections + + enforce_admins = false + pattern = "main" + repository_id = github_repository.this[each.key].name + require_conversation_resolution = true + required_linear_history = true + require_signed_commits = true + + required_pull_request_reviews { + dismiss_stale_reviews = true + require_code_owner_reviews = true + required_approving_review_count = 1 + } + + required_status_checks { + contexts = each.value.required_status_checks_contexts + strict = true + } + + restrict_pushes { + push_allowances = each.value.push_allowances + } +} + +# GitHub Membership Resource +# https://registry.terraform.io/providers/integrations/github/latest/docs/resources/membership + +resource "github_membership" "this" { + for_each = local.users + + role = each.value + username = each.key +} + +# Github Organization Security Manager Resource +# https://registry.terraform.io/providers/integrations/github/latest/docs/resources/organization_security_manager + +resource "github_organization_security_manager" "this" { + team_slug = github_team.parents["security-team"].slug +} + +# Github Repository Resource +# https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository + +resource "github_repository" "this" { + + # Ensure GitHub repository is Private + # checkov:skip=CKV_GIT_1: Public is ok for us since we are an open source project + + for_each = var.repositories + + allow_auto_merge = true + allow_merge_commit = false + allow_rebase_merge = false + allow_squash_merge = true + allow_update_branch = true + archive_on_destroy = true + delete_branch_on_merge = true + description = each.value.description + has_downloads = false + has_discussions = each.value.has_discussions + has_issues = true + has_projects = true + has_wiki = false + is_template = each.value.is_template + name = each.key + squash_merge_commit_message = "BLANK" + squash_merge_commit_title = "PR_TITLE" + topics = each.value.topics + visibility = each.value.visibility + vulnerability_alerts = true + + dynamic "template" { + for_each = each.value.template != null ? [each.value.template] : [] + + content { + owner = "django-commons" + repository = template.value + } + } +} + +# GitHub Repository File Resource +# https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository_file + +resource "github_repository_file" "security_policy" { + for_each = var.repositories + + branch = "main" + content = templatefile("${path.module}/markdown/SECURITY.md.tpl", { repository = each.key }) + file = "SECURITY.md" + repository = each.key + commit_message = "Update SECURITY.md" + commit_author = "Open Source Infrastructure as Code Service Account" + commit_email = "github-sa@osinfra.io" + overwrite_on_create = true + + depends_on = [ + github_branch_protection.this + ] +} + + +# Github Team Resource +# https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team + +# If you need to import a team, you can do so with the following command: +# terraform import github_team.this\[\"google-cloud-platform\"\] + +# To get the team ids, you can run the following curl command with a token that has the read:org scope against your own organization. +# curl -H "Authorization: token $GITHUB_READ_ORG_TOKEN" https://api.github.com/orgs/osinfra-io/teams + +resource "github_team" "parents" { + for_each = var.team_parents + + name = each.key + description = each.value.description + privacy = each.value.privacy +} + +resource "github_team" "children" { + for_each = var.team_children + + description = each.value.description + name = each.key + parent_team_id = github_team.parents[each.value.parent_team_key].id + privacy = github_team.parents[each.value.parent_team_key].privacy +} + +# GitHub Team Membership Resource +# https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team_members + +resource "github_team_members" "parents" { + for_each = var.team_parents + + team_id = github_team.parents[each.key].id + + dynamic "members" { + for_each = each.value.members + + content { + username = members.value + role = "member" + } + } + + dynamic "members" { + for_each = each.value.maintainers + + content { + username = members.value + role = "maintainer" + } + } +} + +resource "github_team_members" "children" { + for_each = var.team_children + + team_id = github_team.children[each.key].id + + dynamic "members" { + for_each = each.value.members + + content { + username = members.value + role = "member" + } + } + + dynamic "members" { + for_each = each.value.maintainers + + content { + username = members.value + role = "maintainer" + } + } +} + +# Github Team Repository Resource +# https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team_repository + +resource "github_team_repository" "children" { + for_each = local.child_team_repositories + + team_id = github_team.children[each.value.team_child].id + repository = each.value.repository + permission = each.value.permission +} + +resource "github_team_repository" "parents" { + for_each = local.parent_team_repositories + + team_id = github_team.parents[each.value.team_parent].id + repository = each.value.repository + permission = each.value.permission +} + +# GitHub Team Settings Resource +# https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team_settings + +resource "github_team_settings" "this" { + for_each = local.review_request_delegations + + review_request_delegation { + algorithm = "LOAD_BALANCE" + member_count = 2 + notify = false + } + + team_id = github_team.parents[each.key].id +} + +# Random Password Resource +# https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password + +resource "random_password" "this" { + for_each = var.organization_secrets + length = 32 + special = false + + keepers = { + rotation_time = time_rotating.this.rotation_rfc3339 + } +} + +# Time Rotating Resource +# https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/rotating + +resource "time_rotating" "this" { + rotation_days = 5 +} diff --git a/terraform/markdown/SECURITY.md.tpl b/terraform/markdown/SECURITY.md.tpl new file mode 100644 index 0000000..e97d9d6 --- /dev/null +++ b/terraform/markdown/SECURITY.md.tpl @@ -0,0 +1,10 @@ +# Security Policy + +Open Source Infrastructure (as Code) exposes identifying information that would not be exposed in a traditional +private organization to share knowledge and best practices. This is a net positive for +the community, but it does come with some risks. + +## Reporting a Vulnerability + +Privately discuss, fix, and publish information about security vulnerabilities in this repository by drafting a new +[security advisory](https://github.com/osinfra-io/${repository}/security/advisories/new). diff --git a/terraform/tfvars/production.tfvars b/terraform/tfvars/production.tfvars new file mode 100644 index 0000000..366a0d0 --- /dev/null +++ b/terraform/tfvars/production.tfvars @@ -0,0 +1,141 @@ +admins = [ + "tim-schilling", + "cunla" +] + +organization_secrets = { +# "GPG_PASSPHRASE" = { +# description = "GPG Passphrase used to encrypt plan.out files" +# visibility = "all" +# } +} + +repositories = { + # Keep the following repositories in alphabetical order + + ".github" = { + description = "A Special Repository." + enable_branch_protection = false + + topics = [] + + push_allowances = [] + } + + "controls" = { + description = "The controls for managing Django Commons projects" + enable_branch_protection = false + + topics = [] + + push_allowances = [] + + visibility = "public" + } + + "membership" = { + description = "Membership repository for the django-commons organization." + visibility = "public" + + topics = [] + + push_allowances = [ + ] + } + + "django-commons-playground" = { + description = "A sample project to test things out" + + topics = [] + } + +} + +team_children = { + "django-community-playground-admins" = { + description = "django-community-playground administrators" + parent_team_key = "django-community-playground" + permission = "admin" + maintainers = [ + "tim-schilling", + "williln", + "ryancheley", + "Stormheg", + "cunla", + ] + members = null + + repositories = [ + "django-community-playground" + ] + } + "django-community-playground-committers" = { + description = "django-community-playground committers" + parent_team_key = "django-community-playground" + permission = "push" + maintainers = [ + "tim-schilling", + "williln", + "ryancheley", + "Stormheg", + "cunla", + ] + members = [ + "priyapahwa", + ] + + repositories = [ + "django-community-playground" + ] + } + +} + +team_parents = { + "admins" = { + description = "django-commons administrators" + maintainers = [ + "tim-schilling", + "williln", + "ryancheley", + "Stormheg", + "cunla", + ] + members = null + } + + "django-community-playground" = { + description = "django-community-playground team" + maintainers = [ + "tim-schilling", + "williln", + "ryancheley", + "Stormheg", + "cunla", + ] + members = [ + "priyapahwa", + ] + permission = "push" + + repositories = [ + "django-community-playground", + ] + + review_request_delegation = true + } + + "security-team" = { + description = "django-commons security team" + maintainers = [ + "tim-schilling", + ] + members = [] + permission = "push" + + repositories = [ + + ] + + } +} diff --git a/terraform/variables.tf b/terraform/variables.tf new file mode 100644 index 0000000..05882c1 --- /dev/null +++ b/terraform/variables.tf @@ -0,0 +1,73 @@ +# Input Variables +# https://www.terraform.io/language/values/variables + +variable "admins" { + description = "A set of admins to add to the organization" + type = set(string) +} + +variable "github_token" { + description = "The GitHub token used for managing the organization" + type = string + sensitive = true +} + +variable "members" { + description = "A set of members to add to the organization" + type = set(string) + default = [] +} + +variable "organization_secrets" { + description = "Map of secrets to add to the organization" + type = map(object({ + description = string + visibility = string + })) +} + +variable "repositories" { + description = "Map of repositories to create" + type = map(object({ + description = string + enable_branch_protection = optional(bool, true) + enable_discord_webhook = optional(bool, true) + enable_datadog_webhook = optional(bool, true) + has_discussions = optional(bool, false) + is_template = optional(bool, false) + push_allowances = optional(list(string), []) + required_status_checks_contexts = optional(list(string), []) + template = optional(string) + topics = optional(list(string)) + + # In most cases, the visibility of your organizations repository should be private. + # However, we are keeping our code public to encourage others to learn from our work. + + visibility = optional(string, "public") + })) +} + +variable "team_children" { + description = "Map of child teams to create" + type = map(object({ + description = string + maintainers = optional(set(string), []) + members = optional(set(string), []) + permission = optional(string, null) + parent_team_key = string + repositories = optional(set(string), []) + })) +} + +variable "team_parents" { + description = "Map of parent teams to create" + type = map(object({ + description = string + maintainers = optional(set(string), []) + members = optional(set(string), []) + permission = optional(string, null) + privacy = optional(string, "closed") + repositories = optional(set(string), []) + review_request_delegation = optional(bool, false) + })) +} From f1756178abf55bd6ed1e5f1b271777f59359912a Mon Sep 17 00:00:00 2001 From: Daniel M Date: Fri, 12 Jul 2024 13:15:06 -0400 Subject: [PATCH 02/39] terraform org mgmt --- terraform/backend.tf | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/terraform/backend.tf b/terraform/backend.tf index c7d7ef1..3b50228 100644 --- a/terraform/backend.tf +++ b/terraform/backend.tf @@ -2,11 +2,8 @@ # https://www.terraform.io/language/settings/backends/configuration terraform { + backend "local" { + path = "terraform.tfstate" + } - # Google Cloud Storage - # https://www.terraform.io/language/settings/backends/gcs -# -# backend "gcs" { -# prefix = "github-organization-management" -# } } From e59e51d4aad8cef19362060ed22c75421bc9eea4 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Fri, 12 Jul 2024 13:17:09 -0400 Subject: [PATCH 03/39] terraform org mgmt --- terraform/tfvars/production.tfvars | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/terraform/tfvars/production.tfvars b/terraform/tfvars/production.tfvars index 366a0d0..fa3148d 100644 --- a/terraform/tfvars/production.tfvars +++ b/terraform/tfvars/production.tfvars @@ -1,13 +1,16 @@ admins = [ "tim-schilling", - "cunla" + "williln", + "ryancheley", + "Stormheg", + "cunla", ] organization_secrets = { -# "GPG_PASSPHRASE" = { -# description = "GPG Passphrase used to encrypt plan.out files" -# visibility = "all" -# } + # "GPG_PASSPHRASE" = { + # description = "GPG Passphrase used to encrypt plan.out files" + # visibility = "all" + # } } repositories = { From 19ce7dcd1a366c780440705db399e6d1dfea99f9 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Sat, 13 Jul 2024 10:03:17 -0400 Subject: [PATCH 04/39] terraform org mgmt --- terraform/backend.tf | 2 +- terraform/main.tf | 106 ++--- terraform/tfstate.json | 680 +++++++++++++++++++++++++++++ terraform/tfvars/production.tfvars | 12 +- terraform/variables.tf | 8 +- 5 files changed, 747 insertions(+), 61 deletions(-) create mode 100644 terraform/tfstate.json diff --git a/terraform/backend.tf b/terraform/backend.tf index 3b50228..a113544 100644 --- a/terraform/backend.tf +++ b/terraform/backend.tf @@ -3,7 +3,7 @@ terraform { backend "local" { - path = "terraform.tfstate" + path = "tfstate.json" } } diff --git a/terraform/main.tf b/terraform/main.tf index 84981c3..19ea47a 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -49,35 +49,35 @@ resource "github_actions_organization_secret" "this" { # Github Branch Protection Resource # https://registry.terraform.io/providers/integrations/github/latest/docs/resources/branch_protection - -resource "github_branch_protection" "this" { - - # GitHub pull requests should require at least 2 approvals - # checkov:skip=CKV_GIT_5: 1 approval is reasonable for a small team - for_each = local.branch_protections - - enforce_admins = false - pattern = "main" - repository_id = github_repository.this[each.key].name - require_conversation_resolution = true - required_linear_history = true - require_signed_commits = true - - required_pull_request_reviews { - dismiss_stale_reviews = true - require_code_owner_reviews = true - required_approving_review_count = 1 - } - - required_status_checks { - contexts = each.value.required_status_checks_contexts - strict = true - } - - restrict_pushes { - push_allowances = each.value.push_allowances - } -} +# +# resource "github_branch_protection" "this" { +# +# # GitHub pull requests should require at least 2 approvals +# # checkov:skip=CKV_GIT_5: 1 approval is reasonable for a small team +# for_each = local.branch_protections +# +# enforce_admins = false +# pattern = "main" +# repository_id = github_repository.this[each.key].name +# require_conversation_resolution = true +# required_linear_history = true +# require_signed_commits = true +# +# required_pull_request_reviews { +# dismiss_stale_reviews = true +# require_code_owner_reviews = true +# required_approving_review_count = 1 +# } +# +# required_status_checks { +# contexts = each.value.required_status_checks_contexts +# strict = true +# } +# +# restrict_pushes { +# push_allowances = each.value.push_allowances +# } +# } # GitHub Membership Resource # https://registry.terraform.io/providers/integrations/github/latest/docs/resources/membership @@ -106,15 +106,15 @@ resource "github_repository" "this" { for_each = var.repositories - allow_auto_merge = true - allow_merge_commit = false - allow_rebase_merge = false - allow_squash_merge = true - allow_update_branch = true + allow_auto_merge = each.value.allow_auto_merge + allow_merge_commit = each.value.allow_merge_commit + allow_rebase_merge = each.value.allow_rebase_merge + allow_squash_merge = each.value.allow_squash_merge + allow_update_branch = each.value.allow_update_branch archive_on_destroy = true delete_branch_on_merge = true description = each.value.description - has_downloads = false + has_downloads = each.value.has_downloads has_discussions = each.value.has_discussions has_issues = true has_projects = true @@ -125,7 +125,7 @@ resource "github_repository" "this" { squash_merge_commit_title = "PR_TITLE" topics = each.value.topics visibility = each.value.visibility - vulnerability_alerts = true + vulnerability_alerts = false dynamic "template" { for_each = each.value.template != null ? [each.value.template] : [] @@ -139,23 +139,23 @@ resource "github_repository" "this" { # GitHub Repository File Resource # https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository_file - -resource "github_repository_file" "security_policy" { - for_each = var.repositories - - branch = "main" - content = templatefile("${path.module}/markdown/SECURITY.md.tpl", { repository = each.key }) - file = "SECURITY.md" - repository = each.key - commit_message = "Update SECURITY.md" - commit_author = "Open Source Infrastructure as Code Service Account" - commit_email = "github-sa@osinfra.io" - overwrite_on_create = true - - depends_on = [ - github_branch_protection.this - ] -} +# +# resource "github_repository_file" "security_policy" { +# for_each = var.repositories +# +# branch = "main" +# content = templatefile("${path.module}/markdown/SECURITY.md.tpl", { repository = each.key }) +# file = "SECURITY.md" +# repository = each.key +# commit_message = "Update SECURITY.md" +# commit_author = "Open Source Infrastructure as Code Service Account" +# commit_email = "github-sa@osinfra.io" +# overwrite_on_create = true +# +# depends_on = [ +# github_branch_protection.this +# ] +# } # Github Team Resource diff --git a/terraform/tfstate.json b/terraform/tfstate.json new file mode 100644 index 0000000..b849fa2 --- /dev/null +++ b/terraform/tfstate.json @@ -0,0 +1,680 @@ +{ + "version": 4, + "terraform_version": "1.5.7", + "serial": 20, + "lineage": "425397de-8394-a003-8a6c-bce854d9cc53", + "outputs": {}, + "resources": [ + { + "mode": "managed", + "type": "github_actions_organization_secret", + "name": "this", + "provider": "provider[\"registry.terraform.io/integrations/github\"]", + "instances": [] + }, + { + "mode": "managed", + "type": "github_membership", + "name": "this", + "provider": "provider[\"registry.terraform.io/integrations/github\"]", + "instances": [ + { + "index_key": "Stormheg", + "schema_version": 0, + "attributes": { + "downgrade_on_destroy": null, + "etag": "W/\"6213547b039be42df8cb11807508ced8ff28c328528a11ab0e26d66e6d78af31\"", + "id": "django-commons:Stormheg", + "role": "admin", + "username": "Stormheg" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + }, + { + "index_key": "cunla", + "schema_version": 0, + "attributes": { + "downgrade_on_destroy": null, + "etag": "W/\"9359ff5dc674c7a5f666266818ba0976714f1a09e3a0422ac379aafea7adeed4\"", + "id": "django-commons:cunla", + "role": "admin", + "username": "cunla" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + }, + { + "index_key": "ryancheley", + "schema_version": 0, + "attributes": { + "downgrade_on_destroy": null, + "etag": "W/\"d4049c77383fa1d8f3415168a07c78f11ca78cfa89f1c927b679b25aa5231dcb\"", + "id": "django-commons:ryancheley", + "role": "admin", + "username": "ryancheley" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + }, + { + "index_key": "tim-schilling", + "schema_version": 0, + "attributes": { + "downgrade_on_destroy": null, + "etag": "W/\"260d3a31af83c84f08c409b8ae8457ac2cbc2054c6769e7161e36ecb59601060\"", + "id": "django-commons:tim-schilling", + "role": "admin", + "username": "tim-schilling" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + }, + { + "index_key": "williln", + "schema_version": 0, + "attributes": { + "downgrade_on_destroy": null, + "etag": "W/\"ab687b07552879a71ff6f83c6b7981a86a89dbf8f12d681db1824e939e0ef153\"", + "id": "django-commons:williln", + "role": "admin", + "username": "williln" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + } + ] + }, + { + "mode": "managed", + "type": "github_repository", + "name": "this", + "provider": "provider[\"registry.terraform.io/integrations/github\"]", + "instances": [ + { + "index_key": ".github", + "schema_version": 1, + "attributes": { + "allow_auto_merge": false, + "allow_merge_commit": false, + "allow_rebase_merge": false, + "allow_squash_merge": false, + "allow_update_branch": false, + "archive_on_destroy": null, + "archived": false, + "auto_init": false, + "default_branch": "main", + "delete_branch_on_merge": false, + "description": "", + "etag": "W/\"1b93e4b77f56afa8f82793bdd73f5ba75a49c5c5a4f666d10218be75adef2a2b\"", + "full_name": "django-commons/.github", + "git_clone_url": "git://github.com/django-commons/.github.git", + "gitignore_template": null, + "has_discussions": false, + "has_downloads": true, + "has_issues": true, + "has_projects": true, + "has_wiki": true, + "homepage_url": "", + "html_url": "https://github.com/django-commons/.github", + "http_clone_url": "https://github.com/django-commons/.github.git", + "id": ".github", + "ignore_vulnerability_alerts_during_read": null, + "is_template": false, + "license_template": null, + "merge_commit_message": "", + "merge_commit_title": "", + "name": ".github", + "node_id": "R_kgDOLkyRSQ", + "pages": [], + "primary_language": "", + "private": false, + "repo_id": 776769865, + "security_and_analysis": [ + { + "advanced_security": [], + "secret_scanning": [ + { + "status": "disabled" + } + ], + "secret_scanning_push_protection": [ + { + "status": "disabled" + } + ] + } + ], + "squash_merge_commit_message": "", + "squash_merge_commit_title": "", + "ssh_clone_url": "git@github.com:django-commons/.github.git", + "svn_url": "https://github.com/django-commons/.github", + "template": [], + "topics": [], + "visibility": "public", + "vulnerability_alerts": false, + "web_commit_signoff_required": false + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjEifQ==" + }, + { + "index_key": "controls", + "schema_version": 1, + "attributes": { + "allow_auto_merge": false, + "allow_merge_commit": false, + "allow_rebase_merge": false, + "allow_squash_merge": false, + "allow_update_branch": false, + "archive_on_destroy": null, + "archived": false, + "auto_init": false, + "default_branch": "main", + "delete_branch_on_merge": false, + "description": "The controls for managing Django Commons projects", + "etag": "W/\"2904577e11d0752a0dd0d33dccfad6cbdecaef68554143a74c4a8b71296b8487\"", + "full_name": "django-commons/controls", + "git_clone_url": "git://github.com/django-commons/controls.git", + "gitignore_template": null, + "has_discussions": false, + "has_downloads": true, + "has_issues": true, + "has_projects": true, + "has_wiki": true, + "homepage_url": "", + "html_url": "https://github.com/django-commons/controls", + "http_clone_url": "https://github.com/django-commons/controls.git", + "id": "controls", + "ignore_vulnerability_alerts_during_read": null, + "is_template": false, + "license_template": null, + "merge_commit_message": "", + "merge_commit_title": "", + "name": "controls", + "node_id": "R_kgDOLkDK8g", + "pages": [], + "primary_language": "", + "private": false, + "repo_id": 775998194, + "security_and_analysis": [ + { + "advanced_security": [], + "secret_scanning": [ + { + "status": "disabled" + } + ], + "secret_scanning_push_protection": [ + { + "status": "disabled" + } + ] + } + ], + "squash_merge_commit_message": "", + "squash_merge_commit_title": "", + "ssh_clone_url": "git@github.com:django-commons/controls.git", + "svn_url": "https://github.com/django-commons/controls", + "template": [], + "topics": [], + "visibility": "public", + "vulnerability_alerts": false, + "web_commit_signoff_required": false + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjEifQ==" + }, + { + "index_key": "django-commons-playground", + "schema_version": 1, + "attributes": { + "allow_auto_merge": false, + "allow_merge_commit": false, + "allow_rebase_merge": false, + "allow_squash_merge": false, + "allow_update_branch": false, + "archive_on_destroy": null, + "archived": false, + "auto_init": false, + "default_branch": "main", + "delete_branch_on_merge": false, + "description": "A sample project to test things out", + "etag": "W/\"fc80aa6c9b3ef87f460913e9aa0e2d9dc37a6f744647390c091d98bc2cd66dea\"", + "full_name": "django-commons/django-commons-playground", + "git_clone_url": "git://github.com/django-commons/django-commons-playground.git", + "gitignore_template": null, + "has_discussions": false, + "has_downloads": true, + "has_issues": true, + "has_projects": true, + "has_wiki": true, + "homepage_url": "", + "html_url": "https://github.com/django-commons/django-commons-playground", + "http_clone_url": "https://github.com/django-commons/django-commons-playground.git", + "id": "django-commons-playground", + "ignore_vulnerability_alerts_during_read": null, + "is_template": false, + "license_template": null, + "merge_commit_message": "", + "merge_commit_title": "", + "name": "django-commons-playground", + "node_id": "R_kgDOLkANrg", + "pages": [], + "primary_language": "Python", + "private": false, + "repo_id": 775949742, + "security_and_analysis": [ + { + "advanced_security": [], + "secret_scanning": [ + { + "status": "disabled" + } + ], + "secret_scanning_push_protection": [ + { + "status": "disabled" + } + ] + } + ], + "squash_merge_commit_message": "", + "squash_merge_commit_title": "", + "ssh_clone_url": "git@github.com:django-commons/django-commons-playground.git", + "svn_url": "https://github.com/django-commons/django-commons-playground", + "template": [], + "topics": [], + "visibility": "public", + "vulnerability_alerts": false, + "web_commit_signoff_required": false + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjEifQ==" + }, + { + "index_key": "membership", + "schema_version": 1, + "attributes": { + "allow_auto_merge": false, + "allow_merge_commit": false, + "allow_rebase_merge": false, + "allow_squash_merge": false, + "allow_update_branch": false, + "archive_on_destroy": null, + "archived": false, + "auto_init": false, + "default_branch": "main", + "delete_branch_on_merge": false, + "description": "", + "etag": "W/\"fe64192cf880ca2292a4ac8b0e9cd75087401fc870c3c5c7f4c3fda2f29aad22\"", + "full_name": "django-commons/membership", + "git_clone_url": "git://github.com/django-commons/membership.git", + "gitignore_template": null, + "has_discussions": true, + "has_downloads": true, + "has_issues": true, + "has_projects": true, + "has_wiki": true, + "homepage_url": "", + "html_url": "https://github.com/django-commons/membership", + "http_clone_url": "https://github.com/django-commons/membership.git", + "id": "membership", + "ignore_vulnerability_alerts_during_read": null, + "is_template": false, + "license_template": null, + "merge_commit_message": "", + "merge_commit_title": "", + "name": "membership", + "node_id": "R_kgDOLklIpA", + "pages": [], + "primary_language": "", + "private": false, + "repo_id": 776554660, + "security_and_analysis": [ + { + "advanced_security": [], + "secret_scanning": [ + { + "status": "disabled" + } + ], + "secret_scanning_push_protection": [ + { + "status": "disabled" + } + ] + } + ], + "squash_merge_commit_message": "", + "squash_merge_commit_title": "", + "ssh_clone_url": "git@github.com:django-commons/membership.git", + "svn_url": "https://github.com/django-commons/membership", + "template": [], + "topics": [], + "visibility": "public", + "vulnerability_alerts": false, + "web_commit_signoff_required": false + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjEifQ==" + } + ] + }, + { + "mode": "managed", + "type": "github_team", + "name": "children", + "provider": "provider[\"registry.terraform.io/integrations/github\"]", + "instances": [ + { + "index_key": "django-community-playground-admins", + "schema_version": 0, + "attributes": { + "create_default_maintainer": false, + "description": "", + "etag": "W/\"29ad110c85967ccc4c149e51eebbbed2f56a49260d60161eb505a443be1f3014\"", + "id": "9757650", + "ldap_dn": "", + "members_count": 5, + "name": "django-community-playground-admins", + "node_id": "T_kwDOCaaRBM4AlOPS", + "parent_team_id": "9757678", + "parent_team_read_id": "9757678", + "parent_team_read_slug": "django-community-playground", + "privacy": "closed", + "slug": "django-community-playground-admins" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + }, + { + "index_key": "django-community-playground-committers", + "schema_version": 0, + "attributes": { + "create_default_maintainer": false, + "description": "", + "etag": "W/\"ab9f29d07ec9c93b0f69329396cc19415de8fcf9d74154649cf4c58dcc604ea4\"", + "id": "9757668", + "ldap_dn": "", + "members_count": 6, + "name": "django-community-playground-committers", + "node_id": "T_kwDOCaaRBM4AlOPk", + "parent_team_id": "9757678", + "parent_team_read_id": "9757678", + "parent_team_read_slug": "django-community-playground", + "privacy": "closed", + "slug": "django-community-playground-committers" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + } + ] + }, + { + "mode": "managed", + "type": "github_team", + "name": "parents", + "provider": "provider[\"registry.terraform.io/integrations/github\"]", + "instances": [ + { + "index_key": "Admins", + "schema_version": 0, + "attributes": { + "create_default_maintainer": false, + "description": "", + "etag": "W/\"edeb1adc95c341b74155a478bb2b9ded90cc7ecfb784acd0949f0f88ed585c51\"", + "id": "9763562", + "ldap_dn": "", + "members_count": 5, + "name": "Admins", + "node_id": "T_kwDOCaaRBM4AlPrq", + "parent_team_id": "", + "parent_team_read_id": "", + "parent_team_read_slug": "", + "privacy": "closed", + "slug": "admins" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + }, + { + "index_key": "django-community-playground", + "schema_version": 0, + "attributes": { + "create_default_maintainer": false, + "description": "", + "etag": "W/\"3ace9f2ddef61397d31c277d9788b10b548997e4b3af63e8bedd48ac037d75fb\"", + "id": "9757678", + "ldap_dn": "", + "members_count": 6, + "name": "django-community-playground", + "node_id": "T_kwDOCaaRBM4AlOPu", + "parent_team_id": "", + "parent_team_read_id": "", + "parent_team_read_slug": "", + "privacy": "closed", + "slug": "django-community-playground" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + } + ] + }, + { + "mode": "managed", + "type": "github_team_members", + "name": "children", + "provider": "provider[\"registry.terraform.io/integrations/github\"]", + "instances": [ + { + "index_key": "django-community-playground-admins", + "schema_version": 0, + "attributes": { + "id": "9757650", + "members": [ + { + "role": "maintainer", + "username": "Stormheg" + }, + { + "role": "maintainer", + "username": "cunla" + }, + { + "role": "maintainer", + "username": "ryancheley" + }, + { + "role": "maintainer", + "username": "tim-schilling" + }, + { + "role": "maintainer", + "username": "williln" + } + ], + "team_id": "9757650" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + }, + { + "index_key": "django-community-playground-committers", + "schema_version": 0, + "attributes": { + "id": "9757668", + "members": [ + { + "role": "maintainer", + "username": "Stormheg" + }, + { + "role": "maintainer", + "username": "cunla" + }, + { + "role": "maintainer", + "username": "ryancheley" + }, + { + "role": "maintainer", + "username": "tim-schilling" + }, + { + "role": "maintainer", + "username": "williln" + }, + { + "role": "member", + "username": "priyapahwa" + } + ], + "team_id": "9757668" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + } + ] + }, + { + "mode": "managed", + "type": "github_team_members", + "name": "parents", + "provider": "provider[\"registry.terraform.io/integrations/github\"]", + "instances": [ + { + "index_key": "Admins", + "schema_version": 0, + "attributes": { + "id": "9763562", + "members": [ + { + "role": "maintainer", + "username": "Stormheg" + }, + { + "role": "maintainer", + "username": "cunla" + }, + { + "role": "maintainer", + "username": "ryancheley" + }, + { + "role": "maintainer", + "username": "tim-schilling" + }, + { + "role": "maintainer", + "username": "williln" + } + ], + "team_id": "9763562" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + }, + { + "index_key": "django-community-playground", + "schema_version": 0, + "attributes": { + "id": "9757678", + "members": [ + { + "role": "maintainer", + "username": "Stormheg" + }, + { + "role": "maintainer", + "username": "cunla" + }, + { + "role": "maintainer", + "username": "ryancheley" + }, + { + "role": "maintainer", + "username": "tim-schilling" + }, + { + "role": "maintainer", + "username": "williln" + }, + { + "role": "member", + "username": "priyapahwa" + } + ], + "team_id": "9757678" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + } + ] + }, + { + "mode": "managed", + "type": "github_team_repository", + "name": "children", + "provider": "provider[\"registry.terraform.io/integrations/github\"]", + "instances": [ + { + "index_key": "django-community-playground-admins-django-commons-playground", + "schema_version": 0, + "attributes": { + "etag": "W/\"3993a06d298b6b2f996a4dc2e0bea9e8a4cac3b14f5f202263e52c89398f66fc\"", + "id": "9757650:django-commons-playground", + "permission": "admin", + "repository": "django-commons-playground", + "team_id": "9757650" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + }, + { + "index_key": "django-community-playground-committers-django-commons-playground", + "schema_version": 0, + "attributes": { + "etag": "W/\"8059d2bfd583f22009db85d9b03c38faa2039416b0e5ed3c7651967314f37132\"", + "id": "9757668:django-commons-playground", + "permission": "maintain", + "repository": "django-commons-playground", + "team_id": "9757668" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + } + ] + }, + { + "mode": "managed", + "type": "github_team_repository", + "name": "parents", + "provider": "provider[\"registry.terraform.io/integrations/github\"]", + "instances": [ + { + "index_key": "django-community-playground-django-commons-playground", + "schema_version": 0, + "attributes": { + "etag": "W/\"5eb93e6ede47d339fe46c6e87e5831b4293538f2e1aba8efb4eaf2ef51e812f5\"", + "id": "9757678:django-commons-playground", + "permission": "triage", + "repository": "django-commons-playground", + "team_id": "9757678" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + } + ] + }, + { + "mode": "managed", + "type": "random_password", + "name": "this", + "provider": "provider[\"registry.terraform.io/hashicorp/random\"]", + "instances": [] + } + ], + "check_results": null +} diff --git a/terraform/tfvars/production.tfvars b/terraform/tfvars/production.tfvars index fa3148d..2d04625 100644 --- a/terraform/tfvars/production.tfvars +++ b/terraform/tfvars/production.tfvars @@ -69,13 +69,13 @@ team_children = { members = null repositories = [ - "django-community-playground" + "django-commons-playground" ] } "django-community-playground-committers" = { description = "django-community-playground committers" parent_team_key = "django-community-playground" - permission = "push" + permission = "maintain" maintainers = [ "tim-schilling", "williln", @@ -88,14 +88,14 @@ team_children = { ] repositories = [ - "django-community-playground" + "django-commons-playground" ] } } team_parents = { - "admins" = { + "Admins" = { description = "django-commons administrators" maintainers = [ "tim-schilling", @@ -119,10 +119,10 @@ team_parents = { members = [ "priyapahwa", ] - permission = "push" + permission = "triage" repositories = [ - "django-community-playground", + "django-commons-playground", ] review_request_delegation = true diff --git a/terraform/variables.tf b/terraform/variables.tf index 05882c1..9b50266 100644 --- a/terraform/variables.tf +++ b/terraform/variables.tf @@ -30,10 +30,16 @@ variable "repositories" { description = "Map of repositories to create" type = map(object({ description = string + allow_auto_merge = optional(bool, false) + allow_merge_commit = optional(bool, false) + allow_rebase_merge = optional(bool, false) + allow_squash_merge = optional(bool, false) + allow_update_branch = optional(bool, false) enable_branch_protection = optional(bool, true) enable_discord_webhook = optional(bool, true) enable_datadog_webhook = optional(bool, true) - has_discussions = optional(bool, false) + has_discussions = optional(bool, true) + has_downloads = optional(bool, true) is_template = optional(bool, false) push_allowances = optional(list(string), []) required_status_checks_contexts = optional(list(string), []) From de7a9320616ff22c71038413d05da9584e92d2d9 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Sat, 13 Jul 2024 12:18:56 -0400 Subject: [PATCH 05/39] terraform org mgmt --- terraform/tfstate.json | 106 ++++++++++++++++++++++++++++- terraform/tfvars/production.tfvars | 11 +++ 2 files changed, 116 insertions(+), 1 deletion(-) diff --git a/terraform/tfstate.json b/terraform/tfstate.json index b849fa2..aed505a 100644 --- a/terraform/tfstate.json +++ b/terraform/tfstate.json @@ -1,7 +1,7 @@ { "version": 4, "terraform_version": "1.5.7", - "serial": 20, + "serial": 28, "lineage": "425397de-8394-a003-8a6c-bce854d9cc53", "outputs": {}, "resources": [ @@ -18,6 +18,19 @@ "name": "this", "provider": "provider[\"registry.terraform.io/integrations/github\"]", "instances": [ + { + "index_key": "Natim", + "schema_version": 0, + "attributes": { + "downgrade_on_destroy": null, + "etag": "W/\"68caaa2a1f43f8105fc4f2b11539ab0a4274703153c2003ec2c3a5b4ef9ce6c7\"", + "id": "django-commons:Natim", + "role": "member", + "username": "Natim" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + }, { "index_key": "Stormheg", "schema_version": 0, @@ -44,6 +57,84 @@ "sensitive_attributes": [], "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" }, + { + "index_key": "gav-fyi", + "schema_version": 0, + "attributes": { + "downgrade_on_destroy": null, + "etag": "W/\"cfb584ec4e3496693ac6d7a7afe2592209ef3970f4f53c0e9b1e7885a63f93b6\"", + "id": "django-commons:gav-fyi", + "role": "member", + "username": "gav-fyi" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + }, + { + "index_key": "jcjudkins", + "schema_version": 0, + "attributes": { + "downgrade_on_destroy": null, + "etag": "W/\"ebbf5aa3bb02451b568616140a51580ec56f433d065c5e003a502362dd808add\"", + "id": "django-commons:jcjudkins", + "role": "member", + "username": "jcjudkins" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + }, + { + "index_key": "joshuadavidthomas", + "schema_version": 0, + "attributes": { + "downgrade_on_destroy": null, + "etag": "W/\"405875598c46e245e59326fca4937f09dda150a215fffb0c61908d08f7d437d7\"", + "id": "django-commons:joshuadavidthomas", + "role": "member", + "username": "joshuadavidthomas" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + }, + { + "index_key": "matthiask", + "schema_version": 0, + "attributes": { + "downgrade_on_destroy": null, + "etag": "W/\"41d15a470cac92785493809b1fe7c6a6b3e21180aa1c20be2feaa185c63bb292\"", + "id": "django-commons:matthiask", + "role": "member", + "username": "matthiask" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + }, + { + "index_key": "pfouque", + "schema_version": 0, + "attributes": { + "downgrade_on_destroy": null, + "etag": "W/\"0571b46dbaa3e06d741cd6ac213d57c36f154725636eee4f93f566f31d307651\"", + "id": "django-commons:pfouque", + "role": "member", + "username": "pfouque" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + }, + { + "index_key": "priyapahwa", + "schema_version": 0, + "attributes": { + "downgrade_on_destroy": null, + "etag": "W/\"804a24a7934e528c0c8c7c6501308f4471d73d5ed38e24a17457f535e1d1f634\"", + "id": "django-commons:priyapahwa", + "role": "member", + "username": "priyapahwa" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + }, { "index_key": "ryancheley", "schema_version": 0, @@ -57,6 +148,19 @@ "sensitive_attributes": [], "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" }, + { + "index_key": "testSchilling", + "schema_version": 0, + "attributes": { + "downgrade_on_destroy": null, + "etag": "W/\"031db6d5f4a2df778bba9d4177ee9adc7f19f75d9de9b00052e6284895753703\"", + "id": "django-commons:testSchilling", + "role": "member", + "username": "testSchilling" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + }, { "index_key": "tim-schilling", "schema_version": 0, diff --git a/terraform/tfvars/production.tfvars b/terraform/tfvars/production.tfvars index 2d04625..640acf3 100644 --- a/terraform/tfvars/production.tfvars +++ b/terraform/tfvars/production.tfvars @@ -5,6 +5,17 @@ admins = [ "Stormheg", "cunla", ] +members = [ + "gav-fyi", + "jcjudkins", + "joshuadavidthomas", + "matthiask", + "nanorepublica", + "Natim", + "pfouque", + "priyapahwa", + "testSchilling", +] organization_secrets = { # "GPG_PASSPHRASE" = { From 5d0ede61248206b0cd6e614bd347c182f17bdc12 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Sat, 13 Jul 2024 12:40:06 -0400 Subject: [PATCH 06/39] terraform org mgmt --- terraform/tfstate.json | 23 ++++++++++++++++++++++- terraform/tfvars/production.tfvars | 1 + 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/terraform/tfstate.json b/terraform/tfstate.json index aed505a..2c283fc 100644 --- a/terraform/tfstate.json +++ b/terraform/tfstate.json @@ -1,7 +1,7 @@ { "version": 4, "terraform_version": "1.5.7", - "serial": 28, + "serial": 29, "lineage": "425397de-8394-a003-8a6c-bce854d9cc53", "outputs": {}, "resources": [ @@ -772,6 +772,27 @@ } ] }, + { + "mode": "managed", + "type": "github_team_settings", + "name": "this", + "provider": "provider[\"registry.terraform.io/integrations/github\"]", + "instances": [ + { + "index_key": "django-community-playground", + "schema_version": 0, + "attributes": { + "id": "T_kwDOCaaRBM4AlOPu", + "review_request_delegation": [], + "team_id": "9757678", + "team_slug": "django-community-playground", + "team_uid": "T_kwDOCaaRBM4AlOPu" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + } + ] + }, { "mode": "managed", "type": "random_password", diff --git a/terraform/tfvars/production.tfvars b/terraform/tfvars/production.tfvars index 640acf3..4a409e0 100644 --- a/terraform/tfvars/production.tfvars +++ b/terraform/tfvars/production.tfvars @@ -143,6 +143,7 @@ team_parents = { description = "django-commons security team" maintainers = [ "tim-schilling", + "matthiask" ] members = [] permission = "push" From 56d0267c955da57994ad042c1fcf9c0182709c0d Mon Sep 17 00:00:00 2001 From: Daniel M Date: Sat, 13 Jul 2024 12:41:08 -0400 Subject: [PATCH 07/39] terraform org mgmt --- terraform/tfstate.json | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/terraform/tfstate.json b/terraform/tfstate.json index 2c283fc..20ceeca 100644 --- a/terraform/tfstate.json +++ b/terraform/tfstate.json @@ -1,7 +1,7 @@ { "version": 4, "terraform_version": "1.5.7", - "serial": 29, + "serial": 30, "lineage": "425397de-8394-a003-8a6c-bce854d9cc53", "outputs": {}, "resources": [ @@ -109,6 +109,19 @@ "sensitive_attributes": [], "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" }, + { + "index_key": "nanorepublica", + "schema_version": 0, + "attributes": { + "downgrade_on_destroy": null, + "etag": "W/\"b2f0f248f8e6b7d47c4c6862abe1069531ffa6d2db127237e0264a301581bb5b\"", + "id": "django-commons:nanorepublica", + "role": "member", + "username": "nanorepublica" + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjAifQ==" + }, { "index_key": "pfouque", "schema_version": 0, From 6edf954405601011df0c027bd7925f9b95b1b289 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Mon, 15 Jul 2024 14:31:20 -0400 Subject: [PATCH 08/39] terraform org mgmt --- terraform/README.md | 103 +++++++++++++++++++++++++++++ terraform/tfvars/production.tfvars | 88 ++++++++++++------------ terraform/variables.tf | 2 - 3 files changed, 145 insertions(+), 48 deletions(-) create mode 100644 terraform/README.md diff --git a/terraform/README.md b/terraform/README.md new file mode 100644 index 0000000..a78e0c2 --- /dev/null +++ b/terraform/README.md @@ -0,0 +1,103 @@ +GitHub Organization as Terraform +================================ + +Structure + +- `variables.tf` - define variable types (classes?), notice there is `variable "repositories" {...` there which has a + few variables marked as optional with default values. Why I chose to have `has_discussions` as a repo variable + while `has_issues` as a constant - I am embarrassed to say I don't have a better answer than laziness :smile: - I just + figured if this is the path we want to take, we can continue adding to it. +- `production.tfvars` - instances, should strictly follow the types in `variables.tf`. +- `main.tf` - build configuration based on instances values from `production.tfvars` (or, if not defined explicitly, + then default value from `variables.tf`) +- `tfstate.json` - Current state file, pulled using `terraform import ..` + +# Why Terraform? + +We can define our "desired/default" repository configuration, and within this configuration: + +- What is enforced from day one (i.e., constant in `resource "github_repository" "this"`) +- What is recommended but can be changed by users (i.e., variable with a default value in `variables.tf` that can be + updated in `production.tfvars`) => Note this can also help us review outliers, you can see all repos which have + non-default values in the `production.tfvars` file. +- What is determined by users (i.e., variables without default value, like `description`) +- What is not configured in the infra-as-code (currently, for example, repo-labels). + +# How to use? + +1. Clone the repository. +2. From the `terraform/` directory, run `terraform init`. +3. Create a github-token with the necessary permissions on the organization. +4. Make changes to `production.tfvars` to reflect the desired state (add/update users, repositories, teams, etc.) +5. To see what changes between the current state of the GitHub organization and the plan + run: `terraform plan -var-file=tfvars/production.tfvars -var github_token=...` +6. To apply the changes, run: `terraform apply -var-file=tfvars/production.tfvars -var github_token=...` + +# What changes can be made + +All changes should be made in `production.tfvars`: + +- Add/Remove organization admins by editing the `admins` list. +- Add/Remove organization members by editing the `members` list. +- Add/Remove/Update repositories by editing the `repositories`. A repository can have the following variables: + ```terraform + repositories = { + "repo-name" = { + description = "repo description" + allow_auto_merge = false # optional, default is false + allow_merge_commit = false # optional, default is false + allow_rebase_merge = false # optional, default is false + allow_squash_merge = false # optional, default is false + allow_update_branch = false # optional, default is false + enable_branch_protection = true # optional, default is true + has_discussions = true # optional, default is true + has_downloads = true # optional, default is true + is_template = false # optional, default is false + push_allowances = [] + required_status_checks_contexts = [] # optional, default is [] + template = "" # optional, default is "" + topics = [] + visibility = "public" # optional, default is "public" + } + # ... + } + ``` +- Add/Remove/Update teams by editing the `team_parents`. A team can have the following variables: + ```terraform + team_parents = { + "Admins" = { + description = "django-commons administrators" + maintainers = ["tim-schilling",] + members = ["cunla",] + permission = "admin" # optional, default is null + privacy = "closed" # optional, default is "closed" + repositories = [ # optional, default is [] + "django-commons/controls", + "django-commons/membership", + "django-commons/terraform", + ] + review_request_delegation = false # optional, default is false + } + # ... + } + ``` +- Add/Remove/Update child-teams by editing the `team_children`. A child-team can have the following variables: + ```terraform + team_children = { + "New-Admins" = { + description = "django-commons administrators" + parent_team_key = "Admins" + maintainers = ["tim-schilling",] + members = ["cunla",] + permission = "admin" # optional, default is null + privacy = "closed" # optional, default is "closed" + repositories = [ # optional, default is [] + "django-commons/controls", + "django-commons/membership", + "django-commons/terraform", + ] + review_request_delegation = false # optional, default is false + } + # ... + } + ``` \ No newline at end of file diff --git a/terraform/tfvars/production.tfvars b/terraform/tfvars/production.tfvars index 4a409e0..0ace0bb 100644 --- a/terraform/tfvars/production.tfvars +++ b/terraform/tfvars/production.tfvars @@ -1,3 +1,4 @@ +# Organization admins admins = [ "tim-schilling", "williln", @@ -5,6 +6,7 @@ admins = [ "Stormheg", "cunla", ] +# Organization members members = [ "gav-fyi", "jcjudkins", @@ -24,6 +26,7 @@ organization_secrets = { # } } +# Organization repositories repositories = { # Keep the following repositories in alphabetical order @@ -32,7 +35,6 @@ repositories = { enable_branch_protection = false topics = [] - push_allowances = [] } @@ -41,35 +43,27 @@ repositories = { enable_branch_protection = false topics = [] - push_allowances = [] - visibility = "public" } "membership" = { description = "Membership repository for the django-commons organization." visibility = "public" - topics = [] - - push_allowances = [ - ] + push_allowances = [] } "django-commons-playground" = { description = "A sample project to test things out" - topics = [] } } -team_children = { - "django-community-playground-admins" = { - description = "django-community-playground administrators" - parent_team_key = "django-community-playground" - permission = "admin" +team_parents = { + "Admins" = { + description = "django-commons administrators" maintainers = [ "tim-schilling", "williln", @@ -78,15 +72,10 @@ team_children = { "cunla", ] members = null - - repositories = [ - "django-commons-playground" - ] } - "django-community-playground-committers" = { - description = "django-community-playground committers" - parent_team_key = "django-community-playground" - permission = "maintain" + + "django-community-playground" = { + description = "django-community-playground team" maintainers = [ "tim-schilling", "williln", @@ -97,17 +86,36 @@ team_children = { members = [ "priyapahwa", ] + permission = "triage" repositories = [ - "django-commons-playground" + "django-commons-playground", ] + + review_request_delegation = true } + "security-team" = { + description = "django-commons security team" + maintainers = [ + "tim-schilling", + "matthiask" + ] + members = [] + permission = "push" + + repositories = [ + + ] + + } } -team_parents = { - "Admins" = { - description = "django-commons administrators" +team_children = { + "django-community-playground-admins" = { + description = "django-community-playground administrators" + parent_team_key = "django-community-playground" + permission = "admin" maintainers = [ "tim-schilling", "williln", @@ -116,10 +124,15 @@ team_parents = { "cunla", ] members = null - } - "django-community-playground" = { - description = "django-community-playground team" + repositories = [ + "django-commons-playground", + ] + } + "django-community-playground-committers" = { + description = "django-community-playground committers" + parent_team_key = "django-community-playground" + permission = "maintain" maintainers = [ "tim-schilling", "williln", @@ -130,27 +143,10 @@ team_parents = { members = [ "priyapahwa", ] - permission = "triage" repositories = [ - "django-commons-playground", + "django-commons-playground" ] - - review_request_delegation = true } - "security-team" = { - description = "django-commons security team" - maintainers = [ - "tim-schilling", - "matthiask" - ] - members = [] - permission = "push" - - repositories = [ - - ] - - } } diff --git a/terraform/variables.tf b/terraform/variables.tf index 9b50266..aefff07 100644 --- a/terraform/variables.tf +++ b/terraform/variables.tf @@ -36,8 +36,6 @@ variable "repositories" { allow_squash_merge = optional(bool, false) allow_update_branch = optional(bool, false) enable_branch_protection = optional(bool, true) - enable_discord_webhook = optional(bool, true) - enable_datadog_webhook = optional(bool, true) has_discussions = optional(bool, true) has_downloads = optional(bool, true) is_template = optional(bool, false) From 1165e81c4f1d72302ba48f16cd01ba27ddb3b986 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Tue, 16 Jul 2024 10:45:37 -0400 Subject: [PATCH 09/39] terraform org mgmt --- terraform/README.md | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/terraform/README.md b/terraform/README.md index a78e0c2..f043029 100644 --- a/terraform/README.md +++ b/terraform/README.md @@ -1,7 +1,7 @@ GitHub Organization as Terraform ================================ -Structure +# Structure - `variables.tf` - define variable types (classes?), notice there is `variable "repositories" {...` there which has a few variables marked as optional with default values. Why I chose to have `has_discussions` as a repo variable @@ -23,16 +23,6 @@ We can define our "desired/default" repository configuration, and within this co - What is determined by users (i.e., variables without default value, like `description`) - What is not configured in the infra-as-code (currently, for example, repo-labels). -# How to use? - -1. Clone the repository. -2. From the `terraform/` directory, run `terraform init`. -3. Create a github-token with the necessary permissions on the organization. -4. Make changes to `production.tfvars` to reflect the desired state (add/update users, repositories, teams, etc.) -5. To see what changes between the current state of the GitHub organization and the plan - run: `terraform plan -var-file=tfvars/production.tfvars -var github_token=...` -6. To apply the changes, run: `terraform apply -var-file=tfvars/production.tfvars -var github_token=...` - # What changes can be made All changes should be made in `production.tfvars`: @@ -100,4 +90,17 @@ All changes should be made in `production.tfvars`: } # ... } - ``` \ No newline at end of file + ``` + +# How to use locally + +You might want to try new settings locally before applying them to the repository automation. +To do so, you can use the following steps: + +1. Clone the repository. +2. From the `terraform/` directory, run `terraform init`. +3. Create a github-token with the necessary permissions on the organization. +4. Make changes to `production.tfvars` to reflect the desired state (add/update users, repositories, teams, etc.) +5. To see what changes between the current state of the GitHub organization and the plan + run: `terraform plan -var-file=tfvars/production.tfvars -var github_token=...` +6. To apply the changes, run: `terraform apply -var-file=tfvars/production.tfvars -var github_token=...` From 6b638e724e85070ae29bb16835015ccf976887a4 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Sat, 20 Jul 2024 10:12:37 -0400 Subject: [PATCH 10/39] remove webhooks --- terraform/locals.tf | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/terraform/locals.tf b/terraform/locals.tf index 54ec649..59ad2da 100644 --- a/terraform/locals.tf +++ b/terraform/locals.tf @@ -8,7 +8,8 @@ locals { } branch_protections = { - for repository_key, repository in var.repositories : repository_key => repository if repository.enable_branch_protection + for repository_key, repository in var.repositories : repository_key => repository + if repository.enable_branch_protection } child_team_repositories = { @@ -21,15 +22,9 @@ locals { permission = team_child.permission } ] - ]) : "${repository.team_child}-${repository.repository}" => repository } - - datadog_webhooks = { - for repository_key, repository in var.repositories : repository_key => repository if repository.enable_datadog_webhook + ]) : "${repository.team_child}-${repository.repository}" => repository } - discord_webhooks = { - for repository_key, repository in var.repositories : repository_key => repository if repository.enable_discord_webhook - } members = { for user in var.members : user => "member" @@ -45,10 +40,12 @@ locals { permission = team_parent.permission } ] - ]) : "${repository.team_parent}-${repository.repository}" => repository } + ]) : "${repository.team_parent}-${repository.repository}" => repository + } review_request_delegations = { - for team_parent_key, team_parent in var.team_parents : team_parent_key => team_parent if team_parent.review_request_delegation + for team_parent_key, team_parent in var.team_parents : team_parent_key => team_parent + if team_parent.review_request_delegation } users = merge(local.admins, local.members) From 2023d5f7677898173ef02eef031a23659359bf03 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Sat, 20 Jul 2024 10:26:51 -0400 Subject: [PATCH 11/39] added permissions to readme --- terraform/README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/terraform/README.md b/terraform/README.md index f043029..0722d04 100644 --- a/terraform/README.md +++ b/terraform/README.md @@ -99,8 +99,14 @@ To do so, you can use the following steps: 1. Clone the repository. 2. From the `terraform/` directory, run `terraform init`. -3. Create a github-token with the necessary permissions on the organization. +3. Create a github-token with the necessary permissions on the organization (see [permissions documentation][1]). + - The `repo` permisison for full control of private repositories. + - The `admin:org` permission for full control of orgs and teams, read and write org projects + - The `delete_repo` permission to delete repositories + 4. Make changes to `production.tfvars` to reflect the desired state (add/update users, repositories, teams, etc.) 5. To see what changes between the current state of the GitHub organization and the plan run: `terraform plan -var-file=tfvars/production.tfvars -var github_token=...` 6. To apply the changes, run: `terraform apply -var-file=tfvars/production.tfvars -var github_token=...` + +[1]: https://developer.hashicorp.com/terraform/tutorials/it-saas/github-user-teams#configure-your-credentials \ No newline at end of file From 6616b25269fb27c47dadcd236d877fe732775337 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Sat, 20 Jul 2024 11:56:19 -0400 Subject: [PATCH 12/39] readme changes --- .github/workflows/plan.yml | 29 ++++++ .gitignore | 2 + README.md | 182 ++++++++++++++++++++++++++++++++++--- 3 files changed, 201 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/plan.yml create mode 100644 .gitignore diff --git a/.github/workflows/plan.yml b/.github/workflows/plan.yml new file mode 100644 index 0000000..a398775 --- /dev/null +++ b/.github/workflows/plan.yml @@ -0,0 +1,29 @@ +name: "Plan org changes and list them in a PR" +on: + pull_request: + branches: + - main + +jobs: + plan-changes: + name: "Org changes plan" + runs-on: ubuntu-latest + + permissions: + pull-requests: write + contents: read + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: terraform plan + uses: dflook/terraform-plan@v1 + with: + path: terraform + label: production + add_github_comment: true + workspace: prod + var_file: tfvars/production.tfvars + variables: | + github_token=${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b250a4f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.backup +notes.txt \ No newline at end of file diff --git a/README.md b/README.md index fdff1cf..8867f21 100644 --- a/README.md +++ b/README.md @@ -9,50 +9,208 @@ Django Commons packages. - [New project](#new-project-playbook) - [Remove project](#remove-project-playbook) - ## New Member Playbook 1. Review new issues/application at https://github.com/django-commons/membership/issues/ 2. If they are a real human and are reasonably trustworthy, comment "Approved" and nothing else -3. The [new_member](https://github.com/django-commons/membership/blob/main/.github/workflows/new_member.yml) action will send the invite and close the issue -4. Add the member to any relevant teams if requested + - If they aren't a real human or reasonably trustworthy, close the issue. + - Apply the needed terraform changes to add the member to the organization. + +### Terraform changes to add member to the organization -If they aren't a real human or reasonably trustworthy, close the issue. +- Change `production.tfvars`: add the username under `members`, like this: + ```terraform + members = [ + # ... + "new_user" + ] + ``` +- Create a pull-request to `main` branch, it will trigger terraform to plan the changes in the organization to be + executed. Review the changes and make sure they align with the request. +- Merge the pull-request, it will trigger terraform to apply the changes in the organization. ## Team Change Playbook 1. If they are a real human and are reasonably trustworthy, comment "Approved" and close the issue manually 2. Add the member to requested team(s) +### Terraform changes to add member to the team + +- Change `production.tfvars`: find the relevant team under `team_parents` or `team_children`, and edit its members: + ```terraform + team_children = { + # ... + "django-community-playground-admins" = { + description = "django-community-playground administrators" + parent_team_key = "django-community-playground" + permission = "admin" + members = [ + # ... + "new_user" + ] + + repositories = [ + "django-commons-playground", + ] + } + } + ``` +- Create a pull-request to `main` branch, it will trigger terraform to plan the changes in the organization to be + executed. Review the changes and make sure they align with the request. +- Merge the pull-request, it will trigger terraform to apply the changes in the organization. + ## New Repository Admin Playbook 1. Confirm with all existing admins that they are okay with the prospective admin 2. If there's disagreement, close the issue and ask for the admins to come to a consensus 3. If there's agreement, add the prospective admin to the [repo]-admins team +### Changes in terraform to add a new repository admin + +Change `production.tfvars`: + +- Find the relevant team under `team_children` and edit its members: + ```terraform + team_children = { + # ... + "django-community-playground-admins" = { + description = "django-community-playground administrators" + parent_team_key = "django-community-playground" + permission = "admin" + members = [ + # ... + "new_user" + ] + + repositories = [ + "django-commons-playground", + ] + } + } + ``` +- Create a pull-request to `main` branch, it will trigger terraform to plan the changes in the organization to be + executed. Review the changes and make sure they align with the request. +- Merge the pull-request, it will trigger terraform to apply the changes in the organization. + ## New Project Playbook -1. Check if repository meets [inbound requirements](https://github.com/django-commons/membership/blob/main/incoming_repo_requirements.md) +1. Check if repository + meets [inbound requirements](https://github.com/django-commons/membership/blob/main/incoming_repo_requirements.md) 2. Confirm who will be the admins and maintainers for the repository 3. PyPI project owner must add you (Django Commons admin) as owner in PyPI 4. (TODO: Determine how this works with transfering out of an org and into the Django Commons org) -5. [Add repository owner to Django Commons as member](https://github.com/orgs/django-commons/people) (they'll be added to a team later) -6. Share link ([https://docs.github.com/en/repositories/creating-and-managing-repositories/transferring-a-repository](https://docs.github.com/en/repositories/creating-and-managing-repositories/transferring-a-repository)) with repo owner to transfer repo +5. [Add repository owner to Django Commons as member](https://github.com/orgs/django-commons/people) (they'll be added + to a team later) +6. Share + link ([https://docs.github.com/en/repositories/creating-and-managing-repositories/transferring-a-repository](https://docs.github.com/en/repositories/creating-and-managing-repositories/transferring-a-repository)) + with repo owner to transfer repo 7. Wait for repository transferred in 8. [Run new team action](https://github.com/django-commons/controls/actions/workflows/new_team.yml) 9. Invite repository admins to [repo]-admins team, repository maintainers to [repo]-committers team 10. Configure environments pypi and testpypi -11. For pypi environment, add Deployment protection rule with reviewers as [repo]-admins and enable "Allow administrators to bypass configured protection rules" -12. Under Actions > General > "Fork pull request workflows from outside collaborators", set "Require approval for first-time contributors" +11. For pypi environment, add Deployment protection rule with reviewers as [repo]-admins and enable "Allow + administrators to bypass configured protection rules" +12. Under Actions > General > "Fork pull request workflows from outside collaborators", set "Require approval for + first-time contributors" 13. Add previous repository owner to [repo]-admins team 14. Set a calender event or reminder for 30 days in the future to remove previous repository owner from team +### Terraform changes to add a new project + +Changes in `production.tfvars` (Assuming repository name is `repo-name`): + +1. Add the new repository to the `repositories` section: + ```terraform + repositories = { + # ... + "repo-name" = { + description = "repo description" + allow_auto_merge = false # optional, default is false + allow_merge_commit = false # optional, default is false + allow_rebase_merge = false # optional, default is false + allow_squash_merge = false # optional, default is false + allow_update_branch = false # optional, default is false + enable_branch_protection = true # optional, default is true + has_discussions = true # optional, default is true + has_downloads = true # optional, default is true + is_template = false # optional, default is false + push_allowances = [] + required_status_checks_contexts = [] # optional, default is [] + template = "" # optional, default is "" + topics = [] + visibility = "public" # optional, default is "public" + } + } + ``` +2. Add the new parent team `repo-name` for the repository in the `team_parents` section with the relevant members: + ```terraform + team_parents = { + # ... + "repo-name" = { + description = "django-community-playground team" + members = [ + "tim-schilling", + "williln", + ] + permission = "triage" + + repositories = [ + "repo-name", + ] + review_request_delegation = true + } + } + ``` +3. Add two new child teams `repo-name-admin` and `repo-name-committers` for the repository in the `team_children` + section + with the relevant members: + ```terraform + team_children = { + # ... + "repo-name-admin" = { + description = "django-community-playground team" + parent_team_key = "repo-name" + members = [ + "tim-schilling", + "williln", + "ryancheley", + "Stormheg", + "cunla", + ] + permission = "admin" + } + "repo-name-committers" = { + description = "django-community-playground team" + parent_team_key = "repo-name" + members = [ + "tim-schilling", + "williln", + "ryancheley", + "Stormheg", + "cunla", + ] + permission = "push" + } + } + ``` +4. Create a pull-request to `main` branch, it will trigger terraform to plan the changes in the organization to be + executed. Review the changes and make sure they align with the request. +5. Merge the pull-request, it will trigger terraform to apply the changes in the organization. + ## Remove Project Playbook 1. Confirm there's agreement amongst current project maintainers to move project out of Django Commons 2. Add new Owner(s) to project in PyPI 3. [Transfer GitHub repo to new owner or Org](https://github.com/orgs/django-commons/people) 4. Wait for repository to be transferred out. -5. [Delete top-level team for repository](https://github.com/orgs/django-commons/teams) (the delete will cascade to the sub-teams) -6. Remove all Django Commons members from PyPI project (except any that are staying on from step 2) -7. (TODO: Determine how to handle transferring a PyPI project out of an organization) +5. Remove all Django Commons members from PyPI project (except any that are staying on from step 2) +6. (TODO: Determine how to handle transferring a PyPI project out of an organization) + +### Terraform changes to remove a project +1. Remove the repository from the `repositories` section in `production.tfvars` +2. Remove the parent team and child teams for the repository from the `team_parents` and `team_children` sections in + `production.tfvars` +3. Create a pull-request to `main` branch, it will trigger terraform to plan the changes in the organization to be + executed. + Review the changes and make sure they align with the request. +4. Merge the pull-request, it will trigger terraform to apply the changes in the organization. From 46ff1b4ce515e678db1a456449394360bd24fa89 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Sat, 20 Jul 2024 12:00:02 -0400 Subject: [PATCH 13/39] readme changes --- .github/workflows/plan.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/plan.yml b/.github/workflows/plan.yml index a398775..fc9e789 100644 --- a/.github/workflows/plan.yml +++ b/.github/workflows/plan.yml @@ -21,9 +21,7 @@ jobs: uses: dflook/terraform-plan@v1 with: path: terraform - label: production add_github_comment: true - workspace: prod var_file: tfvars/production.tfvars variables: | github_token=${{ secrets.GITHUB_TOKEN }} From 9ffc5e6dcb4df6f9b8233281df2d079bc6637531 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Sat, 20 Jul 2024 12:03:58 -0400 Subject: [PATCH 14/39] readme changes --- .github/workflows/plan.yml | 3 ++- terraform/{tfvars => production}/production.tfvars | 0 2 files changed, 2 insertions(+), 1 deletion(-) rename terraform/{tfvars => production}/production.tfvars (100%) diff --git a/.github/workflows/plan.yml b/.github/workflows/plan.yml index fc9e789..ba8462e 100644 --- a/.github/workflows/plan.yml +++ b/.github/workflows/plan.yml @@ -22,6 +22,7 @@ jobs: with: path: terraform add_github_comment: true - var_file: tfvars/production.tfvars + var_file: production.tfvars + workspace: "production" variables: | github_token=${{ secrets.GITHUB_TOKEN }} diff --git a/terraform/tfvars/production.tfvars b/terraform/production/production.tfvars similarity index 100% rename from terraform/tfvars/production.tfvars rename to terraform/production/production.tfvars From b423afcb5c39597bcdeed77de8927e8350f51292 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Sat, 20 Jul 2024 12:07:54 -0400 Subject: [PATCH 15/39] gha --- .github/workflows/plan.yml | 8 ++- terraform/production/org.tfvars | 27 ++++++++ terraform/production/repositories.tfvars | 33 ++++++++++ .../{production.tfvars => teams.tfvars} | 62 ------------------- 4 files changed, 65 insertions(+), 65 deletions(-) create mode 100644 terraform/production/org.tfvars create mode 100644 terraform/production/repositories.tfvars rename terraform/production/{production.tfvars => teams.tfvars} (58%) diff --git a/.github/workflows/plan.yml b/.github/workflows/plan.yml index ba8462e..359da1b 100644 --- a/.github/workflows/plan.yml +++ b/.github/workflows/plan.yml @@ -20,9 +20,11 @@ jobs: - name: terraform plan uses: dflook/terraform-plan@v1 with: - path: terraform add_github_comment: true - var_file: production.tfvars - workspace: "production" + path: "terraform/production" + var_file: | + org.tfvars + repositories.tfvars + teams.tfvars variables: | github_token=${{ secrets.GITHUB_TOKEN }} diff --git a/terraform/production/org.tfvars b/terraform/production/org.tfvars new file mode 100644 index 0000000..07186be --- /dev/null +++ b/terraform/production/org.tfvars @@ -0,0 +1,27 @@ +# Organization admins +admins = [ + "tim-schilling", + "williln", + "ryancheley", + "Stormheg", + "cunla", +] +# Organization members +members = [ + "gav-fyi", + "jcjudkins", + "joshuadavidthomas", + "matthiask", + "nanorepublica", + "Natim", + "pfouque", + "priyapahwa", + "testSchilling", +] + +organization_secrets = { + # "GPG_PASSPHRASE" = { + # description = "GPG Passphrase used to encrypt plan.out files" + # visibility = "all" + # } +} \ No newline at end of file diff --git a/terraform/production/repositories.tfvars b/terraform/production/repositories.tfvars new file mode 100644 index 0000000..05e6ffc --- /dev/null +++ b/terraform/production/repositories.tfvars @@ -0,0 +1,33 @@ +# Organization repositories +repositories = { + # Keep the following repositories in alphabetical order + + ".github" = { + description = "A Special Repository." + enable_branch_protection = false + + topics = [] + push_allowances = [] + } + + "controls" = { + description = "The controls for managing Django Commons projects" + enable_branch_protection = false + + topics = [] + push_allowances = [] + visibility = "public" + } + + "membership" = { + description = "Membership repository for the django-commons organization." + visibility = "public" + topics = [] + push_allowances = [] + } + + "django-commons-playground" = { + description = "A sample project to test things out" + topics = [] + } +} diff --git a/terraform/production/production.tfvars b/terraform/production/teams.tfvars similarity index 58% rename from terraform/production/production.tfvars rename to terraform/production/teams.tfvars index 0ace0bb..39950cb 100644 --- a/terraform/production/production.tfvars +++ b/terraform/production/teams.tfvars @@ -1,65 +1,3 @@ -# Organization admins -admins = [ - "tim-schilling", - "williln", - "ryancheley", - "Stormheg", - "cunla", -] -# Organization members -members = [ - "gav-fyi", - "jcjudkins", - "joshuadavidthomas", - "matthiask", - "nanorepublica", - "Natim", - "pfouque", - "priyapahwa", - "testSchilling", -] - -organization_secrets = { - # "GPG_PASSPHRASE" = { - # description = "GPG Passphrase used to encrypt plan.out files" - # visibility = "all" - # } -} - -# Organization repositories -repositories = { - # Keep the following repositories in alphabetical order - - ".github" = { - description = "A Special Repository." - enable_branch_protection = false - - topics = [] - push_allowances = [] - } - - "controls" = { - description = "The controls for managing Django Commons projects" - enable_branch_protection = false - - topics = [] - push_allowances = [] - visibility = "public" - } - - "membership" = { - description = "Membership repository for the django-commons organization." - visibility = "public" - topics = [] - push_allowances = [] - } - - "django-commons-playground" = { - description = "A sample project to test things out" - topics = [] - } - -} team_parents = { "Admins" = { From 0c37c092aba2d72e0632c17edd22c791d8af1041 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Sat, 20 Jul 2024 12:08:55 -0400 Subject: [PATCH 16/39] gha --- .github/workflows/plan.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/plan.yml b/.github/workflows/plan.yml index 359da1b..db05a5f 100644 --- a/.github/workflows/plan.yml +++ b/.github/workflows/plan.yml @@ -21,10 +21,10 @@ jobs: uses: dflook/terraform-plan@v1 with: add_github_comment: true - path: "terraform/production" + path: "terraform" var_file: | - org.tfvars - repositories.tfvars - teams.tfvars + production/org.tfvars + production/repositories.tfvars + production/teams.tfvars variables: | github_token=${{ secrets.GITHUB_TOKEN }} From 0bb5e56b5fb65f7c4123777e8cf0a150be7e503f Mon Sep 17 00:00:00 2001 From: Daniel M Date: Sat, 20 Jul 2024 12:13:18 -0400 Subject: [PATCH 17/39] gha --- .github/workflows/plan.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/plan.yml b/.github/workflows/plan.yml index db05a5f..7fb9932 100644 --- a/.github/workflows/plan.yml +++ b/.github/workflows/plan.yml @@ -23,8 +23,8 @@ jobs: add_github_comment: true path: "terraform" var_file: | - production/org.tfvars - production/repositories.tfvars - production/teams.tfvars + terraform/production/org.tfvars + terraform/production/repositories.tfvars + terraform/production/teams.tfvars variables: | github_token=${{ secrets.GITHUB_TOKEN }} From bad31e0eb3c2eb23877011e00bf50f1652771ff3 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Sat, 20 Jul 2024 12:15:03 -0400 Subject: [PATCH 18/39] gha --- .github/workflows/plan.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/plan.yml b/.github/workflows/plan.yml index 7fb9932..f155b1d 100644 --- a/.github/workflows/plan.yml +++ b/.github/workflows/plan.yml @@ -22,9 +22,9 @@ jobs: with: add_github_comment: true path: "terraform" + variables: | + github_token = "${{ secrets.GITHUB_TOKEN }}" var_file: | terraform/production/org.tfvars terraform/production/repositories.tfvars terraform/production/teams.tfvars - variables: | - github_token=${{ secrets.GITHUB_TOKEN }} From 61dc4a4309639dfa5a6a77dd2c87a8b684c0a93d Mon Sep 17 00:00:00 2001 From: Daniel M Date: Sat, 20 Jul 2024 12:16:19 -0400 Subject: [PATCH 19/39] gha --- .github/workflows/plan.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/plan.yml b/.github/workflows/plan.yml index f155b1d..b518175 100644 --- a/.github/workflows/plan.yml +++ b/.github/workflows/plan.yml @@ -19,11 +19,13 @@ jobs: - name: terraform plan uses: dflook/terraform-plan@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: add_github_comment: true path: "terraform" variables: | - github_token = "${{ secrets.GITHUB_TOKEN }}" + github_token = "${{ env.GITHUB_TOKEN }}" var_file: | terraform/production/org.tfvars terraform/production/repositories.tfvars From 9ac827d7832cf26ded6c20ea533bc571386cd109 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Sat, 20 Jul 2024 12:23:32 -0400 Subject: [PATCH 20/39] gha --- .github/workflows/plan.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/plan.yml b/.github/workflows/plan.yml index b518175..5b619f4 100644 --- a/.github/workflows/plan.yml +++ b/.github/workflows/plan.yml @@ -12,6 +12,7 @@ jobs: permissions: pull-requests: write contents: read + id-token: write steps: - name: Checkout code From 9cff7bb045693b1e69562b3ecfeaae2cc538ab63 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Sat, 20 Jul 2024 12:26:10 -0400 Subject: [PATCH 21/39] gha --- .github/workflows/plan.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/plan.yml b/.github/workflows/plan.yml index 5b619f4..3aa68bb 100644 --- a/.github/workflows/plan.yml +++ b/.github/workflows/plan.yml @@ -22,11 +22,12 @@ jobs: uses: dflook/terraform-plan@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TERRAFORM_ACTIONS_GITHUB_TOKEN: ${{ secrets.TERRAFORM_MANAGEMENT_GITHUB_TOKEN }} with: add_github_comment: true path: "terraform" variables: | - github_token = "${{ env.GITHUB_TOKEN }}" + github_token = "${{ env.TERRAFORM_ACTIONS_GITHUB_TOKEN }}" var_file: | terraform/production/org.tfvars terraform/production/repositories.tfvars From 298065b46c1aff9109686db2db754d6b48714f55 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Sat, 20 Jul 2024 12:28:05 -0400 Subject: [PATCH 22/39] gha --- .github/workflows/plan.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/plan.yml b/.github/workflows/plan.yml index 3aa68bb..51dafed 100644 --- a/.github/workflows/plan.yml +++ b/.github/workflows/plan.yml @@ -21,13 +21,12 @@ jobs: - name: terraform plan uses: dflook/terraform-plan@v1 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - TERRAFORM_ACTIONS_GITHUB_TOKEN: ${{ secrets.TERRAFORM_MANAGEMENT_GITHUB_TOKEN }} + TERRAFORM_ACTIONS_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: add_github_comment: true path: "terraform" variables: | - github_token = "${{ env.TERRAFORM_ACTIONS_GITHUB_TOKEN }}" + github_token = "${{ secrets.TERRAFORM_MANAGEMENT_GITHUB_TOKEN }}" var_file: | terraform/production/org.tfvars terraform/production/repositories.tfvars From 0874fda168d00e6c5f192adda84064263d308443 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Sat, 20 Jul 2024 12:31:05 -0400 Subject: [PATCH 23/39] gha --- .github/workflows/plan.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/plan.yml b/.github/workflows/plan.yml index 51dafed..cee162f 100644 --- a/.github/workflows/plan.yml +++ b/.github/workflows/plan.yml @@ -12,7 +12,6 @@ jobs: permissions: pull-requests: write contents: read - id-token: write steps: - name: Checkout code From 96000caf7c55d192d19c7099c0b9997516ae13d7 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Sat, 20 Jul 2024 12:33:09 -0400 Subject: [PATCH 24/39] gha --- README.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 8867f21..63cc8eb 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Django Commons packages. ### Terraform changes to add member to the organization -- Change `production.tfvars`: add the username under `members`, like this: +- Change `org.tfvars`: add the username under `members`, like this: ```terraform members = [ # ... @@ -36,7 +36,7 @@ Django Commons packages. ### Terraform changes to add member to the team -- Change `production.tfvars`: find the relevant team under `team_parents` or `team_children`, and edit its members: +- Change `teams.tfvars`: find the relevant team under `team_parents` or `team_children`, and edit its members: ```terraform team_children = { # ... @@ -67,7 +67,7 @@ Django Commons packages. ### Changes in terraform to add a new repository admin -Change `production.tfvars`: +Change `repositories.tfvars`: - Find the relevant team under `team_children` and edit its members: ```terraform @@ -117,9 +117,9 @@ Change `production.tfvars`: ### Terraform changes to add a new project -Changes in `production.tfvars` (Assuming repository name is `repo-name`): +Assuming repository name is `repo-name`: -1. Add the new repository to the `repositories` section: +1. In `repositories.tfvars`, Add the new repository to the `repositories` section: ```terraform repositories = { # ... @@ -142,7 +142,8 @@ Changes in `production.tfvars` (Assuming repository name is `repo-name`): } } ``` -2. Add the new parent team `repo-name` for the repository in the `team_parents` section with the relevant members: +2. In `teams.tfvars`, add the new parent team `repo-name` for the repository in the `team_parents` section with the + relevant members: ```terraform team_parents = { # ... @@ -207,9 +208,10 @@ Changes in `production.tfvars` (Assuming repository name is `repo-name`): 6. (TODO: Determine how to handle transferring a PyPI project out of an organization) ### Terraform changes to remove a project -1. Remove the repository from the `repositories` section in `production.tfvars` + +1. Remove the repository from the `repositories` section in `repositories.tfvars` 2. Remove the parent team and child teams for the repository from the `team_parents` and `team_children` sections in - `production.tfvars` + `teams.tfvars` 3. Create a pull-request to `main` branch, it will trigger terraform to plan the changes in the organization to be executed. Review the changes and make sure they align with the request. From 6bb454d59eb587c1aa40e42a9d067a74dd55c6ae Mon Sep 17 00:00:00 2001 From: Daniel M Date: Sat, 20 Jul 2024 13:24:10 -0400 Subject: [PATCH 25/39] gha --- .github/workflows/apply.yml | 34 ++++++++++++++++++++++++++++++ .github/workflows/new_team.yml | 38 ---------------------------------- .github/workflows/plan.yml | 2 ++ 3 files changed, 36 insertions(+), 38 deletions(-) create mode 100644 .github/workflows/apply.yml delete mode 100644 .github/workflows/new_team.yml diff --git a/.github/workflows/apply.yml b/.github/workflows/apply.yml new file mode 100644 index 0000000..8bc9b03 --- /dev/null +++ b/.github/workflows/apply.yml @@ -0,0 +1,34 @@ +name: "Apply org changes" + +on: + push: + branches: + - main + paths: + - 'terraform/production/*.tfvars' + +jobs: + apply-changes: + name: "Org changes plan" + runs-on: ubuntu-latest + + permissions: + pull-requests: write + contents: read + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: terraform apply + uses: dflook/terraform-apply@v1 + env: + TERRAFORM_ACTIONS_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + path: "terraform" + variables: | + github_token = "${{ secrets.TERRAFORM_MANAGEMENT_GITHUB_TOKEN }}" + var_file: | + terraform/production/org.tfvars + terraform/production/repositories.tfvars + terraform/production/teams.tfvars diff --git a/.github/workflows/new_team.yml b/.github/workflows/new_team.yml deleted file mode 100644 index 6c3e03b..0000000 --- a/.github/workflows/new_team.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: Create new Django Commons teams for a project 🐍 - -on: - workflow_dispatch: - inputs: - repo: - description: "The slug of the repository in django-commons" - required: true - type: string - base-team-name: - description: "The base team name. Typically should match repo" - required: true - type: string - -env: - ORG: django-commons - -jobs: - create-teams: - name: Create teams 📦 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Create teams and assign permissions - env: - GH_TOKEN: ${{ secrets.ORG_CONTROLS_PAT }} - run: >- - TEAM_ID=$(gh api -X POST /orgs/${{ env.ORG }}/teams -f name=${{ inputs.base-team-name }} -f description="People who have access to the project." -f privacy="closed" --jq ".id") - - gh api -X POST /orgs/${{ env.ORG }}/teams -f name="${{ inputs.base-team-name }}-committers" -f description="People who have the ability to commit and maintain the project." -f privacy="closed" -F parent_team_id=${TEAM_ID} - - gh api -X POST /orgs/${{ env.ORG }}/teams -f name="${{ inputs.base-team-name }}-maintainers" -f description="People who administrate the project." -f privacy="closed" -F parent_team_id=${TEAM_ID} - - gh api -X PUT "/orgs/${{ env.ORG }}/teams/${{ inputs.base-team-name }}/repos/${{ env.ORG }}/${{ inputs.repo }}" -f permission="triage" - - gh api -X PUT "/orgs/${{ env.ORG }}/teams/${{ inputs.base-team-name }}-committers/repos/${{ env.ORG }}/${{ inputs.repo }}" -f permission="push" - - gh api -X PUT "/orgs/${{ env.ORG }}/teams/${{ inputs.base-team-name }}-maintainers/repos/${{ env.ORG }}/${{ inputs.repo }}" -f permission="maintain" \ No newline at end of file diff --git a/.github/workflows/plan.yml b/.github/workflows/plan.yml index cee162f..5798060 100644 --- a/.github/workflows/plan.yml +++ b/.github/workflows/plan.yml @@ -3,6 +3,8 @@ on: pull_request: branches: - main + paths: + - 'terraform/production/*.tfvars' jobs: plan-changes: From a9e312bf5bd047e88ce8a3cee3878bcde34b7aef Mon Sep 17 00:00:00 2001 From: Daniel M Date: Sat, 20 Jul 2024 13:29:07 -0400 Subject: [PATCH 26/39] gha --- .github/workflows/apply.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/apply.yml b/.github/workflows/apply.yml index 8bc9b03..8f94007 100644 --- a/.github/workflows/apply.yml +++ b/.github/workflows/apply.yml @@ -13,8 +13,7 @@ jobs: runs-on: ubuntu-latest permissions: - pull-requests: write - contents: read + contents: write steps: - name: Checkout code @@ -32,3 +31,11 @@ jobs: terraform/production/org.tfvars terraform/production/repositories.tfvars terraform/production/teams.tfvars + + - name: Commit changes + uses: devops-infra/action-commit-push@v0.9.2 + with: + github_token: "${{ secrets.GITHUB_TOKEN }}" + commit_prefix: "[AUTO]" + commit_message: "State changes after apply" + force: false From 6661316f8f42af6dffa12203b89ce9b10f570c10 Mon Sep 17 00:00:00 2001 From: tschilling Date: Tue, 23 Jul 2024 19:30:21 -0500 Subject: [PATCH 27/39] Use member as dynamic variable to avoid confusion with the members list. --- terraform/main.tf | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/terraform/main.tf b/terraform/main.tf index 19ea47a..7ada386 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -192,20 +192,20 @@ resource "github_team_members" "parents" { team_id = github_team.parents[each.key].id - dynamic "members" { + dynamic "member" { for_each = each.value.members content { - username = members.value + username = member.value role = "member" } } - dynamic "members" { + dynamic "member" { for_each = each.value.maintainers content { - username = members.value + username = member.value role = "maintainer" } } @@ -216,20 +216,20 @@ resource "github_team_members" "children" { team_id = github_team.children[each.key].id - dynamic "members" { + dynamic "member" { for_each = each.value.members content { - username = members.value + username = member.value role = "member" } } - dynamic "members" { + dynamic "member" { for_each = each.value.maintainers content { - username = members.value + username = member.value role = "maintainer" } } From e042e5b3b52f5663ad4d0096613c1b2d509447f0 Mon Sep 17 00:00:00 2001 From: tschilling Date: Tue, 23 Jul 2024 19:37:31 -0500 Subject: [PATCH 28/39] Adjust repository defaults. The wiki seems like a possibility and the security alerts seems like a good thing to enable by default until someone complains. --- terraform/main.tf | 4 ++-- terraform/variables.tf | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/terraform/main.tf b/terraform/main.tf index 7ada386..dd5bc27 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -118,14 +118,14 @@ resource "github_repository" "this" { has_discussions = each.value.has_discussions has_issues = true has_projects = true - has_wiki = false + has_wiki = each.value.has_wiki is_template = each.value.is_template name = each.key squash_merge_commit_message = "BLANK" squash_merge_commit_title = "PR_TITLE" topics = each.value.topics visibility = each.value.visibility - vulnerability_alerts = false + vulnerability_alerts = true dynamic "template" { for_each = each.value.template != null ? [each.value.template] : [] diff --git a/terraform/variables.tf b/terraform/variables.tf index aefff07..1c97f3d 100644 --- a/terraform/variables.tf +++ b/terraform/variables.tf @@ -38,6 +38,7 @@ variable "repositories" { enable_branch_protection = optional(bool, true) has_discussions = optional(bool, true) has_downloads = optional(bool, true) + has_wiki = optional(bool, false) is_template = optional(bool, false) push_allowances = optional(list(string), []) required_status_checks_contexts = optional(list(string), []) From 3332bf97ed8f0995aa5d77b4305ce02a36c9caf5 Mon Sep 17 00:00:00 2001 From: tschilling Date: Tue, 23 Jul 2024 19:54:40 -0500 Subject: [PATCH 29/39] Comment the main terraform file with descriptions of what each section does. Removes the security template and other sections that aren't 100% obviously critical. We can always re-add these later, but teams can also configure this manually. --- terraform/main.tf | 50 +++++++++++++----------------- terraform/markdown/SECURITY.md.tpl | 10 ------ 2 files changed, 21 insertions(+), 39 deletions(-) delete mode 100644 terraform/markdown/SECURITY.md.tpl diff --git a/terraform/main.tf b/terraform/main.tf index dd5bc27..9562eb0 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -139,23 +139,8 @@ resource "github_repository" "this" { # GitHub Repository File Resource # https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository_file -# -# resource "github_repository_file" "security_policy" { -# for_each = var.repositories -# -# branch = "main" -# content = templatefile("${path.module}/markdown/SECURITY.md.tpl", { repository = each.key }) -# file = "SECURITY.md" -# repository = each.key -# commit_message = "Update SECURITY.md" -# commit_author = "Open Source Infrastructure as Code Service Account" -# commit_email = "github-sa@osinfra.io" -# overwrite_on_create = true -# -# depends_on = [ -# github_branch_protection.this -# ] -# } +# This could be used to generate a file such as a security template. +# See https://github.com/osinfra-io/ for an example # Github Team Resource @@ -167,6 +152,7 @@ resource "github_repository" "this" { # To get the team ids, you can run the following curl command with a token that has the read:org scope against your own organization. # curl -H "Authorization: token $GITHUB_READ_ORG_TOKEN" https://api.github.com/orgs/osinfra-io/teams +# Create the base teams for each repository. resource "github_team" "parents" { for_each = var.team_parents @@ -175,6 +161,7 @@ resource "github_team" "parents" { privacy = each.value.privacy } +# Create the children teams for each repository. resource "github_team" "children" { for_each = var.team_children @@ -187,6 +174,8 @@ resource "github_team" "children" { # GitHub Team Membership Resource # https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team_members +# Define the teams for each repository. The members here +# will have triage permissions. resource "github_team_members" "parents" { for_each = var.team_parents @@ -201,6 +190,8 @@ resource "github_team_members" "parents" { } } + # Maintainer here means the maintainer role for the team. + # It's not a maintainer of the repo. dynamic "member" { for_each = each.value.maintainers @@ -211,6 +202,9 @@ resource "github_team_members" "parents" { } } + +# Define the privileged teams for each repository. The members here +# will have commit or maintainer permissions depending on the team. resource "github_team_members" "children" { for_each = var.team_children @@ -225,6 +219,8 @@ resource "github_team_members" "children" { } } + # Maintainer here means the maintainer role for the team. + # It's not a maintainer of the repo. dynamic "member" { for_each = each.value.maintainers @@ -238,6 +234,7 @@ resource "github_team_members" "children" { # Github Team Repository Resource # https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team_repository +# Create the appropriate permissions for the repository teams. resource "github_team_repository" "children" { for_each = local.child_team_repositories @@ -246,6 +243,7 @@ resource "github_team_repository" "children" { permission = each.value.permission } +# Create the elevated permissions for the repositories' privileged teams. resource "github_team_repository" "parents" { for_each = local.parent_team_repositories @@ -256,22 +254,14 @@ resource "github_team_repository" "parents" { # GitHub Team Settings Resource # https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team_settings - -resource "github_team_settings" "this" { - for_each = local.review_request_delegations - - review_request_delegation { - algorithm = "LOAD_BALANCE" - member_count = 2 - notify = false - } - - team_id = github_team.parents[each.key].id -} +# +# This can be used to enable automatic PR review requests +# GitHub docs: https://docs.github.com/en/organizations/organizing-members-into-teams/managing-code-review-settings-for-your-team#configuring-auto-assignment # Random Password Resource # https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password +# This is necessary to set a GitHub org secret resource "random_password" "this" { for_each = var.organization_secrets length = 32 @@ -285,6 +275,8 @@ resource "random_password" "this" { # Time Rotating Resource # https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/rotating +# This is necessary to use random_password, which is needed +# to set a GitHub org secret resource "time_rotating" "this" { rotation_days = 5 } diff --git a/terraform/markdown/SECURITY.md.tpl b/terraform/markdown/SECURITY.md.tpl deleted file mode 100644 index e97d9d6..0000000 --- a/terraform/markdown/SECURITY.md.tpl +++ /dev/null @@ -1,10 +0,0 @@ -# Security Policy - -Open Source Infrastructure (as Code) exposes identifying information that would not be exposed in a traditional -private organization to share knowledge and best practices. This is a net positive for -the community, but it does come with some risks. - -## Reporting a Vulnerability - -Privately discuss, fix, and publish information about security vulnerabilities in this repository by drafting a new -[security advisory](https://github.com/osinfra-io/${repository}/security/advisories/new). From 8b997ce3f91bc1c73922506ff9c3c33355629199 Mon Sep 17 00:00:00 2001 From: tschilling Date: Tue, 23 Jul 2024 20:03:13 -0500 Subject: [PATCH 30/39] Pin the terraform github action third-party actions to SHA hashes. --- .github/workflows/apply.yml | 5 ++++- .github/workflows/plan.yml | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/apply.yml b/.github/workflows/apply.yml index 8f94007..9ba67f4 100644 --- a/.github/workflows/apply.yml +++ b/.github/workflows/apply.yml @@ -20,7 +20,10 @@ jobs: uses: actions/checkout@v4 - name: terraform apply - uses: dflook/terraform-apply@v1 + # v1.43.0 + # Use the commit hash for security hardening + # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-third-party-actions + uses: dflook/terraform-apply@dcda97d729f1843ede471d2fac989cb946f5622a env: TERRAFORM_ACTIONS_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/.github/workflows/plan.yml b/.github/workflows/plan.yml index 5798060..27c685c 100644 --- a/.github/workflows/plan.yml +++ b/.github/workflows/plan.yml @@ -20,7 +20,10 @@ jobs: uses: actions/checkout@v4 - name: terraform plan - uses: dflook/terraform-plan@v1 + # v1.43.0 + # Use the commit hash for security hardening + # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-third-party-actions + uses: dflook/terraform-plan@d9df4f6c2484e709ba7ffaa16c98a6906f4760cd env: TERRAFORM_ACTIONS_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From 1d0af1e607c58ba1e779cdfb4597dca5644f962c Mon Sep 17 00:00:00 2001 From: tschilling Date: Tue, 23 Jul 2024 20:34:08 -0500 Subject: [PATCH 31/39] Use domain specific naming for variables. This should make it easier to determine what a parent team relates to versus what a child team relates to. --- terraform/locals.tf | 24 +++++++++++----------- terraform/main.tf | 23 ++++++++++----------- terraform/production/teams.tfvars | 33 +++++++++++++++---------------- terraform/variables.tf | 18 ++++++++--------- 4 files changed, 49 insertions(+), 49 deletions(-) diff --git a/terraform/locals.tf b/terraform/locals.tf index 59ad2da..160ba6f 100644 --- a/terraform/locals.tf +++ b/terraform/locals.tf @@ -12,14 +12,14 @@ locals { if repository.enable_branch_protection } - child_team_repositories = { + privileged_repository_team_permissions = { for repository in flatten([ - for team_child_key, team_child in var.team_children : [ - for repository in team_child.repositories : { - team_child = team_child_key + for team_key, team in var.teams_repositories_privileged : [ + for repository in team.repositories : { + team_child = team_key repository = repository - permission = team_child.permission + permission = team.permission } ] ]) : "${repository.team_child}-${repository.repository}" => repository @@ -30,22 +30,22 @@ locals { for user in var.members : user => "member" } - parent_team_repositories = { + repository_team_permissions = { for repository in flatten([ - for team_parent_key, team_parent in var.team_parents : [ - for repository in team_parent.repositories : { - team_parent = team_parent_key + for team_key, team in var.teams_repositories : [ + for repository in team.repositories : { + team_parent = team_key repository = repository - permission = team_parent.permission + permission = team.permission } ] ]) : "${repository.team_parent}-${repository.repository}" => repository } review_request_delegations = { - for team_parent_key, team_parent in var.team_parents : team_parent_key => team_parent - if team_parent.review_request_delegation + for team_key, team in var.teams_repositories : team_key => team + if team.review_request_delegation } users = merge(local.admins, local.members) diff --git a/terraform/main.tf b/terraform/main.tf index 9562eb0..eb049a6 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -152,18 +152,19 @@ resource "github_repository" "this" { # To get the team ids, you can run the following curl command with a token that has the read:org scope against your own organization. # curl -H "Authorization: token $GITHUB_READ_ORG_TOKEN" https://api.github.com/orgs/osinfra-io/teams -# Create the base teams for each repository. +# Create the base teams for each repository and the organization teams for Django Commons. resource "github_team" "parents" { - for_each = var.team_parents + for_each = var.teams_repositories name = each.key description = each.value.description privacy = each.value.privacy } -# Create the children teams for each repository. +# Create the privileged teams for each repository, +# such committers or maintainers. resource "github_team" "children" { - for_each = var.team_children + for_each = var.teams_repositories_privileged description = each.value.description name = each.key @@ -174,7 +175,7 @@ resource "github_team" "children" { # GitHub Team Membership Resource # https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team_members -# Define the teams for each repository. The members here +# Define the team membership for each repository. The members here # will have triage permissions. resource "github_team_members" "parents" { for_each = var.team_parents @@ -203,10 +204,10 @@ resource "github_team_members" "parents" { } -# Define the privileged teams for each repository. The members here +# Define the privileged team membership for each repository. The members here # will have commit or maintainer permissions depending on the team. resource "github_team_members" "children" { - for_each = var.team_children + for_each = var.teams_repositories_privileged team_id = github_team.children[each.key].id @@ -234,18 +235,18 @@ resource "github_team_members" "children" { # Github Team Repository Resource # https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team_repository -# Create the appropriate permissions for the repository teams. +# Create the elevated permissions for the repositories' privileged teams. resource "github_team_repository" "children" { - for_each = local.child_team_repositories + for_each = local.privileged_repository_team_permissions team_id = github_team.children[each.value.team_child].id repository = each.value.repository permission = each.value.permission } -# Create the elevated permissions for the repositories' privileged teams. +# Create the appropriate permissions for the repository teams. resource "github_team_repository" "parents" { - for_each = local.parent_team_repositories + for_each = local.repository_team_permissions team_id = github_team.parents[each.value.team_parent].id repository = each.value.repository diff --git a/terraform/production/teams.tfvars b/terraform/production/teams.tfvars index 39950cb..2c9e9ba 100644 --- a/terraform/production/teams.tfvars +++ b/terraform/production/teams.tfvars @@ -1,5 +1,4 @@ - -team_parents = { +teams_organization = { "Admins" = { description = "django-commons administrators" maintainers = [ @@ -11,7 +10,21 @@ team_parents = { ] members = null } + "security-team" = { + description = "django-commons security team" + maintainers = [ + "tim-schilling", + "matthiask" + ] + members = [] + permission = "push" + + repositories = [ + ] + } +} +teams_repositories = { "django-community-playground" = { description = "django-community-playground team" maintainers = [ @@ -33,23 +46,9 @@ team_parents = { review_request_delegation = true } - "security-team" = { - description = "django-commons security team" - maintainers = [ - "tim-schilling", - "matthiask" - ] - members = [] - permission = "push" - - repositories = [ - - ] - - } } -team_children = { +teams_repositories_privileged = { "django-community-playground-admins" = { description = "django-community-playground administrators" parent_team_key = "django-community-playground" diff --git a/terraform/variables.tf b/terraform/variables.tf index 1c97f3d..52d910d 100644 --- a/terraform/variables.tf +++ b/terraform/variables.tf @@ -52,27 +52,27 @@ variable "repositories" { })) } -variable "team_children" { - description = "Map of child teams to create" +variable "teams_repositories" { + description = "Map of repository teams and Django Commons organization teams to manage" type = map(object({ - description = string + description = string maintainers = optional(set(string), []) members = optional(set(string), []) permission = optional(string, null) - parent_team_key = string + privacy = optional(string, "closed") repositories = optional(set(string), []) + review_request_delegation = optional(bool, false) })) } -variable "team_parents" { - description = "Map of parent teams to create" +variable "teams_repositories_privileged" { + description = "Map of repository teams with elevated permissions to manage" type = map(object({ - description = string + description = string maintainers = optional(set(string), []) members = optional(set(string), []) permission = optional(string, null) - privacy = optional(string, "closed") + parent_team_key = string repositories = optional(set(string), []) - review_request_delegation = optional(bool, false) })) } From bd3f0ad5049308c30e04594f178ccbfcc0be2f7d Mon Sep 17 00:00:00 2001 From: tschilling Date: Tue, 23 Jul 2024 20:36:14 -0500 Subject: [PATCH 32/39] Re-add review request delegation. I didn't see it was used for the playground repo. --- terraform/main.tf | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/terraform/main.tf b/terraform/main.tf index eb049a6..c8281e9 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -255,9 +255,19 @@ resource "github_team_repository" "parents" { # GitHub Team Settings Resource # https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team_settings -# -# This can be used to enable automatic PR review requests -# GitHub docs: https://docs.github.com/en/organizations/organizing-members-into-teams/managing-code-review-settings-for-your-team#configuring-auto-assignment + +# This is used to enable automatic PR review requests +resource "github_team_settings" "this" { + for_each = local.review_request_delegations + + review_request_delegation { + algorithm = "LOAD_BALANCE" + member_count = 2 + notify = false + } + + team_id = github_team.parents[each.key].id +} # Random Password Resource # https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password From 94358afabd6fb515cb5b0b7d10f40fa8abbccd53 Mon Sep 17 00:00:00 2001 From: tschilling Date: Tue, 23 Jul 2024 20:40:39 -0500 Subject: [PATCH 33/39] Attempt to split the org teams from the repo teams. --- terraform/main.tf | 2 +- terraform/variables.tf | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/terraform/main.tf b/terraform/main.tf index c8281e9..def1c9e 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -154,7 +154,7 @@ resource "github_repository" "this" { # Create the base teams for each repository and the organization teams for Django Commons. resource "github_team" "parents" { - for_each = var.teams_repositories + for_each = concat(var.teams_repositories, var.teams_organization) name = each.key description = each.value.description diff --git a/terraform/variables.tf b/terraform/variables.tf index 52d910d..739b6c5 100644 --- a/terraform/variables.tf +++ b/terraform/variables.tf @@ -52,8 +52,21 @@ variable "repositories" { })) } +variable "teams_organization" { + description = "Map of Django Commons organization teams to manage" + type = map(object({ + description = string + maintainers = optional(set(string), []) + members = optional(set(string), []) + permission = optional(string, null) + privacy = optional(string, "closed") + repositories = optional(set(string), []) + review_request_delegation = optional(bool, false) + })) +} + variable "teams_repositories" { - description = "Map of repository teams and Django Commons organization teams to manage" + description = "Map of repository teams to manage" type = map(object({ description = string maintainers = optional(set(string), []) From 16354a69b652a7b337c8ba09995c9d7af811dadb Mon Sep 17 00:00:00 2001 From: tschilling Date: Tue, 23 Jul 2024 21:05:18 -0500 Subject: [PATCH 34/39] Revise the playbooks for the new names for the variables. --- README.md | 185 ++++++++++++++++++++++++++---------------------------- 1 file changed, 90 insertions(+), 95 deletions(-) diff --git a/README.md b/README.md index 63cc8eb..34b1c00 100644 --- a/README.md +++ b/README.md @@ -12,85 +12,69 @@ Django Commons packages. ## New Member Playbook 1. Review new issues/application at https://github.com/django-commons/membership/issues/ -2. If they are a real human and are reasonably trustworthy, comment "Approved" and nothing else - - If they aren't a real human or reasonably trustworthy, close the issue. - - Apply the needed terraform changes to add the member to the organization. - -### Terraform changes to add member to the organization - -- Change `org.tfvars`: add the username under `members`, like this: - ```terraform +2. If they are not a real human or not reasonably trustworthy, close the issue, asking for more information they are a human and not a spam bot. You can explain that by being a member, they can impact repositories immediately. +3. Add the user to the `members` collection in the [`terraform/production/org.tfvars`](https://github.com/django-commons/controls/blob/main/terraform/production/org.tfvars) file. + ```terraform members = [ # ... "new_user" ] - ``` -- Create a pull-request to `main` branch, it will trigger terraform to plan the changes in the organization to be - executed. Review the changes and make sure they align with the request. -- Merge the pull-request, it will trigger terraform to apply the changes in the organization. + ``` +5. If they requested to be on specific repository team(s), in the [`terraform/production/teams.tfvars`](https://github.com/django-commons/controls/blob/main/terraform/production/teams.tfvars) file, for the repository's key under `teams_repositories`, add them to the `members` collection. + ```terraform + teams_repositories = { + "[REPOSITORY]" = { + # ... + members = [ + # ... + "new_user" + ] + } + } + ``` +6. Create a pull-request to `main` branch, it will trigger terraform to plan the changes in the organization to be + executed. Review the changes and make sure they align with the request. +7. Merge the pull-request, it will trigger terraform to apply the changes in the organization. ## Team Change Playbook -1. If they are a real human and are reasonably trustworthy, comment "Approved" and close the issue manually -2. Add the member to requested team(s) - -### Terraform changes to add member to the team - -- Change `teams.tfvars`: find the relevant team under `team_parents` or `team_children`, and edit its members: - ```terraform - team_children = { - # ... - "django-community-playground-admins" = { - description = "django-community-playground administrators" - parent_team_key = "django-community-playground" - permission = "admin" - members = [ - # ... - "new_user" - ] - - repositories = [ - "django-commons-playground", - ] +1. If they are not a real human or not reasonably trustworthy, close the issue, asking for more information they are a human and not a spam bot. You can explain that by being a member, they can impact repositories immediately. +2. For the requested repository's team(s), in the [`terraform/production/teams.tfvars`](https://github.com/django-commons/controls/blob/main/terraform/production/teams.tfvars) file, for the repository's key under `teams_repositories`, add them to the `members` collection. + ```terraform + teams_repositories = { + "[REPOSITORY]" = { + # ... + members = [ + # ... + "new_user" + ] + } } - } - ``` -- Create a pull-request to `main` branch, it will trigger terraform to plan the changes in the organization to be - executed. Review the changes and make sure they align with the request. -- Merge the pull-request, it will trigger terraform to apply the changes in the organization. + ``` +3. Create a pull-request to `main` branch, it will trigger terraform to plan the changes in the organization to be + executed. Review the changes and make sure they align with the request. +4. Merge the pull-request, it will trigger terraform to apply the changes in the organization. -## New Repository Admin Playbook +## New Repository Admin or Committer Playbook -1. Confirm with all existing admins that they are okay with the prospective admin +1. Confirm with all existing admins that they are okay with the change 2. If there's disagreement, close the issue and ask for the admins to come to a consensus -3. If there's agreement, add the prospective admin to the [repo]-admins team - -### Changes in terraform to add a new repository admin - -Change `repositories.tfvars`: - -- Find the relevant team under `team_children` and edit its members: - ```terraform - team_children = { - # ... - "django-community-playground-admins" = { - description = "django-community-playground administrators" - parent_team_key = "django-community-playground" - permission = "admin" - members = [ - # ... - "new_user" - ] - - repositories = [ - "django-commons-playground", - ] +3. For the requested repository's team(s), in the [`terraform/production/teams.tfvars`](https://github.com/django-commons/controls/blob/main/terraform/production/teams.tfvars) + file, for the repository's key under `teams_repositories_privileged`, add them to the `members` collection for the correct team. There will be two privileged teams for each repository, `*-admins` and `*-committers`, the user should be added to the requested team. + ```terraform + teams_repositories_privileged = { + "[REPOSITORY]-[admins | committers]" = { + # ... + members = [ + # ... + "new_user" + ] + } } - } - ``` -- Create a pull-request to `main` branch, it will trigger terraform to plan the changes in the organization to be - executed. Review the changes and make sure they align with the request. -- Merge the pull-request, it will trigger terraform to apply the changes in the organization. + ``` +4. Create a pull-request to `main` branch, it will trigger terraform to plan the changes in the organization to be + executed. Review the changes and make sure they align with the request. +5. Merge the pull-request, it will trigger terraform to apply the changes in the organization. ## New Project Playbook @@ -98,28 +82,27 @@ Change `repositories.tfvars`: meets [inbound requirements](https://github.com/django-commons/membership/blob/main/incoming_repo_requirements.md) 2. Confirm who will be the admins and maintainers for the repository 3. PyPI project owner must add you (Django Commons admin) as owner in PyPI -4. (TODO: Determine how this works with transfering out of an org and into the Django Commons org) -5. [Add repository owner to Django Commons as member](https://github.com/orgs/django-commons/people) (they'll be added +4. (TODO: Determine how this works with transferring out of an org and into the Django Commons org) +5. [Add repository owner to Django Commons as member](#new-member-playbook) (they'll be added to a team later) 6. Share link ([https://docs.github.com/en/repositories/creating-and-managing-repositories/transferring-a-repository](https://docs.github.com/en/repositories/creating-and-managing-repositories/transferring-a-repository)) with repo owner to transfer repo 7. Wait for repository transferred in 8. [Run new team action](https://github.com/django-commons/controls/actions/workflows/new_team.yml) -9. Invite repository admins to [repo]-admins team, repository maintainers to [repo]-committers team -10. Configure environments pypi and testpypi +9. [Make Terraform changes to add new project](#terraform-changes-to-add-a-new-project) +10. [Configure environments](https://docs.github.com/en/actions/administering-github-actions/managing-environments-for-deployment#creating-an-environment) pypi and testpypi in the repository to enable [publishing packages via GitHub Actions](https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/#) 11. For pypi environment, add Deployment protection rule with reviewers as [repo]-admins and enable "Allow administrators to bypass configured protection rules" 12. Under Actions > General > "Fork pull request workflows from outside collaborators", set "Require approval for first-time contributors" -13. Add previous repository owner to [repo]-admins team -14. Set a calender event or reminder for 30 days in the future to remove previous repository owner from team +13. Set a calendar event or reminder for 30 days in the future to remove previous repository owner from team ### Terraform changes to add a new project Assuming repository name is `repo-name`: -1. In `repositories.tfvars`, Add the new repository to the `repositories` section: +1. In [`terraform/production/respositories.tfvars`](https://github.com/django-commons/controls/blob/main/terraform/production/respositories.tfvars), Add the new repository to the `repositories` section: ```terraform repositories = { # ... @@ -133,6 +116,7 @@ Assuming repository name is `repo-name`: enable_branch_protection = true # optional, default is true has_discussions = true # optional, default is true has_downloads = true # optional, default is true + has_wiki = false # optional, default is false is_template = false # optional, default is false push_allowances = [] required_status_checks_contexts = [] # optional, default is [] @@ -142,55 +126,66 @@ Assuming repository name is `repo-name`: } } ``` -2. In `teams.tfvars`, add the new parent team `repo-name` for the repository in the `team_parents` section with the +2. In [`terraform/production/teams.tfvars`](https://github.com/django-commons/controls/blob/main/terraform/production/teams.tfvars), add the new team `repo-name` for the repository in the `teams_repositories` section with the relevant members: ```terraform - team_parents = { + teams_repositories = { # ... "repo-name" = { - description = "django-community-playground team" + description = "repo-name team" members = [ - "tim-schilling", - "williln", + # Put the user from Step 5 here + "username", ] permission = "triage" - repositories = [ - "repo-name", + "repo-name", + ] + maintainers = [ + "tim-schilling", + "williln", + "ryancheley", + "Stormheg", + "cunla", ] - review_request_delegation = true } } ``` -3. Add two new child teams `repo-name-admin` and `repo-name-committers` for the repository in the `team_children` - section - with the relevant members: +3. Add two new child teams `repo-name-admins` and `repo-name-committers` for the repository in the `teams_repositories_privileged` + section with the relevant members: ```terraform - team_children = { + teams_repositories_privileged = { # ... - "repo-name-admin" = { - description = "django-community-playground team" - parent_team_key = "repo-name" + "repo-name-admins" = { + description = "repo-name admins team" + parent_team_key = "repo-name" members = [ + # Put the user from Step 5 here + "username", + ] + permission = "admin" + maintainers = [ "tim-schilling", "williln", "ryancheley", "Stormheg", "cunla", ] - permission = "admin" } "repo-name-committers" = { - description = "django-community-playground team" + description = "repo-name committers team" parent_team_key = "repo-name" members = [ + # Leave empty unless there are committers ready to be designated + ] + permission = "push" + maintainers = [ "tim-schilling", "williln", "ryancheley", "Stormheg", "cunla", ] - permission = "push" } } ``` @@ -209,9 +204,9 @@ Assuming repository name is `repo-name`: ### Terraform changes to remove a project -1. Remove the repository from the `repositories` section in `repositories.tfvars` -2. Remove the parent team and child teams for the repository from the `team_parents` and `team_children` sections in - `teams.tfvars` +1. Remove the repository from the `repositories` section in [`terraform/production/respositories.tfvars`](https://github.com/django-commons/controls/blob/main/terraform/production/respositories.tfvars) +2. Remove the parent team and child teams for the repository from the `teams_repositories` and `teams_repositories_privileged` sections in + [`terraform/production/teams.tfvars`](https://github.com/django-commons/controls/blob/main/terraform/production/teams.tfvars) 3. Create a pull-request to `main` branch, it will trigger terraform to plan the changes in the organization to be executed. Review the changes and make sure they align with the request. From 7e5837fd6687530440da69720b48613ce9dc9b0d Mon Sep 17 00:00:00 2001 From: tschilling Date: Tue, 23 Jul 2024 21:12:13 -0500 Subject: [PATCH 35/39] Fix missed team_parents reference. --- terraform/README.md | 30 ++++++++++++------------------ terraform/main.tf | 2 +- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/terraform/README.md b/terraform/README.md index 0722d04..dfe1050 100644 --- a/terraform/README.md +++ b/terraform/README.md @@ -52,39 +52,33 @@ All changes should be made in `production.tfvars`: # ... } ``` -- Add/Remove/Update teams by editing the `team_parents`. A team can have the following variables: +- Add/Remove/Update repository teams by editing the `teams_repositories`. A team can have the following variables: ```terraform - team_parents = { - "Admins" = { - description = "django-commons administrators" - maintainers = ["tim-schilling",] + teams_repositories = { + "some-repo" = { + description = "some-repo team" members = ["cunla",] - permission = "admin" # optional, default is null + permission = "triage" privacy = "closed" # optional, default is "closed" repositories = [ # optional, default is [] - "django-commons/controls", - "django-commons/membership", - "django-commons/terraform", + "django-commons/some-repo", ] review_request_delegation = false # optional, default is false } # ... } ``` -- Add/Remove/Update child-teams by editing the `team_children`. A child-team can have the following variables: +- Add/Remove/Update privileged repository teams by editing the `teams_repositories_privileged`. A team can have the following variables: ```terraform - team_children = { - "New-Admins" = { - description = "django-commons administrators" - parent_team_key = "Admins" - maintainers = ["tim-schilling",] + teams_repositories_privileged = { + "some-repo-admins" = { + description = "some-repo administrators" + parent_team_key = "some-repo" members = ["cunla",] permission = "admin" # optional, default is null privacy = "closed" # optional, default is "closed" repositories = [ # optional, default is [] - "django-commons/controls", - "django-commons/membership", - "django-commons/terraform", + "django-commons/some-repo", ] review_request_delegation = false # optional, default is false } diff --git a/terraform/main.tf b/terraform/main.tf index def1c9e..ff71164 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -178,7 +178,7 @@ resource "github_team" "children" { # Define the team membership for each repository. The members here # will have triage permissions. resource "github_team_members" "parents" { - for_each = var.team_parents + for_each = var.teams_repositories team_id = github_team.parents[each.key].id From 99c0cfc5675f58b24db53fabb8cab13517680e34 Mon Sep 17 00:00:00 2001 From: tschilling Date: Tue, 23 Jul 2024 21:14:16 -0500 Subject: [PATCH 36/39] Update terraform specific docs with new variable names. --- terraform/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/terraform/README.md b/terraform/README.md index dfe1050..f8712d7 100644 --- a/terraform/README.md +++ b/terraform/README.md @@ -42,6 +42,7 @@ All changes should be made in `production.tfvars`: enable_branch_protection = true # optional, default is true has_discussions = true # optional, default is true has_downloads = true # optional, default is true + has_wiki = false # optional, default is false is_template = false # optional, default is false push_allowances = [] required_status_checks_contexts = [] # optional, default is [] From 274b0984f73c4ad2fd20aba86be3685570bc7931 Mon Sep 17 00:00:00 2001 From: tschilling Date: Tue, 23 Jul 2024 21:16:42 -0500 Subject: [PATCH 37/39] The teams are maps, not lists. These need to be merged rather than concatenated. --- terraform/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/main.tf b/terraform/main.tf index ff71164..6a3e609 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -154,7 +154,7 @@ resource "github_repository" "this" { # Create the base teams for each repository and the organization teams for Django Commons. resource "github_team" "parents" { - for_each = concat(var.teams_repositories, var.teams_organization) + for_each = merge(var.teams_repositories, var.teams_organization) name = each.key description = each.value.description From b0571015e1e2d880a3420054186dcd10788e0047 Mon Sep 17 00:00:00 2001 From: tschilling Date: Tue, 23 Jul 2024 21:20:01 -0500 Subject: [PATCH 38/39] Revert change, use dynamic members. Adds a comment to hopefully reduce confusion. --- terraform/main.tf | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/terraform/main.tf b/terraform/main.tf index 6a3e609..de0425f 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -182,22 +182,24 @@ resource "github_team_members" "parents" { team_id = github_team.parents[each.key].id - dynamic "member" { + dynamic "members" { for_each = each.value.members content { - username = member.value + # members here references the dynamic name, not the looped entity. + username = members.value role = "member" } } # Maintainer here means the maintainer role for the team. # It's not a maintainer of the repo. - dynamic "member" { + dynamic "members" { for_each = each.value.maintainers content { - username = member.value + # members here references the dynamic name, not the looped entity. + username = members.value role = "maintainer" } } @@ -211,22 +213,24 @@ resource "github_team_members" "children" { team_id = github_team.children[each.key].id - dynamic "member" { + dynamic "members" { for_each = each.value.members content { - username = member.value + # members here references the dynamic name, not the looped entity. + username = members.value role = "member" } } # Maintainer here means the maintainer role for the team. # It's not a maintainer of the repo. - dynamic "member" { + dynamic "members" { for_each = each.value.maintainers content { - username = member.value + # members here references the dynamic name, not the looped entity. + username = members.value role = "maintainer" } } From e46b855238893e1a45c9c8ad6d415024ae4ca4ae Mon Sep 17 00:00:00 2001 From: tschilling Date: Tue, 23 Jul 2024 21:21:41 -0500 Subject: [PATCH 39/39] Remove maintainers collections where they are unnecessary. The Django Commons admin team will have access to all teams by being owners in the organization. That isn't controlled by the terraform plan. --- README.md | 21 --------------------- terraform/production/teams.tfvars | 18 ++++-------------- 2 files changed, 4 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 34b1c00..f54c9cd 100644 --- a/README.md +++ b/README.md @@ -141,13 +141,6 @@ Assuming repository name is `repo-name`: repositories = [ "repo-name", ] - maintainers = [ - "tim-schilling", - "williln", - "ryancheley", - "Stormheg", - "cunla", - ] } } ``` @@ -164,13 +157,6 @@ Assuming repository name is `repo-name`: "username", ] permission = "admin" - maintainers = [ - "tim-schilling", - "williln", - "ryancheley", - "Stormheg", - "cunla", - ] } "repo-name-committers" = { description = "repo-name committers team" @@ -179,13 +165,6 @@ Assuming repository name is `repo-name`: # Leave empty unless there are committers ready to be designated ] permission = "push" - maintainers = [ - "tim-schilling", - "williln", - "ryancheley", - "Stormheg", - "cunla", - ] } } ``` diff --git a/terraform/production/teams.tfvars b/terraform/production/teams.tfvars index 2c9e9ba..8202f0a 100644 --- a/terraform/production/teams.tfvars +++ b/terraform/production/teams.tfvars @@ -1,6 +1,7 @@ teams_organization = { "Admins" = { description = "django-commons administrators" + # Use maintainers for organizational teams maintainers = [ "tim-schilling", "williln", @@ -8,15 +9,14 @@ teams_organization = { "Stormheg", "cunla", ] - members = null } "security-team" = { description = "django-commons security team" + # Use maintainers for organizational teams maintainers = [ "tim-schilling", "matthiask" ] - members = [] permission = "push" repositories = [ @@ -27,14 +27,12 @@ teams_organization = { teams_repositories = { "django-community-playground" = { description = "django-community-playground team" - maintainers = [ + members = [ "tim-schilling", "williln", "ryancheley", "Stormheg", "cunla", - ] - members = [ "priyapahwa", ] permission = "triage" @@ -53,14 +51,13 @@ teams_repositories_privileged = { description = "django-community-playground administrators" parent_team_key = "django-community-playground" permission = "admin" - maintainers = [ + members = [ "tim-schilling", "williln", "ryancheley", "Stormheg", "cunla", ] - members = null repositories = [ "django-commons-playground", @@ -70,13 +67,6 @@ teams_repositories_privileged = { description = "django-community-playground committers" parent_team_key = "django-community-playground" permission = "maintain" - maintainers = [ - "tim-schilling", - "williln", - "ryancheley", - "Stormheg", - "cunla", - ] members = [ "priyapahwa", ]