Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 169 additions & 0 deletions .cursor/rules/source-review.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
---
description:
globs:
alwaysApply: false
---
# ソースレビュー ルール

## 概要

このルールは、現在チェックアウトしているブランチに紐づくプルリクエストの内容と差分を自動で取得し、どのような変更が行われたかを要約する一連の流れを、プロンプト一発で実施できるようにします。

---

## 使い方

1. 対象のブランチを `git checkout` でチェックアウトする。
2. 以下のプロンプトを入力するだけで、PRの内容・差分・要約が自動で出力されます。

このブランチで作成されているプルリクエストの内容と差分をチェックし、どのような変更が行われたかまとめてください。
また、レビューを詳しくした方が良いと思われる箇所に関して、提案してください。

---

## 実行フロー(自動化される処理)

1. `git branch --show-current` で現在のブランチ名を取得
2. `PAGER=cat gh pr view` でPRのタイトル・説明・レビュワー・状態などを取得
3. `PAGER=cat gh pr diff` でPRの差分(diff)を取得
4. 取得した情報をもとに、PRの目的・主な変更点・影響範囲などを日本語で要約

---

## レビュー観点(コーディングルールベース)

### 共通ルール
- **ボーイスカウトルール**: コードを見つけたときよりも綺麗にする
- **YAGNI原則**: 必要になるまで機能を実装しない
- **DRY原則**: 再利用可能なロジックを関数に抽出し、コードの重複を避ける
- **KISS原則**: シンプルで分かりやすいコードを書く
- **SOLID原則**: オブジェクト指向設計の原則に従う
- **タイポ検出**: タイポを検出し、修正を提案する

### TypeScript/JavaScript
- インデントには2スペースを使用
- Google JavaScript Style Guideに準拠
- MDNにリストされている非推奨または廃止された機能の使用を避ける
- プロパティ名にはキャメルケースを使用し、必要に応じてPropTypesを適用
- フックの使用に関する公式Reactガイドラインに従う
- インラインスタイルを避ける
- 'var'の代わりに'let'または'const'を使用
- パフォーマンス最適化(メモ化、レンダリング最適化)を適切に実装
- 不要な再レンダリングを防ぐために、関数コンポーネントにはReact.memo()を、クラスコンポーネントにはPureComponentを使用
- コード分割と遅延ロードを実装
- セキュリティ脆弱性(特にXSS)に注意
- ユーザー入力をサニタイズし、dangerouslySetInnerHTMLの使用を避ける

### Go
- インデントにはタブを使用(Go言語の標準的な方法)
- gofmtツールを使用してコードをフォーマット
- goimportsを使用してインポートを管理
- go vetを使用して疑わしい構造を検出
- golintを使用してスタイルエラーを検出
- テストとベンチマークの実行
- Go Style Guideに従う
- クリーンアーキテクチャの原則に従う
- DDD(Domain-Driven Design)の原則を適用

### React (TSX)
- セマンティックなHTML要素を使用
- Reactのベストプラクティスに従う
- WAI-ARIA属性を実装
- 適切なキーボードナビゲーションサポート
- 可視テキストのない要素にはaria-labelまたはaria-labelledbyを使用
- モーダルやその他の動的コンテンツにフォーカス管理を実装

### テスト
- テストフレームワークのベストプラクティスに従う
- テストの説明を詳細に記述
- 適切なアサーションとマッチャーを使用
- 重要なパスとエッジケースのテストカバレッジを実装
- テストを独立させ、テスト間で状態を共有しない

### Docker
- Dockerfile Best Practicesに従う
- マルチステージビルドを活用
- 適切なベースイメージを選択
- ビルドキャッシュを効果的に利用
- 最小権限の原則に従う
- .dockerignoreファイルを使用
- RUN命令を結合
- 適切な環境変数の設定
- 必要最小限のファイルのみをコピー
- ヘルスチェックの実装
- Linterツールの使用を検討

### Markdown
- [markdownlint-cli2](https:/github.com/DavidAnson/markdownlint-cli2)に準拠していること

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Markdownリンク構文のエラーを修正してください

mdc: プレフィックスが不正な形式になっています。標準的なMarkdownリンク構文を使用する必要があります。

以下の差分を適用してリンク構文を修正してください:

-- [markdownlint-cli2](mdc:https:/github.com/DavidAnson/markdownlint-cli2)に準拠していること
+- [markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2)に準拠していること
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2)に準拠していること
🤖 Prompt for AI Agents
In .cursor/rules/source-review.mdc at line 98, the markdown link syntax uses an
incorrect `mdc:` prefix. Replace this with the standard markdown link format by
enclosing the link text in square brackets followed immediately by the URL in
parentheses, ensuring no extra prefixes are included.

---

## 自動実行設定(git push時)

### Git Hooks設定

#### 1. pre-pushフックの作成
```bash
# .githooks/pre-push ファイルを作成
#!/bin/bash
set -e

echo "🔍 ソースレビューを開始します..."

# 現在のブランチ名を取得
CURRENT_BRANCH=$(git branch --show-current)

# プルリクエストが存在するかチェック
if gh pr view --json url 2>/dev/null | grep -q "url"; then
echo "📋 プルリクエストが見つかりました。レビューを実行します..."

# PRの情報を取得
echo "=== PR情報 ==="
PAGER=cat gh pr view

echo ""
echo "=== 差分 ==="
PAGER=cat gh pr diff

echo ""
echo "=== レビュー結果 ==="
echo "このPRのレビューが完了しました。詳細は上記の情報を確認してください。"
else
echo "⚠️ このブランチにはプルリクエストが作成されていません。"
echo "プルリクエストを作成してから再度pushしてください。"
exit 1
fi
Comment on lines +117 to +135
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

プルリクエスト存在チェックのエラーハンドリングを改善してください

現在の実装では以下の問題があります:

  1. gh pr view コマンドのエラーハンドリングが不完全
  2. GitHub認証エラーや接続エラーが適切に処理されていない
  3. exit 1 でpushを完全にブロックするのは過度に厳格な可能性があります

以下の差分でエラーハンドリングを改善することを提案します:

-# プルリクエストが存在するかチェック
-if gh pr view --json url 2>/dev/null | grep -q "url"; then
+# GitHub認証とプルリクエストの存在チェック
+if ! gh auth status >/dev/null 2>&1; then
+    echo "⚠️  GitHub認証が必要です。'gh auth login'を実行してください。"
+    exit 1
+fi
+
+if gh pr view --json url >/dev/null 2>&1; then
     echo "📋 プルリクエストが見つかりました。レビューを実行します..."
     
     # PRの情報を取得
     echo "=== PR情報 ==="
     PAGER=cat gh pr view
     
     echo ""
     echo "=== 差分 ==="
     PAGER=cat gh pr diff
     
     echo ""
     echo "=== レビュー結果 ==="
     echo "このPRのレビューが完了しました。詳細は上記の情報を確認してください。"
 else
     echo "⚠️  このブランチにはプルリクエストが作成されていません。"
-    echo "プルリクエストを作成してから再度pushしてください。"
-    exit 1
+    echo "プルリクエストを作成することをお勧めします。"
+    echo "続行しますか? (y/N): "
+    read -r response
+    if [[ ! "$response" =~ ^[Yy]$ ]]; then
+        exit 1
+    fi
 fi

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In .cursor/rules/source-review.mdc around lines 117 to 135, improve the error
handling for the `gh pr view` command by explicitly checking for command failure
and handling GitHub authentication or connection errors gracefully. Instead of
relying solely on grep, capture the command's exit status and output, provide
clear error messages for authentication or network issues, and avoid using `exit
1` to block the push completely; consider allowing the script to continue or
warn the user without terminating the process.

```
Comment on lines +108 to +136
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

シェルスクリプトのセキュリティとエラーハンドリングを強化してください

pre-pushフックのスクリプトに以下の改善が必要です:

  1. 環境変数の検証
  2. コマンドの実行結果の適切なチェック
  3. 一時的な障害への対応

スクリプトの冒頭に以下の検証を追加することを提案します:

 #!/bin/bash
 set -e
+set -u
+set -o pipefail

 echo "🔍 ソースレビューを開始します..."

+# 必要なコマンドの存在確認
+for cmd in git gh; do
+    if ! command -v "$cmd" >/dev/null 2>&1; then
+        echo "❌ $cmd コマンドが見つかりません。インストールしてください。"
+        exit 1
+    fi
+done
+
 # 現在のブランチ名を取得
-CURRENT_BRANCH=$(git branch --show-current)
+CURRENT_BRANCH=$(git branch --show-current 2>/dev/null || echo "")
+if [[ -z "$CURRENT_BRANCH" ]]; then
+    echo "❌ ブランチ名を取得できませんでした。"
+    exit 1
+fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#!/bin/bash
set -e
echo "🔍 ソースレビューを開始します..."
# 現在のブランチ名を取得
CURRENT_BRANCH=$(git branch --show-current)
# プルリクエストが存在するかチェック
if gh pr view --json url 2>/dev/null | grep -q "url"; then
echo "📋 プルリクエストが見つかりました。レビューを実行します..."
# PRの情報を取得
echo "=== PR情報 ==="
PAGER=cat gh pr view
echo ""
echo "=== 差分 ==="
PAGER=cat gh pr diff
echo ""
echo "=== レビュー結果 ==="
echo "このPRのレビューが完了しました。詳細は上記の情報を確認してください。"
else
echo "⚠️ このブランチにはプルリクエストが作成されていません。"
echo "プルリクエストを作成してから再度pushしてください。"
exit 1
fi
```
#!/bin/bash
set -e
set -u
set -o pipefail
echo "🔍 ソースレビューを開始します..."
# 必要なコマンドの存在確認
for cmd in git gh; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "❌ $cmd コマンドが見つかりません。インストールしてください。"
exit 1
fi
done
# 現在のブランチ名を取得
CURRENT_BRANCH=$(git branch --show-current 2>/dev/null || echo "")
if [[ -z "$CURRENT_BRANCH" ]]; then
echo "❌ ブランチ名を取得できませんでした。"
exit 1
fi
# プルリクエストが存在するかチェック
if gh pr view --json url 2>/dev/null | grep -q "url"; then
echo "📋 プルリクエストが見つかりました。レビューを実行します..."
# PRの情報を取得
echo "=== PR情報 ==="
PAGER=cat gh pr view
echo ""
echo "=== 差分 ==="
PAGER=cat gh pr diff
echo ""
echo "=== レビュー結果 ==="
echo "このPRのレビューが完了しました。詳細は上記の情報を確認してください。"
else
echo "⚠️ このブランチにはプルリクエストが作成されていません。"
echo "プルリクエストを作成してから再度pushしてください。"
exit 1
fi
🤖 Prompt for AI Agents
In .cursor/rules/source-review.mdc around lines 108 to 136, the shell script
lacks robust environment variable validation, command execution result checks,
and retry logic for transient failures. To fix this, add explicit checks for
required environment variables at the start, verify the success of each critical
command before proceeding, and implement retry mechanisms or error handling for
commands that might fail temporarily, ensuring the script exits gracefully with
informative messages on failure.


#### 2. フックファイルの権限設定
```bash
chmod +x .githooks/pre-push
```

#### 3. Git設定でフックディレクトリを指定
```bash
git config core.hooksPath .githooks
```

### 実行タイミング
- `git push` 実行時に自動的にソースレビューが実行される
- プルリクエストが存在しない場合はpushがブロックされる
- レビュー結果がコンソールに出力される

---

## 注意事項
- `gh` CLIがインストールされている必要があります。
- GitHub認証が済んでいる必要があります。
- `PAGER`環境変数の影響でエラーが出る場合は、一時的に`PAGER=cat`を利用します。
- プルリクエストが作成されていないブランチではpushが失敗します。

---

## 例

このブランチで作成されているプルリクエストの内容と差分をチェックし、どのような変更が行われたかまとめてください。

---

このルールを使うことで、PRレビューの効率化・自動化が可能です。
7 changes: 7 additions & 0 deletions .cursor/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// TODO:機能している?
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

JSONにコメントは許可されていません。
このままではパースエラーになります。コメントを削除するか、.jsoncに変更してください。

🧰 Tools
🪛 Biome (1.9.4)

[error] 1-3: Expected an array, an object, or a literal but instead found '// TODO:機能している?'.

Expected an array, an object, or a literal here.

(parse)

🤖 Prompt for AI Agents
In .cursor/settings.json at line 1, there is a comment which is not allowed in
standard JSON and will cause a parse error. Remove the comment entirely or
rename the file to use the .jsonc extension to allow comments.

{
"cursor.review": {
"enabled": true,
"autoReview": true
}
}
3 changes: 1 addition & 2 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ if [ -n "$CHANGED_GO_FILES" ]; then
# Lint
echo "Running golangci-lint..."
docker-compose run -T --rm lint
if [ $? -ne 0 ];then
if [ $? -ne 0 ]; then
echo "golangci-lint failed. Please fix the issues before committing."
exit 1
fi
Expand All @@ -32,7 +32,6 @@ if [ -n "$CHANGED_MD_FILES" ]; then
# Markdown Lint
echo "Running markdownlint..."
docker-compose run -T --rm markdownlint npx markdownlint-cli2 --fix $CHANGED_MD_FILES
# npx markdownlint-cli2 --fix $CHANGED_MD_FILES
if [ $? -ne 0 ]; then
echo "markdownlint failed. Please fix the issues before committing."
exit 1
Expand Down
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,3 @@

# テストカバレッジレポート
*/coverage.out

node_modules/
3 changes: 2 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"bierner.markdown-mermaid",
"bradlc.vscode-tailwindcss",
"christian-kohler.npm-intellisense",
"davidanson.vscode-markdownlint"
"davidanson.vscode-markdownlint",
"ms-azuretools.vscode-docker"
]
}
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
# README

## Setup

[Setup procedure](./docs/develop/setup/index.md)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

リンク先ファイルの存在を確認してください。
docs/develop/setup/index.md が実際に存在するかご確認を。存在しない場合、リンク切れになります。


🏁 Script executed:

#!/bin/bash
# 新規READMEリンク先ファイルの有無チェック
if [ ! -f docs/develop/setup/index.md ]; then
  echo "docs/develop/setup/index.md not found"
  exit 1
fi

Length of output: 138


リンク切れ検出: docs/develop/setup/index.md が存在しません
README.md の 5行目で指定されたリンク先ファイルがリポジトリ内に見つかりませんでした。リンクを正しいパスに修正するか、該当ファイルを追加してください。

  • ファイル: README.md(5行目)
    内容:
    [Setup procedure](./docs/develop/setup/index.md)
    
🤖 Prompt for AI Agents
In README.md at line 5, the link points to ./docs/develop/setup/index.md, but
this file does not exist in the repository. To fix this, either create and add
the missing file at the specified path or update the link to point to the
correct existing file location to avoid broken links.


## Other

![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/zucky2021/book-tracker-v2?labelColor=171717&color=FF570A&link=https%3A%2F%2Fcoderabbit.ai&label=CodeRabbit%20Reviews)
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ services:
markdownlint:
image: node:24-alpine
container_name: markdownlint
restart: no
working_dir: /work
volumes:
- .:/work
Expand Down
1 change: 1 addition & 0 deletions docs/develop/setup/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Setup (環境構築)
1 change: 1 addition & 0 deletions docs/develop/setup/script.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#!/bin/bash
1 change: 1 addition & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const App: React.FC = () => {
const checkHealth = async (retries = 5) => {
for (let i = 0; i < retries; i++) {
try {
// FIXME:URLのハードコードを避ける
const response = await fetch("http://localhost:8080/health");
Comment on lines +12 to 13
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

エンドポイントURLを環境変数に切り出してください。
ハードコーディングされたURLは環境によって異なるため、Viteのimport.meta.env.VITE_HEALTH_URLなどで設定を注入し、FIXMEコメントは修正後に削除しましょう。

-          // FIXME:URLのハードコードを避ける
-          const response = await fetch("http://localhost:8080/health");
+          const healthUrl = import.meta.env.VITE_HEALTH_URL || "http://localhost:8080/health";
+          const response = await fetch(healthUrl);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// FIXME:URLのハードコードを避ける
const response = await fetch("http://localhost:8080/health");
const healthUrl = import.meta.env.VITE_HEALTH_URL || "http://localhost:8080/health";
const response = await fetch(healthUrl);
🤖 Prompt for AI Agents
In frontend/src/App.tsx around lines 12 to 13, the health check URL is hardcoded
as "http://localhost:8080/health". Replace this hardcoded URL with an
environment variable by using Vite's import.meta.env.VITE_HEALTH_URL to inject
the URL dynamically. After updating the code to use the environment variable,
remove the FIXME comment to reflect the fix.

if (response.ok) {
const data = await response.json();
Expand Down
19 changes: 12 additions & 7 deletions frontend/src/components/Memo.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useState } from "react";
import { memo, useEffect, useState } from "react";

type MemoProps = {
bookId: string;
Expand All @@ -17,12 +17,14 @@ type MemoData = {
};

// メモモーダル
const Memo = ({ bookId, userId, onClose }: MemoProps) => {
const Memo = memo(({ bookId, userId, onClose }: MemoProps) => {
const [memoText, setMemoText] = useState("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [imgFile, setImgFile] = useState<File | null>(null);

const maxMemoLength = 1000;

useEffect(() => {
const fetchMemo = async () => {
setLoading(true);
Expand All @@ -34,7 +36,6 @@ const Memo = ({ bookId, userId, onClose }: MemoProps) => {
);
if (res.ok) {
const data: { memo: MemoData | null } = await res.json();
console.log("Fetched memo data:", data.memo?.text);
setMemoText(data.memo?.text || "");
} else if (res.status === 404) {
setMemoText("");
Expand Down Expand Up @@ -68,7 +69,7 @@ const Memo = ({ bookId, userId, onClose }: MemoProps) => {
formData.append("bookId", bookId);
formData.append("text", memoText);
if (imgFile) {
formData.append("image", imgFile);
formData.append("imgFile", imgFile);
}

const res = await fetch("http://localhost:8080/api/memo", {
Expand All @@ -86,12 +87,12 @@ const Memo = ({ bookId, userId, onClose }: MemoProps) => {

return (
<div
className="fixed inset-0 z-50 flex items-center justify-items-center bg-black/10"
className="fixed inset-0 z-50 flex items-center justify-center bg-black/10"
role="dialog"
aria-modal="true"
>
<div className="mx-auto w-80 rounded bg-white p-6 shadow-lg">
<h4 className="test-lg mb-2 font-bold">Memo</h4>
<h4 className="text-lg mb-2 font-bold">Memo</h4>
{loading ? (
<p>Loading...</p>
) : (
Expand All @@ -103,7 +104,11 @@ const Memo = ({ bookId, userId, onClose }: MemoProps) => {
onChange={(e) => setMemoText(e.target.value)}
placeholder="Let's Input memo!"
required
maxLength={maxMemoLength}
/>
<p className="text-sm text-gray-500">
{memoText.length}/{maxMemoLength}
</p>
<input
type="file"
accept="image/*"
Expand Down Expand Up @@ -131,6 +136,6 @@ const Memo = ({ bookId, userId, onClose }: MemoProps) => {
</div>
</div>
);
};
});

export default Memo;
Loading