This commit is contained in:
parent
02d6d34b02
commit
f0a74c1122
21
.github/workflows/pyruff.yml
vendored
21
.github/workflows/pyruff.yml
vendored
@ -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: |
|
||||||
curl -v -X POST \
|
# イベントがプルリクエストの場合はPRにコメントを追加する
|
||||||
-H "Content-Type: application/json" \
|
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
||||||
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
|
echo "Posting Ruff Lint results to Pull Request..."
|
||||||
-d @lint-result.json \
|
curl -v -X POST \
|
||||||
${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/issues/8/comments
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
|
||||||
|
-d @lint-result.json \
|
||||||
|
${{ 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
2
.gitignore
vendored
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@ logger = get_logger(__name__)
|
|||||||
|
|
||||||
def example():
|
def example():
|
||||||
logger.info("Application started")
|
logger.info("Application started")
|
||||||
print("Hello, World!")
|
print("Hello, World!")
|
||||||
|
|
||||||
|
|
||||||
example()
|
example()
|
||||||
|
|||||||
@ -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列|
|
|
||||||
@ -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 = []
|
||||||
@ -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['missed']} | {data['coverage']} | "
|
f"| {data['filename']} | {data['statements']} | "
|
||||||
f"{data['missing_lines']} |")
|
f"{data['missed']} | {data['coverage']} | "
|
||||||
|
f"{data['missing_lines']} |"
|
||||||
|
)
|
||||||
for data in self.coverage_data
|
for data in self.coverage_data
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@ -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__":
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import threading
|
|||||||
|
|
||||||
|
|
||||||
class Singleton(object):
|
class Singleton(object):
|
||||||
|
"""シングルトンパターンの基底クラス"""
|
||||||
_instances = {}
|
_instances = {}
|
||||||
_lock = threading.Lock()
|
_lock = threading.Lock()
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user