# GitHub + Cloudflare Pages スキル デモガイド

GitHub と Cloudflare Pages を連携させて、自動デプロイされるウェブサイトを構築するデモを3つ紹介します。

---

## 📋 前提条件

- **Cloudflare アカウント** — 接続済み（manus-config で確認可能）
- **GitHub アカウント** — gh CLI で認証済み
- **Cloudflare API トークン** — 環境変数またはスキル設定で利用可能

---

## デモ 1: シンプルな静的ウェブサイト 🌐

### 概要
- **用途**: ポートフォリオ、ブログ、ランディングページ
- **特徴**: HTML/CSS/JS だけで構築、ビルド不要
- **デプロイ**: GitHub の main ブランチへの push で自動デプロイ
- **URL**: `https://{project-name}.pages.dev`

### ファイル構造
```
demo-static-site/
├── index.html          ← メインページ
├── styles.css          ← スタイルシート
├── script.js           ← JavaScript ロジック
├── about.html          ← 追加ページ
└── images/
    └── logo.png
```

### セットアップ手順

#### 1️⃣ ローカルリポジトリ初期化
```bash
mkdir demo-static-site
cd demo-static-site
git init
git config user.name "Your Name"
git config user.email "your@email.com"
```

#### 2️⃣ HTML ファイル作成
```html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>My Static Site</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <h1>Welcome to Cloudflare Pages</h1>
    <p>This site auto-deploys from GitHub!</p>
    <script src="script.js"></script>
</body>
</html>
```

#### 3️⃣ GitHub にリポジトリ作成
```bash
gh repo create demo-static-site --public --source=. --remote=origin --push
```

#### 4️⃣ Cloudflare Pages 接続（via Cloudflare API）
スキルの `setup_github_pages.py` を使用：
```bash
python3 /path/to/setup_github_pages.py \
  --account-id YOUR_ACCOUNT_ID \
  --repo-owner your-github-username \
  --repo-name demo-static-site \
  --repo-id $(gh api repos/your-github-username/demo-static-site --jq '.id') \
  --project-name demo-static-site
```

#### 5️⃣ デプロイ確認
- Cloudflare ダッシュボード → **Workers & Pages** → **demo-static-site**
- デプロイステータス確認
- `https://demo-static-site.pages.dev` でサイト確認

### 自動デプロイの仕組み

| イベント | 動作 |
|---------|-----|
| `git push origin main` | GitHub が Cloudflare に webhook を発火 |
| Webhook 受信 | Cloudflare Pages が最新コード取得開始 |
| ビルド不要 | 静的ファイルを即座に配信（キャッシュ自動設定） |
| グローバル配信 | Cloudflare エッジネットワークで世界中に配信 |

### カスタム設定（Cloudflare ダッシュボード）

**Settings → Build**
- **Build command**: （空白 - 静的ファイルのみ）
- **Build output directory**: `.` （ルートディレクトリ）

**Settings → Build watch paths**
- **Include paths**: `*.html`, `*.css`, `images/*`
- **Exclude paths**: `*.test.js`, `.env*`

---

## デモ 2: エッジ関数付きインタラクティブサイト 🚀

### 概要
- **用途**: API エンドポイント、動的コンテンツ、認証ロジック
- **特徴**: Cloudflare Workers Functions を活用
- **エッジ実行**: リクエスト/レスポンスを Cloudflare エッジで処理
- **低遅延**: サーバーレスで高速応答

### ファイル構造
```
demo-interactive-site/
├── index.html                  ← フロントエンド
├── functions/
│   ├── middleware.js           ← グローバル middleware
│   └── api/
│       ├── greeting.js         ← GET /api/greeting
│       ├── users.js            ← GET /api/users, POST /api/users
│       └── [id].js             ← GET /api/users/:id
├── public/
│   ├── styles.css
│   └── app.js
└── wrangler.toml              ← Workers 設定（オプション）
```

### エッジ関数の例

#### 🔹 例 1: シンプルな API レスポンス
```javascript
// functions/api/greeting.js
export default {
  async fetch(request, env, ctx) {
    const name = new URL(request.url).searchParams.get('name') || 'World';
    return new Response(JSON.stringify({
      message: `Hello, ${name}!`,
      timestamp: new Date().toISOString()
    }), {
      headers: { 'Content-Type': 'application/json' },
    });
  }
};
```

**使用方法:**
```bash
curl https://demo-interactive.pages.dev/api/greeting?name=Alice
# レスポンス: {"message":"Hello, Alice!","timestamp":"2026-10-07T..."}
```

#### 🔹 例 2: 環境変数を使用した認証
```javascript
// functions/api/secure.js
export default {
  async fetch(request, env, ctx) {
    const token = request.headers.get('Authorization');
    const validToken = env.API_SECRET;

    if (token !== `Bearer ${validToken}`) {
      return new Response('Unauthorized', { status: 401 });
    }

    return new Response(JSON.stringify({
      data: 'Secret data',
      user: 'authenticated_user'
    }), {
      headers: { 'Content-Type': 'application/json' }
    });
  }
};
```

#### 🔹 例 3: 動的ルーティング
```javascript
// functions/api/users/[id].js
export default {
  async fetch(request, env, ctx) {
    const userId = request.url.split('/').pop();
    
    return new Response(JSON.stringify({
      id: userId,
      name: `User ${userId}`,
      email: `user${userId}@example.com`
    }), {
      headers: { 'Content-Type': 'application/json' }
    });
  }
};
```

**使用方法:**
```bash
curl https://demo-interactive.pages.dev/api/users/123
# レスポンス: {"id":"123","name":"User 123","email":"user123@example.com"}
```

#### 🔹 例 4: フロントエンドから API 呼び出し
```javascript
// public/app.js
async function fetchGreeting(name) {
  const response = await fetch(`/api/greeting?name=${name}`);
  const data = await response.json();
  document.getElementById('result').textContent = data.message;
}

// フォームから呼び出し
document.getElementById('form').addEventListener('submit', (e) => {
  e.preventDefault();
  const name = document.getElementById('name').value;
  fetchGreeting(name);
});
```

### セットアップ手順

#### 1️⃣ プロジェクト初期化
```bash
mkdir demo-interactive-site
cd demo-interactive-site
git init
```

#### 2️⃣ フォルダ構造作成
```bash
mkdir -p functions/api public
```

#### 3️⃣ HTML を作成
```html
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Interactive Demo</title>
    <link rel="stylesheet" href="public/styles.css">
</head>
<body>
    <h1>🚀 Cloudflare Pages + Functions</h1>
    <form id="form">
        <input type="text" id="name" placeholder="Your name" required>
        <button type="submit">Greet Me</button>
    </form>
    <p id="result"></p>
    <script src="public/app.js"></script>
</body>
</html>
```

#### 4️⃣ エッジ関数を配置
- `functions/api/greeting.js` ファイルを上記コード例で作成

#### 5️⃣ GitHub リポジトリ作成＆プッシュ
```bash
gh repo create demo-interactive-site --public --source=. --remote=origin --push
```

#### 6️⃣ Cloudflare Pages に接続
```bash
python3 /path/to/setup_github_pages.py \
  --account-id YOUR_ACCOUNT_ID \
  --repo-owner your-github-username \
  --repo-name demo-interactive-site \
  --repo-id $(gh api repos/your-github-username/demo-interactive-site --jq '.id') \
  --project-name demo-interactive-site
```

### 環境変数の設定

Cloudflare ダッシュボード → **Settings → Environment variables**

```json
{
  "API_SECRET": "super-secret-token-here",
  "API_TIMEOUT": "30"
}
```

API 経由での設定：
```bash
curl -X PUT https://api.cloudflare.com/client/v4/accounts/{account_id}/pages/projects/demo-interactive-site/deployments/production/environment/env_vars \
  -H "Authorization: Bearer {api_token}" \
  -d '{
    "env_vars": {
      "API_SECRET": "super-secret-token-here"
    }
  }'
```

---

## デモ 3: モノレポでの複数プロジェクト管理 🏢

### 概要
- **用途**: 複数のサイト/API を1つのリポジトリで管理
- **特徴**: パス監視を使用して変更検出、効率的な CI/CD
- **利点**: 依存関係共有、統一バージョン管理

### ファイル構造
```
monorepo-demo/
├── packages/
│   ├── landing/           ← ランディングページ (Cloudflare Pages)
│   │   ├── index.html
│   │   ├── styles.css
│   │   └── script.js
│   ├── blog/              ← ブログサイト (Cloudflare Pages)
│   │   ├── index.html
│   │   ├── posts/
│   │   └── build.sh       ← ビルドスクリプト (static generator)
│   ├── api/               ← バックエンド API (Cloudflare Workers)
│   │   ├── src/
│   │   └── functions/
│   └── shared/            ← 共有ユーティリティ
│       └── utils.js
├── .github/
│   └── workflows/
│       └── deploy.yml     ← CI/CD ワークフロー
└── package.json
```

### 各プロジェクト個別設定

#### 🔸 プロジェクト A: ランディングページ
```
Cloudflare Pages Project: "monorepo-landing"
  Build watch paths:
    Include: packages/landing/**
    Exclude: packages/landing/*.test.js
  Build output directory: packages/landing
  Environment branch: main
```

#### 🔸 プロジェクト B: ブログサイト
```
Cloudflare Pages Project: "monorepo-blog"
  Build watch paths:
    Include: packages/blog/**
    Exclude: packages/blog/node_modules/**
  Build command: npm run build
  Build output directory: packages/blog/dist
  Environment branch: main
```

#### 🔸 プロジェクト C: API
```
Cloudflare Pages Project: "monorepo-api"
  Build watch paths:
    Include: packages/api/**
    Exclude: packages/api/tests/**
  Build output directory: packages/api
  Environment branch: main
```

### パス監視の実装例

#### 方法 1: Cloudflare ダッシュボード UI

**Settings → Build → Build watch paths**

| プロジェクト | Include Paths | Exclude Paths |
|-----------|---|---|
| landing | `packages/landing/**` | `*.test.js`, `node_modules/**` |
| blog | `packages/blog/**` | `node_modules/**` |
| api | `packages/api/**` | `tests/**` |

#### 方法 2: Cloudflare API 経由

```bash
# ランディングページの設定
curl -X PATCH https://api.cloudflare.com/client/v4/accounts/{account_id}/pages/projects/monorepo-landing \
  -H "Authorization: Bearer {api_token}" \
  -d '{
    "build_config": {
      "build_caching_enabled": true,
      "path_includes": ["packages/landing/**"],
      "path_excludes": ["*.test.js"]
    }
  }'
```

### 実装のポイント

#### 1️⃣ 共有依存関係
```javascript
// packages/shared/utils.js
export function formatDate(date) {
  return new Date(date).toLocaleDateString('ja-JP');
}

export function apiBaseUrl() {
  return process.env.API_URL || 'https://api.example.com';
}
```

#### 2️⃣ 各パッケージでの利用
```javascript
// packages/landing/script.js
import { formatDate, apiBaseUrl } from '../shared/utils.js';

fetch(`${apiBaseUrl()}/greeting`)
  .then(r => r.json())
  .then(data => {
    console.log('Server time:', formatDate(data.timestamp));
  });
```

#### 3️⃣ ビルドスクリプト（ブログ用）
```bash
#!/bin/bash
# packages/blog/build.sh

echo "🔨 Building blog..."
npm install
node generate-posts.js  # Markdown → HTML
npm run optimize        # 画像最適化
echo "✅ Blog built successfully!"
```

#### 4️⃣ package.json での統一管理
```json
{
  "name": "monorepo-demo",
  "version": "1.0.0",
  "workspaces": [
    "packages/landing",
    "packages/blog",
    "packages/api",
    "packages/shared"
  ],
  "scripts": {
    "build": "npm --workspaces run build",
    "test": "npm --workspaces run test",
    "deploy": "npm --workspaces run deploy"
  }
}
```

### パス監視が機能する条件

✅ **デプロイがトリガーされる:**
```bash
# landing パッケージ更新
git add packages/landing/index.html
git commit -m "Update landing page"
git push origin main
→ monorepo-landing が自動デプロイ ✨
```

✅ **複数パッケージ更新時も個別処理:**
```bash
# landing と blog の両方を更新
git add packages/landing/* packages/blog/*
git commit -m "Update landing and blog"
git push origin main
→ monorepo-landing デプロイ + monorepo-blog デプロイ ✨
```

❌ **デプロイがスキップされる（設定外パッケージ）:**
```bash
# api フォルダのみ更新（landing の Include paths に含まれない）
git add packages/api/*
git commit -m "Update API"
git push origin main
→ monorepo-landing はスキップ（変更なし）
→ monorepo-api のみデプロイ ✨
```

### トラブルシューティング

| 問題 | 原因 | 解決策 |
|-----|-----|------|
| デプロイがスキップ | パス監視で対象外 | Include paths に `*` を追加または正しいパスを指定 |
| ビルド失敗 | ビルドコマンド不正 | ダッシュボードでビルドログを確認 |
| 依存関係エラー | ワークスペース未設定 | `package.json` に `workspaces` フィールド追加 |

---

## 💡 実装のベストプラクティス

### 1. 環境ごとのビルド設定
```bash
# Production
BUILD_COMMAND="npm run build:prod"
BUILD_OUTPUT="dist"

# Preview（プルリクエスト）
# 同じ設定を使用、または異なるコマンドを指定
```

### 2. GitHub Actions での自動テスト
```yaml
# .github/workflows/test.yml
name: Test & Deploy
on: [push]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - run: npm install && npm test
      - run: npm run build
```

### 3. Pages 関数のテスト
```javascript
// functions/api/__tests__/greeting.test.js
import handler from '../greeting';

test('greeting responds with correct format', async () => {
  const response = await handler.fetch(
    new Request('http://example.com/api/greeting?name=Alice'),
    {}
  );
  const data = await response.json();
  expect(data.message).toBe('Hello, Alice!');
});
```

### 4. カスタムドメイン設定
Cloudflare ダッシュボール → **Pages プロジェクト → Settings → Domains**
- `pages.dev` のデフォルトドメイン OR
- カスタムドメイン（CNAME で指定）
- サブドメイン（例: `blog.example.com`）

---

## 🚀 まとめ

| デモ | 用途 | 複雑度 | デプロイ時間 |
|-----|-----|-----|----|
| **1. 静的サイト** | ポートフォリオ、ランディングページ | ⭐☆☆ | 数秒 |
| **2. インタラクティブ** | 動的 API、リアルタイム機能 | ⭐⭐⭐ | 30秒 |
| **3. モノレポ** | 大規模プロジェクト、マイクロサービス | ⭐⭐⭐⭐ | 1-2分 |

すべてのデモは以下の利点を享受できます：
- ✨ **自動デプロイ**: GitHub push で即座にライブ
- 🌍 **グローバル配信**: Cloudflare エッジネットワーク
- ⚡ **高速**: キャッシング＆エッジ実行
- 🔒 **セキュリティ**: DDoS 保護、SSL/TLS 対応
- 📊 **監視**: デプロイログと統計情報

---

## 📚 関連リソース

- **Cloudflare Pages ドキュメント**: https://developers.cloudflare.com/pages/
- **Cloudflare Workers Functions**: https://developers.cloudflare.com/workers/
- **Skill: github-cloudflare-pages**: `SKILL.md`

---

**次のステップ**: 上記のデモをお手元で実装してみてください！不明な点があれば、スキルのリファレンスドキュメントをご参照ください。
