【T-01】GitHub Actions × WordPress:Markdown自動投稿システムの構築ガイド
GitHub Actions × WordPress:Markdown自動投稿システムの構築ガイド
はじめに
技術ブログを運用する際、「使い慣れたエディタでMarkdownを書き、Gitで管理し、自動でサイトに反映させたい」と考えたことはありませんか?
本記事では、GitHub ActionsとWordPress REST APIを組み合わせた自動投稿システム「wp-story-sync」の設計と実装を詳しく解説します。
システムアーキテクチャ
本システムは、高い保守性と安全性を確保するためにコンポーネントを分離したモジュール設計を採用しています。
全体構成
リポジトリの stories/ ディレクトリにMarkdownファイルが追加・更新されると、GitHub Actionsがトリガーされ、検証を経てWordPressへ配信されます。
┌─────────────────────────┐
│ GitHub リポジトリ │
│ │
│ stories/ │
│ ├─ aquaria/ │ (水の惑星シリーズ)
│ ├─ solaris/ │ (音楽宇宙シリーズ)
│ ├─ ragteller/ │ (火星コロニーシリーズ)
│ └─ tech/ │ (技術記事)
└────────┬────────────────┘
│ git push
▼
┌─────────────────────────┐
│ GitHub Actions │
│ │
│ Job 1: Validate │ (バリデーション + Dry-run)
│ Job 2: Publish │ (本番投稿 / mainのみ)
└────────┬────────────────┘
│ REST API
▼
┌─────────────────────────┐
│ WordPress サイト │
│ │
│ カテゴリ・タグ自動設定 │
│ 新規作成 or 更新判定 │
│ 公開 / 予約投稿 │
└─────────────────────────┘
コンポーネントの役割
| レイヤー | 役割 | 主なファイル |
|---|---|---|
| Config | 設定情報・環境変数の管理 | lib/config.py |
| WP Client | REST APIを介した通信・認証 | lib/wp_client.py |
| Cache | カテゴリ・タグ情報のキャッシュ | lib/wp_cache.py |
| Processor | Markdown解析・HTML変換・ロジック実行 | lib/story_processor.py |
| Validation | 6層バリデーション(スキーマ・日付・衝突・類似度・HTML・コンテンツ) | lib/validation.py |
| Workflow | CI/CDパイプラインの定義 | .github/workflows/wp-sync.yaml |
実装のポイント
安全なAPIクライアント設計
WordPressClient クラスでは、誤った投稿を防ぐための Dry-run(テスト実行)モード を標準搭載しています。
# lib/wp_client.py (設計パターン抜粋)
def _request(self, method: str, path: str, **kwargs) -> requests.Response:
# Dry-run時は書き込み系メソッドをモックに差し替え
if self.config.dry_run and method.upper() in ('POST', 'PUT', 'PATCH', 'DELETE'):
self.logger.info("DRY RUN: %s %s", method.upper(), path)
return self._create_mock_response()
# 実際のAPIリクエスト
url = self.config.endpoint(path)
return self.session.request(method, url, auth=self.config.auth_tuple(), **kwargs)
この設計により、本番環境に影響を与えずに全フローを検証できます。CI/CDの「検証ジョブ」でもこのDry-runモードが活用されています。
ストーリー処理エンジン
frontmatter ライブラリを使用してメタデータを解析し、MarkdownをHTMLに変換します。既存記事がある場合は slug をキーに自動で「更新」へ分岐させるのがポイントです。
処理フロー:
1. Frontmatter解析
story.md → メタデータ (title, slug, categories, tags, status)
+ 本文 (Markdown)
2. Markdown → HTML変換
**太字** → <strong>太字</strong>
```code``` → <pre><code>code</code></pre>
3. 同期判定
slug "my-article" でWordPressを検索
│
├─ 見つかった → 既存記事を「更新」(PUT)
│
└─ 見つからない → 新規記事を「作成」(POST)
Markdownファイルの書き方
Markdownファイルの先頭に「フロントマター」としてメタデータを定義します。
---
title: "記事のタイトル"
slug: "url-friendly-name"
categories: ["Tech"]
tags: ["WordPress", "自動化"]
status: "publish"
date: "2025-01-11"
excerpt: "記事の要約(100文字程度)"
---
# ここから本文
本文をMarkdown記法で自由に書けます。
**太字**や`コード`も使えます。
| フィールド | 必須 | 説明 | 例 |
|---|---|---|---|
| title | ○ | 記事タイトル | “はじめてのGitHub Actions” |
| slug | ○ | URL用の短い名前(英数字とハイフン) | “first-github-actions” |
| categories | ○ | カテゴリ(配列) | [“Tech”] |
| status | ○ | 公開状態 | “publish” or “draft” |
| date | ○ | 公開日 | “2025-01-11” |
| excerpt | ○ | 要約文 | “GitHub Actionsの入門記事” |
| tags | △ | タグ(配列、任意) | [“入門”, “CI/CD”] |
GitHub Actions ワークフロー
本番環境への安全なデプロイを実現するため、ワークフローを 「検証」 と 「公開」 の2ジョブに分離しています。
# .github/workflows/wp-sync.yaml (構成概要)
jobs:
validate:
name: Validate and Dry-Run
# 全ブランチで実行。バリデーションとドライランを行う
publish:
name: Publish to WordPress
needs: validate
if: github.ref == 'refs/heads/main' # mainブランチのみ
environment: production # GitHub環境保護ルールを適用
env:
WP_APP_PASSWORD: ${{ secrets.WP_APP_PASSWORD }}
セキュリティ上の工夫
- Application Password: ログイン用パスワードではなく、WordPress専用の認証トークンを使用
- Secrets管理: パスワードやURLはコードに含めず、GitHub Secretsで厳重に管理
- 環境保護ルール: 本番投稿前にGitHub上で「手動承認」を必須にすることが可能
- ブランチ保護: mainブランチへのpushのみ本番投稿を実行
多層防御による公開制御
記事が誤って公開されないよう、3つのゲートを設けています。
ゲート1: ブランチ保護
└─ mainブランチへのpushのみ投稿を実行
ゲート2: 環境保護
└─ 本番環境への投稿には手動承認が必要
ゲート3: バリデーション
└─ 6層の検証をパスした記事のみ投稿可能
(スキーマ / 日付 / slug衝突 / 類似度 / HTML / コンテンツ)
パフォーマンスと運用監視
キャッシュ戦略
カテゴリIDやタグ情報の取得を高速化するため、スレッドセーフなキャッシュ機構を導入しています。これにより、大量のファイルを処理する際のAPI呼び出し回数を劇的に削減しています。
1回目: カテゴリ「Tech」のID → API呼び出し → 結果をキャッシュ
2回目: カテゴリ「Tech」のID → キャッシュから即座に取得(API不要)
ログと証跡管理
GitHub Actionsのアーティファクト機能を利用し、実行時のログやバリデーション結果を保存します。
validation-logs: 構文チェック・バリデーションの結果publish-logs: WordPressから返却された投稿IDなどの詳細- 処理時間やエラーの詳細も自動記録
よくあるトラブルと対処法
| トラブル | 原因 | 対処法 |
|---|---|---|
| バリデーションエラー | フロントマターの記述ミス | エラーメッセージに従って修正 |
| 投稿が反映されない | Dry-runモードのまま | 本番モードに切り替え |
| 403エラー | 認証情報の期限切れ | アプリケーションパスワードを再発行 |
| 記事が重複 | slugの重複 | ユニークなslugに変更 |
まとめ
このシステムを導入することで、開発者は 「記事を書くこと」 だけに集中でき、WordPressの管理画面を開く手間から解放されます。
1. モジュラー設計 → 機能追加や修正が容易
2. 多層防御 → バリデーションとDry-runによるミス防止
3. 自動化パイプライン → 書いてpushするだけで公開
4. 拡張性 → 画像アップロードやマルチサイト対応も視野に
5. 可観測性 → ログとアーティファクトで状況把握
「書くこと」に集中し、「公開すること」は自動化する。それがwp-story-syncの目指す世界です。
参考リンク
この記事は wp-story-sync プロジェクトの設計と実装を基にしています。