diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..d2466d8 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 2 diff --git a/.github/workflows/linters.yaml b/.github/workflows/linters.yaml new file mode 100644 index 0000000..fcc13ef --- /dev/null +++ b/.github/workflows/linters.yaml @@ -0,0 +1,23 @@ +name: linters + +on: + pull_request: + branches: [ '*' ] + +permissions: + contents: read + +jobs: + tf-linters: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: opentofu/setup-opentofu@v1 + - run: | + tofu fmt -check -diff -recursive . + + shell-linter: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ludeeus/action-shellcheck@master diff --git a/.github/workflows/semantic-pr.yaml b/.github/workflows/semantic-pr.yaml new file mode 100644 index 0000000..f86fdc9 --- /dev/null +++ b/.github/workflows/semantic-pr.yaml @@ -0,0 +1,23 @@ +name: semantic-pr + +on: + pull_request_target: + types: + - opened + - reopened + - edited + - synchronize + +permissions: + pull-requests: read + +jobs: + pr-title: + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + requireScope: true + subjectPattern: ^(?![A-Z]).+$ diff --git a/.github/workflows/tofu.yaml b/.github/workflows/tofu.yaml new file mode 100644 index 0000000..011b3ae --- /dev/null +++ b/.github/workflows/tofu.yaml @@ -0,0 +1,30 @@ +name: tofu + +on: + push: + branches: + - "main" + schedule: + - cron: "0 4 * * *" + +concurrency: + group: ${{ github.workflow }} + +jobs: + org: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: opentofu/setup-opentofu@v1 + - uses: bitwarden/sm-action@v2 + with: + access_token: ${{ secrets.BW_ACCESS_TOKEN }} + base_url: https://vault.bitwarden.com + secrets: | + b7d22a8b-8185-4d62-8bf6-b1d400b87552 > PG_CONN_STR + a288b2ae-a336-4425-9b07-b1f100cd05ec > TF_VAR_gh_token + - run: | + tofu init -upgrade + - run: | + GITHUB_TOKEN="${TF_VAR_gh_token}" tofu \ + apply -auto-approve -input=false -lock=true -no-color diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3208627 --- /dev/null +++ b/.gitignore @@ -0,0 +1,40 @@ +# Local .terraform directories +**/.terraform/* + +# .tfstate files +*.tfstate +*.tfstate.* + +# Crash log files +crash.log +crash.*.log + +# Exclude all .tfvars files, which are likely to contain sensitive data, such as +# password, private keys, and other secrets. These should not be part of version +# control as they are data points which are potentially sensitive and subject +# to change depending on the environment. +*.tfvars +*.tfvars.json + +# Ignore override files as they are usually used to override resources locally and so +# are not checked in +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Include override files you do wish to add to version control using negated pattern +# !example_override.tf + +# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan +# example: *tfplan* + +# Ignore CLI configuration files +.terraformrc +terraform.rc + +# MacOS attributes files +.DS_Store + +# Brew bundle lock +Brewfile.lock.json diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl new file mode 100644 index 0000000..d9b9d7a --- /dev/null +++ b/.terraform.lock.hcl @@ -0,0 +1,46 @@ +# This file is maintained automatically by "tofu init". +# Manual edits may be lost in future updates. + +provider "registry.opentofu.org/hashicorp/github" { + version = "6.3.0" + hashes = [ + "h1:AG//wDT67eInhTk+SQdDz5o8R8YIIBrZGz7C9TXKDOw=", + "zh:04fe3b820fe8c247b98b9d6810b8bb84d3e8ac08054faf450c42489815ef4bfa", + "zh:24096b2d16208d1411a58bdb8df8cd9f0558fb9054ffeb95c4e7e90a9a34f976", + "zh:2b27332adf8d08fbdc08b5f55e87691bce02c311219e6deb39c08753bd93db6d", + "zh:335dd6c2d50fcdce2ef0cc194465fdf9df1f5fdecc805804c78df30a4eb2e11e", + "zh:383a6879565969dbdf5405b651cd870c09c615dbd3df2554e5574d39d161c98c", + "zh:4903038a6bc605f372e1569695db4a2e2862e1fc6cf4faf9e13c5f8f4fa2ed94", + "zh:4cc4dffbee8b28102d38abe855b7440d4f4226261b43fda2ec289b48c3de1537", + "zh:57c30c6fe0b64fa86906700ceb1691562b62f2b1ef0404952aeb4092acb6acb3", + "zh:7bf518396fb00e4f55c406f2ffb5583b43278682a92f0864a0c47e3a74627bbb", + "zh:93c2c5cb90f74ad3c0874b7f7d8a866f28a852f0eda736c6aef8ce65d4061f4d", + "zh:9562a82a6193a2db110fb34d1aceeedb27c0a640058dce9c31b37b17eeb5f4e7", + "zh:ac97f2d111703a219f27fcbf5e89460ea98f9168badcc0913c8b214a37f76814", + "zh:c882af4d33b761ec198cedac212ab1c114d97540119dc97daca38021ab3edd0a", + "zh:c9ffd0a37f07a93af02a1caa90bfbea27a952d3e5badf4aab866ec71cdb184a3", + "zh:fbd1fee2c9df3aa19cf8851ce134dea6e45ea01cb85695c1726670c285797e25", + ] +} + +provider "registry.opentofu.org/integrations/github" { + version = "6.2.1" + constraints = "6.2.1" + hashes = [ + "h1:uDerb9YJo3vAO+wKw+Z064InX5aXom+nKLDry2eGf14=", + "zh:172aa5141c525174f38504a0d2e69d0d16c0a0b941191b7170fe6ae4d7282e30", + "zh:1a098b731fa658c808b591d030cc17cc7dfca1bf001c3c32e596f8c1bf980e9f", + "zh:245d6a1c7e632d8ae4bdd2da2516610c50051e81505cf420a140aa5fa076ea90", + "zh:43c61c230fb4ed26ff1b04b857778e65be3d8f80292759abbe2a9eb3c95f6d97", + "zh:59bb7dd509004921e4322a196be476a2f70471b462802f09d03d6ce96f959860", + "zh:5cb2ab8035d015c0732107c109210243650b6eb115e872091b0f7b98c2763777", + "zh:69d2a6acfcd686f7e859673d1c8a07fc1fc1598a881493f19d0401eb74c0f325", + "zh:77f36d3f46911ace5c50dee892076fddfd64a289999a5099f8d524c0143456d1", + "zh:87df41097dfcde72a1fbe89caca882af257a4763c2e1af669c74dcb8530f9932", + "zh:899dbe621f32d58cb7c6674073a6db8328a9db66eecfb0cc3fc13299fd4e62e7", + "zh:ad2eb7987f02f7dd002076f65a685730705d04435313b5cf44d3a6923629fb29", + "zh:b2145ae7134dba893c7f74ad7dfdc65fdddf6c7b1d0ce7e2f3baa96212322fd8", + "zh:bd6bae3ac5c3f96ad9219d3404aa006ef1480e9041d4c95df1808737e37d911b", + "zh:e89758b20ae59f1b9a6d32c107b17846ddca9634b868cf8f5c927cbb894b1b1f", + ] +} diff --git a/Brewfile b/Brewfile new file mode 100644 index 0000000..f9e5ca3 --- /dev/null +++ b/Brewfile @@ -0,0 +1 @@ +brew "opentofu" diff --git a/README.md b/README.md new file mode 100644 index 0000000..7c1430e --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Organization management + +Managing organization with GitOps via OpenTofu! diff --git a/members.tf b/members.tf new file mode 100644 index 0000000..dfe93c1 --- /dev/null +++ b/members.tf @@ -0,0 +1,26 @@ +module "shanduur" { + source = "./modules/member" + username = "shanduur" + role = "admin" +} + +module "shanduur_auto" { + source = "./modules/member" + username = "shanduur-auto" + role = "admin" +} + +module "niesmaczne" { + source = "./modules/member" + username = "niesmaczne" +} + +module "team_core" { + source = "./modules/team" + name = "core" + members = [ + { username = module.shanduur.username, role = "maintainer" }, + { username = module.shanduur_auto.username, role = "maintainer" }, + { username = module.niesmaczne.username }, + ] +} diff --git a/modules/member/main.tf b/modules/member/main.tf new file mode 100644 index 0000000..2704485 --- /dev/null +++ b/modules/member/main.tf @@ -0,0 +1,4 @@ +resource "github_membership" "member" { + username = var.username + role = var.role +} diff --git a/modules/member/outputs.tf b/modules/member/outputs.tf new file mode 100644 index 0000000..a52e775 --- /dev/null +++ b/modules/member/outputs.tf @@ -0,0 +1,3 @@ +output "username" { + value = github_membership.member.username +} diff --git a/modules/member/providers.tf b/modules/member/providers.tf new file mode 100644 index 0000000..8dc300c --- /dev/null +++ b/modules/member/providers.tf @@ -0,0 +1,9 @@ +terraform { + required_providers { + github = {} + } +} + +provider "github" { + owner = "anza-labs" +} diff --git a/modules/member/variables.tf b/modules/member/variables.tf new file mode 100644 index 0000000..0a07e53 --- /dev/null +++ b/modules/member/variables.tf @@ -0,0 +1,9 @@ +variable "username" { + type = string +} + +variable "role" { + type = string + description = "Role assigned to the user" + default = "member" +} diff --git a/modules/repository/main.tf b/modules/repository/main.tf new file mode 100644 index 0000000..7a7e0e6 --- /dev/null +++ b/modules/repository/main.tf @@ -0,0 +1,78 @@ +resource "github_repository" "repo" { + name = var.name + description = var.description + visibility = var.is_public ? "public" : "private" + archived = var.archived + topics = var.topics + homepage_url = var.homepage_url + + archive_on_destroy = true + has_discussions = false + has_issues = true + has_wiki = false + has_projects = true + + allow_update_branch = true + allow_auto_merge = true + allow_squash_merge = true + allow_merge_commit = false + allow_rebase_merge = false + delete_branch_on_merge = true + web_commit_signoff_required = true + + squash_merge_commit_title = "PR_TITLE" + squash_merge_commit_message = "PR_BODY" + + dynamic "pages" { + for_each = var.enable_pages ? [1] : [] + content { + source { + branch = "gh-pages" + path = "/" + } + } + } +} + +resource "github_issue_labels" "labels" { + repository = github_repository.repo.name + + dynamic "label" { + for_each = var.labels + content { + name = label.value.name + description = label.value.description + color = label.value.color + } + } +} + +resource "github_branch_protection_v3" "protection" { + count = var.is_public ? 1 : 0 + repository = github_repository.repo.name + branch = "main" + + enforce_admins = false + require_signed_commits = true + require_conversation_resolution = true + + required_status_checks { + strict = true + checks = var.required_status_checks + } + + required_pull_request_reviews { + required_approving_review_count = 1 + dismiss_stale_reviews = true + require_code_owner_reviews = false + require_last_push_approval = false + } +} + +resource "github_repository_milestone" "milestone" { + for_each = var.milestones + + owner = "anza-labs" + repository = github_repository.repo.name + title = each.value +} diff --git a/modules/repository/outputs.tf b/modules/repository/outputs.tf new file mode 100644 index 0000000..e69de29 diff --git a/modules/repository/providers.tf b/modules/repository/providers.tf new file mode 100644 index 0000000..8dc300c --- /dev/null +++ b/modules/repository/providers.tf @@ -0,0 +1,9 @@ +terraform { + required_providers { + github = {} + } +} + +provider "github" { + owner = "anza-labs" +} diff --git a/modules/repository/variables.tf b/modules/repository/variables.tf new file mode 100644 index 0000000..c8dfeea --- /dev/null +++ b/modules/repository/variables.tf @@ -0,0 +1,74 @@ +variable "name" { + description = "Name of the repository" + type = string +} + +variable "description" { + description = "" + type = string + default = "" +} + +variable "homepage_url" { + description = "" + type = string + default = "" +} + +variable "archived" { + description = "" + type = bool +} + +variable "is_public" { + description = "" + type = bool + default = false +} + +variable "enable_pages" { + description = "" + type = bool + default = false +} + +variable "required_status_checks" { + description = "" + type = list(string) + default = [ + "DCO", + "pr-title", + ] +} + +variable "topics" { + description = "" + type = list(string) + default = [ + "hacktoberfest", + ] +} + +variable "milestones" { + description = "" + type = set(string) + default = [] +} + +variable "labels" { + description = "List of labeles that are assigned to repository" + type = list(object({ + name = string + color = string + description = string + })) + default = [ + { name = "area/dependency", color = "0052cc", description = "Issues or PRs related to dependency changes." }, + { name = "do-not-merge", color = "e11d21", description = "Indicates that a PR should not merge." }, + { name = "kind/bug", color = "e11d21", description = "Categorizes issue or PR as related to a bug." }, + { name = "kind/documentation", color = "c7def8", description = "Categorizes issue or PR as related to documentation." }, + { name = "kind/feature", color = "c7def8", description = "Categorizes issue or PR as related to a new feature." }, + { name = "good first issue", color = "7057ff", description = "Denotes an issue ready for a new contributor, according to the \"help wanted\" guidelines." }, + { name = "help wanted", color = "006b75", description = "Denotes an issue that needs help from a contributor. Must meet \"help wanted\" guidelines." }, + ] +} diff --git a/modules/team/main.tf b/modules/team/main.tf new file mode 100644 index 0000000..eb1f01f --- /dev/null +++ b/modules/team/main.tf @@ -0,0 +1,16 @@ +resource "github_team" "team" { + name = var.name +} + +resource "github_team_members" "members" { + team_id = github_team.team.id + count = length(var.members) == 0 ? 0 : 1 + + dynamic "members" { + for_each = var.members + content { + username = members.value.username + role = members.value.role + } + } +} diff --git a/modules/team/outputs.tf b/modules/team/outputs.tf new file mode 100644 index 0000000..e69de29 diff --git a/modules/team/providers.tf b/modules/team/providers.tf new file mode 100644 index 0000000..8dc300c --- /dev/null +++ b/modules/team/providers.tf @@ -0,0 +1,9 @@ +terraform { + required_providers { + github = {} + } +} + +provider "github" { + owner = "anza-labs" +} diff --git a/modules/team/variables.tf b/modules/team/variables.tf new file mode 100644 index 0000000..415d901 --- /dev/null +++ b/modules/team/variables.tf @@ -0,0 +1,12 @@ +variable "name" { + description = "Name of the team" + type = string +} + +variable "members" { + type = list(object({ + username = string + role = optional(string, "member") + })) + default = [] +} diff --git a/organization.tf b/organization.tf new file mode 100644 index 0000000..7a5edb5 --- /dev/null +++ b/organization.tf @@ -0,0 +1,26 @@ +resource "github_organization_settings" "anza_labs" { + billing_email = "mateusz.urbanek.98@gmail.com" + name = "anza-labs" + + default_repository_permission = "read" + has_organization_projects = true + has_repository_projects = true + web_commit_signoff_required = false + + members_can_create_pages = true + members_can_create_public_pages = true + members_can_create_private_pages = false + members_can_create_repositories = false + members_can_create_public_repositories = false + members_can_create_internal_repositories = false + members_can_create_private_repositories = false + members_can_fork_private_repositories = false + + dependabot_alerts_enabled_for_new_repositories = true + dependabot_security_updates_enabled_for_new_repositories = true + dependency_graph_enabled_for_new_repositories = true + + secret_scanning_enabled_for_new_repositories = true + secret_scanning_push_protection_enabled_for_new_repositories = true +} + diff --git a/providers.tf b/providers.tf new file mode 100644 index 0000000..2197abf --- /dev/null +++ b/providers.tf @@ -0,0 +1,17 @@ +terraform { + backend "pg" { + schema_name = "tofu_remote_state_anza_labs" + } + + required_providers { + github = { + source = "integrations/github" + version = "6.2.1" + } + } +} + +provider "github" { + owner = "anza-labs" + token = var.gh_token +} diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..9c89bd2 --- /dev/null +++ b/renovate.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ "config:recommended" ], + "labels": [ "area/dependency" ], + "vulnerabilityAlerts": { + "enabled": true + } +} diff --git a/repositories.tf b/repositories.tf new file mode 100644 index 0000000..cb86587 --- /dev/null +++ b/repositories.tf @@ -0,0 +1,35 @@ +module "org_mgmt" { + source = "./modules/repository" + name = "org-mgmt" + archived = false + topics = [] +} + +module "scribe" { + source = "./modules/repository" + name = "scribe" + archived = false + is_public = true +} + +module "manifests" { + source = "./modules/repository" + name = "manifests" + archived = false + is_public = true +} + +module "infra" { + source = "./modules/repository" + name = "infra" + archived = false + is_public = true +} + +module "charts" { + source = "./modules/repository" + name = "charts" + archived = false + is_public = true + enable_pages = true +} diff --git a/variables.tf b/variables.tf new file mode 100644 index 0000000..7463e77 --- /dev/null +++ b/variables.tf @@ -0,0 +1,4 @@ +variable "gh_token" { + type = string + sensitive = true +}