diff --git a/.github/workflows/modtest-dev.yaml b/.github/workflows/modtest-dev.yaml deleted file mode 100644 index c98e5f1..0000000 --- a/.github/workflows/modtest-dev.yaml +++ /dev/null @@ -1,29 +0,0 @@ -name: "ModTest: dev" - -on: - pull_request: - push: - branches: - - main - -jobs: - modtest: - if : ${{ github.event_name }} == "pull_request" - uses: HappyPathway/centralized-actions/.github/workflows/modtest.yml@main - with: - workspace: dev - workspace_repo: github-repos - workspace_branch: main - repo_clone_type: https - mod_source: repo/github - - github_server: ${{vars.GH_SERVER}} - github_org: ${{ github.repository_owner }} - branch: ${{ github.head_ref }} - terraform_version: ${{vars.TERRAFORM_VERSION}} - terraform_api_token_name: ${{ vars.TERRAFORM_API_TOKEN_NAME }} - terraform_api: ${{vars.TERRAFORM_API}} - - secrets: - TFE_TOKEN: ${{ secrets.TFE_TOKEN }} - GH_TOKEN: ${{ secrets.GH_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/terraform-doc.yaml b/.github/workflows/terraform-doc.yaml deleted file mode 100644 index aeb3272..0000000 --- a/.github/workflows/terraform-doc.yaml +++ /dev/null @@ -1,45 +0,0 @@ -name: "Terraform Doc" - -on: - pull_request: - -env: - GITHUB_OWNER: ${{ vars.GH_ORG }} - GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} - GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }} - TF_WORKSPACE: happypathway - TFE_TOKEN: ${{ secrets.TFE_TOKEN }} - -jobs: - tf-doc: - runs-on: ubuntu-latest - steps: - - name: Checkout Code - uses: actions/checkout@v4 - id: checkout - with: - ref: ${{ github.event.pull_request.head.ref }} - - - name: Setup Terraform - uses: hashicorp/setup-terraform@v3.1.2 - with: - terraform_version: ${{ vars.terraform_version }} - cli_config_credentials_token: ${{ secrets.TFE_TOKEN }} - cli_config_credentials_hostname: ${{ vars.terraform_api }} - - - name: terraform init - run: terraform init -upgrade - - - name: Render terraform docs inside the README.md and push changes back to PR branch - uses: terraform-docs/gh-actions@v1.2.0 - with: - working-dir: . - output-file: README.md - output-method: inject - git-push: "true" - - # terraform-docs/gh-actions@v1.0.0 modifies .git files with owner root:root, and the following steps fail with - # insufficient permission for adding an object to repository database .git/objects - # since the expected user is runner:docker. See https://github.com/terraform-docs/gh-actions/issues/90 - - name: Fix .git owner - run: sudo chown runner:docker -R .git \ No newline at end of file diff --git a/.github/workflows/terraform-test.yml b/.github/workflows/terraform-test.yml new file mode 100644 index 0000000..ea0d63f --- /dev/null +++ b/.github/workflows/terraform-test.yml @@ -0,0 +1,55 @@ +name: "Terraform Test and Tag" + +on: + push: + branches: + - main + pull_request: + branches: + - main + +permissions: + contents: write + pull-requests: read + +jobs: + terraform: + name: "Terraform Test" + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: "~>1.6.0" + terraform_wrapper: false + + - name: Terraform Format + id: fmt + run: terraform fmt -check + continue-on-error: false + + - name: Terraform Init + id: init + run: terraform init -backend=false + + - name: Terraform Validate + id: validate + run: terraform validate + + - name: Run Terraform Tests + id: test + run: terraform test + + - name: Bump version and push tag + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + uses: anothrNick/github-tag-action@1.67.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DEFAULT_BUMP: patch + WITH_V: true \ No newline at end of file diff --git a/.github/workflows/terraform.yaml b/.github/workflows/terraform.yaml deleted file mode 100644 index 0df3f90..0000000 --- a/.github/workflows/terraform.yaml +++ /dev/null @@ -1,84 +0,0 @@ -name: "Terraform Validate" - -on: - workflow_dispatch: - push: - branches: - - main - -env: - GITHUB_OWNER: ${{ vars.GH_ORG }} - GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} - GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }} - TF_WORKSPACE: happypathway - TFE_TOKEN: ${{ secrets.TFE_TOKEN }} - -jobs: - setup-terraform: - outputs: - commit_sha: ${{ steps.checkout.outputs.commit }} - - runs-on: ubuntu-latest - steps: - - name: Checkout Code - uses: actions/checkout@v4 - id: checkout - - - name: Setup Terraform - uses: hashicorp/setup-terraform@v3.1.2 - with: - terraform_version: ${{ vars.terraform_version }} - cli_config_credentials_token: ${{ secrets.TFE_TOKEN }} - cli_config_credentials_hostname: ${{ vars.terraform_api }} - - - name: terraform init - run: terraform init -upgrade - - - uses: actions/upload-artifact@master - name: Archive Configuration - if: github.ref == 'refs/heads/main' - with: - name: terraform_dir - path: .terraform - retention-days: 1 - include-hidden-files: true - - - uses: actions/upload-artifact@master - name: Archive Lockfile - if: github.ref == 'refs/heads/main' - with: - name: terraform_lockfile - path: .terraform.lock.hcl - retention-days: 1 - include-hidden-files: true - - terraform-validate: - needs: setup-terraform - uses: HappyPathway/centralized-actions/.github/workflows/terraform-test.yml@main - with: - terraform_version: ${{ vars.terraform_version }} - terraform_api: ${{ vars.terraform_api }} - github_username: ${{ github.actor }} - github_email: ${{ github.actor }}@roknsound.com - github_org: ${{ github.repository_owner }} - setup_terraform: true - terraform_init: false - cache: ${{ github.workspace }} - download_cache: true - commit_sha: ${{ needs.setup-terraform.outputs.commit_sha }} - secrets: - TFE_TOKEN: ${{ secrets.TFE_TOKEN }} - GH_TOKEN: ${{ secrets.GH_TOKEN }} - GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }} - - gtag: - needs: terraform-validate - if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' - uses: HappyPathway/centralized-actions/.github/workflows/gtag.yml@main - with: - patch: true - github_org: ${{ vars.GH_ORG }} - github_username: ${{ vars.GH_USERNAME }} - github_email: ${{ vars.GH_EMAIL }} - secrets: - GH_TOKEN: ${{ secrets.GH_TOKEN }} diff --git a/.github/workflows/terraform.yml b/.github/workflows/terraform.yml deleted file mode 100644 index 8e89014..0000000 --- a/.github/workflows/terraform.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: "Gtag" - -on: - workflow_dispatch: - pull_request: - push: - branches: - - main - -jobs: - terraform: - uses: HappyPathway/centralized-actions/.github/workflows/terraform-test.yml@main - with: - terraform_version: ${{vars.TERRAFORM_VERSION}} - terraform_api: ${{vars.TERRAFORM_API}} - terraform_init: true - github_username: ${{vars.GH_USERNAME}} - github_email: ${{vars.GH_EMAIL}} - github_org: ${{ vars.GH_ORG }} - download_cache: false - secrets: - TFE_TOKEN: ${{ secrets.TFE_TOKEN }} - GH_TOKEN: ${{ secrets.GH_TOKEN }} - - gtag: - if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' - needs: terraform - uses: HappyPathway/centralized-actions/.github/workflows/gtag.yml@main - with: - patch: true - github_org: ${{ vars.GH_ORG }} - github_username: ${{ vars.GH_USERNAME }} - github_email: ${{ vars.GH_EMAIL }} - secrets: - GH_TOKEN: ${{ secrets.GH_TOKEN }} diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl index d7a42aa..451f5b9 100644 --- a/.terraform.lock.hcl +++ b/.terraform.lock.hcl @@ -4,6 +4,7 @@ provider "registry.terraform.io/integrations/github" { version = "6.5.0" hashes = [ + "h1:KN6W+TRczQXMQLAI5Cn/xpvJzq8r+/AQCZaxGURXQ3A=", "h1:ikIBPXI5lx7cV4aqLWYuL+LQnHmf+FybvdpcsfAqK4o=", "zh:3088bfd30c51ebfcb7c8d829465ec7b3c19af684cf1aff1ea1111ad3c6421c11", "zh:34f9054b0123f9fa7ab8ebc73591d2cf502f1cc75e7594bde42ce799fcac32b6", diff --git a/github_branch.tf b/github_branch.tf index cd10a81..c04d1bb 100644 --- a/github_branch.tf +++ b/github_branch.tf @@ -35,32 +35,42 @@ locals { resource "github_branch_protection" "main" { count = (var.enforce_prs && !var.github_is_private) || var.github_is_private ? 1 : 0 - repository_id = local.github_repo.node_id - pattern = var.github_default_branch - - # Basic protection settings - enforce_admins = var.github_enforce_admins_branch_protection + repository_id = local.github_repo.node_id + pattern = var.github_default_branch + enforce_admins = var.github_enforce_admins_branch_protection allows_deletions = false allows_force_pushes = false require_signed_commits = true required_linear_history = true require_conversation_resolution = true + lock_branch = false - required_status_checks { - strict = try(var.required_status_checks.strict, false) - contexts = try(var.required_status_checks.contexts, []) + dynamic "required_status_checks" { + for_each = var.required_status_checks != null ? ["true"] : [] + content { + strict = try(var.required_status_checks.strict, false) + contexts = try(var.required_status_checks.contexts, []) + } } - required_pull_request_reviews { - dismiss_stale_reviews = var.github_dismiss_stale_reviews - restrict_dismissals = true - pull_request_bypassers = var.pull_request_bypassers - require_code_owner_reviews = var.github_require_code_owner_reviews - required_approving_review_count = var.github_required_approving_review_count + dynamic "required_pull_request_reviews" { + for_each = var.enforce_prs ? ["true"] : [] + content { + dismiss_stale_reviews = var.github_dismiss_stale_reviews + restrict_dismissals = true + require_code_owner_reviews = var.github_require_code_owner_reviews + required_approving_review_count = var.github_required_approving_review_count + require_last_push_approval = true + } } - restrict_pushes { - push_allowances = var.github_push_restrictions + dynamic "push_restrictions" { + for_each = length(var.github_push_restrictions) > 0 ? ["true"] : [] + content { + users = var.github_push_restrictions + teams = [] + apps = [] + } } lifecycle { diff --git a/github_repo.tf b/github_repo.tf index 2cde3f1..5b6fd5f 100644 --- a/github_repo.tf +++ b/github_repo.tf @@ -2,6 +2,12 @@ locals { repo_name = var.force_name ? var.name : "${var.name}-${formatdate("YYYYMMDD", timestamp())}" github_repo = var.create_repo ? github_repository.repo[0] : data.github_repository.existing[0] + + validate_merge_options = ( + var.github_allow_merge_commit || + var.github_allow_squash_merge || + var.github_allow_rebase_merge + ) ? null : file("ERROR: At least one merge option must be enabled") } resource "github_repository" "repo" { @@ -12,6 +18,7 @@ resource "github_repository" "repo" { has_issues = var.github_has_issues has_projects = var.github_has_projects has_wiki = var.github_has_wiki + has_downloads = var.github_has_downloads auto_init = var.github_auto_init archive_on_destroy = var.archive_on_destroy archived = var.archived @@ -19,7 +26,14 @@ resource "github_repository" "repo" { topics = var.github_repo_topics homepage_url = var.homepage_url gitignore_template = var.gitignore_template + license_template = var.license_template is_template = var.is_template + has_discussions = try(var.github_has_discussions, false) + merge_commit_title = try(var.github_merge_commit_title, "MERGE_MESSAGE") + merge_commit_message = try(var.github_merge_commit_message, "PR_TITLE") + squash_merge_commit_title = try(var.github_squash_merge_commit_title, "COMMIT_OR_PR_TITLE") + squash_merge_commit_message = try(var.github_squash_merge_commit_message, "COMMIT_MESSAGES") + allow_update_branch = try(var.github_allow_update_branch, true) allow_merge_commit = var.github_allow_merge_commit allow_squash_merge = var.github_allow_squash_merge @@ -27,6 +41,18 @@ resource "github_repository" "repo" { allow_auto_merge = var.github_allow_auto_merge delete_branch_on_merge = var.github_delete_branch_on_merge + security_and_analysis { + advanced_security { + status = try(var.security_and_analysis.advanced_security.status, "disabled") + } + secret_scanning { + status = try(var.security_and_analysis.secret_scanning.status, "disabled") + } + secret_scanning_push_protection { + status = try(var.security_and_analysis.secret_scanning_push_protection.status, "disabled") + } + } + dynamic "template" { for_each = var.template_repo == null ? [] : ["*"] content { @@ -59,6 +85,17 @@ resource "github_repository" "repo" { } } + dynamic "pages" { + for_each = var.pages_config == null ? [] : ["true"] + content { + source { + branch = try(var.pages_config.branch, "gh-pages") + path = try(var.pages_config.path, "/") + } + cname = try(var.pages_config.cname, null) + } + } + lifecycle { ignore_changes = [ auto_init, diff --git a/github_repo.tftest.hcl b/github_repo.tftest.hcl index a78edb1..97c39e6 100644 --- a/github_repo.tftest.hcl +++ b/github_repo.tftest.hcl @@ -1,12 +1,13 @@ # valid_string_concat.tftest.hcl variables { - force_name = true - github_is_private = true - repo_org = "test-org" - name = "test-repo" - enforce_prs = false + name = "github-repo-test" + repo_org = "HappyPathway" + force_name = true + github_is_private = true + enforce_prs = false archive_on_destroy = false - github_org_teams = [] + github_org_teams = [] + admin_teams = ["test-team"] github_repo_description = "Test repository" github_repo_topics = ["test", "terraform"] create_repo = true @@ -39,13 +40,13 @@ variables { content = "Test content" } ] - admin_teams = ["test-team"] } run "repo_tests" { command = plan + assert { - condition = github_repository.repo.name == "test-repo" + condition = github_repository.repo.name == "github-repo-test" error_message = "Github Repo name did not match expected" } } @@ -53,7 +54,6 @@ run "repo_tests" { run "create_new_repository" { command = plan - // Basic repository checks assert { condition = module.github_repo[0].name == var.name error_message = "Repository name does not match input" @@ -74,7 +74,6 @@ run "create_new_repository" { error_message = "Repository topics should include 'terraform'" } - // Security and analysis checks assert { condition = module.github_repo[0].security_and_analysis.advanced_security.status == "enabled" error_message = "Advanced security should be enabled" diff --git a/variables.tf b/variables.tf index 4e79d40..5e3485e 100644 --- a/variables.tf +++ b/variables.tf @@ -102,7 +102,13 @@ variable "github_enforce_admins_branch_protection" { } variable "github_allow_auto_merge" { - description = "Allow auto-merging pull requests" + description = "Allow pull requests to be automatically merged" + type = bool + default = false +} + +variable "github_has_downloads" { + description = "Enable downloads feature" type = bool default = false } @@ -322,3 +328,91 @@ variable "environments" { })) default = [] } + +variable "github_repo_topics" { + description = "List of topics for the repository" + type = list(string) + default = [] +} + +variable "license_template" { + description = "License template to use for the repository" + type = string + default = null +} + +variable "github_has_downloads" { + description = "Enable downloads feature" + type = bool + default = false +} + +variable "github_has_discussions" { + description = "Enable discussions feature" + type = bool + default = false +} + +variable "license_template" { + description = "License template to use for the repository" + type = string + default = null +} + +variable "github_has_downloads" { + description = "Enable downloads feature" + type = bool + default = false +} + +variable "github_has_discussions" { + description = "Enable discussions feature" + type = bool + default = false +} + +variable "github_merge_commit_title" { + description = "Title for merge commits" + type = string + default = "MERGE_MESSAGE" +} + +variable "github_merge_commit_message" { + description = "Message for merge commits" + type = string + default = "PR_TITLE" +} + +variable "github_squash_merge_commit_title" { + description = "Title for squash merge commits" + type = string + default = "COMMIT_OR_PR_TITLE" +} + +variable "github_squash_merge_commit_message" { + description = "Message for squash merge commits" + type = string + default = "COMMIT_MESSAGES" +} + +variable "github_allow_update_branch" { + description = "Allow updating pull request branches" + type = bool + default = true +} + +variable "github_allow_update_branch" { + description = "Allow updating pull request branches" + type = bool + default = true +} + +variable "pages_config" { + description = "Configuration for GitHub Pages" + type = object({ + branch = optional(string, "gh-pages") + path = optional(string, "/") + cname = optional(string) + }) + default = null +} \ No newline at end of file