Skip to content

Decouple environment approvals from deploy_order; add make smoke contract #34

@akuzminsky

Description

@akuzminsky

Problem

modules/service-repo/environments.tf couples two orthogonal concerns: deploy ordering (deploy_order) and required-reviewer protection rules. Any environment with deploy_order > 0 gets a reviewers block automatically, which:

  1. Fails with HTTP 422 on private repos that aren't on GitHub Enterprise Cloud — required-reviewer environment protection is EC-only for private repos.
  2. Conflates intent — sequencing and approval are independent decisions, not the same decision.

Separately, there's no slot in the CD pipeline for verifying a sandbox deploy actually works before production deploy starts.

Proposed changes

1. Decouple require_approval from deploy_order

modules/service-repo/variables.tf:

variable "environments" {
  type = map(object({
    deploy_order     = number
    require_approval = optional(bool, false)
  }))
}

variable "approval_team_slug" {
  type        = string
  default     = null
  description = <<-EOT
    GitHub team slug whose members can approve deployments to environments
    where require_approval = true. Required only if any environment opts in.
    Required reviewers on private repos need GitHub Enterprise Cloud.
  EOT
}

modules/service-repo/environments.tf:

data "github_team" "approval" {
  count = anytrue([for env in var.environments : env.require_approval]) ? 1 : 0
  slug  = var.approval_team_slug
}

resource "github_repository_environment" "cd" {
  for_each    = var.environments
  environment = "live-${each.key}"
  repository  = github_repository.this.name

  dynamic "reviewers" {
    for_each = each.value.require_approval ? [1] : []
    content {
      teams = [data.github_team.approval[0].id]
    }
  }
}

Add a validation: if any env sets require_approval = true and approval_team_slug is null, fail with a clear message.

2. Add make smoke contract

modules/service-repo/templates/makefile.mk:

.PHONY: smoke
smoke:
    @if [ -x scripts/smoke.sh ]; then \
      scripts/smoke.sh "$(ENV)"; \
    else \
      echo "smoke[$(ENV)]: no scripts/smoke.sh, skipping"; \
    fi

CD workflow template — insert after the "Remove Plan" step, inside the env loop:

      - name: "Smoke test"
        run: |
          make smoke ENV=${{ matrix.env }}

Fail-open: missing script logs one line and exits 0. Smoke runs after every env, including production, so post-deploy health regressions surface as workflow failures.

3. Documentation

Module README updates:

  • New require_approval and approval_team_slug inputs with the EC-on-private-repos caveat called out.
  • scripts/smoke.sh contract: input $1 = env name, exit 0 = healthy, non-zero = halt pipeline.
  • Copy-pasteable stub script in the README, not scaffolded into customer repos by Terraform.

Acceptance criteria

  • require_approval defaults to false; envs with deploy_order > 0 no longer get reviewers implicitly.
  • require_approval = true works on EC orgs for opted-in envs.
  • Variable validation fails cleanly when require_approval = true is set without approval_team_slug.
  • make smoke ENV=<env> runs scripts/smoke.sh <env> if present, no-ops with a log line otherwise.
  • CD workflow runs make smoke after each env's apply.
  • Failed smoke in live-sandbox halts the job — live-production deploy does not run.
  • Dog-fooding repo (aws-service-infrahouse-app) reapplies cleanly with require_approval = false on production, no approval_team_slug set, no 422.
  • README updated with new inputs and the scripts/smoke.sh contract.

Migration

No external customers yet — no migration shim needed. Bump module to next major version: default behavior changes from "implicit reviewers when deploy_order > 0" to "no reviewers unless require_approval = true."

Reference

Original failure on aws-service-infrahouse-app:

PUT https://api.github.com/repos/infrahouse/aws-service-infrahouse-app/environments/live-production: 422
Failed to create the environment protection rule. Please ensure the billing plan
supports the required reviewers protection rule.

GitHub feature gate: deployment protection rules with required reviewers are Enterprise Cloud only for private repos. Pro/Team plans get environments but not required reviewers for private repos.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions