Skip to content

Commit 3bdc2af

Browse files
authored
Merge pull request #202 from dflook/large-plan
Truncate large plans in PR comments
2 parents e7b4aa7 + 5f69332 commit 3bdc2af

File tree

4 files changed

+121
-14
lines changed

4 files changed

+121
-14
lines changed

.github/workflows/test-apply.yaml

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -841,7 +841,7 @@ jobs:
841841
- name: Plan
842842
uses: ./terraform-plan
843843
with:
844-
label: test-apply warnings (hash)
844+
label: test-apply warnings_hash
845845
path: tests/workflows/test-apply/warnings
846846

847847
- name: Create warning
@@ -851,7 +851,7 @@ jobs:
851851
- name: Apply
852852
uses: ./terraform-apply
853853
with:
854-
label: test-apply warnings (hash)
854+
label: test-apply warnings_hash
855855
path: tests/workflows/test-apply/warnings
856856

857857
warnings_text:
@@ -866,7 +866,7 @@ jobs:
866866
- name: Plan
867867
uses: dflook/terraform-plan@v1.22.2
868868
with:
869-
label: test-apply warnings (text)
869+
label: test-apply warnings_text
870870
path: tests/workflows/test-apply/warnings
871871

872872
- name: Create warning
@@ -876,5 +876,63 @@ jobs:
876876
- name: Apply
877877
uses: ./terraform-apply
878878
with:
879-
label: test-apply warnings (text)
879+
label: test-apply warnings_text
880880
path: tests/workflows/test-apply/warnings
881+
882+
long_plan:
883+
runs-on: ubuntu-latest
884+
name: Apply a plan that doesn't fit in a comment
885+
env:
886+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
887+
steps:
888+
- name: Checkout
889+
uses: actions/checkout@v2
890+
891+
- name: Plan
892+
uses: ./terraform-plan
893+
with:
894+
label: test-apply long_plan
895+
path: tests/workflows/test-apply/long_plan
896+
897+
- name: Apply
898+
uses: ./terraform-apply
899+
with:
900+
label: test-apply long_plan
901+
path: tests/workflows/test-apply/long_plan
902+
903+
long_plan_changes:
904+
runs-on: ubuntu-latest
905+
name: Don't apply a changed plan that doesn't fit in a comment
906+
env:
907+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
908+
steps:
909+
- name: Checkout
910+
uses: actions/checkout@v2
911+
912+
- name: Plan
913+
uses: ./terraform-plan
914+
with:
915+
label: test-apply long_plan_changes
916+
path: tests/workflows/test-apply/long_plan
917+
918+
- name: Apply
919+
uses: ./terraform-apply
920+
id: apply
921+
continue-on-error: true
922+
with:
923+
label: test-apply long_plan_changes
924+
path: tests/workflows/test-apply/long_plan
925+
variables: |
926+
length = 2
927+
928+
- name: Check failed to apply
929+
run: |
930+
if [[ "${{ steps.apply.outcome }}" != "failure" ]]; then
931+
echo "Apply did not fail correctly"
932+
exit 1
933+
fi
934+
935+
if [[ "${{ steps.apply.outputs.failure-reason }}" != "plan-changed" ]]; then
936+
echo "::error:: failure-reason not set correctly"
937+
exit 1
938+
fi

image/src/github_pr_comment/__main__.py

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import re
66
import sys
77
from pathlib import Path
8-
from typing import (NewType, Optional, cast)
8+
from typing import (NewType, Optional, cast, Tuple)
99

1010
import canonicaljson
1111

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

238+
def format_plan_text(plan_text: str) -> Tuple[str, str]:
239+
"""
240+
Format the given plan for insertion into a PR comment
241+
"""
242+
243+
max_body_size = 50000 # bytes
244+
245+
def truncate(t):
246+
lines = []
247+
total_size = 0
248+
249+
for line in t.splitlines():
250+
line_size = len(line.encode()) + 1 # + newline
251+
if total_size + line_size > max_body_size:
252+
lines.append('Plan is too large to fit in a PR comment. See the full plan in the workflow log.')
253+
break
254+
255+
lines.append(line)
256+
total_size += line_size
257+
258+
return '\n'.join(lines)
259+
260+
if len(plan_text.encode()) > max_body_size:
261+
# needs truncation
262+
return 'trunc', truncate(plan_text)
263+
else:
264+
return 'text', plan_text
265+
238266
def main() -> int:
239267
if len(sys.argv) < 2:
240268
sys.stderr.write(f'''Usage:
@@ -274,14 +302,15 @@ def main() -> int:
274302
headers = comment.headers.copy()
275303
headers['plan_job_ref'] = job_workflow_ref()
276304
headers['plan_hash'] = plan_hash(body, comment.issue_url)
305+
headers['plan_text_format'], plan_text = format_plan_text(body)
277306

278307
comment = update_comment(
279308
github,
280309
comment,
281310
description=description,
282311
summary=create_summary(body),
283312
headers=headers,
284-
body=body,
313+
body=plan_text,
285314
status=status
286315
)
287316

@@ -316,23 +345,29 @@ def main() -> int:
316345
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")
317346
comment = update_comment(github, comment, status=f':x: Plan not applied in {job_markdown_ref()} (Plan has changed)')
318347

319-
sys.stdout.write("""Performing diff between the pull request plan and the plan generated at execution time ...
320-
> are lines from the plan in the pull request
321-
< are lines from the plan generated at execution
322-
Plan changes:
323-
""")
324-
325348
approved_plan_path = os.path.join(os.environ['STEP_TMP_DIR'], 'approved-plan.txt')
326349
with open(approved_plan_path, 'w') as f:
327350
f.write(comment.body.strip())
328351
proposed_plan_path = os.path.join(os.environ['STEP_TMP_DIR'], 'proposed-plan.txt')
329352
with open(proposed_plan_path, 'w') as f:
330-
f.write(proposed_plan.strip())
353+
_, formatted_proposed_plan = format_plan_text(proposed_plan.strip())
354+
f.write(formatted_proposed_plan.strip())
355+
331356
debug(f'diff {proposed_plan_path} {approved_plan_path}')
332357
diff_complete = subprocess.run(['diff', proposed_plan_path, approved_plan_path], check=False, capture_output=True, encoding='utf-8')
333358
sys.stdout.write(diff_complete.stdout)
334359
sys.stderr.write(diff_complete.stderr)
335360

361+
if diff_complete.returncode != 0:
362+
sys.stdout.write("""Performing diff between the pull request plan and the plan generated at execution time.
363+
> are lines from the plan in the pull request
364+
< are lines from the plan generated at execution
365+
Plan differences:
366+
""")
367+
368+
if comment.headers.get('plan_text_format', 'text') == 'trunc':
369+
sys.stdout.write('\nThe plan text was too large for a PR comment, not all differences may be shown here.')
370+
336371
if plan_ref := comment.headers.get('plan_job_ref'):
337372
sys.stdout.write(f'\nCompare with the plan generated by the dflook/terraform-plan action in {plan_ref}\n')
338373

image/src/github_pr_comment/hash.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def plan_hash(plan_text: str, salt: str) -> str:
1818
TODO: Change to use the plan json output
1919
"""
2020

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

2323
plan = remove_warnings(remove_unchanged_attributes(plan_text))
2424

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
resource "random_id" "a" {
2+
count = 250
3+
4+
byte_length = 3
5+
}
6+
7+
resource "random_id" "b" {
8+
byte_length = var.length
9+
}
10+
11+
variable "length" {
12+
type = number
13+
default = 3
14+
}

0 commit comments

Comments
 (0)