diff --git a/.github/workflows/commit-check.yml b/.github/workflows/commit-check.yml index d9faf0e..b68a5dc 100644 --- a/.github/workflows/commit-check.yml +++ b/.github/workflows/commit-check.yml @@ -16,8 +16,6 @@ jobs: with: fetch-depth: 0 # Required for merge-base checks - uses: ./ # self test - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed for PR comments with: message: true branch: true diff --git a/README.md b/README.md index 826bc2e..6da81e2 100644 --- a/README.md +++ b/README.md @@ -52,8 +52,6 @@ jobs: with: fetch-depth: 0 # Required for merge-base checks - uses: commit-check/commit-check-action@v2 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed for PR comments with: message: true branch: true @@ -123,7 +121,7 @@ jobs: - Default: `false` > [!IMPORTANT] -> `pr-comments` is an experimental feature. By default, it's disabled. To use it, you need to set `GITHUB_TOKEN` in the GitHub Action. +> `pr-comments` is an experimental feature. By default, it's disabled. > > This feature currently doesn’t work with forked repositories. For more details, refer to issue [#77](https://github.com/commit-check/commit-check-action/issues/77). diff --git a/action.yml b/action.yml index 68df492..98552ef 100644 --- a/action.yml +++ b/action.yml @@ -69,3 +69,4 @@ runs: DRY_RUN: ${{ inputs.dry-run }} JOB_SUMMARY: ${{ inputs.job-summary }} PR_COMMENTS: ${{ inputs.pr-comments }} + GITHUB_TOKEN: ${{ github.token }} diff --git a/main.py b/main.py index 2ec7ea5..00876a1 100755 --- a/main.py +++ b/main.py @@ -35,35 +35,94 @@ def log_env_vars(): print(f"PR_COMMENTS = {PR_COMMENTS}\n") -def run_commit_check() -> int: - """Runs the commit-check command and logs the result.""" - args = [ - "--message", - "--branch", - "--author-name", - "--author-email", - ] - args = [ - arg - for arg, value in zip( - args, +def get_pr_commit_messages() -> list[str] | None: + """Get commit messages for all commits in a PR, excluding merge commits. + + In a GitHub Actions PR context, HEAD points to an auto-generated merge commit + (refs/pull/{N}/merge), not the actual PR commits. This function retrieves the + real commit messages so they can be validated individually. + + Returns None if not in a PR context or if no commits are found. + """ + base_ref = os.getenv("GITHUB_BASE_REF", "") + if not base_ref: + return None + + try: + result = subprocess.run( [ - MESSAGE, - BRANCH, - AUTHOR_NAME, - AUTHOR_EMAIL, + "git", + "log", + "--no-merges", + f"origin/{base_ref}..HEAD", + "--format=%B%x00", + "--reverse", ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + check=False, ) - if value == "true" + if result.returncode != 0 or not result.stdout.strip(): + return None + messages = [m.strip() for m in result.stdout.split("\x00") if m.strip()] + return messages if messages else None + except Exception: + return None + + +def run_commit_check() -> int: + """Runs the commit-check command and logs the result.""" + other_check_flags = [ + ("--branch", BRANCH), + ("--author-name", AUTHOR_NAME), + ("--author-email", AUTHOR_EMAIL), ] + other_args = [arg for arg, value in other_check_flags if value == "true"] - command = ["commit-check"] + args - print(" ".join(command)) + ret_code = 0 with open("result.txt", "w") as result_file: - result = subprocess.run( - command, stdout=result_file, stderr=subprocess.PIPE, check=False - ) - return result.returncode + if MESSAGE == "true": + commit_messages = get_pr_commit_messages() + if commit_messages: + # PR context: check each commit message individually to avoid + # only checking the auto-generated merge commit at HEAD. + for msg in commit_messages: + command = ["commit-check", "--message"] + print(" ".join(command)) + result = subprocess.run( + command, + input=msg, + text=True, + stdout=result_file, + stderr=subprocess.STDOUT, + check=False, + ) + ret_code += result.returncode + else: + # Non-PR context: let commit-check determine what to check from git. + command = ["commit-check", "--message"] + print(" ".join(command)) + result = subprocess.run( + command, + stdout=result_file, + stderr=subprocess.STDOUT, + check=False, + ) + ret_code += result.returncode + + if other_args: + command = ["commit-check"] + other_args + print(" ".join(command)) + result = subprocess.run( + command, + stdout=result_file, + stderr=subprocess.STDOUT, + check=False, + ) + ret_code += result.returncode + + return ret_code def read_result_file() -> str | None: