Skip to content

Commit

Permalink
feat: Terraform GitHub Actions (#10)
Browse files Browse the repository at this point in the history
* feat: Terraform GitHub Actions

Verified these on another repo. Everything works as previously discussed. These are composite actions so they run quickly. They expect that terraform is already installed (which we'd do in previous steps via asdf). Each sub command will be its own step which ultimately will write its own PR comment.
  • Loading branch information
Aaron Batilo authored Aug 27, 2020
1 parent 469b0bc commit fd66f4d
Show file tree
Hide file tree
Showing 9 changed files with 306 additions and 1 deletion.
2 changes: 1 addition & 1 deletion helmfile-actions/apply.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ function helmfileApply {
if [ ${exitCode} -eq 0 ]; then
echo "Successfully ran helmfile apply command."
else
echo "Error: Failed to run helmfile diff"
echo "Error: Failed to run helmfile apply"
fi

echo "${output}"
Expand Down
35 changes: 35 additions & 0 deletions terraform-actions/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# terraform-actions

There is a single `./main.sh` file that delegates down to other scripts based on the `command` that you pass in.

## Inputs
```yaml
inputs:
command:
description: "The terraform command to run"
required: true
working_directory:
description: "The directory to run all the commands in"
required: false
default: "."
```
## Examples
Example usage of `terraform plan`:
```yaml
- uses: iStreamPlanet/github-actions/terraform-actions@main
if: github.event_name == 'pull_request'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
command: plan
working_directory: terraform/
```

Example usage of `terraform apply`:
```yaml
- uses: iStreamPlanet/github-actions/terraform-actions@main
with:
command: apply
working_directory: terraform/
```
19 changes: 19 additions & 0 deletions terraform-actions/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: "terraform action"
description: "A composite action wrapper around terraform"
inputs:
command:
description: "The terraform command to run"
required: true
working_directory:
description: "The directory to run all the commands in"
required: false
default: "."
outputs: {}
runs:
using: "composite"
steps:
- name: terraform
shell: bash
id: main
working-directory: ${{ inputs.working_directory }}
run: ${{ github.action_path }}/main.sh "${{ inputs.command }}" "${{ inputs.working_directory }}"
16 changes: 16 additions & 0 deletions terraform-actions/apply.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash

function terraformApply {
output=$(terraform apply -no-color -auto-approve -input=false ${*} 2>&1)
exitCode=$?

if [ ${exitCode} -eq 0 ]; then
echo "Successfully ran terraform apply command."
else
echo "Error: Failed to run terraform apply"
fi

echo "${output}"
echo
exit ${exitCode}
}
41 changes: 41 additions & 0 deletions terraform-actions/fmt.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/bash

function terraformFmt {
output=$(terraform fmt -check=true -write=false -diff -recursive -no-color ${*} 2>&1)
exitCode=$?
commentStatus="Failed"

if [ ${exitCode} -eq 0 ]; then
echo "Successfully ran terraform fmt command."
echo "${output}"
echo
exit ${exitCode}
fi

echo "Error: terraform fmt found changes"
echo "${output}"
echo

if [ "$GITHUB_EVENT_NAME" == "pull_request" ] && [ "${commentStatus}" == "Failed" ]; then
commentWrapper="#### \`terraform fmt\` ${commentStatus} for \`${workingDir}\`
<details><summary>Show Output</summary>
\`\`\`diff
${output}
\`\`\`
</details>
"

payload=$(echo "${commentWrapper}" | jq -R --slurp '{body: .}')
commentsURL=$(cat ${GITHUB_EVENT_PATH} | jq -r .pull_request.comments_url)
echo "${payload}" | curl -s -S -H "Authorization: token ${GITHUB_TOKEN}" --header "Content-Type: application/json" --data @- "${commentsURL}" > /dev/null
fi

# https://github.community/t5/GitHub-Actions/set-output-Truncates-Multiline-Strings/m-p/38372/highlight/true#M3322
output="${output//'%'/'%25'}"
output="${output//$'\n'/'%0A'}"
output="${output//$'\r'/'%0D'}"
echo "::set-output name=output::${output}"
exit ${exitCode}
}
41 changes: 41 additions & 0 deletions terraform-actions/init.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/bash

function terraformInit {
output=$(terraform init -no-color -input=false ${*} 2>&1)
exitCode=$?
commentStatus="Failed"

if [ ${exitCode} -eq 0 ]; then
echo "Successfully ran terraform init command."
echo "${output}"
echo
exit ${exitCode}
fi

echo "Error: Failed to run terraform init"
echo "${output}"
echo

if [ "$GITHUB_EVENT_NAME" == "pull_request" ] && [ "${commentStatus}" == "Failed" ]; then
commentWrapper="#### \`terraform init\` ${commentStatus} for \`${workingDir}\`
<details><summary>Show Output</summary>
\`\`\`
${output}
\`\`\`
</details>
"

payload=$(echo "${commentWrapper}" | jq -R --slurp '{body: .}')
commentsURL=$(cat ${GITHUB_EVENT_PATH} | jq -r .pull_request.comments_url)
echo "${payload}" | curl -s -S -H "Authorization: token ${GITHUB_TOKEN}" --header "Content-Type: application/json" --data @- "${commentsURL}" > /dev/null
fi

# https://github.community/t5/GitHub-Actions/set-output-Truncates-Multiline-Strings/m-p/38372/highlight/true#M3322
output="${output//'%'/'%25'}"
output="${output//$'\n'/'%0A'}"
output="${output//$'\r'/'%0D'}"
echo "::set-output name=output::${output}"
exit ${exitCode}
}
41 changes: 41 additions & 0 deletions terraform-actions/main.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/bash

if [ "$#" -ne 2 ]; then
echo 'Usage: ./main.sh $command $working_directory'
exit 1
fi

workingDir="$2"
function main {
command="$1"
scriptDir=$(dirname ${0})
source ${scriptDir}/apply.sh
source ${scriptDir}/fmt.sh
source ${scriptDir}/init.sh
source ${scriptDir}/plan.sh
source ${scriptDir}/validate.sh

case "${command}" in
apply)
terraformApply
;;
fmt)
terraformFmt
;;
init)
terraformInit
;;
plan)
terraformPlan
;;
validate)
terraformValidate
;;
*)
echo "Error: Unrecognized command ${command}"
exit 1
;;
esac
}

main "$1"
71 changes: 71 additions & 0 deletions terraform-actions/plan.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/bin/bash

function terraformPlan {
output=$(terraform plan -no-color -detailed-exitcode ${*} 2>&1)
exitCode=$?
hasChanges=false
commentStatus="Failed"

if [ ${exitCode} -eq 0 ]; then
echo "Successfully ran terraform plan command. No changes were found"
echo "${output}"
echo
echo "::set-output name=has-changes::${hasChanges}"
exit ${exitCode}
fi

if [ ${exitCode} -eq 2 ]; then
# This should still be a success
exitCode=0
hasChanges=true
commentStatus="Success"

echo "Successfully ran terraform plan command. Changes were found"
echo "${output}"
echo

if echo "${output}" | egrep '^-{72}$' &> /dev/null; then
# Reformat the 72 dashes that terraform uses as a horizontal divider in
# output. This confuses the GitHub Flavored Markdown `diff` type because
# it thinks the dashes are all removal diffs.
output=$(echo "${output}" | sed -n -r '/-{72}/,/-{72}/{ /-{72}/d; p }')
fi
# Unindent the actual diff in the output of the plan. The plan text is
# indented once which breaks GHFM from highlighting the diff correctly.
output=$(echo "${output}" | sed -r -e 's/^ \+/\+/g' | sed -r -e 's/^ ~/~/g' | sed -r -e 's/^ -/-/g')

# If output is longer than max length (65536 characters), keep last part
output=$(echo "${output}" | tail -c 65000 )
fi

if [ ${exitCode} -ne 0 ]; then
echo "Error: Failed to run terraform plan"
echo "${output}"
echo
fi

if [ "$GITHUB_EVENT_NAME" == "pull_request" ] && ([ "${hasChanges}" == "true" ] || [ "${commentStatus}" == "Failed" ]); then
commentWrapper="#### \`terraform plan\` ${commentStatus} for \`${workingDir}\`
<details><summary>Show Output</summary>
\`\`\`diff
${output}
\`\`\`
</details>
"

payload=$(echo "${commentWrapper}" | jq -R --slurp '{body: .}')
commentsURL=$(cat ${GITHUB_EVENT_PATH} | jq -r .pull_request.comments_url)
echo "${payload}" | curl -s -S -H "Authorization: token ${GITHUB_TOKEN}" --header "Content-Type: application/json" --data @- "${commentsURL}" > /dev/null
fi

echo "::set-output name=has-changes::${hasChanges}"

# https://github.community/t5/GitHub-Actions/set-output-Truncates-Multiline-Strings/m-p/38372/highlight/true#M3322
output="${output//'%'/'%25'}"
output="${output//$'\n'/'%0A'}"
output="${output//$'\r'/'%0D'}"
echo "::set-output name=output::${output}"
exit ${exitCode}
}
41 changes: 41 additions & 0 deletions terraform-actions/validate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/bash

function terraformValidate {
output=$(terraform validate -no-color ${*} 2>&1)
exitCode=$?
commentStatus="Failed"

if [ ${exitCode} -eq 0 ]; then
echo "Successfully ran terraform validate command."
echo "${output}"
echo
exit ${exitCode}
fi

echo "Error: Failed to run terraform validate"
echo "${output}"
echo

if [ "$GITHUB_EVENT_NAME" == "pull_request" ] && [ "${commentStatus}" == "Failed" ]; then
commentWrapper="#### \`terraform validate\` ${commentStatus} for \`${workingDir}\`
<details><summary>Show Output</summary>
\`\`\`
${output}
\`\`\`
</details>
"

payload=$(echo "${commentWrapper}" | jq -R --slurp '{body: .}')
commentsURL=$(cat ${GITHUB_EVENT_PATH} | jq -r .pull_request.comments_url)
echo "${payload}" | curl -s -S -H "Authorization: token ${GITHUB_TOKEN}" --header "Content-Type: application/json" --data @- "${commentsURL}" > /dev/null
fi

# https://github.community/t5/GitHub-Actions/set-output-Truncates-Multiline-Strings/m-p/38372/highlight/true#M3322
output="${output//'%'/'%25'}"
output="${output//$'\n'/'%0A'}"
output="${output//$'\r'/'%0D'}"
echo "::set-output name=output::${output}"
exit ${exitCode}
}

0 comments on commit fd66f4d

Please sign in to comment.