diff --git a/.github/workflows/c2c-ratio-pr.yml b/.github/workflows/c2c-ratio-pr.yml new file mode 100644 index 00000000..f9a96aa5 --- /dev/null +++ b/.github/workflows/c2c-ratio-pr.yml @@ -0,0 +1,51 @@ +name: Comment-to-code (C2C) ratio + +on: + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: >- + ${{ github.ref != 'refs/heads/master' && + github.event_name != 'merge_group' && + !startsWith(github.ref, 'refs/heads/gh-readonly-queue') }} + +permissions: + contents: read + +jobs: + comment-to-code-ratio: + runs-on: ubuntu-24.04 + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Install cloc + run: npm install --global cloc + + - name: Generate report + continue-on-error: true + uses: deep5050/comment-to-code-ratio-action@v1.0.1 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + issue_number: ${{ github.event.pull_request.number }} + options: >- + --quiet + --git + --diff + ${{ github.event.pull_request.base.sha }} + ${{ github.event.pull_request.head.sha }} + --md + --out=report.md + --exclude-dir=.git,3rdparty,build + + - name: Check ratio + id: ratio + run: python3 scripts/check_comment_to_code_ratio.py report.md --limit 10 + + - name: Print final percentage + if: always() + run: | + echo "Comment percentage for PR changes: ${{ steps.ratio.outputs.comment_percentage || 'N/A' }}%" diff --git a/scripts/check_comment_to_code_ratio.py b/scripts/check_comment_to_code_ratio.py new file mode 100644 index 00000000..6962037b --- /dev/null +++ b/scripts/check_comment_to_code_ratio.py @@ -0,0 +1,108 @@ +import argparse +import os +import sys +from pathlib import Path + +EXPECTED_HEADER_SUFFIX = [ + "== files", + "!= files", + "+ files", + "- files", + "== blank", + "!= blank", + "+ blank", + "- blank", + "== comment", + "!= comment", + "+ comment", + "- comment", + "== code", + "!= code", + "+ code", + "- code", +] +# Zero-based indices in `cloc --diff --md` rows after splitting by `|`. +COMMENT_COLUMN_INDICES = (10, 11, 12) +CODE_COLUMN_INDICES = (14, 15, 16) + + +def parse_changed_totals(report_path): + changed_comments = 0 + changed_code = 0 + header_found = False + + for raw_line in report_path.read_text(encoding="utf-8").splitlines(): + if "|" not in raw_line: + continue + + cells = [cell.strip() for cell in raw_line.split("|")] + if cells and cells[-1] == "": + cells = cells[:-1] + + if cells and cells[0] in {"Language", "File"}: + if cells[1:] != EXPECTED_HEADER_SUFFIX: + sys.exit("Unexpected cloc diff table format.") + header_found = True + continue + + if not cells: + continue + if cells[0].startswith((":", "-", "SUM:")): + continue + if not header_found: + continue + + try: + changed_comments += sum( + int(cells[index]) for index in COMMENT_COLUMN_INDICES + ) + changed_code += sum(int(cells[index]) for index in CODE_COLUMN_INDICES) + except (IndexError, ValueError): + continue + + if not header_found: + print("Unable to find cloc diff table header in report.") + sys.exit(1) + + return changed_comments, changed_code + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("report") + parser.add_argument("--limit", type=float, default=10.0) + args = parser.parse_args() + + report_path = Path(args.report) + if not report_path.exists(): + print(f"{report_path} was not generated by the comment ratio action.") + sys.exit(1) + + changed_comments, changed_code = parse_changed_totals(report_path) + total_changed_nonblank = changed_comments + changed_code + comment_percentage = ( + 0.0 + if total_changed_nonblank == 0 + else changed_comments * 100.0 / total_changed_nonblank + ) + + print(f"Comment percentage for PR changes: {comment_percentage:.2f}%") + + github_output = os.getenv("GITHUB_OUTPUT") + if github_output: + with Path(github_output).open("a", encoding="utf-8") as output: + output.write(f"changed_comments={changed_comments}\n") + output.write(f"changed_code={changed_code}\n") + output.write(f"comment_percentage={comment_percentage:.2f}\n") + + if comment_percentage > args.limit: + print( + f"Comment percentage for PR changes is {comment_percentage:.2f}%, " + f"which exceeds {args.limit:.2f}%.", + file=sys.stderr, + ) + sys.exit(1) + + +if __name__ == "__main__": + main()