Repository: chanzuckerberg/fogg
Pull request #425 (2020-03-13 22:19:52)

[fix] bug in circle ci config generation

Not sure how this worked or how it got broken, but we were generating a
config that circleci would not validate.

### Test Plan
* ci

### References

codecov-io: # [Codecov](https://codecov.io/gh/chanzuckerberg/fogg/pull/425?src=pr&el=h1) Report
> Merging [#425](https://codecov.io/gh/chanzuckerberg/fogg/pull/425?src=pr&el=desc) into [master](https://codecov.io/gh/chanzuckerberg/fogg/commit/cb0956da34522b7f16f8d5d0e279045703d5ed97&el=desc) will **increase** coverage by `0.32%`.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/chanzuckerberg/fogg/pull/425/graphs/tree.svg?width=650&height=150&src=pr&token=57DhtZ7g2A)](https://codecov.io/gh/chanzuckerberg/fogg/pull/425?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #425      +/-   ##
==========================================
+ Coverage   76.85%   77.18%   +0.32%     
==========================================
  Files          22       22              
  Lines        1819     1819              
==========================================
+ Hits         1398     1404       +6     
+ Misses        281      277       -4     
+ Partials      140      138       -2     
```


| [Impacted Files](https://codecov.io/gh/chanzuckerberg/fogg/pull/425?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [config/v2/resolvers.go](https://codecov.io/gh/chanzuckerberg/fogg/pull/425/diff?src=pr&el=tree#diff-Y29uZmlnL3YyL3Jlc29sdmVycy5nbw==) | `94.96% <0.00%> (+0.67%)` | :arrow_up: |
| [apply/apply.go](https://codecov.io/gh/chanzuckerberg/fogg/pull/425/diff?src=pr&el=tree#diff-YXBwbHkvYXBwbHkuZ28=) | `63.36% <0.00%> (+0.73%)` | :arrow_up: |
| [plan/ci.go](https://codecov.io/gh/chanzuckerberg/fogg/pull/425/diff?src=pr&el=tree#diff-cGxhbi9jaS5nbw==) | `93.86% <0.00%> (+1.22%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/chanzuckerberg/fogg/pull/425?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/chanzuckerberg/fogg/pull/425?src=pr&el=footer). Last update [cb0956d...ae9cdf2](https://codecov.io/gh/chanzuckerberg/fogg/pull/425?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/templates/circleci/.circleci/config.yml.tmpl b/templates/circleci/.circleci/config.yml.tmpl
index f3b8d89b..7b422b1e 100644
--- a/templates/circleci/.circleci/config.yml.tmpl
+++ b/templates/circleci/.circleci/config.yml.tmpl
@@ -15,10 +15,10 @@ jobs:
 {{- if not (eq (len $circleCI.SSHFingerprints) 0) }}
       # HACK(el):Switch to key that reads private tf modules after checking out our code
       - add_ssh_keys:
-        fingerprints:
-        {{- range $sshFingerprint := $circleCI.SSHFingerprints }}
-          - {{ $sshFingerprint }}
-        {{- end }}
+          fingerprints:
+          {{- range $sshFingerprint := $circleCI.SSHFingerprints }}
+            - {{ $sshFingerprint }}
+          {{- end }}
 {{- end }}
       - run: python3 -m venv venv
       - run: . venv/bin/activate


Test patch:
diff --git a/apply/golden_file_test.go b/apply/golden_file_test.go
index 90a4dd0a..0809505c 100644
--- a/apply/golden_file_test.go
+++ b/apply/golden_file_test.go
@@ -33,6 +33,7 @@ func TestIntegration(t *testing.T) {
 		{"v2_minimal_valid_yaml", "fogg.yml"},
 		{"v2_no_aws_provider_yaml", "fogg.yml"},
 		{"github_actions", "fogg.yml"},
+		{"circleci", "fogg.yml"},
 	}
 
 	for _, tc := range testCases {
diff --git a/testdata/circleci/.circleci/config.yml b/testdata/circleci/.circleci/config.yml
new file mode 100644
index 00000000..76f12ec3
--- /dev/null
+++ b/testdata/circleci/.circleci/config.yml
@@ -0,0 +1,190 @@
+# Auto-generated by fogg. Do not edit
+# Make improvements in fogg, so that everyone can benefit.
+version: 2
+working_directory: &working_directory ~/fogg
+
+jobs:
+  terraform_0:
+    docker:
+      - image: circleci/python:3.8
+    working_directory: *working_directory
+    steps:
+      - checkout
+      # HACK(el):Switch to key that reads private tf modules after checking out our code
+      - add_ssh_keys:
+          fingerprints:
+            - d2:52:20:b3:82:e2:ee:e8:98:02:05:15:b5:37:f6:2c
+      - run: python3 -m venv venv
+      - run: . venv/bin/activate
+      - run: pip install awscli --upgrade --user
+      - run: aws --version
+      - run: FOGG_VERSION="vundefined+undefined+dirty" make setup
+      - run:
+          command: |
+            .fogg/bin/fogg apply
+            if [ "$(git status --porcelain)" != "" ]; then
+              echo "git tree is dirty after running fogg apply"
+              echo "ensure you run fogg apply on your branch"
+              git status
+              exit 1
+            fi
+      - run:
+          command: |
+            cd terraform/global
+            make  lint
+  terraform_1:
+    docker:
+      - image: circleci/python:3.8
+    working_directory: *working_directory
+    steps:
+      - checkout
+      # HACK(el):Switch to key that reads private tf modules after checking out our code
+      - add_ssh_keys:
+          fingerprints:
+            - d2:52:20:b3:82:e2:ee:e8:98:02:05:15:b5:37:f6:2c
+      - run: python3 -m venv venv
+      - run: . venv/bin/activate
+      - run: pip install awscli --upgrade --user
+      - run: aws --version
+      - run: FOGG_VERSION="vundefined+undefined+dirty" make setup
+      - run:
+          command: |
+            .fogg/bin/fogg apply
+            if [ "$(git status --porcelain)" != "" ]; then
+              echo "git tree is dirty after running fogg apply"
+              echo "ensure you run fogg apply on your branch"
+              git status
+              exit 1
+            fi
+  terraform_2:
+    docker:
+      - image: circleci/python:3.8
+    working_directory: *working_directory
+    steps:
+      - checkout
+      # HACK(el):Switch to key that reads private tf modules after checking out our code
+      - add_ssh_keys:
+          fingerprints:
+            - d2:52:20:b3:82:e2:ee:e8:98:02:05:15:b5:37:f6:2c
+      - run: python3 -m venv venv
+      - run: . venv/bin/activate
+      - run: pip install awscli --upgrade --user
+      - run: aws --version
+      - run: FOGG_VERSION="vundefined+undefined+dirty" make setup
+      - run:
+          command: |
+            .fogg/bin/fogg apply
+            if [ "$(git status --porcelain)" != "" ]; then
+              echo "git tree is dirty after running fogg apply"
+              echo "ensure you run fogg apply on your branch"
+              git status
+              exit 1
+            fi
+  terraform_3:
+    docker:
+      - image: circleci/python:3.8
+    working_directory: *working_directory
+    steps:
+      - checkout
+      # HACK(el):Switch to key that reads private tf modules after checking out our code
+      - add_ssh_keys:
+          fingerprints:
+            - d2:52:20:b3:82:e2:ee:e8:98:02:05:15:b5:37:f6:2c
+      - run: python3 -m venv venv
+      - run: . venv/bin/activate
+      - run: pip install awscli --upgrade --user
+      - run: aws --version
+      - run: FOGG_VERSION="vundefined+undefined+dirty" make setup
+      - run:
+          command: |
+            .fogg/bin/fogg apply
+            if [ "$(git status --porcelain)" != "" ]; then
+              echo "git tree is dirty after running fogg apply"
+              echo "ensure you run fogg apply on your branch"
+              git status
+              exit 1
+            fi
+  terraform_4:
+    docker:
+      - image: circleci/python:3.8
+    working_directory: *working_directory
+    steps:
+      - checkout
+      # HACK(el):Switch to key that reads private tf modules after checking out our code
+      - add_ssh_keys:
+          fingerprints:
+            - d2:52:20:b3:82:e2:ee:e8:98:02:05:15:b5:37:f6:2c
+      - run: python3 -m venv venv
+      - run: . venv/bin/activate
+      - run: pip install awscli --upgrade --user
+      - run: aws --version
+      - run: FOGG_VERSION="vundefined+undefined+dirty" make setup
+      - run:
+          command: |
+            .fogg/bin/fogg apply
+            if [ "$(git status --porcelain)" != "" ]; then
+              echo "git tree is dirty after running fogg apply"
+              echo "ensure you run fogg apply on your branch"
+              git status
+              exit 1
+            fi
+  terraform_5:
+    docker:
+      - image: circleci/python:3.8
+    working_directory: *working_directory
+    steps:
+      - checkout
+      # HACK(el):Switch to key that reads private tf modules after checking out our code
+      - add_ssh_keys:
+          fingerprints:
+            - d2:52:20:b3:82:e2:ee:e8:98:02:05:15:b5:37:f6:2c
+      - run: python3 -m venv venv
+      - run: . venv/bin/activate
+      - run: pip install awscli --upgrade --user
+      - run: aws --version
+      - run: FOGG_VERSION="vundefined+undefined+dirty" make setup
+      - run:
+          command: |
+            .fogg/bin/fogg apply
+            if [ "$(git status --porcelain)" != "" ]; then
+              echo "git tree is dirty after running fogg apply"
+              echo "ensure you run fogg apply on your branch"
+              git status
+              exit 1
+            fi
+  terraform_6:
+    docker:
+      - image: circleci/python:3.8
+    working_directory: *working_directory
+    steps:
+      - checkout
+      # HACK(el):Switch to key that reads private tf modules after checking out our code
+      - add_ssh_keys:
+          fingerprints:
+            - d2:52:20:b3:82:e2:ee:e8:98:02:05:15:b5:37:f6:2c
+      - run: python3 -m venv venv
+      - run: . venv/bin/activate
+      - run: pip install awscli --upgrade --user
+      - run: aws --version
+      - run: FOGG_VERSION="vundefined+undefined+dirty" make setup
+      - run:
+          command: |
+            .fogg/bin/fogg apply
+            if [ "$(git status --porcelain)" != "" ]; then
+              echo "git tree is dirty after running fogg apply"
+              echo "ensure you run fogg apply on your branch"
+              git status
+              exit 1
+            fi
+
+workflows:
+  version: 2
+  terraform:
+    jobs:
+      - terraform_0
+      - terraform_1
+      - terraform_2
+      - terraform_3
+      - terraform_4
+      - terraform_5
+      - terraform_6
diff --git a/testdata/circleci/.fogg-version b/testdata/circleci/.fogg-version
new file mode 100644
index 00000000..2d5cfc01
--- /dev/null
+++ b/testdata/circleci/.fogg-version
@@ -0,0 +1,1 @@
+undefined+undefined+dirty
\ No newline at end of file
diff --git a/testdata/circleci/.gitattributes b/testdata/circleci/.gitattributes
new file mode 100644
index 00000000..2f819c43
--- /dev/null
+++ b/testdata/circleci/.gitattributes
@@ -0,0 +1,4 @@
+fogg.tf linguist-generated
+Makefile linguist-generated
+.travis.yml linguist-generated
+.circleci/config.yml linguist-generated
diff --git a/testdata/circleci/.gitignore b/testdata/circleci/.gitignore
new file mode 100644
index 00000000..69565ae8
--- /dev/null
+++ b/testdata/circleci/.gitignore
@@ -0,0 +1,34 @@
+# Auto-generated by fogg. Do not edit
+# Make improvements in fogg, so that everyone can benefit.
+
+# Compiled files
+*.tfstate
+*.tfstate.*.backup
+*.tfstate.backup
+*tfvars
+
+# Module directory
+.terraform/
+
+# Pycharm folder
+.idea
+
+# Editor Swap Files
+*.swp
+*.swo
+*.swn
+*.swm
+*.swl
+*.swk
+
+.fogg
+/terraform.d
+
+.DS_Store
+.vscode
+.envrc
+
+buildevents.plan
+check-plan.output
+
+venv
diff --git a/testdata/circleci/.terraform.d/plugin-cache/.gitignore b/testdata/circleci/.terraform.d/plugin-cache/.gitignore
new file mode 100644
index 00000000..504d4d91
--- /dev/null
+++ b/testdata/circleci/.terraform.d/plugin-cache/.gitignore
@@ -0,0 +1,5 @@
+# Auto-generated by fogg. Do not edit
+# Make improvements in fogg, so that everyone can benefit.
+
+*
+!.gitignore
diff --git a/testdata/circleci/Makefile b/testdata/circleci/Makefile
new file mode 100644
index 00000000..daf62166
--- /dev/null
+++ b/testdata/circleci/Makefile
@@ -0,0 +1,146 @@
+# Auto-generated by fogg. Do not edit
+# Make improvements in fogg, so that everyone can benefit.
+
+include scripts/common.mk
+
+ENVS=
+MODULES=
+ACCOUNTS=
+
+all: check
+
+setup: ## set up working directory by installing dependencies
+	curl -s https://raw.githubusercontent.com/chanzuckerberg/fogg/master/download.sh | bash -s -- -b .fogg/bin vundefined+undefined+dirty
+	.fogg/bin/fogg setup
+.PHONY: setup
+
+lint: lint-terraform ## lint the code in this repo
+.PHONY: lint
+
+lint-terraform: lint-accounts lint-envs lint-modules ## lint the terraform code in this repo
+.PHONY: lint-terraform
+
+lint-accounts:  ## lint the terrarform code in terraform/accounts
+	@for account in $(ACCOUNTS); do \
+		$(MAKE) -C terraform/accounts/$$account lint || exit $$?; \
+	done
+.PHONY: lint-accounts
+
+lint-envs: ## lint the terraform code in terraform/envs
+	@for env in $(ENVS); do \
+		$(MAKE) -C terraform/envs/$$env lint || exit $$?; \
+	done; \
+	$(MAKE) -C terraform/global lint || exit $$?
+.PHONY: lint-envs
+
+lint-modules: ## lint the terraform code in terraform/modules
+	@for module in $(MODULES); do \
+		$(MAKE) -C terraform/modules/$$module lint || exit $$?; \
+	done
+.PHONY: lint-modules
+
+fmt: fmt-accounts fmt-envs fmt-global fmt-modules ## format code in this repo
+.PHONY: fmt
+
+fmt-accounts: ## format code in terraform/accounts
+	@for account in $(ACCOUNTS); do \
+		$(MAKE) -C terraform/accounts/$$account fmt || exit $$?; \
+	done
+.PHONY: fmt-accounts
+
+fmt-envs: ## format the code in teraform/envs
+	@for env in $(ENVS); do \
+		$(MAKE) -C terraform/envs/$$env fmt || exit $$?; \
+	done
+.PHONY: fmt-envs
+
+fmt-global: ## format the code in terraform/global
+	$(MAKE) -C terraform/global fmt || exit $$?
+.PHONY: fmt-global
+
+fmt-modules:  ## format the code in terraform/modules
+	@for module in $(MODULES); do \
+		$(MAKE) -C terraform/modules/$$module fmt || exit $$?; \
+	done
+.PHONY: fmt-modules
+
+docs: docs-envs docs-global docs-modules ## update docs
+.PHONY: docs
+
+docs-accounts: ## update docs in terraform/accounts
+	@for account in $(ACCOUNTS); do \
+		$(MAKE) -C terraform/accounts/$$account docs || exit $$?; \
+	done
+.PHONY: docs-accounts
+
+docs-envs: ## update the docs in terraform/envs
+	@for env in $(ENVS); do \
+		$(MAKE) -C terraform/envs/$$env docs || exit $$?; \
+	done
+.PHONY: docs-envs
+
+docs-global: ## update the docs in terraform/global
+	$(MAKE) -C terraform/global docs || exit $$?;
+.PHONY: docs-global
+
+docs-modules: ## update the docs in terraform/modules
+	@for module in $(MODULES); do \
+		$(MAKE) -C terraform/modules/$$module docs || exit $$?; \
+	done
+.PHONY: docs-modules
+
+test:
+.PHONY: test
+
+check: check-plan check-docs ## check all code in the repo
+.PHONY: check
+
+check-plan: check-plan-accounts check-plan-envs check-plan-global ## check terraform plans across all components
+.PHONY: check-plan
+
+check-plan-accounts: ## check terraform plans in terraform/accounts
+	@for account in $(ACCOUNTS); do \
+		$(MAKE) -C terraform/accounts/$$account check-plan || exit $$?; \
+	done
+.PHONY: check-plan-accounts
+
+check-plan-envs: ## check terraform plans in terraform/envs
+	@for env in $(ENVS); do \
+		$(MAKE) -C terraform/envs/$$env check-plan || exit $$?; \
+	done
+.PHONY: check-plan-envs
+
+check-plan-global: ## check terraform plans in terraform/global
+	$(MAKE) -C terraform/global check-plan || exit $$?
+.PHONY: check-plan-global
+
+check-docs: ## check terraform docs
+	@for mod in $(MODULES); do \
+		$(MAKE) -C terraform/modules/$$module check-docs || exit $$?; \
+	done
+.PHONY: check-docs
+
+clean: clean-accounts clean-envs clean-global ## clean the repo
+.PHONY: clean
+
+clean-accounts: ## clean in terraform/accounts
+	@for account in $(ACCOUNTS); do \
+		$(MAKE) -C terraform/accounts/$$account clean || exit $$?; \
+	done
+.PHONY: clean-accounts
+
+clean-envs: ## clean in terraform/envs
+	@for env in $(ENVS); do \
+		$(MAKE) -C terraform/envs/$$env clean || exit $$?; \
+	done
+.PHONY: clean-envs
+
+clean-global: ## clean in terraform/global
+	$(MAKE) -C terraform/global clean || exit $$?
+.PHONY: clean-global
+
+
+help: ## display help for this makefile
+	@fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
+.PHONY: help
+
diff --git a/testdata/circleci/README.md b/testdata/circleci/README.md
new file mode 100644
index 00000000..e69de29b
diff --git a/testdata/circleci/fogg.yml b/testdata/circleci/fogg.yml
new file mode 100644
index 00000000..1f792cbc
--- /dev/null
+++ b/testdata/circleci/fogg.yml
@@ -0,0 +1,19 @@
+defaults:
+  backend:
+    bucket: bucket
+    profile: foo
+    region: region
+  owner: foo@example.com
+  project: foo
+  providers: {}
+  terraform_version: 1.1.1
+  tools:
+    circle_ci:
+      aws_iam_role_name: infraci
+      command: lint
+      enabled: true
+      test_buckets: 7
+      ssh_key_fingerprints:
+        - d2:52:20:b3:82:e2:ee:e8:98:02:05:15:b5:37:f6:2c
+
+version: 2
diff --git a/testdata/circleci/scripts/common.mk b/testdata/circleci/scripts/common.mk
new file mode 100644
index 00000000..7ce87601
--- /dev/null
+++ b/testdata/circleci/scripts/common.mk
@@ -0,0 +1,26 @@
+# Auto-generated by fogg. Do not edit
+# Make improvements in fogg, so that everyone can benefit.
+
+SHELL=/bin/bash -o pipefail
+TF_VARS := $(patsubst %,-e%,$(filter TF_VAR_%,$(.VARIABLES)))
+REPO_ROOT := $(shell git rev-parse --show-toplevel)
+REPO_RELATIVE_PATH := $(shell git rev-parse --show-prefix)
+AUTO_APPROVE := false
+REFRESH_ENABLED ?= true # Should terraform refresh during plan/apply
+
+TFENV_DIR ?= $(REPO_ROOT)/.fogg/tfenv
+export PATH :=$(TFENV_DIR)/libexec:$(TFENV_DIR)/versions/$(TERRAFORM_VERSION)/:$(REPO_ROOT)/.fogg/bin:$(PATH)
+export TF_PLUGIN_CACHE_DIR=$(REPO_ROOT)/.terraform.d/plugin-cache
+export TF_IN_AUTOMATION=1
+terraform_command ?= $(TFENV_DIR)/versions/$(TERRAFORM_VERSION)/terraform
+MODE ?= local
+
+tfenv: ## install the tfenv tool
+	@if [ ! -d ${TFENV_DIR} ]; then \
+		git clone -q https://github.com/chanzuckerberg/tfenv.git $(TFENV_DIR); \
+	fi
+.PHONY: tfenv
+
+terraform: tfenv ## ensure that the proper version of terraform is installed
+	@${TFENV_DIR}/bin/tfenv install $(TERRAFORM_VERSION)
+.PHONY: terraform
diff --git a/testdata/circleci/scripts/component.mk b/testdata/circleci/scripts/component.mk
new file mode 100644
index 00000000..c7b8d430
--- /dev/null
+++ b/testdata/circleci/scripts/component.mk
@@ -0,0 +1,123 @@
+# Auto-generated by fogg. Do not edit
+# Make improvements in fogg, so that everyone can benefit.
+
+SELF_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
+CHECK_PLANFILE_PATH ?= check-plan.output
+
+include $(SELF_DIR)/common.mk
+
+all:
+.PHONY: all
+
+setup: ## set up local dependencies for this repo
+	$(MAKE) -C $(REPO_ROOT) setup
+.PHONY: setup
+
+check: lint check-plan ## run all checks for this component
+.PHONY: check
+
+fmt: terraform ## format code in this component
+	$(terraform_command) fmt $(TF_ARGS)
+.PHONY: fmt
+
+lint: lint-terraform-fmt lint-tflint ## run all linters for this component
+.PHONY: lint
+
+lint-tflint: ## run the tflint linter for this component
+	@printf "tflint: "
+ifeq ($(TFLINT_ENABLED),1)
+	@tflint || exit $$?;
+else
+	@echo "disabled"
+endif
+.PHONY: lint-tflint
+
+lint-terraform-fmt: terraform ## run `terraform fmt` in check mode
+	@printf "fmt check: "
+	@for f in $(TF); do \
+		printf . \
+		$(terraform_command) fmt $(TF_ARGS) --check=true --diff=true $$f || exit $$? ; \
+	done
+	@echo
+.PHONY: lint-terraform-fmt
+
+check-auth: check-auth-aws check-auth-heroku ## check that authentication is properly set up for this component
+.PHONY: check-auth
+
+check-auth-aws:
+	@for p in $(AWS_BACKEND_PROFILE) $(AWS_PROVIDER_PROFILE); do \
+		aws --profile $$p sts get-caller-identity > /dev/null || (echo "AWS AUTH error. This component is configured to use a profile named '$$p'. Please add one to your ~/.aws/config" && exit -1); \
+	done
+.PHONY: check-auth-aws
+
+check-auth-heroku:
+ifeq ($(HEROKU_PROVIDER),1)
+	@echo "Checking heroku auth..."
+	@if command heroku >/dev/null; then \
+		heroku auth:whoami || (echo "Not authenticated to heroku. For SSO accounts, run 'heroku login', for non-sso accounts set HEROKU_EMAIL and HEROKU_API_KEY" && exit -1); \
+	else \
+		echo "Heroku CLI not installed, can't check auth."; \
+	fi
+endif
+.PHONY: check-auth-heroku
+
+refresh:
+	@$(terraform_command) refresh $(TF_ARGS)
+	@date +%s > .terraform/refreshed_at
+.PHONY: refresh
+
+refresh-cached:
+	@last_refresh=`cat .terraform/refreshed_at || echo '0'`; \
+	current_time=`date +%s`; \
+	if (( current_time - last_refresh > 600 )); then \
+		echo "It has been awhile since the last refresh. It is time."; \
+		$(MAKE) refresh; \
+	else \
+		echo "Not time to refresh yet."; \
+	fi;
+.PHONY: refresh-cached
+
+plan: check-auth init fmt refresh-cached ## run a terraform plan
+	@$(terraform_command) plan $(TF_ARGS) -refresh=false -input=false
+.PHONY: plan
+
+apply: check-auth init refresh ## run a terraform apply
+	@$(terraform_command) apply $(TF_ARGS) -refresh=false -auto-approve=$(AUTO_APPROVE)
+.PHONY: apply
+
+docs:
+	echo
+.PHONY: docs
+
+clean: ## clean modules and plugins for this component
+	-rm -rfv .terraform/modules
+	-rm -rfv .terraform/plugins
+.PHONY: clean
+
+test:
+.PHONY: test
+
+init: terraform check-auth ## run terraform init for this component
+	@$(terraform_command) init -input=false
+.PHONY: init
+
+check-plan: check-auth init refresh-cached ## run a terraform plan and check that it does not fail
+	@$(terraform_command) plan $(TF_ARGS) -detailed-exitcode -lock=false -out=$(CHECK_PLANFILE_PATH) ; \
+	ERR=$$?; \
+	if [ $$ERR -eq 0 ] ; then \
+		echo "Success"; \
+	elif [ $$ERR -eq 1 ] ; then \
+		echo "Error in plan execution."; \
+		exit 1; \
+	elif [ $$ERR -eq 2 ] ; then \
+		echo "Diff";  \
+	fi ; \
+	if [ -n "$(BUILDEVENT_FILE)" ]; then \
+		fogg exp entropy -f $(CHECK_PLANFILE_PATH) -o $(BUILDEVENT_FILE) ; \
+	fi
+	rm $(CHECK_PLANFILE_PATH)
+.PHONY: check-plan
+
+run: check-auth ## run an arbitrary terraform command, CMD. ex `make run CMD='show'`
+	@$(terraform_command) $(CMD)
+.PHONY: run
diff --git a/testdata/circleci/scripts/failed_output_only b/testdata/circleci/scripts/failed_output_only
new file mode 100644
index 00000000..a21d3dab
--- /dev/null
+++ b/testdata/circleci/scripts/failed_output_only
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+t=`mktemp` && \
+exec $@  2>&1 > $t || \
+(
+    a=$?;
+    echo $a;
+    cat $t;
+    rm $t;
+    exit $a
+)
+
+
diff --git a/testdata/circleci/scripts/module.mk b/testdata/circleci/scripts/module.mk
new file mode 100644
index 00000000..d8353c0d
--- /dev/null
+++ b/testdata/circleci/scripts/module.mk
@@ -0,0 +1,49 @@
+# Auto-generated by fogg. Do not edit
+# Make improvements in fogg, so that everyone can benefit.
+
+SELF_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
+
+include $(SELF_DIR)/common.mk
+
+all: fmt lint doc
+.PHONY: all
+
+fmt: terraform ## run terraform fmt on this module
+	@printf "fmt: ";
+	@for f in $(TF); do printf .; $(terraform_command) fmt $(TF_ARGS) $$f; done
+	@echo
+.PHONY: fmt
+
+check: lint check-docs ## run all checks on this module
+.PHONY: check
+
+lint: lint-tf ## run all linters on this module
+.PHONY: lint
+
+lint-tf: terraform ## run terraform linters on this module
+	@printf "fmt: ";
+	@for f in $(TF); do \
+	  printf .; $(terraform_command) fmt $(TF_ARGS) --check=true --diff=true || exit $$? ; \
+	done
+	@echo
+.PHONY: lint-tf
+
+readme: ## update this module's README.md 
+	bash $(REPO_ROOT)/scripts/update-readme.sh update
+.PHONY: readme
+
+docs: readme ## update all docs for this module
+.PHONY: docs
+
+check-docs: ## check that this module's docs are up-to-date
+	@bash $(REPO_ROOT)/scripts/update-readme.sh check; \
+	if [ ! $$? -eq 0 ];  then \
+		echo "Docs are out of date, run \`make docs\`"; \
+	fi
+.PHONY: check-docs
+
+clean:
+.PHONY: clean
+
+test:
+.PHONY: test
diff --git a/testdata/circleci/scripts/update-readme.sh b/testdata/circleci/scripts/update-readme.sh
new file mode 100644
index 00000000..abe8fb47
--- /dev/null
+++ b/testdata/circleci/scripts/update-readme.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# Auto-generated by fogg. Do not edit
+# Make improvements in fogg, so that everyone can benefit.
+
+# I would have written this directly in the Makefile, but that was difficult.
+
+CMD="$1"
+
+TMP=`mktemp`
+TMP2=`mktemp`
+terraform-docs md . > "$TMP"
+sed '/^<!-- START -->$/,/<!-- END -->/{//!d;}' README.md | sed "/^<!-- START -->$/r $TMP" > $TMP2
+
+case "$CMD" in
+    update)
+        mv $TMP2 README.md
+    ;;
+    check)
+        diff $TMP2 README.md >/dev/null
+    ;;
+esac
+
+exit $?
diff --git a/testdata/circleci/terraform/global/Makefile b/testdata/circleci/terraform/global/Makefile
new file mode 100644
index 00000000..e4bfd01a
--- /dev/null
+++ b/testdata/circleci/terraform/global/Makefile
@@ -0,0 +1,18 @@
+# Auto-generated by fogg. Do not edit
+# Make improvements in fogg, so that everyone can benefit.
+
+export TERRAFORM_VERSION := 1.1.1
+export TFLINT_ENABLED := 0
+export TF_PLUGIN_CACHE_DIR := ../..//.terraform.d/plugin-cache
+export AWS_BACKEND_PROFILE := foo
+
+
+
+
+include ../..//scripts/component.mk
+
+
+help: ## display help for this makefile
+	@fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
+.PHONY: help
+
diff --git a/testdata/circleci/terraform/global/README.md b/testdata/circleci/terraform/global/README.md
new file mode 100644
index 00000000..e69de29b
diff --git a/testdata/circleci/terraform/global/fogg.tf b/testdata/circleci/terraform/global/fogg.tf
new file mode 100644
index 00000000..8f3256c0
--- /dev/null
+++ b/testdata/circleci/terraform/global/fogg.tf
@@ -0,0 +1,96 @@
+# Auto-generated by fogg. Do not edit
+# Make improvements in fogg, so that everyone can benefit.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+terraform {
+  required_version = "~>1.1.1"
+
+  backend "s3" {
+    bucket = "bucket"
+
+
+    key = "terraform/foo/global.tfstate"
+
+    encrypt = true
+    region  = "region"
+    profile = "foo"
+  }
+}
+
+variable "env" {
+  type    = string
+  default = ""
+}
+
+variable "project" {
+  type    = string
+  default = "foo"
+}
+
+
+
+variable "component" {
+  type    = string
+  default = "global"
+}
+
+
+
+variable "owner" {
+  type    = string
+  default = "foo@example.com"
+}
+
+variable "tags" {
+  type = map(string)
+  default = {
+    project   = "foo"
+    env       = ""
+    service   = "global"
+    owner     = "foo@example.com"
+    managedBy = "terraform"
+  }
+}
+
+
+
+
+
+
+provider random {
+  version = "~> 2.2"
+}
+
+provider template {
+  version = "~> 2.1"
+}
+
+provider archive {
+  version = "~> 1.3"
+}
+
+provider null {
+  version = "~> 2.1"
+}
+
+provider local {
+  version = "~> 1.4"
+}
+
+provider tls {
+  version = "~> 2.1"
+}
diff --git a/testdata/circleci/terraform/global/main.tf b/testdata/circleci/terraform/global/main.tf
new file mode 100644
index 00000000..e69de29b
diff --git a/testdata/circleci/terraform/global/outputs.tf b/testdata/circleci/terraform/global/outputs.tf
new file mode 100644
index 00000000..e69de29b
diff --git a/testdata/circleci/terraform/global/terraform.d b/testdata/circleci/terraform/global/terraform.d
new file mode 120000
index 00000000..a1f7d0d3
--- /dev/null
+++ b/testdata/circleci/terraform/global/terraform.d
@@ -0,0 +1,1 @@
+../../terraform.d
\ No newline at end of file