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 プロジェクトの設計と実装を基にしています。