このリポジトリのslidesディレクトリにあるMarkdownファイルを、Cloudflare Pagesに自動デプロイするための設定手順です。
🔧 初回設定
1. Cloudflare Pages プロジェクトの作成
- Cloudflare Dashboard にログイン
- Workers & Pages → Create a project をクリック
- Pages で 直接アップロードを使用する を選択
- プロジェクト名を入力(例:
my-slides) - プロジェクトを作成
2. Cloudflare API トークンの取得
- Cloudflare Dashboard → My Profile → API Tokens
- Create Token をクリック
- Custom token を選択
- 以下の権限を設定:
- Zone:Zone:Read
- Zone:Page Rules:Edit
- Account:Cloudflare Pages:Edit
- Account resources: Include - All accounts
- Zone resources: Include - All zones
- トークンを生成してコピー
3. Account ID の取得
- Cloudflare Dashboard のアカウントのダッシュボードのURLから Account ID をコピー
4. GitHub Secrets の設定
リポジトリの Settings → Secrets and variables → Actions → Repository secrets → New repository secret で以下のSecretを追加:
| Secret名 | 値 |
|---|---|
CLOUDFLARE_API_TOKEN | 手順2で取得したAPIトークン |
CLOUDFLARE_ACCOUNT_ID | 手順3で取得したAccount ID |
CLOUDFLARE_PROJECT_NAME | 手順1で作成したプロジェクト名 |
📝 スライドの作成
基本的なファイル構造
slides/
├── themes/
│ └── custom.css
└── your-slide.md
Markdownファイルのテンプレート
---
marp: true
theme: custom
title: スライドのタイトル
description: スライドの説明
author: あなたの名前
---
# タイトル
内容
---
## セクション
内容
---
<!-- _class: lead -->
# 終了スライド🚀 デプロイ方法
.github/workflowに追加
name: Deploy Slides to Cloudflare Pages
on:
workflow_dispatch:
permissions:
contents: read
actions: read
deployments: write
concurrency:
group: slides-deploy
cancel-in-progress: true
jobs:
build-and-deploy:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install Marp CLI
run: |
echo "📦 Installing Marp CLI..."
npm install -g @marp-team/marp-cli
echo ""
echo "✅ Marp CLI installed successfully"
echo "Version information:"
marp --version
echo ""
echo "Help information:"
marp --help | head -10
- name: Create output directory
run: mkdir -p public
- name: Convert Markdown slides to HTML
run: |
echo "🔍 Checking workspace structure..."
echo "Current directory: $(pwd)"
echo "Workspace contents:"
ls -la
echo ""
echo "📁 Slides directory contents:"
if [ -d "slides" ]; then
find slides -type f | head -10
else
echo "❌ slides directory not found!"
fi
# Check if slides directory exists and has markdown files
if [ ! -d "slides" ]; then
echo "Error: slides directory not found"
exit 1
fi
# Count markdown files
md_files=$(find slides -maxdepth 1 -name "*.md" -type f | wc -l)
if [ "$md_files" -eq 0 ]; then
echo "Warning: No markdown files found in slides directory"
echo "Creating a placeholder index page"
mkdir -p public
cat > public/index.html << 'EOF'
<!DOCTYPE html>
<html><head><title>No Slides</title></head>
<body><h1>No slides found</h1><p>Add .md files to the slides directory.</p></body>
</html>
EOF
exit 0
fi
echo "Found $md_files markdown file(s) to process"
# List the markdown files to be processed
echo ""
echo "📝 Markdown files found:"
find slides -maxdepth 1 -name "*.md" -type f | while read file; do
echo " - $file ($(wc -l < "$file") lines)"
done
echo ""
echo "🔨 Starting Marp conversion..."
# Convert slides to HTML (output to default location)
if [ -d "slides/themes" ]; then
echo "Using custom theme directory: slides/themes"
echo "Theme files:"
find slides/themes -name "*.css" | while read theme; do
echo " - $theme"
done
echo "Running: marp slides/*.md --html --theme-set slides/themes --allow-local-files"
if ! marp slides/*.md --html --theme-set slides/themes --allow-local-files; then
echo "❌ Marp conversion failed with custom theme, trying with default theme..."
marp slides/*.md --html --allow-local-files
fi
else
echo "Using default theme"
echo "Running: marp slides/*.md --html --allow-local-files"
if ! marp slides/*.md --html --allow-local-files; then
echo "❌ Marp conversion failed!"
echo "Trying individual file conversion..."
# Try converting files individually as fallback
for md_file in slides/*.md; do
if [ -f "$md_file" ]; then
echo "Converting: $md_file"
marp "$md_file" --html --allow-local-files || echo "Failed to convert $md_file"
fi
done
fi
fi
echo ""
echo "📁 Checking generated files..."
echo "Files in slides directory after conversion:"
ls -la slides/
# Move generated HTML files to public directory
echo ""
echo "🚚 Moving HTML files to public directory..."
mkdir -p public
# Find and move all HTML files from slides directory
html_files_found=0
for html_file in slides/*.html; do
if [ -f "$html_file" ]; then
filename=$(basename "$html_file")
echo "Moving: $html_file -> public/$filename"
mv "$html_file" "public/$filename"
html_files_found=$((html_files_found + 1))
fi
done
echo "Moved $html_files_found HTML file(s) to public directory"
echo ""
echo "✅ Marp conversion completed"
echo "Generated files in public directory:"
ls -la public/ || echo "No files generated"
- name: Create index page
run: |
# Skip if no HTML files were generated
html_count=$(find public -maxdepth 1 -name "*.html" -type f | wc -l)
if [ "$html_count" -eq 0 ]; then
echo "No HTML files found, skipping index generation"
exit 0
fi
cat > public/index.html << 'EOF'
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Slides</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
h1 { color: #333; }
.slide-list { list-style: none; padding: 0; }
.slide-item { margin: 10px 0; padding: 15px; border: 1px solid #ddd; border-radius: 5px; }
.slide-item a { text-decoration: none; color: #0066cc; font-weight: bold; }
.slide-item a:hover { text-decoration: underline; }
.slide-meta { font-size: 0.9em; color: #666; margin-top: 5px; }
</style>
</head>
<body>
<h1>📊 Slides</h1>
<ul class="slide-list">
EOF
# Generate list of slides
for html in public/*.html; do
if [ "$html" != "public/index.html" ] && [ -f "$html" ]; then
filename=$(basename "$html" .html)
filesize=$(du -h "$html" | cut -f1)
modified=$(date -r "$html" "+%Y-%m-%d %H:%M")
echo " <li class=\"slide-item\">" >> public/index.html
echo " <a href=\"$filename.html\">$filename</a>" >> public/index.html
echo " <div class=\"slide-meta\">Size: $filesize | Modified: $modified</div>" >> public/index.html
echo " </li>" >> public/index.html
fi
done
cat >> public/index.html << 'EOF'
</ul>
<p><small>Generated automatically from markdown files in the slides directory.</small></p>
</body>
</html>
EOF
echo "Generated index page with $(find public -maxdepth 1 -name "*.html" -not -name "index.html" | wc -l) slide(s)"
- name: Debug Cloudflare credentials
run: |
echo "🔍 Debugging Cloudflare configuration..."
# Check if secrets are set (without revealing values)
if [ -z "${{ secrets.CLOUDFLARE_API_TOKEN }}" ]; then
echo "❌ CLOUDFLARE_API_TOKEN is not set"
else
echo "✅ CLOUDFLARE_API_TOKEN is set (length: ${#CLOUDFLARE_API_TOKEN})"
fi
if [ -z "${{ secrets.CLOUDFLARE_ACCOUNT_ID }}" ]; then
echo "❌ CLOUDFLARE_ACCOUNT_ID is not set"
else
echo "✅ CLOUDFLARE_ACCOUNT_ID is set: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}"
fi
if [ -z "${{ secrets.CLOUDFLARE_PROJECT_NAME }}" ]; then
echo "❌ CLOUDFLARE_PROJECT_NAME is not set"
else
echo "✅ CLOUDFLARE_PROJECT_NAME is set: ${{ secrets.CLOUDFLARE_PROJECT_NAME }}"
fi
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
- name: Test Cloudflare API connection
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_PROJECT_NAME: ${{ secrets.CLOUDFLARE_PROJECT_NAME }}
run: |
echo "🌐 Testing Cloudflare API connection..."
# Test API token validity
response=$(curl -s -w "%{http_code}" -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \
-H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
-H "Content-Type: application/json")
http_code=${response: -3}
body=${response%???}
echo "HTTP Status: $http_code"
echo "Response: $body"
if [ "$http_code" != "200" ]; then
echo "❌ API token verification failed"
exit 1
else
echo "✅ API token is valid"
fi
# Test Pages project access
echo "📄 Testing Pages project access..."
project_response=$(curl -s -w "%{http_code}" -X GET "https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/pages/projects/${CLOUDFLARE_PROJECT_NAME}" \
-H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
-H "Content-Type: application/json")
project_http_code=${project_response: -3}
project_body=${project_response%???}
echo "Project HTTP Status: $project_http_code"
echo "Project Response: $project_body"
if [ "$project_http_code" != "200" ]; then
echo "❌ Pages project access failed"
echo "🔧 Possible issues:"
echo " - Project name '${CLOUDFLARE_PROJECT_NAME}' doesn't exist"
echo " - API token doesn't have Cloudflare Pages:Edit permission"
echo " - Account ID is incorrect"
else
echo "✅ Pages project access successful"
fi
- name: Verify build output before deployment
run: |
echo "🔍 Verifying build output in public directory..."
echo "================================================"
# Check if public directory exists
if [ ! -d "public" ]; then
echo "❌ ERROR: public directory does not exist!"
exit 1
fi
# Show directory structure
echo "📁 Public directory structure:"
find public -type f -name "*" | head -20
# Count files
total_files=$(find public -type f | wc -l)
html_files=$(find public -name "*.html" | wc -l)
echo ""
echo "📊 File count summary:"
echo " Total files: $total_files"
echo " HTML files: $html_files"
# Show file sizes
echo ""
echo "📏 File sizes:"
du -h public/* 2>/dev/null || echo "No files in public directory"
# Show first few lines of each HTML file
echo ""
echo "📄 HTML file previews:"
for html in public/*.html; do
if [ -f "$html" ]; then
echo "--- $(basename "$html") ---"
head -5 "$html"
echo ""
fi
done
# Check if index.html exists
if [ -f "public/index.html" ]; then
echo "✅ index.html exists"
index_size=$(wc -c < public/index.html)
echo " Size: $index_size bytes"
else
echo "❌ WARNING: index.html missing"
fi
# Validate that we have content to deploy
if [ "$total_files" -eq 0 ]; then
echo "❌ ERROR: No files to deploy!"
exit 1
fi
echo ""
echo "✅ Build verification completed. Ready for deployment."
- name: Deploy to Cloudflare Pages
uses: cloudflare/pages-action@v1
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: ${{ secrets.CLOUDFLARE_PROJECT_NAME }}
directory: public
wranglerVersion: '3'
- name: Post-deployment verification
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_PROJECT_NAME: ${{ secrets.CLOUDFLARE_PROJECT_NAME }}
run: |
echo "🎉 Deployment completed!"
echo ""
echo "📊 Deployment summary:"
echo " Project: ${CLOUDFLARE_PROJECT_NAME}"
echo " Account: ${CLOUDFLARE_ACCOUNT_ID}"
echo ""
# Get project info to show the URL
project_info=$(curl -s -X GET "https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/pages/projects/${CLOUDFLARE_PROJECT_NAME}" \
-H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
-H "Content-Type: application/json")
デプロイ
GitHub CLIを使って手動でワークフローを実行:
# GitHub CLIをインストール(未インストールの場合)
brew install gh
# 認証(初回のみ)
gh auth login
# ワークフローを実行
gh workflow run "Deploy Slides to Cloudflare Pages" --ref mainスライドをデプロイする機会がそうそうないので手動デプロイのみにしています。
ワークフローの状況確認
# 実行中のワークフローを確認
gh run list --workflow=deploy-slides
# 特定のランの詳細を確認
gh run view <run-id>🎨 カスタムテーマ
slides/themes/custom.css でテーマをカスタマイズできます。詳細はMarpのドキュメントを参照してください。
🔍 トラブルシューティング
デプロイが失敗する場合
- GitHub Actionsのログを確認
- Cloudflare Pagesの設定を確認
- API トークンの権限を確認
Marpエラーが出る場合
- ファイルの先頭に正しいfront matterが設定されているか確認
- テーマファイルが正しく配置されているか確認