From c3affd43ddaf8acaf79c224c6514f3af1b487bea Mon Sep 17 00:00:00 2001 From: "Byungjin Park (Claud)" Date: Tue, 27 Aug 2024 23:17:32 +0900 Subject: [PATCH] Improve access configuration for repository module (#23) --- modules/org-team/membership.tf | 6 +- modules/repository/README.md | 17 ++---- modules/repository/access.tf | 93 +++++++++++++++++++++++++++++ modules/repository/collaborators.tf | 58 ------------------ modules/repository/issue-labels.tf | 2 +- modules/repository/outputs.tf | 27 ++------- modules/repository/teams.tf | 56 ----------------- modules/repository/variables.tf | 91 ++++++++++------------------ 8 files changed, 140 insertions(+), 210 deletions(-) create mode 100644 modules/repository/access.tf delete mode 100644 modules/repository/collaborators.tf delete mode 100644 modules/repository/teams.tf diff --git a/modules/org-team/membership.tf b/modules/org-team/membership.tf index b47bfeb..9ee4e59 100644 --- a/modules/org-team/membership.tf +++ b/modules/org-team/membership.tf @@ -12,6 +12,10 @@ locals { } ] membership = concat(local.members, local.maintainers) + unsynced_membership = (!var.membership_sync_enabled + ? local.membership + : [] + ) } @@ -21,7 +25,7 @@ locals { resource "github_team_membership" "this" { for_each = { - for member in(!var.membership_sync_enabled ? local.membership : []) : + for member in local.unsynced_membership : member.username => member } diff --git a/modules/repository/README.md b/modules/repository/README.md index 9cb62d3..147d4e9 100644 --- a/modules/repository/README.md +++ b/modules/repository/README.md @@ -4,6 +4,7 @@ This module creates following resources. - `github_repository` - `github_repository_collaborator` (optional) +- `github_repository_collaborators` (optional) - `github_team_repository` (optional) - `github_repository_deploy_key` (optional) - `github_issue_label` (optional) @@ -36,6 +37,7 @@ No modules. | [github_issue_label.this](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/issue_label) | resource | | [github_repository.this](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository) | resource | | [github_repository_collaborator.this](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository_collaborator) | resource | +| [github_repository_collaborators.this](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository_collaborators) | resource | | [github_repository_deploy_key.this](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository_deploy_key) | resource | | [github_team_repository.this](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team_repository) | resource | @@ -44,8 +46,7 @@ No modules. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [name](#input\_name) | (Required) A name of the repository. | `string` | n/a | yes | -| [admin\_collaborators](#input\_admin\_collaborators) | (Optional) A list of users as collaborator with `admin` permission to the repository. You can use GitHub username. | `set(string)` | `[]` | no | -| [admin\_teams](#input\_admin\_teams) | (Optional) A list of teams with `admin` permission to the repository. You can use GitHub team id or the GitHub team slug. | `set(string)` | `[]` | no | +| [access](#input\_access) | (Optional) A configuration for the repository access. `access` block as defined below.
(Optional) `collaborators` - A list of collaborators to the repository. Each item of `collaborators` block as defined below.
(Required) `username` - The GitHub username to add to the repository as a collaborator.
(Optional) `role` - The role to grant the collaborator in the repository. Valid values are `read`, `triage`, `write`, `maintain`, `admin` or the name of an existing custom repository role within the organisation. Default is `write`.
(Optional) `teams` - A list of teams to the repository. Each item of `teams` block as defined below.
(Required) `team` - The GitHub team id or the GitHub team slug.
(Optional) `role` - The role to grant the team in the repository. Valid values are `read`, `triage`, `write`, `maintain`, `admin` or the name of an existing custom repository role within the organisation. Default is `read`.
(Optional) `sync_enabled` - Whether to sync the repository access. Accesses added outside of the Terraform code will be removed. Defaults to `false`. |
object({
collaborators = optional(list(object({
username = string
role = optional(string, "write")
})), [])
teams = optional(list(object({
team = string
role = optional(string, "read")
})), [])
sync_enabled = optional(bool, false)
})
| `{}` | no | | [archive\_on\_destroy](#input\_archive\_on\_destroy) | (Optional) Set to `true` to archive the repository instead of deleting on destroy. | `bool` | `false` | no | | [archived](#input\_archived) | (Optional) Specify if the repository should be archived. Defaults to `false`. NOTE: Currently, the API does not support unarchiving. | `bool` | `false` | no | | [branches](#input\_branches) | (Optional) A list of branches to create and manage within the repository. | `set(string)` | `[]` | no | @@ -56,29 +57,22 @@ No modules. | [features](#input\_features) | (Optional) A list of enabled features on the repository. Available features: `ISSUES`, `PROJECTS`, `WIKI`. | `set(string)` |
[
"ISSUES"
]
| no | | [homepage](#input\_homepage) | (Optional) A URL of website describing the repository. | `string` | `null` | no | | [is\_template](#input\_is\_template) | (Optional) Set to `true` if this is a template repository. | `bool` | `false` | no | -| [issue\_labels](#input\_issue\_labels) | (Optional) A list of issue labels for the repository. Each member of `issue_labels` block as defined below.
(Required) `name` - The name of the label.
(Required) `color` - A 6 character hex code, without the leading #, identifying the color of the label.
(Optional) `description` - A short description of the label. | `set(map(string))` | `[]` | no | -| [maintain\_collaborators](#input\_maintain\_collaborators) | (Optional) A list of users as collaborator with `maintain` permission to the repository. You can use GitHub username. | `set(string)` | `[]` | no | -| [maintain\_teams](#input\_maintain\_teams) | (Optional) A list of teams with `maintain` permission to the repository. You can use GitHub team id or the GitHub team slug. | `set(string)` | `[]` | no | +| [issue\_labels](#input\_issue\_labels) | (Optional) A list of issue labels for the repository. Each member of `issue_labels` block as defined below.
(Required) `name` - The name of the label.
(Required) `color` - A 6 character hex code, without the leading #, identifying the color of the label.
(Optional) `description` - A short description of the label. |
set(object({
name = string
color = string
description = optional(string, "Managed by Terraform.")
}))
| `[]` | no | | [merge\_strategies](#input\_merge\_strategies) | (Optional) A list of allowed strategies for merging pull requests on the repository. Available strategies: `MERGE_COMMIT`, `SQUASH`, `REBASE`. | `set(string)` |
[
"SQUASH",
"REBASE"
]
| no | | [pages\_cname](#input\_pages\_cname) | (Optional) The custom domain for the repository. This can only be set after the repository has been created. | `string` | `null` | no | | [pages\_enabled](#input\_pages\_enabled) | (Optional) Set to true to enable GitHub Pages for the repository. GitHub Pages is designed to host your personal, organization, or project pages from a GitHub repository. | `bool` | `false` | no | | [pages\_source\_branch](#input\_pages\_source\_branch) | (Optional) The repository branch used to publish the site's source files. Defaults to `gh-pages` branch. | `string` | `"gh-pages"` | no | | [pages\_source\_path](#input\_pages\_source\_path) | (Optional) The repository directory path from which the site publishes. Defaults to `/`. | `string` | `"/"` | no | -| [read\_collaborators](#input\_read\_collaborators) | (Optional) A list of users as collaborator with `read` permission to the repository. You can use GitHub username. | `set(string)` | `[]` | no | -| [read\_teams](#input\_read\_teams) | (Optional) A list of teams with `read` permission to the repository. You can use GitHub team id or the GitHub team slug. | `set(string)` | `[]` | no | | [template](#input\_template) | (Optional) Use a template repository, license or gitignore to create the repository.this resource. `template` block as defined below.
(Optional) `gitignore` - Choose which files not to track from a list of templates. Use the name of the template without the extension. For example, `Haskell`.
(Optional) `init_readme` - Set to `true` to produce an initial commit with README.md in the repository.
(Optional) `license` - A license tells others what they can and can't do with your code. Use the name of the license template without the extension. For example, `mit` or `mpl-2.0`.
(Optional) `repository` - Start this repository with a template repository's contents. The full name of the repository is required. A string of the form `owner/repository`. | `any` | `{}` | no | | [topics](#input\_topics) | (Optional) A list of topics for the repository. | `set(string)` | `[]` | no | -| [triage\_collaborators](#input\_triage\_collaborators) | (Optional) A list of users as collaborator with `triage` permission to the repository. You can use GitHub username. | `set(string)` | `[]` | no | -| [triage\_teams](#input\_triage\_teams) | (Optional) A list of teams with `triage` permission to the repository. You can use GitHub team id or the GitHub team slug. | `set(string)` | `[]` | no | | [visibility](#input\_visibility) | (Optional) Can be `public`, `private` or `internal`. `internal` visibility is only available if your organization is associated with an enterprise account using GitHub Enterprise Cloud or GitHub Enterprise Server 2.20+. | `string` | `"private"` | no | | [vulnerability\_alerts](#input\_vulnerability\_alerts) | (Optional) Set to true to enable security alerts for vulnerable dependencies. Enabling requires alerts to be enabled on the owner level. GitHub enables the alerts on public repos but disables them on private repos by default. | `bool` | `false` | no | -| [write\_collaborators](#input\_write\_collaborators) | (Optional) A list of users as collaborator with `write` permission to the repository. You can use GitHub username. | `set(string)` | `[]` | no | -| [write\_teams](#input\_write\_teams) | (Optional) A list of teams with `write` permission to the repository. You can use GitHub team id or the GitHub team slug. | `set(string)` | `[]` | no | ## Outputs | Name | Description | |------|-------------| +| [access](#output\_access) | The configuration for the repository access. | | [archived](#output\_archived) | Whether the repository is archived. | | [branches](#output\_branches) | A list of the repository branches excluding initial branch. | | [default\_branch](#output\_default\_branch) | The default branch of the repository. | @@ -97,7 +91,6 @@ No modules. | [name](#output\_name) | The name of the repository. | | [node\_id](#output\_node\_id) | The node ID of the GitHub repository. This is GraphQL global node id for use with v4 API. | | [pages](#output\_pages) | The repository's GitHub Pages configuration. | -| [permissions](#output\_permissions) | The access control list which manage individual and team access to the repository. | | [ssh\_clone\_url](#output\_ssh\_clone\_url) | The URL that can be provided to `git clone` to clone the repository anonymously via SSH. | | [template](#output\_template) | The template of the repository. | | [topics](#output\_topics) | A list of topics for the repository. | diff --git a/modules/repository/access.tf b/modules/repository/access.tf new file mode 100644 index 0000000..2d70842 --- /dev/null +++ b/modules/repository/access.tf @@ -0,0 +1,93 @@ +locals { + default_roles = { + "read" = "pull" + "triage" = "triage" + "write" = "push" + "maintain" = "maintain" + "admin" = "admin" + } + + unsynced_collaborators = (!var.access.sync_enabled + ? var.access.collaborators + : [] + ) + unsynced_teams = (!var.access.sync_enabled + ? var.access.teams + : [] + ) +} + +################################################### +# Collaborator Access to GitHub Repository +################################################### + +resource "github_repository_collaborator" "this" { + for_each = { + for collaborator in local.unsynced_collaborators : + collaborator.username => collaborator + } + + repository = github_repository.this.name + username = each.key + permission = (contains(keys(local.default_roles), each.value.role) + ? local.default_roles[each.value.role] + : each.value.role + ) + + permission_diff_suppression = true +} + + +################################################### +# Team Access to GitHub Repository +################################################### + +resource "github_team_repository" "this" { + for_each = { + for team in local.unsynced_teams : + team.team => team + } + + repository = github_repository.this.name + team_id = each.key + permission = (contains(keys(local.default_roles), each.value.role) + ? local.default_roles[each.value.role] + : each.value.role + ) +} + + +################################################### +# Access to GitHub Repository with Sync +################################################### + +resource "github_repository_collaborators" "this" { + count = var.access.sync_enabled ? 1 : 0 + + repository = github_repository.this.name + + dynamic "user" { + for_each = var.access.collaborators + iterator = collaborator + + content { + username = collaborator.value.username + permission = (contains(keys(local.default_roles), collaborator.value.role) + ? local.default_roles[collaborator.value.role] + : collaborator.value.role + ) + } + } + + dynamic "team" { + for_each = var.access.teams + + content { + team_id = team.value.team + permission = (contains(keys(local.default_roles), team.value.role) + ? local.default_roles[team.value.role] + : team.value.role + ) + } + } +} diff --git a/modules/repository/collaborators.tf b/modules/repository/collaborators.tf deleted file mode 100644 index 1751bb7..0000000 --- a/modules/repository/collaborators.tf +++ /dev/null @@ -1,58 +0,0 @@ -locals { - read_collaborators = [ - for collaborator in var.read_collaborators : { - username = collaborator - permission = "pull" - } - ] - triage_collaborators = [ - for collaborator in var.triage_collaborators : { - username = collaborator - permission = "triage" - } - ] - write_collaborators = [ - for collaborator in var.write_collaborators : { - username = collaborator - permission = "push" - } - ] - maintain_collaborators = [ - for collaborator in var.maintain_collaborators : { - username = collaborator - permission = "maintain" - } - ] - admin_collaborators = [ - for collaborator in var.admin_collaborators : { - username = collaborator - permission = "admin" - } - ] - - collaborators = concat( - local.read_collaborators, - local.triage_collaborators, - local.write_collaborators, - local.maintain_collaborators, - local.admin_collaborators, - ) -} - - -################################################### -# Collaborators for GitHub Repository -################################################### - -resource "github_repository_collaborator" "this" { - for_each = { - for collaborator in local.collaborators : - collaborator.username => collaborator - } - - repository = github_repository.this.name - username = each.key - permission = each.value.permission - - permission_diff_suppression = true -} diff --git a/modules/repository/issue-labels.tf b/modules/repository/issue-labels.tf index 11eab60..4f112e1 100644 --- a/modules/repository/issue-labels.tf +++ b/modules/repository/issue-labels.tf @@ -12,5 +12,5 @@ resource "github_issue_label" "this" { name = each.key color = each.value.color - description = try(each.value.description, "Managed by Terraform.") + description = each.value.description } diff --git a/modules/repository/outputs.tf b/modules/repository/outputs.tf index 77f97f0..53ffcfd 100644 --- a/modules/repository/outputs.tf +++ b/modules/repository/outputs.tf @@ -99,29 +99,12 @@ output "issue_labels" { ] } -output "permissions" { - description = "The access control list which manage individual and team access to the repository." +output "access" { + description = "The configuration for the repository access." value = { - read = { - teams = var.read_teams - collaborators = var.read_collaborators - } - triage = { - teams = var.triage_teams - collaborators = var.triage_collaborators - } - write = { - teams = var.write_teams - collaborators = var.write_collaborators - } - maintain = { - teams = var.maintain_teams - collaborators = var.maintain_collaborators - } - admin = { - teams = var.admin_teams - collaborators = var.admin_collaborators - } + collaborators = var.access.collaborators + teams = var.access.teams + sync_enabled = var.access.sync_enabled } } diff --git a/modules/repository/teams.tf b/modules/repository/teams.tf deleted file mode 100644 index 77f533b..0000000 --- a/modules/repository/teams.tf +++ /dev/null @@ -1,56 +0,0 @@ -locals { - read_teams = [ - for team in var.read_teams : { - team_id = team - permission = "pull" - } - ] - triage_teams = [ - for team in var.triage_teams : { - team_id = team - permission = "triage" - } - ] - write_teams = [ - for team in var.write_teams : { - team_id = team - permission = "push" - } - ] - maintain_teams = [ - for team in var.maintain_teams : { - team_id = team - permission = "maintain" - } - ] - admin_teams = [ - for team in var.admin_teams : { - team_id = team - permission = "admin" - } - ] - - teams = concat( - local.read_teams, - local.triage_teams, - local.write_teams, - local.maintain_teams, - local.admin_teams, - ) -} - - -################################################### -# Teams for GitHub Repository -################################################### - -resource "github_team_repository" "this" { - for_each = { - for team in local.teams : - team.team_id => team - } - - repository = github_repository.this.name - team_id = each.key - permission = each.value.permission -} diff --git a/modules/repository/variables.tf b/modules/repository/variables.tf index acc14f5..50751a5 100644 --- a/modules/repository/variables.tf +++ b/modules/repository/variables.tf @@ -100,68 +100,39 @@ variable "issue_labels" { (Required) `color` - A 6 character hex code, without the leading #, identifying the color of the label. (Optional) `description` - A short description of the label. EOF - type = set(map(string)) - default = [] -} - -variable "read_teams" { - description = "(Optional) A list of teams with `read` permission to the repository. You can use GitHub team id or the GitHub team slug." - type = set(string) - default = [] -} - -variable "triage_teams" { - description = "(Optional) A list of teams with `triage` permission to the repository. You can use GitHub team id or the GitHub team slug." - type = set(string) - default = [] -} - -variable "write_teams" { - description = "(Optional) A list of teams with `write` permission to the repository. You can use GitHub team id or the GitHub team slug." - type = set(string) - default = [] -} - -variable "maintain_teams" { - description = "(Optional) A list of teams with `maintain` permission to the repository. You can use GitHub team id or the GitHub team slug." - type = set(string) - default = [] -} - -variable "admin_teams" { - description = "(Optional) A list of teams with `admin` permission to the repository. You can use GitHub team id or the GitHub team slug." - type = set(string) - default = [] -} - -variable "read_collaborators" { - description = "(Optional) A list of users as collaborator with `read` permission to the repository. You can use GitHub username." - type = set(string) - default = [] -} - -variable "triage_collaborators" { - description = "(Optional) A list of users as collaborator with `triage` permission to the repository. You can use GitHub username." - type = set(string) - default = [] -} - -variable "write_collaborators" { - description = "(Optional) A list of users as collaborator with `write` permission to the repository. You can use GitHub username." - type = set(string) - default = [] -} - -variable "maintain_collaborators" { - description = "(Optional) A list of users as collaborator with `maintain` permission to the repository. You can use GitHub username." - type = set(string) - default = [] + type = set(object({ + name = string + color = string + description = optional(string, "Managed by Terraform.") + })) + default = [] + nullable = false } -variable "admin_collaborators" { - description = "(Optional) A list of users as collaborator with `admin` permission to the repository. You can use GitHub username." - type = set(string) - default = [] +variable "access" { + description = <