修正
All checks were successful
Python Test / python-test (push) Successful in 10s

This commit is contained in:
ry.yamafuji 2025-12-05 00:44:16 +09:00
parent 02d6d34b02
commit f0a74c1122
8 changed files with 83 additions and 53 deletions

View File

@ -48,8 +48,19 @@ jobs:
- name: pull_request message with Ruff Lint results - name: pull_request message with Ruff Lint results
id: prMessageRuffLint id: prMessageRuffLint
run: | run: |
# イベントがプルリクエストの場合はPRにコメントを追加する
if [ "${{ github.event_name }}" = "pull_request" ]; then
echo "Posting Ruff Lint results to Pull Request..."
curl -v -X POST \ curl -v -X POST \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \ -H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
-d @lint-result.json \ -d @lint-result.json \
${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/issues/8/comments ${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/issues/${{ github.event.pull_request.number }}/comments
else
echo "Not a pull request event."
echo "Ruff Lint results:"
echo "-------------------"
cat lint-result.md
echo "-------------------"
echo "No PR detected. Skipping API comment."
fi

2
.gitignore vendored
View File

@ -4,7 +4,9 @@ __pycache__/
*.py[cod] *.py[cod]
*$py.class *$py.class
ruff-report.* ruff-report.*
lint-result.md
lint-result.json lint-result.json
# C extensions # C extensions
*.so *.so

View File

@ -1,15 +0,0 @@
## Linter(リンタ)レビュー
以下の指摘事項があります。コードを見直してください。
総数:6個
### 指摘事項一覧
|コード|重要性|項目|ファイル名|行数|
|---|---|---|---|---|
|W291|🟢低|行末に不要な空白があります。|examples/example.py|14行目27列 〜 14行目28列|
|W291|🟢低|行末に不要な空白があります。|scripts/generate_coverage.py|5行目23列 〜 5行目24列|
|E501|🟢低|行が長すぎます。79文字以内にしてください。|scripts/generate_coverage.py|38行目89列 〜 38行目107列|
|W291|🟢低|行末に不要な空白があります。|scripts/generate_linter_from_json.py|4行目23列 〜 4行目24列|
|W291|🟢低|行末に不要な空白があります。|scripts/generate_linter_from_json.py|25行目31列 〜 25行目39列|
|W292|🟢低|ファイルの最後に改行がありません。|scripts/generate_linter_from_json.py|53行目42列 〜 53行目42列|

View File

@ -1,6 +1,12 @@
line-length = 88 line-length = 88
# 末尾スペース・空行まわりをチェックするのは E と W系のルール # 末尾スペース・空行まわりをチェックするのは E と W系のルール
# E7xx/E9xx構文/実行時エラーの可能性)
# W1xx/W5xxスタイル・フォーマット関連
# DXXXドキュメンテーション文字列関連
# F (未使用インポートなどのエラー)
# BXXバグの可能性
[lint] [lint]
select = ["E", "W"] select = ["F", "E", "W", "D101", "B"]
ignore = [] ignore = []

View File

@ -2,9 +2,11 @@ import re
class GenerateCoverage: class GenerateCoverage:
def __init__(self, """カバレッジ結果を解析して Markdown テーブルを生成"""
coverage_file="pytest-coverage.txt",
output_file="coverage_table.md"): def __init__(
self, coverage_file="pytest-coverage.txt", output_file="coverage_table.md"
):
""" """
初期化 初期化
@ -35,26 +37,28 @@ class GenerateCoverage:
if in_coverage_section and line.strip().startswith("---"): if in_coverage_section and line.strip().startswith("---"):
continue continue
# Coverage セクションの終わりを検出TOTAL行の次の空行 # Coverage セクションの終わりを検出TOTAL行の次の空行
if in_coverage_section and (line.strip().startswith("TOTAL") or line.strip().startswith("=")): if in_coverage_section and (
line.strip().startswith("TOTAL") or line.strip().startswith("=")
):
break break
# Coverage データを抽出 # Coverage データを抽出
if in_coverage_section: if in_coverage_section:
match = re.match( match = re.match(r"(.+?)\s+(\d+)\s+(\d+)\s+(\d+%)\s*(.*)", line)
r"(.+?)\s+(\d+)\s+(\d+)\s+(\d+%)\s*(.*)", line)
if match: if match:
filename = match.group(1).strip() filename = match.group(1).strip()
statements = match.group(2).strip() statements = match.group(2).strip()
missed = match.group(3).strip() missed = match.group(3).strip()
coverage = match.group(4).strip() coverage = match.group(4).strip()
missing_lines = match.group( missing_lines = match.group(5).strip() if match.group(5) else "-"
5).strip() if match.group(5) else "-" coverage_info.append(
coverage_info.append({ {
"filename": filename, "filename": filename,
"statements": statements, "statements": statements,
"missed": missed, "missed": missed,
"coverage": coverage, "coverage": coverage,
"missing_lines": missing_lines "missing_lines": missing_lines,
}) }
)
self.coverage_data = coverage_info self.coverage_data = coverage_info
@ -72,9 +76,11 @@ class GenerateCoverage:
# テーブル行を生成 # テーブル行を生成
table_rows = [ table_rows = [
(f"| {data['filename']} | {data['statements']} | " (
f"| {data['filename']} | {data['statements']} | "
f"{data['missed']} | {data['coverage']} | " f"{data['missed']} | {data['coverage']} | "
f"{data['missing_lines']} |") f"{data['missing_lines']} |"
)
for data in self.coverage_data for data in self.coverage_data
] ]

View File

@ -7,13 +7,18 @@ print(f"Project Name: {PROJECT_NAME}")
CODE_MAP = { CODE_MAP = {
"W291": {"message": "行末に不要な空白があります。", "severity": "🟢低"}, "W291": {"message": "行末に不要な空白があります。", "severity": "🟢低"},
"W292": {"message": "ファイルの最後に改行がありません。", "severity": "🟢低"} , "W292": {"message": "ファイルの最後に改行がありません。", "severity": "🟢低"},
"E501": { "E501": {
"message": "行が長すぎます。79文字以内にしてください。", "message": "行が長すぎます。79文字以内にしてください。",
"severity": "🟢低", "severity": "🟢低",
}, },
"D101": {
"message": "クラスにドキュメンテーション文字列がありません。",
"severity": "⚪️無害",
},
} }
def get_relative_path(absolute_path: str) -> str: def get_relative_path(absolute_path: str) -> str:
""" """
絶対パスからプロジェクトルートからの相対パスを取得 絶対パスからプロジェクトルートからの相対パスを取得
@ -26,7 +31,8 @@ def get_relative_path(absolute_path: str) -> str:
class GenerateLinter: class GenerateLinter:
def __init__(self, json_file="ruff-report.json", output_file="lint-result.json"): """Linterレポートを生成するクラス"""
def __init__(self, json_file="ruff-report.json", output_file="lint-result"):
""" """
初期化 初期化
""" """
@ -46,43 +52,56 @@ class GenerateLinter:
_str += f"総数:{len(data)}\n" _str += f"総数:{len(data)}\n"
_str += "### 指摘事項一覧\n" _str += "### 指摘事項一覧\n"
_str += "|コード|重要性|項目|ファイル名|行数|\n" _str += "|コード|重要性|項目|ファイル名|行数|自動修正|\n"
_str += "|---|---|---|---|---|\n" _str += "|---|---|---|---|---|---|\n"
for issue in data: for issue in data:
code = issue.get("code", "-") code = issue.get("code", "-")
severity = ( severity = (
CODE_MAP.get(code, {}).get("severity", "❓不明") CODE_MAP.get(code, {}).get("severity", "❓不明") if code != "-" else "-"
if code != "-"
else "-"
) )
message = CODE_MAP.get(code, {}).get("message", issue.get("message", "-")) message = CODE_MAP.get(code, {}).get("message", issue.get("message", "-"))
filename = get_relative_path(issue.get("filename", "-")) filename = get_relative_path(issue.get("filename", "-"))
file_link = f"./{filename}"
line = "" line = ""
if issue.get("location") and issue["location"].get("row"): if issue.get("location") and issue["location"].get("row"):
line = f"{issue['location']['row']}行目" line = f"{issue['location']['row']}行目"
if issue["location"].get("column"): if issue["location"].get("column"):
line += f"{issue['location']['column']}" line += f"{issue['location']['column']}"
if issue.get("end_location"): if issue.get("end_location"):
if issue["end_location"].get("row"): if issue["end_location"].get("row"):
line += f"{issue['end_location']['row']}行目" line += f"{issue['end_location']['row']}行目"
if issue["end_location"].get("column"): if issue["end_location"].get("column"):
line += f"{issue['end_location']['column']}" line += f"{issue['end_location']['column']}"
auto_fix = "" if issue.get("fix") else ""
_str += f"|{code}|{severity}|{message}|"
_str += f"[{filename}]({file_link})|{line}|{auto_fix}|\n"
_str += "\n\n"
_str += "### 自動修正コマンド\n"
_str += ("自動修正が可能な指摘事項については、"
"以下のコマンドで自動修正を試みることができます。\n\n"
)
_str += "```bash\n"
_str += "ruff check --fix .\n"
_str += "```\n\n"
_str += f"|{code}|{severity}|{message}|{filename}|{line}|\n"
return _str return _str
def generate_lint_report_json(self): def generate_lint_report_json(self):
with open(self.json_file, "r") as f: with open(self.json_file, "r") as f:
data = json.load(f) data = json.load(f)
with open(self.output_file, "w") as f: with open(f"{self.output_file}.md", "w") as f:
# report_body = self._genarate_lint_report(data) report_body = self._genarate_lint_report(data)
# f.write(report_body) f.write(report_body)
with open(f"{self.output_file}.json", "w") as f:
report = {"body": self._genarate_lint_report(data)} report = {"body": self._genarate_lint_report(data)}
json.dump(report, f, ensure_ascii=False, indent=4) json.dump(report, f, ensure_ascii=False, indent=4)
print(f"Linter report generated: {self.output_file}") print(f"Linter report generated: {self.output_file}.md, {self.output_file}.json")
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -11,6 +11,7 @@ import threading
class Singleton(object): class Singleton(object):
"""シングルトンパターンの基底クラス"""
_instances = {} _instances = {}
_lock = threading.Lock() _lock = threading.Lock()