diff --git a/.github/workflows/opentofu.yml b/.github/workflows/opentofu.yml index 8c94fd1..89faae0 100644 --- a/.github/workflows/opentofu.yml +++ b/.github/workflows/opentofu.yml @@ -204,7 +204,7 @@ jobs: const maxLength = 12000; const output = fs.existsSync('validation-output.txt') ? fs.readFileSync('validation-output.txt', 'utf8') - .replace(/\u001b\[[0-?]*[ -/]*[@-~]/g, '') + .replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, '') .replace(/\b(?:gh[pousr]_[A-Za-z0-9_]{20,}|github_pat_[A-Za-z0-9_]{20,})\b/g, '***REDACTED***') .replace(/````/g, '\\`\\`\\`\\`') : 'No validation output was captured.'; @@ -376,6 +376,7 @@ jobs: contents: read id-token: write runs-on: ${{ inputs.runs-on }} + environment: ${{ inputs.environment }} if: github.event_name == 'push' && github.ref == 'refs/heads/main' needs: [test] steps: @@ -401,6 +402,7 @@ jobs: known_hosts: ${{ secrets.SSH_KNOWN_HOSTS }} - name: OpenTofu Apply env: + OPENTOFU_ENVIRONMENT_GATED: "true" NAMECHEAP_API_KEY: ${{ secrets.NAMECHEAP_API_KEY }} CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} run: make apply diff --git a/README.md b/README.md index 3913afe..d937b2f 100644 --- a/README.md +++ b/README.md @@ -81,10 +81,10 @@ drift. ## Available Workflows | Workflow | Description | -|----------|-------------| -| `opentofu.yml` | OpenTofu/Terraform CI/CD with PR validation and an apply on every push to `main` | +|---|---| +| `opentofu.yml` | OpenTofu/Terraform CI/CD with PR validation and an environment-gated apply on every push to `main` | -Same-repository PRs run tests and a credentialed plan; fork PRs run tests only. A push to `main` runs tests followed by a fresh apply, which does not reuse the PR plan. +Same-repository PRs run tests and a credentialed plan; fork PRs run tests only. A push to `main` runs tests followed by a fresh apply, which does not reuse the PR plan. The apply job uses the caller's `environment` input (default `production`); repository owners must configure that GitHub Environment with the required protection rules. ## Runners @@ -100,5 +100,6 @@ See [images](https://github.com/makeitworkcloud/images) for container source and 1. Grant `id-token: write` in the caller workflow so GitHub OIDC can authenticate the cloud provider. 2. For AWS roots, ensure the default `aws-role-to-assume` exists (`arn:aws:iam::332355796717:role/github-actions-sops-kms`) or pass another role ARN. 3. For GCP roots, pass both `gcp-workload-identity-provider` and `gcp-service-account`; this selects Google Workload Identity Federation instead of AWS credentials. -4. Create caller workflow in `.github/workflows/`. -5. Ensure repository has required files (e.g., `Makefile` with expected targets). +4. Create the caller workflow in `.github/workflows/`. +5. Create the GitHub Environment selected by `environment` (default `production`) and configure its required reviewers and protection rules before allowing a `main` apply. +6. Ensure the repository has required files (e.g., `Makefile` with expected targets).