Skip to content

Commit 785d02e

Browse files
authored
feat: Support posting pull request comments (#71)
* feat: support add pull request comments * add .pre-commit-config.yaml to format code * fix: requirements.txt and test new input * fix: just checking PR_COMMENTS * feat: add GITHUB_TOKEN * update readme.md * update exiting comments or delete it to add new one * try to fix 'Issue' object has no attribute 'get_issue_comments' * update .commit-check.yml * try to fix posting issue comment * docs: update readme * docs: update links
1 parent f0c5269 commit 785d02e

File tree

7 files changed

+218
-46
lines changed

7 files changed

+218
-46
lines changed

.commit-check.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,19 @@ checks:
77
[optional body]\n
88
[optional footer(s)]\n\n
99
More details please refer to https://www.conventionalcommits.org"
10-
suggest: git commit --amend --no-verify
10+
suggest: please check your commit message whether matches above regex
1111

1212
- check: branch
13-
regex: ^(bugfix|feature|release|hotfix|task|dependabot)\/.+|(master)|(main)|(HEAD)|(PR-.+)
14-
error: "Branches must begin with these types: bugfix/ feature/ release/ hotfix/ task/"
15-
suggest: git checkout -b type/branch_name
13+
regex: ^(bugfix|feature|release|hotfix|task|chore)\/.+|(master)|(main)|(HEAD)|(PR-.+)
14+
error: "Branches must begin with these types: bugfix/ feature/ release/ hotfix/ task/ chore/"
15+
suggest: run command `git checkout -b type/branch_name`
1616

1717
- check: author_name
1818
regex: ^[A-Za-z ,.\'-]+$|.*(\[bot])
1919
error: The committer name seems invalid
20-
suggest: git config user.name "Peter Shen"
20+
suggest: run command `git config user.name "Your Name"`
2121

2222
- check: author_email
2323
regex: ^\S+@\S+\.\S+$
2424
error: The committer email seems invalid
25-
suggest: git config user.email petershen@example.com
25+
suggest: run command `git config user.email yourname@example.com`

.github/workflows/commit-check.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@ jobs:
1313
with:
1414
ref: ${{ github.event.pull_request.head.sha }}
1515
- uses: ./ # self test
16+
env:
17+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # used by `pr-comments`
1618
with:
1719
message: true
1820
branch: true
1921
author-name: true
2022
author-email: true
2123
commit-signoff: true
2224
job-summary: true
25+
pr-comments: true

.pre-commit-config.yaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# https://pre-commit.com/
2+
ci:
3+
autofix_commit_msg: 'ci: auto fixes from pre-commit.com hooks'
4+
autoupdate_commit_msg: 'ci: pre-commit autoupdate'
5+
6+
repos:
7+
- repo: https://github.com/pre-commit/pre-commit-hooks
8+
rev: v5.0.0
9+
hooks:
10+
- id: check-yaml
11+
- id: check-toml
12+
- id: end-of-file-fixer
13+
- id: trailing-whitespace
14+
- id: name-tests-test
15+
- id: requirements-txt-fixer
16+
- repo: https://github.com/psf/black-pre-commit-mirror
17+
rev: 24.10.0
18+
hooks:
19+
- id: black
20+
# FIXME: main.py:109: error: Item "None" of "str | None" has no attribute "split" [union-attr]
21+
# - repo: https://github.com/pre-commit/mirrors-mypy
22+
# rev: v1.12.0
23+
# hooks:
24+
# - id: mypy
25+
- repo: https://github.com/codespell-project/codespell
26+
rev: v2.3.0
27+
hooks:
28+
- id: codespell

README.md

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ jobs:
2525
runs-on: ubuntu-latest
2626
steps:
2727
- uses: actions/checkout@v4
28+
with:
29+
ref: ${{ github.event.pull_request.head.sha }} # Checkout PR HEAD commit
2830
- uses: commit-check/commit-check-action@v1
2931
with:
3032
message: true
@@ -34,6 +36,7 @@ jobs:
3436
commit-signoff: true
3537
dry-run: true
3638
job-summary: true
39+
pr-comments: true
3740
```
3841
3942
## Optional Inputs
@@ -72,22 +75,41 @@ jobs:
7275

7376
### `job-summary`
7477

75-
- **Description**: display job summary to a workflow run
78+
- **Description**: display job summary to the workflow run
7679
- Default: 'true'
7780

81+
### `pr-comments`
82+
83+
- **Description**: post results to the pull request comments
84+
- Default: 'true'
85+
86+
> [!IMPORTANT]
87+
> This is a experimental feature
88+
> use it you need to set `GITHUB_TOKEN` in the GitHub Action.
89+
7890
Note: the default rule of above inputs is following [this configuration](https://github.com/commit-check/commit-check/blob/main/.commit-check.yml), if you want to customize just add your `.commit-check.yml` config file under your repository root directory.
7991

8092
## GitHub Action job summary
8193

82-
By default, commit-check-action results are shown on the job summary page of the workflow.
94+
By default, commit-check-action results are shown on the job summary page of the workflow.
8395

8496
### Success job summary
8597

86-
![Success job summary](https://github.com/commit-check/.github/blob/main/screenshot/success-summary.png)
98+
![Success job summary](https://github.com/commit-check/.github/blob/main/screenshot/success-job-summary.png)
8799

88100
### Failure job summary
89101

90-
![Failure job summary](https://github.com/commit-check/.github/blob/main/screenshot/failure-summary.png)
102+
![Failure job summary](https://github.com/commit-check/.github/blob/main/screenshot/failure-job-summary.png)
103+
104+
## GitHub Pull Request comments
105+
106+
### Success pull request comment
107+
108+
![Success pull request comment](https://github.com/commit-check/.github/blob/main/screenshot/success-pr-comments.png)
109+
110+
### Failure pull request comment
111+
112+
![Failure pull request comment](https://github.com/commit-check/.github/blob/main/screenshot/failure-pr-comments.png)
91113

92114
## Badging your repository
93115

action.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ inputs:
3030
required: false
3131
default: false
3232
job-summary:
33-
description: add a job summary
33+
description: display job summary to the workflow run
34+
required: false
35+
default: true
36+
pr-comments:
37+
description: post results to the pull request comments
3438
required: false
3539
default: true
3640
runs:
@@ -55,3 +59,4 @@ runs:
5559
COMMIT_SIGNOFF: ${{ inputs.commit-signoff }}
5660
DRY_RUN: ${{ inputs.dry-run }}
5761
JOB_SUMMARY: ${{ inputs.job-summary }}
62+
PR_COMMENTS: ${{ inputs.pr-comments }}

main.py

Lines changed: 147 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,59 +3,171 @@
33
import sys
44
import subprocess
55
import re
6+
from github import Github
7+
8+
9+
# Constants for message titles
10+
SUCCESS_TITLE = "# Commit-Check ✔️"
11+
FAILURE_TITLE = "# Commit-Check ❌"
12+
13+
# Environment variables
14+
MESSAGE = os.getenv("MESSAGE", "false")
15+
BRANCH = os.getenv("BRANCH", "false")
16+
AUTHOR_NAME = os.getenv("AUTHOR_NAME", "false")
17+
AUTHOR_EMAIL = os.getenv("AUTHOR_EMAIL", "false")
18+
COMMIT_SIGNOFF = os.getenv("COMMIT_SIGNOFF", "false")
19+
DRY_RUN = os.getenv("DRY_RUN", "false")
20+
JOB_SUMMARY = os.getenv("JOB_SUMMARY", "false")
21+
PR_COMMENTS = os.getenv("PR_COMMENTS", "false")
22+
GITHUB_STEP_SUMMARY = os.environ["GITHUB_STEP_SUMMARY"]
23+
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
24+
GITHUB_REPOSITORY = os.getenv("GITHUB_REPOSITORY")
25+
GITHUB_REF = os.getenv("GITHUB_REF")
26+
27+
28+
def log_env_vars():
29+
"""Logs the environment variables for debugging purposes."""
30+
print(f"MESSAGE = {MESSAGE}")
31+
print(f"BRANCH = {BRANCH}")
32+
print(f"AUTHOR_NAME = {AUTHOR_NAME}")
33+
print(f"AUTHOR_EMAIL = {AUTHOR_EMAIL}")
34+
print(f"COMMIT_SIGNOFF = {COMMIT_SIGNOFF}")
35+
print(f"DRY_RUN = {DRY_RUN}")
36+
print(f"JOB_SUMMARY = {JOB_SUMMARY}")
37+
print(f"PR_COMMENTS = {PR_COMMENTS}\n")
638

739

840
def run_commit_check() -> int:
9-
args = ["--message", "--branch", "--author-name", "--author-email", "--commit-signoff"]
10-
args = [arg for arg, value in zip(args, [MESSAGE, BRANCH, AUTHOR_NAME, AUTHOR_EMAIL, COMMIT_SIGNOFF]) if value == "true"]
41+
"""Runs the commit-check command and logs the result."""
42+
args = [
43+
"--message",
44+
"--branch",
45+
"--author-name",
46+
"--author-email",
47+
"--commit-signoff",
48+
]
49+
args = [
50+
arg
51+
for arg, value in zip(
52+
args, [MESSAGE, BRANCH, AUTHOR_NAME, AUTHOR_EMAIL, COMMIT_SIGNOFF]
53+
)
54+
if value == "true"
55+
]
1156

1257
command = ["commit-check"] + args
1358
print(" ".join(command))
1459
with open("result.txt", "w") as result_file:
15-
result = subprocess.run(command, stdout=result_file, stderr=subprocess.PIPE, check=False)
60+
result = subprocess.run(
61+
command, stdout=result_file, stderr=subprocess.PIPE, check=False
62+
)
1663
return result.returncode
1764

1865

66+
def read_result_file() -> str | None:
67+
"""Reads the result.txt file and removes ANSI color codes."""
68+
if os.path.getsize("result.txt") > 0:
69+
with open("result.txt", "r") as result_file:
70+
result_text = re.sub(
71+
r"\x1B\[[0-9;]*[a-zA-Z]", "", result_file.read()
72+
) # Remove ANSI colors
73+
return result_text.rstrip()
74+
return None
75+
76+
1977
def add_job_summary() -> int:
78+
"""Adds the commit check result to the GitHub job summary."""
2079
if JOB_SUMMARY == "false":
21-
sys.exit()
80+
return 0
2281

23-
if os.path.getsize("result.txt") > 0:
24-
with open("result.txt", "r") as result_file:
25-
result_text = re.sub(r'\x1B\[[0-9;]*[a-zA-Z]', '', result_file.read()) # Remove ANSI colors
82+
result_text = read_result_file()
2683

27-
with open(GITHUB_STEP_SUMMARY, "a") as summary_file:
28-
summary_file.write("### Commit-Check ❌\n```\n")
29-
summary_file.write(result_text)
30-
summary_file.write("```")
31-
return 1
32-
else:
33-
with open(GITHUB_STEP_SUMMARY, "a") as summary_file:
34-
summary_file.write("### Commit-Check ✔️\n")
84+
summary_content = (
85+
SUCCESS_TITLE
86+
if result_text is None
87+
else f"{FAILURE_TITLE}\n```\n{result_text}\n```"
88+
)
89+
90+
with open(GITHUB_STEP_SUMMARY, "a") as summary_file:
91+
summary_file.write(summary_content)
92+
93+
return 0 if result_text is None else 1
94+
95+
96+
def add_pr_comments() -> int:
97+
"""Posts the commit check result as a comment on the pull request."""
98+
if PR_COMMENTS == "false":
3599
return 0
36100

101+
try:
102+
token = os.getenv("GITHUB_TOKEN")
103+
repo_name = os.getenv("GITHUB_REPOSITORY")
104+
pr_number = os.getenv("GITHUB_REF").split("/")[-2]
37105

38-
MESSAGE = os.getenv("MESSAGE", "false")
39-
BRANCH = os.getenv("BRANCH", "false")
40-
AUTHOR_NAME = os.getenv("AUTHOR_NAME", "false")
41-
AUTHOR_EMAIL = os.getenv("AUTHOR_EMAIL", "false")
42-
COMMIT_SIGNOFF = os.getenv("COMMIT_SIGNOFF", "false")
43-
DRY_RUN = os.getenv("DRY_RUN", "false")
44-
JOB_SUMMARY = os.getenv("JOB_SUMMARY", "false")
45-
GITHUB_STEP_SUMMARY = os.environ["GITHUB_STEP_SUMMARY"]
106+
# Initialize GitHub client
107+
g = Github(token)
108+
repo = g.get_repo(repo_name)
109+
pull_request = repo.get_issue(int(pr_number))
110+
111+
# Prepare comment content
112+
result_text = read_result_file()
113+
pr_comments = (
114+
SUCCESS_TITLE
115+
if result_text is None
116+
else f"{FAILURE_TITLE}\n```\n{result_text}\n```"
117+
)
118+
119+
# Fetch all existing comments on the PR
120+
comments = pull_request.get_comments()
121+
122+
# Track if we found a matching comment
123+
matching_comments = []
124+
last_comment = None
125+
126+
for comment in comments:
127+
if comment.body.startswith(SUCCESS_TITLE) or comment.body.startswith(
128+
FAILURE_TITLE
129+
):
130+
matching_comments.append(comment)
131+
if matching_comments:
132+
last_comment = matching_comments[-1]
133+
134+
if last_comment.body == pr_comments:
135+
print(f"PR comment already up-to-date for PR #{pr_number}.")
136+
return 0
137+
else:
138+
# If the last comment doesn't match, update it
139+
print(f"Updating the last comment on PR #{pr_number}.")
140+
last_comment.edit(pr_comments)
141+
142+
# Delete all older matching comments
143+
for comment in matching_comments[:-1]:
144+
print(f"Deleting an old comment on PR #{pr_number}.")
145+
comment.delete()
146+
else:
147+
# No matching comments, create a new one
148+
print(f"Creating a new comment on PR #{pr_number}.")
149+
pull_request.create_comment(body=pr_comments)
150+
151+
return 0 if result_text is None else 1
152+
except Exception as e:
153+
print(f"Error posting PR comment: {e}", file=sys.stderr)
154+
return 1
155+
156+
157+
def main():
158+
"""Main function to run commit-check, add job summary and post PR comments."""
159+
log_env_vars()
160+
161+
# Combine return codes
162+
ret_code = run_commit_check()
163+
ret_code += add_job_summary()
164+
ret_code += add_pr_comments()
46165

47-
print(f"MESSAGE = {MESSAGE}")
48-
print(f"BRANCH = {BRANCH}")
49-
print(f"AUTHOR_NAME = {AUTHOR_NAME}")
50-
print(f"AUTHOR_EMAIL = {AUTHOR_EMAIL}")
51-
print(f"COMMIT_SIGNOFF = {COMMIT_SIGNOFF}")
52-
print(f"DRY_RUN = {DRY_RUN}")
53-
print(f"JOB_SUMMARY = {JOB_SUMMARY}\n")
166+
if DRY_RUN == "true":
167+
ret_code = 0
54168

55-
ret_code = run_commit_check()
56-
ret_code += add_job_summary() # Combine return codes
169+
sys.exit(ret_code)
57170

58-
if DRY_RUN == "true":
59-
ret_code = 0
60171

61-
sys.exit(ret_code)
172+
if __name__ == "__main__":
173+
main()

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
# Install commit-check CLI
22
# For details please see: https://github.com/commit-check/commit-check
33
commit-check==0.8.3
4+
# Interact with the GitHub API.
5+
PyGithub==2.4.0

0 commit comments

Comments
 (0)