なぜ自動生成する?
手書きの HTML を管理する手間を省き、データや Markdown から一貫した構造のページを作れます。
Markdown から
README や記事 Markdown を HTML に変換。ブログやドキュメントサイトに最適。
JSON / API から
GitHub API 外部データから動的に一覧ページを生成。ポートフォリオやダッシュボードに。
テンプレートエンジン
Handlebars や EJS でレイアウトを分離。見た目の変更が容易です。
Pages と統合
生成 → デプロイを1つのワークフローで完結。手動アップロードは不要。
ワークフロー集
用途に合わせて選べる5パターン。YAML をコピーしてそのまま使えます。
01 Markdown → HTML 推奨
リポジトリ内の Markdown ファイルを marked で HTML に変換し、テンプレートに埋め込んで index.html を出力します。
.github/workflows/generate-index.ymlname: Generate index.html from Markdown
on:
push:
branches: ["main"]
paths:
- "content/**"
- "scripts/**"
- "template.html"
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- name: Generate index.html
run: node scripts/build.js
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v3
with:
path: ./public
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/deploy-pages@v4
id: deployment
scripts/build.js(marked + テンプレート)
const fs = require('fs');
const { marked } = require('marked');
const md = fs.readFileSync('content/index.md', 'utf-8');
const htmlContent = marked(md);
const template = fs.readFileSync('template.html', 'utf-8');
const output = template.replace('{{content}}', htmlContent);
fs.mkdirSync('public', { recursive: true });
fs.writeFileSync('public/index.html', output);
if (fs.existsSync('assets')) {
const cp = require('child_process');
cp.execSync('cp -r assets public/');
}
template.html の例
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>My Site</title>
<style>body{max-width:680px;margin:2rem auto;padding:0 1rem;font-family:sans-serif;line-height:1.7}</style>
</head>
<body>
<main>{{content}}</main>
</body>
</html>
package.json
{
"devDependencies": {
"marked": "^14.0.0"
}
}
paths フィルタで Markdown やテンプレート変更時のみ実行。ビルド成果物は public/ にまとめて upload-pages-artifact で一括アップロードします。
02 JSON → HTML データ駆動
JSON ファイルに定義したデータから Handlebars テンプレートで index.html を生成します。プロダクト一覧、メンバー紹介、リンク集などに最適です。
.github/workflows/generate-from-json.ymlname: Generate index.html from JSON
on:
push:
branches: ["main"]
paths:
- "data.json"
- "template.html"
- "scripts/build.js"
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: node scripts/build.js
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v3
with:
path: ./dist
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/deploy-pages@v4
id: deployment
scripts/build.js(Handlebars)
const fs = require('fs');
const Handlebars = require('handlebars');
const data = JSON.parse(fs.readFileSync('data.json', 'utf-8'));
const template = fs.readFileSync('template.html', 'utf-8');
const compile = Handlebars.compile(template);
fs.mkdirSync('dist', { recursive: true });
fs.writeFileSync('dist/index.html', compile(data));
data.json の例
{
"title": "My Projects",
"items": [
{ "name": "Project A", "url": "https://example.com/a", "desc": "Description here" },
{ "name": "Project B", "url": "https://example.com/b", "desc": "Another project" }
]
}
template.html(Handlebars)
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>{{title}}</title></head>
<body>
<h1>{{title}}</h1>
<ul>
{{#each items}}
<li><a href="{{url}}">{{name}}</a> — {{desc}}</li>
{{/each}}
</ul>
</body>
</html>
03 Python スクリプト 軽量
Node.js を使わず、Python の文字列テンプレートで index.html を生成します。依存がほぼなく、シンプルに動かしたい場合に最適です。
.github/workflows/generate-python.ymlname: Generate index.html with Python
on:
push:
branches: ["main"]
paths:
- "data.json"
- "scripts/build.py"
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- run: python scripts/build.py
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v3
with:
path: ./public
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/deploy-pages@v4
id: deployment
scripts/build.py の例
import json
import os
with open("data.json", "r", encoding="utf-8") as f:
data = json.load(f)
items_html = "".join(
f'<li><a href="{item[\"url\"]}">{item[\"name\"]}</a> — {item[\"desc\"]}</li>'
for item in data["items"]
)
html = f"""<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>{data['title']}</title></head>
<body>
<h1>{data['title']}</h1>
<ul>{items_html}</ul>
</body>
</html>"""
os.makedirs("public", exist_ok=True)
with open("public/index.html", "w", encoding="utf-8") as f:
f.write(html)
04 GitHub API → index.html 動的
GitHub API からリポジトリ一覧やプロフィール情報を取得して、ポートフォリオページを自動生成します。
.github/workflows/generate-from-api.ymlname: Generate index.html from GitHub API
on:
schedule:
- cron: "0 0 * * 0" # 毎週日曜日
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- name: Fetch repos and build
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: node scripts/build.js
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v3
with:
path: ./public
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/deploy-pages@v4
id: deployment
scripts/build.js(GitHub API)
const fs = require('fs');
async function fetchRepos() {
const res = await fetch('https://api.github.com/users/USERNAME/repos?sort=updated', {
headers: {
'Authorization': `Bearer ${process.env.GITHUB_TOKEN}`,
'User-Agent': 'github-actions',
},
});
return await res.json();
}
async function build() {
const repos = await fetchRepos();
const items = repos.map(r => `<li><a href="${r.html_url}">${r.name}</a> — ${r.description || ''}</li>`).join('');
const html = `<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>My Repos</title></head>
<body><h1>My Repositories</h1><ul>${items}</ul></body></html>`;
fs.mkdirSync('public', { recursive: true });
fs.writeFileSync('public/index.html', html);
}
build();
GITHUB_TOKEN 付きで 1000 req/h の制限がありま。リポジトリ数が多い場合はページネーションに対応してください。
05 gh-pages ブランチにコミット 従来方式
生成した index.html を gh-pages ブランチにプッシュして公開するパターンです。GitHub Actions 公式の Pages デプロイではなく、従来のブランチベース公開を使う場合に有効です。
.github/workflows/generate-and-commit.ymlname: Generate and Commit index.html
on:
push:
branches: ["main"]
paths:
- "content/**"
- "scripts/**"
workflow_dispatch:
jobs:
generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: node scripts/build.js
- name: Push to gh-pages
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git checkout --orphan gh-pages-temp
git rm -rf .
cp -r public/* .
git add .
git commit -m "deploy: update index.html [skip ci]"
git branch -M gh-pages
git push -f origin gh-pages
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ポイントまとめ
比較表
| 方式 | 依存 | 向き |
|---|---|---|
| Markdown + marked | Node.js | ブログ、ドキュメント |
| JSON + Handlebars | Node.js | データ駆動の一覧 |
| Python 文字列 | Python(標準 | 軽量、依存なし |
| GitHub API | Node.js + fetch | ポートフォリオ、ダッシュボード |
| gh-pages ブランチ | git | 従来のブランチ公開 |
よくある注意点
pathsフィルタで不要なビルドを防ぐ- コミットメッセージに
[skip ci]を入れて無限ループ回避 - テンプレートエンジン使わずとも、Python/Node の文字列補間で十分な場合も多い
- OGP 画像生成と組み合わせると、完自動化のラディングページが作れる
さっそく試してみる
上のYAMLをコピーして .github/workflows/ に貼り付けるだけで始められます。