terragrunt-action
terragrunt-action copied to clipboard
Plan output not properly formatted
Describe the bug Capturing the terragrunt plan output to use it into a comment is not properly formatted. The plan output is on a single line.
To Reproduce Steps to reproduce the behavior, code snippets and examples which can be used to reproduce the issue.
- name: Check terragrunt HCL
uses: gruntwork-io/terragrunt-action@v1
id: fmt
with:
tf_version: ${{ inputs.tf_version }}
tg_version: ${{ inputs.tg_version }}
tg_dir: ${{ inputs.workdir }}
tg_command: 'hclfmt --terragrunt-check --terragrunt-diff'
- name: Plan
id: plan
uses: gruntwork-io/terragrunt-action@v1
with:
tg_comment: 1
tf_version: ${{ inputs.tf_version }}
tg_version: ${{ inputs.tg_version }}
tg_dir: ${{ inputs.workdir }}
tg_command: 'plan'
- uses: actions/github-script@v6
if: github.event_name == 'pull_request'
env:
PLAN: "terraform\n${{ steps.plan.outputs.tg_action_output }}"
with:
github-token: ${{ inputs.GITHUB_TOKEN }}
script: |
// 1. Retrieve existing bot comments for the PR
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
})
const botComment = comments.find(comment => {
return comment.user.type === 'Bot' && comment.body.includes('Terragrunt Format and Style')
})
// 2. Prepare format of the comment
const output = `#### Terragrunt Format and Style 🖌\`${{ steps.fmt.outcome }}\`
#### Terragrunt Plan 📖\`${{ steps.plan.outcome }}\`
<details><summary>Show Plan</summary>
\`\`\`\n
${{ steps.plan.outputs.tg_action_output }}
\`\`\`
</details>
*Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`, Workflow: \`${{ github.workflow }}\`*`;
// 3. If we have a comment, update it, otherwise create a new one
if (botComment) {
github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: output
})
} else {
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
})
}
Expected behavior Should output the plan formatted as shown in the action log:
Nice to have
- [X] Terminal output
- [X] Screenshots
Versions
- Terragrunt Action version: v1
- Environment details (Terragrunt version, Terraform version, etc.):
- tg version: 0.46.3
- tf version: 1.5.6
Additional context I've tried a few things to output in the proper format but no luck so far.
@Mmasson-01 @denis256 Was having similar problem with terraform, and it was solved but reformatting the plan output. I think it should work here too
# Sed is taking all lines that begin with one or more spaces followed by a `+` or `-`.
# It stores the amount of spaces in `\1` and the +/- in `\2`.
# Then replace that portion of the line with `\2\1` (+/- followed by the number of matched spaces).
# cat is used instead of echo to avoid issues with quotes.
# plan is limited initially to 65300 characters as its the GitHub env variable limit
- name: Reformat Plan
if: steps.plan.outcome == 'success'
run: |
plan=$(cat <<'EOF'
${{ format('{0}{1}', steps.plan.outputs.stdout, steps.plan.outputs.stderr) }}
EOF
)
echo "PLAN<<EOF" >> $GITHUB_ENV
echo "${plan:0:65300}" | sed -E 's/^([[:space:]]+)([-+])/\2\1/g' | grep -v 'Refreshing state' >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
@vorotech I attempted to use the code you pasted and am getting the following error:
Error: Process completed with exit code 1.
Error: Unable to process file command 'env' successfully.
Error: Invalid value. Matching delimiter not found 'EOF'
The code I am using is the following:
- name: Reformat Plan
if: steps.plan.outcome == 'success'
run: |
plan=$(cat <<'EOF'
${{ format('{0}', steps.plan.outputs.tg_action_output) }}
EOF
)
echo "PLAN<<EOF" >> $GITHUB_ENV
echo "${plan:0:65300}" | sed -E 's/^([[:space:]]+)([-+])/\2\1/g' | grep -v 'Refreshing state' >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
- uses: actions/github-script@v6
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: "${{ env.PLAN }}"
})
Unless I am doing something glaringly stupid (possible since I am sleep deprived) it looks like it will have to be modified a bit. I have been looking for an excuse to learn sed so I can take it on when I have time. Just an FYI.
@aiell0 I haven't tested myself. I mentioned that this is a code I'm using with terraform, not terrugrunt yet. And to be precise my previous step with plan looks like. Maybe the show command does slightly different output than plan.
- name: Plan Terraform
id: plan
continue-on-error: true
run: |
cd ${{ inputs.TF_PATH }}
terraform plan -input=false -no-color -out=tfplan \
&& terraform show -no-color tfplan
I will give it a try and update here.
Yo! in case you still have the same issue, here's my current workaround for this:
Note 1: I use a matrix to iterate through several project
Note 2: For commenting the PR I use if: always() as I want to get the result regardless if the plan fails or not.
- name: Terragrunt Check HCL Formatting
id: terragrunt_fmt
uses: ./.github/actions/terragrunt
with:
TG_COMMAND: 'hclfmt --terragrunt-check --terragrunt-diff'
TG_VERSION: ${{ env.TG_VERSION }}
TG_DIR: ${{ matrix.tg_dirs }}
TF_VERSION: ${{ env.TF_VERSION }}
- name: Terraform ${{ env.TG_COMMAND }}
uses: gruntwork-io/terragrunt-action@v1
id: tg_action
with:
tg_command: ${{ env.TG_COMMAND }}
tf_version: ${{ env.TF_VERSION }}
tg_version: ${{ env.TG_VERSION }}
tg_dir: ${{ matrix.tg_dirs }}
- name: Update result icon
if: always()
run: |
if [[ ${{ steps.terragrunt_fmt.outcome }} == "success" ]] ; then echo "RESULT_VALIDATE_ICON=$(echo ✅)" >> $GITHUB_ENV ; else echo "RESULT_VALIDATE_ICON=$(echo ❌)" >> $GITHUB_ENV ; fi
if [[ ${{ steps.tg_action.outcome }} == "success" ]] ; then echo "RESULT_ACTION_ICON=$(echo ✅)" >> $GITHUB_ENV ; else echo "RESULT_ACTION_ICON=$(echo ❌)" >> $GITHUB_ENV ; fi
- name: Output Cleaning
if: always()
run: |
TG_OUT=$(echo "${{ steps.tg_action.outputs.tg_action_output }}" | sed 's|%0A|\n|g')
echo "TG_PLAN_OUTPUT<<EOF" >> $GITHUB_ENV
echo "$TG_OUT" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
- name: Comment PR
uses: actions/github-script@v6
if: always()
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `### *${{ github.workflow }}* Action ([Run #${{ github.run_number }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})) Summary 🚀
#### Target directory: ${{ matrix.tg_dirs }}
#### Terragrunt Format and Style 🖌️ ${{ steps.terragrunt_fmt.outcome }} ${{ env.RESULT_VALIDATE_ICON }}
#### Terraform Plan 📖 **${{ steps.tg_action.outcome }}** ${{ env.RESULT_PLAN_ICON }}
<details><summary>Show Plan</summary>
\`\`\`\n
${{ env.TG_PLAN_OUTPUT }}
\`\`\`
</details>
Pusher: *@${{ github.actor }}*, Action: *${{ github.event_name }}*`
})
@escanoru I would suggest add ${TG_OUT:0:65300} on your output cleaning step to avoid argument too long error from GitHub script.
@escanoru I ended up needing to clarify the statement a bit but got this working:
- name: Output Cleaning
id: clean
run: |
TG_OUT=$(echo '${{ steps.plan.outputs.tg_action_output }}' | sed 's|%0A|\n|g ; s|%3C|<|g')
echo "TG_PLAN_OUTPUT<<EOF" >> $GITHUB_ENV
echo "${TG_OUT:0:65300}" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
Thanks so much to everyone in here!
This is still fix problem on the surface. The real solution should fix within Terragrunt action script.
Found a bug here....if the plan fails, it doesn't show up. Will look into a fix.
@escanoru I would suggest add
${TG_OUT:0:65300}on your output cleaning step to avoid argument too long error from GitHub script.
Thanks for the tip @kamontat, I didn't know about the Github action character limitation.
Found a bug here....if the plan fails, it doesn't show up. Will look into a fix.
I came across this today, the reason why this happening is because I forgot to add if: always() condition on the 'Output Cleaning' step, after adding this, I caused an error on purpose and was able to get the fail plan. Hope this helps you.
Cheers!
If you look in the action code, it escapes the plan so it can be passed as the action output.
You can find how it is done here: clean_multiline_text.
To make it multiline again you can unescape it in your script:
` .... const multiline_plan = unescape('${{ inputs.terragrunt_plan_output }}'); .... Show plan
${multiline_plan}
.... `
It would be nicer if the output could be formatted as JSON instead.