Skip to content

Commit

Permalink
Update formatting (#4)
Browse files Browse the repository at this point in the history
* Leave fmt diff unchanged

* Add diff change highlighting and update readme

* Bump to Terraform v0.14.5, used for fmt output

* Make sed consistent

* Drop all lines after the dashes

* Quit when the line is found

* Reverse rule and revert other changes

* Final fix?

* Can't use Q, try different

* sed version problem?

* Revert

* Refreshing state... not always there

* Force rebuild

* Cleanup mistake

* Format readme

* More readme fixing

* Update CHANGELOG.md
  • Loading branch information
robburger authored Jan 26, 2021
1 parent 900d61a commit b7622fb
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 71 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Changelog

## v1.2.0

- Bump to Terraform v0.14.5 internally (only affects `fmt`)
- Change to leave `fmt` output as-is
- Add colourisation to `plan` diffs where there are changes (on by default, controlled with `HIGHLIGHT_CHANGES` environment variable)
- Update readme

## v1.1.0

- Adds better parsing for Terraform v0.14

## v1.0.0

- Initial release.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM hashicorp/terraform:0.12.28
FROM hashicorp/terraform:0.14.5

LABEL repository="https://github.com/robburger/terraform-pr-commenter" \
homepage="https://github.com/robburger/terraform-pr-commenter" \
Expand Down
141 changes: 74 additions & 67 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
# Terraform PR Commenter

Adds opinionated comments to PR's based on Terraform `fmt`, `init` and `plan` outputs.

## Summary

This Docker-based GitHub Action is designed to work in tandem with [hashicorp/setup-terraform](https://github.com/hashicorp/setup-terraform) with the wrapper enabled, taking the output from a `fmt`, `init` or `plan`, formatting it and adding it to a pull request. Any previous comments from this Action are removed to keep the PR timeline clean.

Support (for now) is [limited to Linux](https://help.github.com/en/actions/creating-actions/about-actions#types-of-actions) as Docker-based GitHub Actions can only be used on Linux runners.

## Usage

This action can only be run after a Terraform `fmt`, `init`, or `plan` has completed, and the output has been captured. Terraform rarely writes to `stdout` and `stderr` in the same action, so we concatenate the `commenter_input`:

```yaml
- uses: robburger/terraform-pr-commenter@v1
env:
Expand All @@ -19,20 +23,24 @@ This action can only be run after a Terraform `fmt`, `init`, or `plan` has compl
```
### Inputs
| Name | Requirement | Description |
| -------------------- | ----------- | ----------- |
| `commenter_type` | _required_ | The type of comment. Options: [`fmt`, `init`, `plan`] |
| `commenter_input` | _required_ | The comment to post from a previous step output. |
| `commenter_exitcode` | _required_ | The exit code from a previous step output. |
| Name | Requirement | Description |
| -------------------- | ----------- | ----------------------------------------------------- |
| `commenter_type` | _required_ | The type of comment. Options: [`fmt`, `init`, `plan`] |
| `commenter_input` | _required_ | The comment to post from a previous step output. |
| `commenter_exitcode` | _required_ | The exit code from a previous step output. |

### Environment Variables
| Name | Requirement | Description |
| ------------------------ | ----------- | ----------- |
| `GITHUB_TOKEN` | _required_ | Used to execute API calls. The `${{ secrets.GITHUB_TOKEN }}` already has permissions, but if you're using your own token, ensure it has the `repo` scope. |
| `TF_WORKSPACE` | _optional_ | Default: `default`. This is used to separate multiple comments on a pull request in a matrix run. |
| `EXPAND_SUMMARY_DETAILS` | _optional_ | Default: `true`. This controls whether the comment output is collapsed or not. |

Both of these environment variables can be set at `job` or `step` level. For example, you could collapse all outputs but expand on a `plan`:
| Name | Requirement | Description |
| ------------------------ | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GITHUB_TOKEN` | _required_ | Used to execute API calls. The `${{ secrets.GITHUB_TOKEN }}` already has permissions, but if you're using your own token, ensure it has the `repo` scope. |
| `TF_WORKSPACE` | _optional_ | Default: `default`. This is used to separate multiple comments on a pull request in a matrix run. |
| `EXPAND_SUMMARY_DETAILS` | _optional_ | Default: `true`. This controls whether the comment output is collapsed or not. |
| `HIGHLIGHT_CHANGES` | _optional_ | Default: `true`. This switches `~` to `!` in `plan` diffs to highlight Terraform changes in orange. Set to `false` to disable. |

All of these environment variables can be set at `job` or `step` level. For example, you could collapse all outputs but expand on a `plan`:

```yaml
jobs:
terraform:
Expand All @@ -57,7 +65,9 @@ jobs:
```

## Examples

Single workspace build, full example:

```yaml
name: 'Terraform'
Expand All @@ -71,17 +81,18 @@ jobs:
terraform:
name: 'Terraform'
runs-on: ubuntu-latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TF_IN_AUTOMATION: true
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Terraform
uses: hashicorp/setup-terraform@v1
env:
TF_IN_AUTOMATION: true
with:
cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}
terraform_version: 0.12.28
terraform_version: 0.14.5
- name: Terraform Format
id: fmt
Expand All @@ -91,8 +102,6 @@ jobs:
- name: Post Format
if: always() && github.ref != 'refs/heads/master' && (steps.fmt.outcome == 'success' || steps.fmt.outcome == 'failure')
uses: robburger/terraform-pr-commenter@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
commenter_type: fmt
commenter_input: ${{ format('{0}{1}', steps.fmt.outputs.stdout, steps.fmt.outputs.stderr) }}
Expand All @@ -105,8 +114,6 @@ jobs:
- name: Post Init
if: always() && github.ref != 'refs/heads/master' && (steps.init.outcome == 'success' || steps.init.outcome == 'failure')
uses: robburger/terraform-pr-commenter@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
commenter_type: init
commenter_input: ${{ format('{0}{1}', steps.init.outputs.stdout, steps.init.outputs.stderr) }}
Expand All @@ -119,8 +126,6 @@ jobs:
- name: Post Plan
if: always() && github.ref != 'refs/heads/master' && (steps.plan.outcome == 'success' || steps.plan.outcome == 'failure')
uses: robburger/terraform-pr-commenter@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
commenter_type: plan
commenter_input: ${{ format('{0}{1}', steps.plan.outputs.stdout, steps.plan.outputs.stderr) }}
Expand All @@ -133,76 +138,78 @@ jobs:
```

Multi-workspace matrix/parallel build:

```yaml
...
jobs:
terraform:
name: 'Terraform'
runs-on: ubuntu-latest
strategy:
matrix:
workspace: [audit, staging]
env:
TF_WORKSPACE: ${{ matrix['workspace'] }}
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Terraform
uses: hashicorp/setup-terraform@v1
env:
TF_IN_AUTOMATION: true
with:
cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}
terraform_version: 0.12.28
- name: Terraform Init - ${{ matrix['workspace'] }}
id: init
run: terraform init
- name: Post Init - ${{ matrix['workspace'] }}
if: always() && github.ref != 'refs/heads/master' && (steps.init.outcome == 'success' || steps.init.outcome == 'failure')
uses: robburger/terraform-pr-commenter@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
commenter_type: init
commenter_input: ${{ format('{0}{1}', steps.init.outputs.stdout, steps.init.outputs.stderr) }}
commenter_exitcode: ${{ steps.init.outputs.exitcode }}
- name: Terraform Plan - ${{ matrix['workspace'] }}
id: plan
run: terraform plan -out ${{ matrix['workspace'] }}.plan
- name: Post Plan - ${{ matrix['workspace'] }}
if: always() && github.ref != 'refs/heads/master' && (steps.plan.outcome == 'success' || steps.plan.outcome == 'failure')
uses: robburger/terraform-pr-commenter@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
runs-on: ubuntu-latest
strategy:
matrix:
workspace: [audit, staging]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TF_IN_AUTOMATION: true
TF_WORKSPACE: ${{ matrix['workspace'] }}
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Terraform
uses: hashicorp/setup-terraform@v1
with:
cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}
terraform_version: 0.14.5
- name: Terraform Init - ${{ matrix['workspace'] }}
id: init
run: terraform init
- name: Post Init - ${{ matrix['workspace'] }}
if: always() && github.ref != 'refs/heads/master' && (steps.init.outcome == 'success' || steps.init.outcome == 'failure')
uses: robburger/terraform-pr-commenter@v1
with:
commenter_type: plan
commenter_input: ${{ format('{0}{1}', steps.plan.outputs.stdout, steps.plan.outputs.stderr) }}
commenter_exitcode: ${{ steps.plan.outputs.exitcode }}
commenter_type: init
commenter_input: ${{ format('{0}{1}', steps.init.outputs.stdout, steps.init.outputs.stderr) }}
commenter_exitcode: ${{ steps.init.outputs.exitcode }}
- name: Terraform Plan - ${{ matrix['workspace'] }}
id: plan
run: terraform plan -out ${{ matrix['workspace'] }}.plan
- name: Post Plan - ${{ matrix['workspace'] }}
if: always() && github.ref != 'refs/heads/master' && (steps.plan.outcome == 'success' || steps.plan.outcome == 'failure')
uses: robburger/terraform-pr-commenter@v1
with:
commenter_type: plan
commenter_input: ${{ format('{0}{1}', steps.plan.outputs.stdout, steps.plan.outputs.stderr) }}
commenter_exitcode: ${{ steps.plan.outputs.exitcode }}
...
```

"What's the crazy-looking `if:` doing there?" Good question! It's broken into 3 logic groups separated by `&&`, so all need to return `true` for the step to run:
1) `always()` - ensures that the step is run regardless of the outcome in any previous steps
2) `github.ref != 'refs/heads/master'` - prevents the step running on a `master` branch
3) `(steps.step_id.outcome == 'success' || steps.step_id.outcome == 'failure')` - limits the run to the specific `step_id` only when there's a `success` or `failed` outcome.

1. `always()` - ensures that the step is run regardless of the outcome in any previous steps
2. `github.ref != 'refs/heads/master'` - prevents the step running on a `master` branch
3. `(steps.step_id.outcome == 'success' || steps.step_id.outcome == 'failure')` - limits the run to the specific `step_id` only when there's a `success` or `failed` outcome.

In English: "Always run this step, but only on a pull request and only when the previous step succeeds or fails."

## Screenshots

### fmt

![fmt](images/fmt-output.png)

### plan

![fmt](images/plan-output.png)

## Troubleshooting & Contributing

Feel free to head over to the [Issues](https://github.com/robburger/terraform-pr-commenter/issues) tab to see if the issue you're having has already been reported. If not, [open a new one](https://github.com/robburger/terraform-pr-commenter/issues/new) and be sure to include as much relevant information as possible, including code-samples, and a description of what you expect to be happening.

## License

[MIT](LICENSE)
13 changes: 10 additions & 3 deletions entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ else
DETAILS_STATE=""
fi

# Read HIGHLIGHT_CHANGES environment variable or use "true"
COLOURISE=${HIGHLIGHT_CHANGES:-true}

ACCEPT_HEADER="Accept: application/vnd.github.v3+json"
AUTH_HEADER="Authorization: token $GITHUB_TOKEN"
CONTENT_HEADER="Content-Type: application/json"
Expand Down Expand Up @@ -94,7 +97,7 @@ $INPUT
if [[ $EXIT_CODE -eq 3 ]]; then
ALL_FILES_DIFF=""
for file in $INPUT; do
THIS_FILE_DIFF=$(terraform fmt -no-color -write=false -diff "$file" | sed -n '/@@.*/,//{/@@.*/d;p}')
THIS_FILE_DIFF=$(terraform fmt -no-color -write=false -diff "$file")
ALL_FILES_DIFF="$ALL_FILES_DIFF
<details$DETAILS_STATE><summary><code>$file</code></summary>
Expand Down Expand Up @@ -178,11 +181,15 @@ if [[ $COMMAND == 'plan' ]]; then

# Exit Code: 0, 2
# Meaning: 0 = Terraform plan succeeded with no changes. 2 = Terraform plan succeeded with changes.
# Actions: Strip out the refresh section (everything before the 72 '-' characters) and build PR comment.
# Actions: Strip out the refresh section, ignore everything after the 72 dashes, format, colourise and build PR comment.
if [[ $EXIT_CODE -eq 0 || $EXIT_CODE -eq 2 ]]; then
CLEAN_PLAN=$(echo "$INPUT" | sed -n '/Refreshing state\.\.\./!p') # Strip refresh section
CLEAN_PLAN=$(echo "$CLEAN_PLAN" | sed -nr '/-{72}/q;p') # Ignore everything after the 72 dashes (happens when saving a plan to file)
CLEAN_PLAN=${CLEAN_PLAN::65300} # GitHub has a 65535-char comment limit - truncate plan, leaving space for comment wrapper
CLEAN_PLAN=$(echo "$CLEAN_PLAN" | sed -E 's/^([[:blank:]]*)([-+~])/\2\1/g') # Move any diff characters to start of line
CLEAN_PLAN=$(echo "$CLEAN_PLAN" | sed -r 's/^([[:blank:]]*)([-+~])/\2\1/g') # Move any diff characters to start of line
if [[ $COLOURISE == 'true' ]]; then
CLEAN_PLAN=$(echo "$CLEAN_PLAN" | sed -r 's/^~/!/g') # Replace ~ with ! to colourise the diff in GitHub comments
fi
PR_COMMENT="### Terraform \`plan\` Succeeded for Workspace: \`$WORKSPACE\`
<details$DETAILS_STATE><summary>Show Output</summary>
Expand Down

0 comments on commit b7622fb

Please sign in to comment.