Skip to content

Commit 444c0ab

Browse files
authored
Merge pull request #34 from dflook/collapsable-plan
Make PR comment plans collapsable
2 parents d8f958c + 63b84db commit 444c0ab

File tree

9 files changed

+523
-16
lines changed

9 files changed

+523
-16
lines changed

.github/workflows/test-plan.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ jobs:
118118
runs-on: ubuntu-latest
119119
name: Change terraform 14
120120
env:
121+
TF_PLAN_COLLAPSE_LENGTH: 30
121122
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
122123
steps:
123124
- name: Checkout

.github/workflows/test.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ jobs:
1818
- name: Install dependencies
1919
run: |
2020
python -m pip install --upgrade pip
21-
pip install pytest
21+
pip install -r tests/requirements.txt
2222
2323
- name: Run tests
2424
run: |
25-
PYTHONPATH=image/tools pytest tests
25+
GITHUB_TOKEN=No PYTHONPATH=image/tools pytest tests

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.terraform/
22
/.idea/
33
.pytest_cache/
4-
/venv/
4+
/venv/
5+
.terraform.lock.hcl

CHANGELOG.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,21 @@ The actions are versioned as a suite. Some actions may have no change in behavio
88

99
When using an action you can specify the version as:
1010

11-
- `@v1.5.2` to use an exact release
12-
- `@v1.5` to use the latest patch release for the specific minor version
11+
- `@v1.6.0` to use an exact release
12+
- `@v1.6` to use the latest patch release for the specific minor version
1313
- `@v1` to use the latest patch release for the specific major version
1414

15+
## [1.6.0] - 2021-02-25
16+
17+
### Added
18+
- PR comments use a one line summary of the terraform output, with the full output in a collapsable pane.
19+
20+
If a plan is short the output is shown by default. This can be controlled with the `TF_PLAN_COLLAPSE_LENGTH` environment
21+
variable for the [dflook/terraform-plan](terraform-plan) action.
22+
23+
### Fixed
24+
- Now makes far fewer github api requests to avoid rate limiting.
25+
1526
## [1.5.2] - 2021-01-16
1627

1728
### Fixed
@@ -95,6 +106,7 @@ First release of the GitHub Actions:
95106
- [dflook/terraform-new-workspace](terraform-new-workspace)
96107
- [dflook/terraform-destroy-workspace](terraform-destroy-workspace)
97108

109+
[1.6.0]: https://github.com/dflook/terraform-github-actions/compare/v1.5.2...v1.6.0
98110
[1.5.2]: https://github.com/dflook/terraform-github-actions/compare/v1.5.1...v1.5.2
99111
[1.5.1]: https://github.com/dflook/terraform-github-actions/compare/v1.5.0...v1.5.1
100112
[1.5.0]: https://github.com/dflook/terraform-github-actions/compare/v1.4.2...v1.5.0

image/tools/github_pr_comment.py

Lines changed: 89 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import re
66
import sys
77
import datetime
8+
import hashlib
89
from typing import Optional, Dict, Iterable
910

1011
import requests
@@ -98,25 +99,50 @@ def find_pr() -> str:
9899
raise Exception(f"The {event_type} event doesn\'t relate to a Pull Request.")
99100

100101
def current_user() -> str:
102+
103+
token_hash = hashlib.sha256(os.environ["GITHUB_TOKEN"].encode()).hexdigest()
104+
105+
try:
106+
with open(f'.dflook-terraform/token-cache/{token_hash}') as f:
107+
username = f.read()
108+
debug(f'GITHUB_TOKEN username: {username}')
109+
return username
110+
except Exception as e:
111+
debug(str(e))
112+
101113
response = github_api_request('get', 'https://api.github.com/user')
102114
if response.status_code != 403:
103115
user = response.json()
104116
debug('GITHUB_TOKEN user:')
105117
debug(json.dumps(user))
106118

107-
return user['login']
119+
username = user['login']
120+
else:
121+
# Assume this is the github actions app token
122+
username = 'github-actions[bot]'
108123

109-
# Assume this is the github actions app token
110-
return 'github-actions[bot]'
124+
try:
125+
os.makedirs('.dflook-terraform/token-cache', exist_ok=True)
126+
with open(f'.dflook-terraform/token-cache/{token_hash}', 'w') as f:
127+
f.write(username)
128+
except Exception as e:
129+
debug(str(e))
130+
131+
debug(f'GITHUB_TOKEN username: {username}')
132+
return username
111133

112134
class TerraformComment:
113135
"""
114136
The GitHub comment for this specific terraform plan
115137
"""
116138

117-
def __init__(self, pr_url: str):
139+
def __init__(self, pr_url: str=None):
118140
self._plan = None
119141
self._status = None
142+
self._comment_url = None
143+
144+
if pr_url is None:
145+
return
120146

121147
response = github_api_request('get', pr_url)
122148
response.raise_for_status()
@@ -125,20 +151,20 @@ def __init__(self, pr_url: str):
125151
response = github_api_request('get', self._issue_url)
126152
response.raise_for_status()
127153

128-
self._comment_url = None
154+
username = current_user()
155+
129156
debug('Looking for an existing comment:')
130157
for comment in response.json():
131158
debug(json.dumps(comment))
132-
if comment['user']['login'] == current_user():
133-
match = re.match(rf'{re.escape(self._comment_identifier)}\n```(?:hcl)?(.*?)```(.*)', comment['body'], re.DOTALL)
159+
if comment['user']['login'] == username:
160+
match = re.match(rf'{re.escape(self._comment_identifier)}.*```(?:hcl)?(.*?)```.*', comment['body'], re.DOTALL)
134161

135162
if not match:
136-
match = re.match(rf'{re.escape(self._old_comment_identifier)}\n```(.*?)```(.*)', comment['body'], re.DOTALL)
163+
match = re.match(rf'{re.escape(self._old_comment_identifier)}\n```(.*?)```.*', comment['body'], re.DOTALL)
137164

138165
if match:
139166
self._comment_url = comment['url']
140167
self._plan = match.group(1).strip()
141-
self._status = match.group(2).strip()
142168
return
143169

144170
@property
@@ -270,12 +296,64 @@ def status(self) -> Optional[str]:
270296
def status(self, status: str) -> None:
271297
self._status = status.strip()
272298

273-
def update_comment(self):
299+
def body(self) -> str:
274300
body = f'{self._comment_identifier}\n```hcl\n{self.plan}\n```'
275301

276302
if self.status:
277303
body += '\n' + self.status
278304

305+
return body
306+
307+
def collapsable_body(self) -> str:
308+
309+
try:
310+
collapse_threshold = int(os.environ['TF_PLAN_COLLAPSE_LENGTH'])
311+
except (ValueError, KeyError):
312+
collapse_threshold = 10
313+
314+
open = ''
315+
highlighting = ''
316+
317+
if self.plan.startswith('Error'):
318+
open = ' open'
319+
elif 'Plan:' in self.plan:
320+
highlighting = 'hcl'
321+
num_lines = len(self.plan.splitlines())
322+
if num_lines < collapse_threshold:
323+
open = ' open'
324+
325+
body = f'''{self._comment_identifier}
326+
<details{open}>
327+
<summary>{self.summary()}</summary>
328+
329+
```{highlighting}
330+
{self.plan}
331+
```
332+
</details>
333+
'''
334+
335+
if self.status:
336+
body += '\n' + self.status
337+
338+
return body
339+
340+
def summary(self) -> str:
341+
summary = None
342+
343+
for line in self.plan.splitlines():
344+
if line.startswith('No changes') or line.startswith('Error'):
345+
return line
346+
347+
if line.startswith('Plan:'):
348+
summary = line
349+
350+
if line.startswith('Changes to Outputs'):
351+
return summary + ' Changes to Outputs.'
352+
353+
return summary
354+
355+
def update_comment(self):
356+
body = self.collapsable_body()
279357
debug(body)
280358

281359
if self._comment_url is None:
@@ -313,5 +391,6 @@ def update_comment(self):
313391
if tf_comment.plan is None:
314392
exit(1)
315393
print(tf_comment.plan)
394+
exit(0)
316395

317396
tf_comment.update_comment()

terraform-plan/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,21 @@ The [dflook/terraform-apply](https://github.com/dflook/terraform-github-actions/
100100
env:
101101
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
102102
```
103+
104+
* `TF_PLAN_COLLAPSE_LENGTH`
105+
106+
When PR comments are enabled, the terraform output is included in a collapsable pane.
107+
108+
If a terraform plan has fewer lines than this value, the pane is expanded
109+
by default when the comment is displayed.
110+
111+
```yaml
112+
env:
113+
TF_PLAN_COLLAPSE_LENGTH: 30
114+
```
115+
116+
- Optional
117+
- Default: 10
103118

104119
## Outputs
105120

terraform-version/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,5 @@ jobs:
7070
run: echo "The terraform version was ${{ steps.terraform-version.outputs.terraform }}"
7171

7272
- name: Print aws provider version
73-
run: echo "The terraform version was ${{ steps.terraform-version.outputs.aws }}"
73+
run: echo "The aws provider version was ${{ steps.terraform-version.outputs.aws }}"
7474
```

tests/requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
requests
2+
pytest

0 commit comments

Comments
 (0)