GitHub Actions

index.html を
自動生成する

Markdown、JSON、テンプレート、APIレスポンスなどから index.html を自動生成して GitHub Pages にデプロイするワークフローをまとめました。

ワークフローを見る →
5 パターン · Node.js / Python · コピペ対応

なぜ自動生成する?

手書きの 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.yml
name: 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.yml
name: 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.yml
name: 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.yml
name: 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 API は未認証で 60 req/h、GITHUB_TOKEN 付きで 1000 req/h の制限がありま。リポジトリ数が多い場合はページネーションに対応してください。

05 gh-pages ブランチにコミット 従来方式

生成した index.html を gh-pages ブランチにプッシュして公開するパターンです。GitHub Actions 公式の Pages デプロイではなく、従来のブランチベース公開を使う場合に有効です。

.github/workflows/generate-and-commit.yml
name: 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 }}
Settings:リポジトリ設定 > Pages > Source を「Deploy from a branch」にして gh-pages ブランチを選択してください。

ポイントまとめ

比較表

方式依存向き
Markdown + markedNode.jsブログ、ドキュメント
JSON + HandlebarsNode.jsデータ駆動の一覧
Python 文字列Python(標準軽量、依存なし
GitHub APINode.js + fetchポートフォリオ、ダッシュボード
gh-pages ブランチgit従来のブランチ公開

よくある注意点

  • paths フィルタで不要なビルドを防ぐ
  • コミットメッセージに [skip ci] を入れて無限ループ回避
  • テンプレートエンジン使わずとも、Python/Node の文字列補間で十分な場合も多い
  • OGP 画像生成と組み合わせると、完自動化のラディングページが作れる

さっそく試してみる

上のYAMLをコピーして .github/workflows/ に貼り付けるだけで始められます。

ワークフローを選ぶ →