diff --git a/.commit-check.yml b/.commit-check.yml index bfabe35..15ac7e4 100644 --- a/.commit-check.yml +++ b/.commit-check.yml @@ -7,19 +7,19 @@ checks: [optional body]\n [optional footer(s)]\n\n More details please refer to https://www.conventionalcommits.org" - suggest: git commit --amend --no-verify + suggest: please check your commit message whether matches above regex - check: branch - regex: ^(bugfix|feature|release|hotfix|task|dependabot)\/.+|(master)|(main)|(HEAD)|(PR-.+) - error: "Branches must begin with these types: bugfix/ feature/ release/ hotfix/ task/" - suggest: git checkout -b type/branch_name + regex: ^(bugfix|feature|release|hotfix|task|chore)\/.+|(master)|(main)|(HEAD)|(PR-.+) + error: "Branches must begin with these types: bugfix/ feature/ release/ hotfix/ task/ chore/" + suggest: run command `git checkout -b type/branch_name` - check: author_name regex: ^[A-Za-z ,.\'-]+$|.*(\[bot]) error: The committer name seems invalid - suggest: git config user.name "Peter Shen" + suggest: run command `git config user.name "Your Name"` - check: author_email regex: ^\S+@\S+\.\S+$ error: The committer email seems invalid - suggest: git config user.email petershen@example.com + suggest: run command `git config user.email yourname@example.com` diff --git a/.github/workflows/commit-check.yml b/.github/workflows/commit-check.yml index 96d775a..ecea18c 100644 --- a/.github/workflows/commit-check.yml +++ b/.github/workflows/commit-check.yml @@ -13,6 +13,8 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha }} - uses: ./ # self test + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # used by `pr-comments` with: message: true branch: true @@ -20,3 +22,4 @@ jobs: author-email: true commit-signoff: true job-summary: true + pr-comments: true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..b45ec09 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,28 @@ +# https://pre-commit.com/ +ci: + autofix_commit_msg: 'ci: auto fixes from pre-commit.com hooks' + autoupdate_commit_msg: 'ci: pre-commit autoupdate' + +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: check-yaml + - id: check-toml + - id: end-of-file-fixer + - id: trailing-whitespace + - id: name-tests-test + - id: requirements-txt-fixer +- repo: https://github.com/psf/black-pre-commit-mirror + rev: 24.10.0 + hooks: + - id: black +# FIXME: main.py:109: error: Item "None" of "str | None" has no attribute "split" [union-attr] +# - repo: https://github.com/pre-commit/mirrors-mypy +# rev: v1.12.0 +# hooks: +# - id: mypy +- repo: https://github.com/codespell-project/codespell + rev: v2.3.0 + hooks: + - id: codespell diff --git a/README.md b/README.md index 426ff0e..c9a2679 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} # Checkout PR HEAD commit - uses: commit-check/commit-check-action@v1 with: message: true @@ -34,6 +36,7 @@ jobs: commit-signoff: true dry-run: true job-summary: true + pr-comments: true ``` ## Optional Inputs @@ -72,22 +75,41 @@ jobs: ### `job-summary` -- **Description**: display job summary to a workflow run +- **Description**: display job summary to the workflow run - Default: 'true' +### `pr-comments` + +- **Description**: post results to the pull request comments +- Default: 'true' + +> [!IMPORTANT] +> This is a experimental feature +> use it you need to set `GITHUB_TOKEN` in the GitHub Action. + 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. ## GitHub Action job summary -By default, commit-check-action results are shown on the job summary page of the workflow. +By default, commit-check-action results are shown on the job summary page of the workflow. ### Success job summary - + ### Failure job summary - + + +## GitHub Pull Request comments + +### Success pull request comment + + + +### Failure pull request comment + + ## Badging your repository diff --git a/action.yml b/action.yml index 833ba66..804f81c 100644 --- a/action.yml +++ b/action.yml @@ -30,7 +30,11 @@ inputs: required: false default: false job-summary: - description: add a job summary + description: display job summary to the workflow run + required: false + default: true + pr-comments: + description: post results to the pull request comments required: false default: true runs: @@ -55,3 +59,4 @@ runs: COMMIT_SIGNOFF: ${{ inputs.commit-signoff }} DRY_RUN: ${{ inputs.dry-run }} JOB_SUMMARY: ${{ inputs.job-summary }} + PR_COMMENTS: ${{ inputs.pr-comments }} diff --git a/main.py b/main.py index 2b07aec..eb0af9c 100755 --- a/main.py +++ b/main.py @@ -3,59 +3,171 @@ import sys import subprocess import re +from github import Github + + +# Constants for message titles +SUCCESS_TITLE = "# Commit-Check ✔️" +FAILURE_TITLE = "# Commit-Check ❌" + +# Environment variables +MESSAGE = os.getenv("MESSAGE", "false") +BRANCH = os.getenv("BRANCH", "false") +AUTHOR_NAME = os.getenv("AUTHOR_NAME", "false") +AUTHOR_EMAIL = os.getenv("AUTHOR_EMAIL", "false") +COMMIT_SIGNOFF = os.getenv("COMMIT_SIGNOFF", "false") +DRY_RUN = os.getenv("DRY_RUN", "false") +JOB_SUMMARY = os.getenv("JOB_SUMMARY", "false") +PR_COMMENTS = os.getenv("PR_COMMENTS", "false") +GITHUB_STEP_SUMMARY = os.environ["GITHUB_STEP_SUMMARY"] +GITHUB_TOKEN = os.getenv("GITHUB_TOKEN") +GITHUB_REPOSITORY = os.getenv("GITHUB_REPOSITORY") +GITHUB_REF = os.getenv("GITHUB_REF") + + +def log_env_vars(): + """Logs the environment variables for debugging purposes.""" + print(f"MESSAGE = {MESSAGE}") + print(f"BRANCH = {BRANCH}") + print(f"AUTHOR_NAME = {AUTHOR_NAME}") + print(f"AUTHOR_EMAIL = {AUTHOR_EMAIL}") + print(f"COMMIT_SIGNOFF = {COMMIT_SIGNOFF}") + print(f"DRY_RUN = {DRY_RUN}") + print(f"JOB_SUMMARY = {JOB_SUMMARY}") + print(f"PR_COMMENTS = {PR_COMMENTS}\n") def run_commit_check() -> int: - args = ["--message", "--branch", "--author-name", "--author-email", "--commit-signoff"] - args = [arg for arg, value in zip(args, [MESSAGE, BRANCH, AUTHOR_NAME, AUTHOR_EMAIL, COMMIT_SIGNOFF]) if value == "true"] + """Runs the commit-check command and logs the result.""" + args = [ + "--message", + "--branch", + "--author-name", + "--author-email", + "--commit-signoff", + ] + args = [ + arg + for arg, value in zip( + args, [MESSAGE, BRANCH, AUTHOR_NAME, AUTHOR_EMAIL, COMMIT_SIGNOFF] + ) + if value == "true" + ] command = ["commit-check"] + args print(" ".join(command)) with open("result.txt", "w") as result_file: - result = subprocess.run(command, stdout=result_file, stderr=subprocess.PIPE, check=False) + result = subprocess.run( + command, stdout=result_file, stderr=subprocess.PIPE, check=False + ) return result.returncode +def read_result_file() -> str | None: + """Reads the result.txt file and removes ANSI color codes.""" + if os.path.getsize("result.txt") > 0: + with open("result.txt", "r") as result_file: + result_text = re.sub( + r"\x1B\[[0-9;]*[a-zA-Z]", "", result_file.read() + ) # Remove ANSI colors + return result_text.rstrip() + return None + + def add_job_summary() -> int: + """Adds the commit check result to the GitHub job summary.""" if JOB_SUMMARY == "false": - sys.exit() + return 0 - if os.path.getsize("result.txt") > 0: - with open("result.txt", "r") as result_file: - result_text = re.sub(r'\x1B\[[0-9;]*[a-zA-Z]', '', result_file.read()) # Remove ANSI colors + result_text = read_result_file() - with open(GITHUB_STEP_SUMMARY, "a") as summary_file: - summary_file.write("### Commit-Check ❌\n```\n") - summary_file.write(result_text) - summary_file.write("```") - return 1 - else: - with open(GITHUB_STEP_SUMMARY, "a") as summary_file: - summary_file.write("### Commit-Check ✔️\n") + summary_content = ( + SUCCESS_TITLE + if result_text is None + else f"{FAILURE_TITLE}\n```\n{result_text}\n```" + ) + + with open(GITHUB_STEP_SUMMARY, "a") as summary_file: + summary_file.write(summary_content) + + return 0 if result_text is None else 1 + + +def add_pr_comments() -> int: + """Posts the commit check result as a comment on the pull request.""" + if PR_COMMENTS == "false": return 0 + try: + token = os.getenv("GITHUB_TOKEN") + repo_name = os.getenv("GITHUB_REPOSITORY") + pr_number = os.getenv("GITHUB_REF").split("/")[-2] -MESSAGE = os.getenv("MESSAGE", "false") -BRANCH = os.getenv("BRANCH", "false") -AUTHOR_NAME = os.getenv("AUTHOR_NAME", "false") -AUTHOR_EMAIL = os.getenv("AUTHOR_EMAIL", "false") -COMMIT_SIGNOFF = os.getenv("COMMIT_SIGNOFF", "false") -DRY_RUN = os.getenv("DRY_RUN", "false") -JOB_SUMMARY = os.getenv("JOB_SUMMARY", "false") -GITHUB_STEP_SUMMARY = os.environ["GITHUB_STEP_SUMMARY"] + # Initialize GitHub client + g = Github(token) + repo = g.get_repo(repo_name) + pull_request = repo.get_issue(int(pr_number)) + + # Prepare comment content + result_text = read_result_file() + pr_comments = ( + SUCCESS_TITLE + if result_text is None + else f"{FAILURE_TITLE}\n```\n{result_text}\n```" + ) + + # Fetch all existing comments on the PR + comments = pull_request.get_comments() + + # Track if we found a matching comment + matching_comments = [] + last_comment = None + + for comment in comments: + if comment.body.startswith(SUCCESS_TITLE) or comment.body.startswith( + FAILURE_TITLE + ): + matching_comments.append(comment) + if matching_comments: + last_comment = matching_comments[-1] + + if last_comment.body == pr_comments: + print(f"PR comment already up-to-date for PR #{pr_number}.") + return 0 + else: + # If the last comment doesn't match, update it + print(f"Updating the last comment on PR #{pr_number}.") + last_comment.edit(pr_comments) + + # Delete all older matching comments + for comment in matching_comments[:-1]: + print(f"Deleting an old comment on PR #{pr_number}.") + comment.delete() + else: + # No matching comments, create a new one + print(f"Creating a new comment on PR #{pr_number}.") + pull_request.create_comment(body=pr_comments) + + return 0 if result_text is None else 1 + except Exception as e: + print(f"Error posting PR comment: {e}", file=sys.stderr) + return 1 + + +def main(): + """Main function to run commit-check, add job summary and post PR comments.""" + log_env_vars() + + # Combine return codes + ret_code = run_commit_check() + ret_code += add_job_summary() + ret_code += add_pr_comments() -print(f"MESSAGE = {MESSAGE}") -print(f"BRANCH = {BRANCH}") -print(f"AUTHOR_NAME = {AUTHOR_NAME}") -print(f"AUTHOR_EMAIL = {AUTHOR_EMAIL}") -print(f"COMMIT_SIGNOFF = {COMMIT_SIGNOFF}") -print(f"DRY_RUN = {DRY_RUN}") -print(f"JOB_SUMMARY = {JOB_SUMMARY}\n") + if DRY_RUN == "true": + ret_code = 0 -ret_code = run_commit_check() -ret_code += add_job_summary() # Combine return codes + sys.exit(ret_code) -if DRY_RUN == "true": - ret_code = 0 -sys.exit(ret_code) +if __name__ == "__main__": + main() diff --git a/requirements.txt b/requirements.txt index cd2611c..8a6ff3f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ # Install commit-check CLI # For details please see: https://github.com/commit-check/commit-check commit-check==0.8.3 +# Interact with the GitHub API. +PyGithub==2.4.0
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: