Skip to content

Decouple environment approvals from deploy_order; add make smoke#35

Merged
akuzminsky merged 1 commit into
mainfrom
decouple-env-approvals-add-smoke
May 26, 2026
Merged

Decouple environment approvals from deploy_order; add make smoke#35
akuzminsky merged 1 commit into
mainfrom
decouple-env-approvals-add-smoke

Conversation

@akuzminsky
Copy link
Copy Markdown
Member

Summary

  • Decouple required-reviewer environment protection from deploy_order
  • New approval_team_slug variable gates reviewers: set it to opt in, leave null (default) for no reviewers
  • Environments with deploy_order > 0 only get reviewers when approval_team_slug is set
  • Add make smoke target to makefile template (fail-open: no-ops if scripts/smoke.sh missing)
  • Add "Smoke Test" step to CD workflow after every environment's apply

Closes #34

Context

PR #33 hit a 422 from GitHub API because required-reviewer environment protection rules need Enterprise Cloud for private repos. The old code unconditionally added reviewers to any environment with deploy_order > 0.

Now aws-service-infrahouse-app (private, Team plan) works because approval_team_slug defaults to null — no reviewers, no 422. Enterprise Cloud orgs can opt in by setting approval_team_slug.

Test plan

  • Verify terraform plan shows no reviewers on live-production (approval_team_slug not set)
  • Verify CD workflow template includes the smoke test step
  • Verify make smoke no-ops gracefully when scripts/smoke.sh doesn't exist

🤖 Generated with Claude Code

…ract

Closes #34

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

State s3://infrahouse-github-control-state/terraform.tfstate

Affected resources counts

Success 🟢 Add 🟡 Change Destroy
1 5 0

Affected resources by action

Action Resources
🟢 module.aws_service_infrahouse_app.github_repository_environment.cd["production"]
🟡 github_team_members.admins
🟡 github_team_members.dev
🟡 module.aws_service_infrahouse_app.github_repository_environment.cd["sandbox"]
🟡 module.aws_service_infrahouse_app.github_repository_file.makefile_fragment[0]
🟡 module.aws_service_infrahouse_app.github_repository_file.terraform_cd[0]
STDOUT
Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create
  ~ update in-place

Terraform will perform the following actions:

  # github_team_members.admins will be updated in-place
  ~ resource "github_team_members" "admins" {
        id      = "14268696"
        # (1 unchanged attribute hidden)

      - members {
          - role     = "maintainer" -> null
          - username = "infrahouse8" -> null
        }
      + members {
          + role     = "member"
          + username = "infrahouse8"
        }

        # (1 unchanged block hidden)
    }

  # github_team_members.dev will be updated in-place
  ~ resource "github_team_members" "dev" {
        id      = "7332815"
        # (1 unchanged attribute hidden)

      - members {
          - role     = "maintainer" -> null
          - username = "infrahouse8" -> null
        }
      + members {
          + role     = "member"
          + username = "infrahouse8"
        }

        # (2 unchanged blocks hidden)
    }

  # module.aws_service_infrahouse_app.github_repository_environment.cd["production"] will be created
  + resource "github_repository_environment" "cd" {
      + can_admins_bypass   = true
      + environment         = "live-production"
      + id                  = (known after apply)
      + prevent_self_review = false
      + repository          = "aws-service-infrahouse-app"
      + repository_id       = (known after apply)
    }

  # module.aws_service_infrahouse_app.github_repository_environment.cd["sandbox"] will be updated in-place
  ~ resource "github_repository_environment" "cd" {
        id                  = "aws-service-infrahouse-app:live-sandbox"
        # (6 unchanged attributes hidden)

      - deployment_branch_policy {
          - custom_branch_policies = true -> null
          - protected_branches     = false -> null
        }
    }

  # module.aws_service_infrahouse_app.github_repository_file.makefile_fragment[0] will be updated in-place
  ~ resource "github_repository_file" "makefile_fragment" {
      ~ commit_message      = "Update makefiles/Makefile" -> "Add makefiles/Makefile"
      ~ content             = <<-EOT
            # This file is managed by Terraform in infrahouse/github-control repository.
            # Do not edit this file, all changes will be overwritten.
            # If you need to add custom targets, create a new file in the makefiles/ directory.
            
            SHELL := /usr/bin/env bash
            
            .DEFAULT_GOAL := help
            define PRINT_HELP_PYSCRIPT
            import re, sys
            
            for line in sys.stdin:
            	match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line)
            	if match:
            		target, help = match.groups()
            		print("%-40s %s" % (target, help))
            endef
            export PRINT_HELP_PYSCRIPT
            
            help: ## Print this help
            	cat $(MAKEFILE_LIST) | python -c "$$PRINT_HELP_PYSCRIPT"
            
            .PHONY: bootstrap
            bootstrap: ## Build development environment
            	pip install -r requirements.txt
            
            .PHONY: lint
            lint:  ## Check code style
            	terraform fmt -check -recursive
            
            .PHONY: format
            format:  ## Format terraform files
            	terraform fmt -recursive
            
            .PHONY: init
            init:
            	terraform init
            
            .PHONY: plan
            plan: init ## Run terraform plan
            	set -o pipefail ; terraform plan -no-color --out=tf.plan 2> plan.stderr | tee plan.stdout || (cat plan.stderr; exit 1)
            
            .PHONY: apply
            apply: ## Run terraform apply
            	terraform apply -auto-approve -input=false tf.plan
            
          + .PHONY: smoke
          + smoke: ## Run post-deploy smoke test if scripts/smoke.sh exists
          + 	@if [ -x scripts/smoke.sh ]; then \
          + 		scripts/smoke.sh "$(ENV)"; \
          + 	else \
          + 		echo "smoke[$(ENV)]: no scripts/smoke.sh, skipping"; \
          + 	fi
          + 
            .PHONY: clean
            clean: ## Remove generated files
            	rm -fr .terraform
            	rm -f .terraform.lock.hcl
            	rm -f plan.stderr plan.stdout tf.plan
        EOT
        id                  = "aws-service-infrahouse-app:makefiles/Makefile:main"
        # (8 unchanged attributes hidden)
    }

  # module.aws_service_infrahouse_app.github_repository_file.terraform_cd[0] will be updated in-place
  ~ resource "github_repository_file" "terraform_cd" {
      ~ content             = <<-EOT
            ---
            name: "Terraform CD"
            
            on:  # yamllint disable-line rule:truthy
              pull_request:
                types:
                  - "closed"
            
            permissions:
              id-token: "write"
              contents: "read"
            
            concurrency:
              group: "terraform"
              cancel-in-progress: false
            
            jobs:
              deploy-order-0:
                if: "github.event.pull_request.merged"
                strategy:
                  fail-fast: false
                  matrix:
                    env: ["sandbox"]
                name: "Terraform Apply ${{ matrix.env }}"
                runs-on: ubuntu-24.04
                environment: "live-${{ matrix.env }}"
                timeout-minutes: 60
                env:
                  GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
                  REGION_JSON: "${{ vars.AWS_DEFAULT_REGION }}"
                  ROLE_GITHUB_JSON: "${{ vars.ROLE_GITHUB }}"
                  ROLE_SM_JSON: "${{ vars.ROLE_STATE_MANAGER }}"
                  TF_VAR_environment: "${{ matrix.env }}"
            
                defaults:
                  run:
                    shell: "bash"
            
                steps:
                  - name: "Checkout"
                    uses: "actions/checkout@v6"
            
                  - name: "Extract Variables"
                    id: "extract_vars"
                    run: |
                      REGION=$(echo '${{ env.REGION_JSON }}' | jq -r '.${{ matrix.env }}')
                      echo "REGION=$REGION" >> "$GITHUB_OUTPUT"
                      ROLE_GITHUB=$(echo '${{ env.ROLE_GITHUB_JSON }}' | jq -r '.${{ matrix.env }}')
                      echo "ROLE_GITHUB=$ROLE_GITHUB" >> "$GITHUB_OUTPUT"
                      ROLE_SM=$(echo '${{ env.ROLE_SM_JSON }}' | jq -r '.${{ matrix.env }}')
                      echo "ROLE_STATE_MANAGER=$ROLE_SM" >> "$GITHUB_OUTPUT"
            
                  - name: "Configure AWS Credentials"
                    uses: "aws-actions/configure-aws-credentials@v6"
                    with:
                      role-to-assume: "${{ steps.extract_vars.outputs.ROLE_GITHUB }}"
                      role-session-name: "github-actions-${{ matrix.env }}"
                      aws-region: "${{ steps.extract_vars.outputs.REGION }}"
            
                  - name: "Set Terraform version"
                    id: "terraform_version"
                    run: |
                      echo "IH_TF_VERSION=$(cat .terraform-version)" >> "$GITHUB_OUTPUT"
            
                  - name: "Setup Terraform"
                    uses: "hashicorp/setup-terraform@v4"
                    with:
                      terraform_version: "${{ steps.terraform_version.outputs.IH_TF_VERSION }}"
            
                  - name: "Set up Python"
                    uses: "actions/setup-python@v6"
                    with:
                      python-version: "3.14"
            
                  - name: "Setup Python Environment"
                    working-directory: "environments/${{ matrix.env }}"
                    run: |
                      make bootstrap
            
                  - name: "Download Terraform Plan"
                    working-directory: "environments/${{ matrix.env }}"
                    run: |
                      ih-plan \
                        --aws-assume-role-arn \
                        ${{ steps.extract_vars.outputs.ROLE_STATE_MANAGER }} \
                        download \
                        plans/${{ matrix.env }}/${{ github.event.pull_request.number }}.plan \
                        tf.plan
            
                  - name: "Terraform Init"
                    working-directory: "environments/${{ matrix.env }}"
                    run: |
                      terraform init -input=false
            
                  - name: "Terraform Validate"
                    working-directory: "environments/${{ matrix.env }}"
                    run: |
                      terraform validate -no-color
            
                  - name: "Terraform Apply"
                    working-directory: "environments/${{ matrix.env }}"
                    run: |
                      make apply
            
                  - name: "Remove Plan"
                    working-directory: "environments/${{ matrix.env }}"
                    run: |
                      ih-plan \
                        --aws-assume-role-arn \
                        ${{ steps.extract_vars.outputs.ROLE_STATE_MANAGER }} \
                        remove \
                        plans/${{ matrix.env }}/${{ github.event.pull_request.number }}.plan
          + 
          +       - name: "Smoke Test"
          +         working-directory: "environments/${{ matrix.env }}"
          +         run: |
          +           make smoke ENV=${{ matrix.env }}
              deploy-order-1:
                if: "github.event.pull_request.merged"
                needs: ["deploy-order-0"]
                strategy:
                  fail-fast: false
                  matrix:
                    env: ["production"]
                name: "Terraform Apply ${{ matrix.env }}"
                runs-on: ubuntu-24.04
                environment: "live-${{ matrix.env }}"
                timeout-minutes: 60
                env:
                  GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
                  REGION_JSON: "${{ vars.AWS_DEFAULT_REGION }}"
                  ROLE_GITHUB_JSON: "${{ vars.ROLE_GITHUB }}"
                  ROLE_SM_JSON: "${{ vars.ROLE_STATE_MANAGER }}"
                  TF_VAR_environment: "${{ matrix.env }}"
            
                defaults:
                  run:
                    shell: "bash"
            
                steps:
                  - name: "Checkout"
                    uses: "actions/checkout@v6"
            
                  - name: "Extract Variables"
                    id: "extract_vars"
                    run: |
                      REGION=$(echo '${{ env.REGION_JSON }}' | jq -r '.${{ matrix.env }}')
                      echo "REGION=$REGION" >> "$GITHUB_OUTPUT"
                      ROLE_GITHUB=$(echo '${{ env.ROLE_GITHUB_JSON }}' | jq -r '.${{ matrix.env }}')
                      echo "ROLE_GITHUB=$ROLE_GITHUB" >> "$GITHUB_OUTPUT"
                      ROLE_SM=$(echo '${{ env.ROLE_SM_JSON }}' | jq -r '.${{ matrix.env }}')
                      echo "ROLE_STATE_MANAGER=$ROLE_SM" >> "$GITHUB_OUTPUT"
            
                  - name: "Configure AWS Credentials"
                    uses: "aws-actions/configure-aws-credentials@v6"
                    with:
                      role-to-assume: "${{ steps.extract_vars.outputs.ROLE_GITHUB }}"
                      role-session-name: "github-actions-${{ matrix.env }}"
                      aws-region: "${{ steps.extract_vars.outputs.REGION }}"
            
                  - name: "Set Terraform version"
                    id: "terraform_version"
                    run: |
                      echo "IH_TF_VERSION=$(cat .terraform-version)" >> "$GITHUB_OUTPUT"
            
                  - name: "Setup Terraform"
                    uses: "hashicorp/setup-terraform@v4"
                    with:
                      terraform_version: "${{ steps.terraform_version.outputs.IH_TF_VERSION }}"
            
                  - name: "Set up Python"
                    uses: "actions/setup-python@v6"
                    with:
                      python-version: "3.14"
            
                  - name: "Setup Python Environment"
                    working-directory: "environments/${{ matrix.env }}"
                    run: |
                      make bootstrap
            
                  - name: "Download Terraform Plan"
                    working-directory: "environments/${{ matrix.env }}"
                    run: |
                      ih-plan \
                        --aws-assume-role-arn \
                        ${{ steps.extract_vars.outputs.ROLE_STATE_MANAGER }} \
                        download \
                        plans/${{ matrix.env }}/${{ github.event.pull_request.number }}.plan \
                        tf.plan
            
                  - name: "Terraform Init"
                    working-directory: "environments/${{ matrix.env }}"
                    run: |
                      terraform init -input=false
            
                  - name: "Terraform Validate"
                    working-directory: "environments/${{ matrix.env }}"
                    run: |
                      terraform validate -no-color
            
                  - name: "Terraform Apply"
                    working-directory: "environments/${{ matrix.env }}"
                    run: |
                      make apply
            
                  - name: "Remove Plan"
                    working-directory: "environments/${{ matrix.env }}"
                    run: |
                      ih-plan \
                        --aws-assume-role-arn \
                        ${{ steps.extract_vars.outputs.ROLE_STATE_MANAGER }} \
                        remove \
                        plans/${{ matrix.env }}/${{ github.event.pull_request.number }}.plan
          + 
          +       - name: "Smoke Test"
          +         working-directory: "environments/${{ matrix.env }}"
          +         run: |
          +           make smoke ENV=${{ matrix.env }}
        EOT
        id                  = "aws-service-infrahouse-app:.github/workflows/terraform-CD.yml:main"
        # (9 unchanged attributes hidden)
    }

Plan: 1 to add, 5 to change, 0 to destroy.

Warning: Argument is deprecated

  with module.ih_8_repos.github_repository.repo,
  on modules/local-repo/repos.tf line 4, in resource "github_repository" "repo":
   4:   has_downloads        = false

This attribute is no longer in use, but it hasn't been removed yet. It will
be removed in a future version. See
https://github.com/orgs/community/discussions/102145#discussioncomment-8351756

(and 6 more similar warnings elsewhere)

Warning: Deprecated attribute

  on .terraform/modules/actions-runner-pem-493370826424-uw1/data_sources.tf line 11, in data "external" "secret_value":
  11:     "python", "${path.module}/assets/get_secret.py", data.aws_region.current.name, aws_secretsmanager_secret.secret.id, data.aws_iam_role.caller_role.arn

The attribute "name" is deprecated. Refer to the provider documentation for
details.

(and 5 more similar warnings elsewhere)

─────────────────────────────────────────────────────────────────────────────

Saved the plan to: tf.plan

To perform exactly these actions, run the following command to apply:
    terraform apply "tf.plan"
metadata
eyJzMzovL2luZnJhaG91c2UtZ2l0aHViLWNvbnRyb2wtc3RhdGUvdGVycmFmb3JtLnRmc3RhdGUiOiB7InN1Y2Nlc3MiOiB0cnVlLCAiYWRkIjogMSwgImNoYW5nZSI6IDUsICJkZXN0cm95IjogMH19

@akuzminsky akuzminsky merged commit 3035517 into main May 26, 2026
2 checks passed
@akuzminsky akuzminsky deleted the decouple-env-approvals-add-smoke branch May 26, 2026 22:23
@akuzminsky akuzminsky deployed to production May 26, 2026 22:23 — with GitHub Actions Active
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Decouple environment approvals from deploy_order; add make smoke contract

2 participants