diff --git a/README.md b/README.md deleted file mode 100644 index ae019cfd4..000000000 --- a/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# ๐Ÿš€ Release Please Versioning Management - -Check action doc https://github.com/googleapis/release-please-action - -This repo is managed by Release Please. This is an automated Semver control System. - -## How release please works - -Release Please automates CHANGELOG generation, the creation of GitHub releases, -and version bumps for your projects. Release Please does so by parsing your -git history, looking for [Conventional Commit messages](https://www.conventionalcommits.org/), -and creating release PRs. - -### How should I write my commits? - -Release Please assumes you are using [Conventional Commit messages](https://www.conventionalcommits.org/). - -The most important prefixes you should have in mind are: - -* `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) - patch. -* `feat:` which represents a new feature, and correlates to a SemVer minor. -* `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change - (indicated by the `!`) and will result in a SemVer major. - -### What's a Release PR? - -Rather than continuously releasing what's landed to your default branch, -release-please maintains Release PRs: - -These Release PRs are kept up-to-date as additional work is merged. When you're -ready to tag a release, simply merge the release PR. - -### Troubleshooting - -If you merge accidentaly a conventional commit, you can move the commits starting analysis to another commit following this guide https://github.com/googleapis/release-please/blob/288949797c6bfd95e08d74a6222e7d26e0020a7c/docs/manifest-releaser.md?plain=1#L140-L153 diff --git a/modules/gh-team/.terraform-docs.yml b/modules/gh-team/.terraform-docs.yml new file mode 100644 index 000000000..3a69365ff --- /dev/null +++ b/modules/gh-team/.terraform-docs.yml @@ -0,0 +1,48 @@ +formatter: "markdown" + +version: "" + +header-from: docs/header.md +footer-from: docs/footer.md + +recursive: + enabled: false + path: modules + include-main: true + +sections: + hide: [] + show: [] + +content: "" + +output: + file: "README.md" + mode: inject + template: |- + + {{ .Content }} + + +output-values: + enabled: false + from: "" + +sort: + enabled: true + by: name + +settings: + anchor: true + color: true + default: true + description: false + escape: true + hide-empty: false + html: true + indent: 2 + lockfile: true + read-comments: true + required: true + sensitive: true + type: true diff --git a/modules/gh-team/CHANGELOG.md b/modules/gh-team/CHANGELOG.md new file mode 100644 index 000000000..5d33d5ceb --- /dev/null +++ b/modules/gh-team/CHANGELOG.md @@ -0,0 +1,6 @@ +# Changelog + +## 0.1.0 (2026-02-27) + +### Features + diff --git a/modules/gh-team/README.md b/modules/gh-team/README.md new file mode 100644 index 000000000..7057c700f --- /dev/null +++ b/modules/gh-team/README.md @@ -0,0 +1,89 @@ + +# **GitHub Team Terraform Module** + +## Overview + +This module provisions a single GitHub Team and automatically manages its members using a single strongly-typed `config` object. +It supports nested teams (`parentTeamId`), privacy settings (`closed`/`secret`), and manages all members with the standard `member` role while keeping the external interface minimal and JSON-friendly. + +The module is designed for Prefappโ€™s Internal Developer Platform and automated team provisioning pipelines. It accepts input directly from external programs (Python, Node.js, Go, etc.) via JSON, ensuring full Terraform validation and type safety. + +## Key Features + +- **Single complex object**: All configuration lives in one `config` variable โ€” perfect for programmatic generation. +- **Full validation**: Enforces name, privacy, username format, and required fields at plan time. +- **Nested team support**: Automatic `parent_team_id` handling. +- **JSON-native**: Feed `jsondecode(file("team-config.json"))` directly. +- **Clean outputs**: Every value is exposed as a separate output for easy consumption in larger pipelines. +- **GitHub Provider v6+**: Uses the latest official `integrations/github` provider. + +## Basic Usage + +### Minimal team with members + +```hcl +module "my_team" { + source = "git::https://github.com/prefapp/tfm.git//modules/gh-team" + + config = jsondecode(file("${path.root}/team-config.json")) +} +``` + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.7.0 | +| [github](#requirement\_github) | ~> 6.0 | + +## Providers + +| Name | Version | +|------|---------| +| [github](#provider\_github) | ~> 6.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [github_team.this](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team) | resource | +| [github_team_membership.members](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team_membership) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [config](#input\_config) | GitHub team configuration as a single complex object |
object({
group = object({
name = string
description = optional(string, "")
privacy = optional(string, "closed")
parentTeamId = optional(number, null) # null or team ID (number)
})

group_members = optional(list(object({
username = string
teamId = optional(string) # kept for compatibility, ignored during creation
})), [])
})
| n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [team\_description](#output\_team\_description) | Description of the created GitHub team | +| [team\_id](#output\_team\_id) | ID of the created GitHub team | +| [team\_members](#output\_team\_members) | List of usernames added to the team | +| [team\_name](#output\_team\_name) | Name of the created GitHub team | +| [team\_parent\_id](#output\_team\_parent\_id) | Parent team ID (null if none) | +| [team\_privacy](#output\_team\_privacy) | Privacy setting of the team (closed/secret) | +| [team\_slug](#output\_team\_slug) | Slug of the created GitHub team | + +## Examples + +For detailed examples, refer to the [module examples](https://github.com/prefapp/tfm/tree/main/modules/github-team/_examples): + +- [basic](https://github.com/prefapp/tfm/tree/main/modules/github-team/_examples/basic) - Minimal team creation with JSON input + +## Resources + +- **github\_team**: [Official Documentation](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team) +- **github\_team\_membership**: [Official Documentation](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team_membership) +- **GitHub Terraform Provider**: [Official Documentation](https://registry.terraform.io/providers/integrations/github/latest/docs) + +## Support + +For issues, questions, or contributions related to this module, please visit the [repository's issue tracker](https://github.com/prefapp/tfm/issues). + diff --git a/modules/gh-team/_examples/basic/config.json b/modules/gh-team/_examples/basic/config.json new file mode 100644 index 000000000..e238015c6 --- /dev/null +++ b/modules/gh-team/_examples/basic/config.json @@ -0,0 +1,11 @@ +{ + "group": { + "name": "็งใฎใƒใƒผใƒ ", + "description": "Prefapp all description", + "privacy": "closed", + "parentTeamId": null + }, + "group_members": [ + { "username": "frmadem-user", "teamId": "16210762" } + ] +} diff --git a/modules/gh-team/_examples/basic/main.tf b/modules/gh-team/_examples/basic/main.tf new file mode 100644 index 000000000..e110e4174 --- /dev/null +++ b/modules/gh-team/_examples/basic/main.tf @@ -0,0 +1,18 @@ +terraform { + required_providers { + github = { + source = "integrations/github" + version = "~> 6.0" + } + } +} + +module "example_team" { + source = "../../" # points to the module root + + config = jsondecode(file("${path.module}/config.json")) +} + +output "team_id" { + value = module.example_team.team_id +} diff --git a/modules/gh-team/docs/footer.md b/modules/gh-team/docs/footer.md new file mode 100644 index 000000000..dba4e83f5 --- /dev/null +++ b/modules/gh-team/docs/footer.md @@ -0,0 +1,17 @@ + +## Examples + +For detailed examples, refer to the [module examples](https://github.com/prefapp/tfm/tree/main/modules/github-team/_examples): + +- [basic](https://github.com/prefapp/tfm/tree/main/modules/github-team/_examples/basic) - Minimal team creation with JSON input + +## Resources + +- **github_team**: [Official Documentation](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team) +- **github_team_membership**: [Official Documentation](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team_membership) +- **GitHub Terraform Provider**: [Official Documentation](https://registry.terraform.io/providers/integrations/github/latest/docs) + +## Support + +For issues, questions, or contributions related to this module, please visit the [repository's issue tracker](https://github.com/prefapp/tfm/issues). + diff --git a/modules/gh-team/docs/header.md b/modules/gh-team/docs/header.md new file mode 100644 index 000000000..0e45e945f --- /dev/null +++ b/modules/gh-team/docs/header.md @@ -0,0 +1,29 @@ +# **GitHub Team Terraform Module** + +## Overview + +This module provisions a single GitHub Team and automatically manages its members using a single strongly-typed `config` object. +It supports nested teams (`parentTeamId`), privacy settings (`closed`/`secret`), and manages all members with the standard `member` role while keeping the external interface minimal and JSON-friendly. + +The module is designed for Prefappโ€™s Internal Developer Platform and automated team provisioning pipelines. It accepts input directly from external programs (Python, Node.js, Go, etc.) via JSON, ensuring full Terraform validation and type safety. + +## Key Features + +- **Single complex object**: All configuration lives in one `config` variable โ€” perfect for programmatic generation. +- **Full validation**: Enforces name, privacy, username format, and required fields at plan time. +- **Nested team support**: Automatic `parent_team_id` handling. +- **JSON-native**: Feed `jsondecode(file("team-config.json"))` directly. +- **Clean outputs**: Every value is exposed as a separate output for easy consumption in larger pipelines. +- **GitHub Provider v6+**: Uses the latest official `integrations/github` provider. + +## Basic Usage + +### Minimal team with members + +```hcl +module "my_team" { + source = "git::https://github.com/prefapp/tfm.git//modules/gh-team" + + config = jsondecode(file("${path.root}/team-config.json")) +} +``` diff --git a/modules/gh-team/main.tf b/modules/gh-team/main.tf new file mode 100644 index 000000000..0cdcbd2d6 --- /dev/null +++ b/modules/gh-team/main.tf @@ -0,0 +1,18 @@ +# Create the GitHub Team +resource "github_team" "this" { + name = var.config.group.name + description = var.config.group.description + privacy = var.config.group.privacy + parent_team_id = var.config.group.parentTeamId +} + +# Add team memberships +resource "github_team_membership" "members" { + for_each = { + for m in var.config.group_members : m.username => m + } + + team_id = github_team.this.id + username = each.value.username + role = "member" +} diff --git a/modules/gh-team/outputs.tf b/modules/gh-team/outputs.tf new file mode 100644 index 000000000..896f01028 --- /dev/null +++ b/modules/gh-team/outputs.tf @@ -0,0 +1,36 @@ +# Outputs section + +output "team_id" { + description = "ID of the created GitHub team" + value = github_team.this.id +} + +output "team_slug" { + description = "Slug of the created GitHub team" + value = github_team.this.slug +} + +output "team_name" { + description = "Name of the created GitHub team" + value = github_team.this.name +} + +output "team_description" { + description = "Description of the created GitHub team" + value = github_team.this.description +} + +output "team_privacy" { + description = "Privacy setting of the team (closed/secret)" + value = github_team.this.privacy +} + +output "team_members" { + description = "List of usernames added to the team" + value = [for m in var.config.group_members : m.username] +} + +output "team_parent_id" { + description = "Parent team ID (null if none)" + value = github_team.this.parent_team_id +} diff --git a/modules/gh-team/variables.tf b/modules/gh-team/variables.tf new file mode 100644 index 000000000..01a2ad42e --- /dev/null +++ b/modules/gh-team/variables.tf @@ -0,0 +1,34 @@ +variable "config" { + description = "GitHub team configuration as a single complex object" + type = object({ + group = object({ + name = string + description = optional(string, "") + privacy = optional(string, "closed") + parentTeamId = optional(number, null) # null or team ID (number) + }) + + group_members = optional(list(object({ + username = string + teamId = optional(string) # kept for compatibility, ignored during creation + })), []) + }) + + # โ”€โ”€โ”€ Validations โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + validation { + condition = contains(["closed", "secret"], var.config.group.privacy) + error_message = "group.privacy must be 'closed' or 'secret'." + } + + validation { + condition = length(trimspace(var.config.group.name)) > 0 + error_message = "group.name cannot be empty." + } + + validation { + condition = alltrue([ + for m in var.config.group_members : length(trimspace(m.username)) > 0 + ]) + error_message = "Every member in group_members must have a non-empty username." + } +} diff --git a/modules/gh-team/versions.tf b/modules/gh-team/versions.tf new file mode 100644 index 000000000..f47edaf42 --- /dev/null +++ b/modules/gh-team/versions.tf @@ -0,0 +1,12 @@ +terraform { + + required_version = ">= 1.7.0" + + required_providers { + github = { + source = "integrations/github" + version = "~> 6.0" + } + } +} + diff --git a/release-please-config.json b/release-please-config.json index 1791e861c..afeca0cac 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -151,6 +151,9 @@ "modules/aws-secretsmanager-replication": { "package-name": "aws-secretsmanager-replication" }, + "modules/gh-team": { + "package-name": "gh-team" + }, "modules/azure-vnet-gateway": { "package-name": "azure-vnet-gateway" },