Skip to content

Commit

Permalink
Merge pull request #202 from dflook/large-plan
Browse files Browse the repository at this point in the history
Truncate large plans in PR comments
  • Loading branch information
dflook authored Aug 7, 2022
2 parents e7b4aa7 + 5f69332 commit 3bdc2af
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 14 deletions.
66 changes: 62 additions & 4 deletions .github/workflows/test-apply.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -841,7 +841,7 @@ jobs:
- name: Plan
uses: ./terraform-plan
with:
label: test-apply warnings (hash)
label: test-apply warnings_hash
path: tests/workflows/test-apply/warnings

- name: Create warning
Expand All @@ -851,7 +851,7 @@ jobs:
- name: Apply
uses: ./terraform-apply
with:
label: test-apply warnings (hash)
label: test-apply warnings_hash
path: tests/workflows/test-apply/warnings

warnings_text:
Expand All @@ -866,7 +866,7 @@ jobs:
- name: Plan
uses: dflook/[email protected]
with:
label: test-apply warnings (text)
label: test-apply warnings_text
path: tests/workflows/test-apply/warnings

- name: Create warning
Expand All @@ -876,5 +876,63 @@ jobs:
- name: Apply
uses: ./terraform-apply
with:
label: test-apply warnings (text)
label: test-apply warnings_text
path: tests/workflows/test-apply/warnings

long_plan:
runs-on: ubuntu-latest
name: Apply a plan that doesn't fit in a comment
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Checkout
uses: actions/checkout@v2

- name: Plan
uses: ./terraform-plan
with:
label: test-apply long_plan
path: tests/workflows/test-apply/long_plan

- name: Apply
uses: ./terraform-apply
with:
label: test-apply long_plan
path: tests/workflows/test-apply/long_plan

long_plan_changes:
runs-on: ubuntu-latest
name: Don't apply a changed plan that doesn't fit in a comment
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Checkout
uses: actions/checkout@v2

- name: Plan
uses: ./terraform-plan
with:
label: test-apply long_plan_changes
path: tests/workflows/test-apply/long_plan

- name: Apply
uses: ./terraform-apply
id: apply
continue-on-error: true
with:
label: test-apply long_plan_changes
path: tests/workflows/test-apply/long_plan
variables: |
length = 2
- name: Check failed to apply
run: |
if [[ "${{ steps.apply.outcome }}" != "failure" ]]; then
echo "Apply did not fail correctly"
exit 1
fi
if [[ "${{ steps.apply.outputs.failure-reason }}" != "plan-changed" ]]; then
echo "::error:: failure-reason not set correctly"
exit 1
fi
53 changes: 44 additions & 9 deletions image/src/github_pr_comment/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import re
import sys
from pathlib import Path
from typing import (NewType, Optional, cast)
from typing import (NewType, Optional, cast, Tuple)

import canonicaljson

Expand Down Expand Up @@ -235,6 +235,34 @@ def is_approved(proposed_plan: str, comment: TerraformComment) -> bool:
debug('Approving plan based on plan text')
return plan_cmp(proposed_plan, comment.body)

def format_plan_text(plan_text: str) -> Tuple[str, str]:
"""
Format the given plan for insertion into a PR comment
"""

max_body_size = 50000 # bytes

def truncate(t):
lines = []
total_size = 0

for line in t.splitlines():
line_size = len(line.encode()) + 1 # + newline
if total_size + line_size > max_body_size:
lines.append('Plan is too large to fit in a PR comment. See the full plan in the workflow log.')
break

lines.append(line)
total_size += line_size

return '\n'.join(lines)

if len(plan_text.encode()) > max_body_size:
# needs truncation
return 'trunc', truncate(plan_text)
else:
return 'text', plan_text

def main() -> int:
if len(sys.argv) < 2:
sys.stderr.write(f'''Usage:
Expand Down Expand Up @@ -274,14 +302,15 @@ def main() -> int:
headers = comment.headers.copy()
headers['plan_job_ref'] = job_workflow_ref()
headers['plan_hash'] = plan_hash(body, comment.issue_url)
headers['plan_text_format'], plan_text = format_plan_text(body)

comment = update_comment(
github,
comment,
description=description,
summary=create_summary(body),
headers=headers,
body=body,
body=plan_text,
status=status
)

Expand Down Expand Up @@ -316,23 +345,29 @@ def main() -> int:
sys.stdout.write("The plan on the PR must be up to date. Alternatively, set the auto_approve input to 'true' to apply outdated plans\n")
comment = update_comment(github, comment, status=f':x: Plan not applied in {job_markdown_ref()} (Plan has changed)')

sys.stdout.write("""Performing diff between the pull request plan and the plan generated at execution time ...
> are lines from the plan in the pull request
< are lines from the plan generated at execution
Plan changes:
""")

approved_plan_path = os.path.join(os.environ['STEP_TMP_DIR'], 'approved-plan.txt')
with open(approved_plan_path, 'w') as f:
f.write(comment.body.strip())
proposed_plan_path = os.path.join(os.environ['STEP_TMP_DIR'], 'proposed-plan.txt')
with open(proposed_plan_path, 'w') as f:
f.write(proposed_plan.strip())
_, formatted_proposed_plan = format_plan_text(proposed_plan.strip())
f.write(formatted_proposed_plan.strip())

debug(f'diff {proposed_plan_path} {approved_plan_path}')
diff_complete = subprocess.run(['diff', proposed_plan_path, approved_plan_path], check=False, capture_output=True, encoding='utf-8')
sys.stdout.write(diff_complete.stdout)
sys.stderr.write(diff_complete.stderr)

if diff_complete.returncode != 0:
sys.stdout.write("""Performing diff between the pull request plan and the plan generated at execution time.
> are lines from the plan in the pull request
< are lines from the plan generated at execution
Plan differences:
""")

if comment.headers.get('plan_text_format', 'text') == 'trunc':
sys.stdout.write('\nThe plan text was too large for a PR comment, not all differences may be shown here.')

if plan_ref := comment.headers.get('plan_job_ref'):
sys.stdout.write(f'\nCompare with the plan generated by the dflook/terraform-plan action in {plan_ref}\n')

Expand Down
2 changes: 1 addition & 1 deletion image/src/github_pr_comment/hash.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def plan_hash(plan_text: str, salt: str) -> str:
TODO: Change to use the plan json output
"""

debug(f'Hashing with salt {salt} and plan:\n{plan_text}')
debug(f'Hashing with salt {salt}')

plan = remove_warnings(remove_unchanged_attributes(plan_text))

Expand Down
14 changes: 14 additions & 0 deletions tests/workflows/test-apply/long_plan/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
resource "random_id" "a" {
count = 250

byte_length = 3
}

resource "random_id" "b" {
byte_length = var.length
}

variable "length" {
type = number
default = 3
}

0 comments on commit 3bdc2af

Please sign in to comment.