GitHub Actionsでブログを定期投稿する方法(週1自動更新)
「毎週更新しようと決めたのに、気づけば1ヶ月投稿が止まっている」——個人ブログだけでなく、社内の技術ブログや採用広報を担当している方にも、この経験は身に覚えがあるはずです。更新が止まると検索順位は下がり、読者も離れる。でも対策は案外シンプルです。一度仕組みを作れば、あとは「書くこと」だけに集中できます。
この記事では、GitHub Actionsを使ってブログを自動化する方法を解説します。設定ファイルを1本書けば、毎週月曜朝8時に記事が自動で公開され続けます。
| Before | After |
|---|---|
| 記事を書く → コピペ → 管理画面から投稿 | GitHubにpushするだけ |
私自身、この仕組みを導入してから6ヶ月間、週1更新を一度も欠かしていません。最初の設定さえ終われば、あとは記事を書いてフォルダに置くだけです。
初期設定には30〜45分程度かかります(Git操作に慣れていれば15分以内)。
この記事を読む前に確認すること
- [ ] GitHubアカウントを持っている
- [ ] はてなブログを開設している
- [ ]
post_to_hatena.py(記事03で解説)が手元にある → まだの方は記事03:はてなブログにAPIで投稿する方法(AtomPub完全ガイド)を先にご覧ください
この記事ははてなブログ向けの手順です。 WordPressなど他のプラットフォームでも「投稿スクリプト」部分を差し替えるだけで同じ仕組みが使えます。
この記事でできること
- GitHubにpushするだけで記事が自動投稿される
- 完全放置でもブログが毎週更新される状態になる
- 下書き(
articles/フォルダ)と公開の制御も可能
この記事でわかること
- GitHub Actionsの基本構造と、ブログ自動化に向いている理由
push(GitHubへのファイルアップロード)やschedule(cron)など複数のトリガーの使い分け- はてなブログAPIキーなどのシークレット情報を安全に管理する方法
- 実際のワークフローYAMLをコピペで動かす手順
- 実行ログの読み方と、よくあるエラーの対処法
GitHub Actionsとブログ自動化の仕組み
GitHub Actionsは、GitHubが提供するCI/CD(継続的インテグレーション/デリバリー)サービスです。「リポジトリ(GitHubでファイルを管理するフォルダのような場所)へのpush(ファイルのアップロード)」「スケジュール(cron)」「手動実行」などのイベントをトリガーに、任意のスクリプトをGitHubのサーバー上で自動実行できます。
ブログ自動化との相性がよい点が3つあります。
- 無料枠が十分 パブリックリポジトリは無制限、プライベートも月2000分(週1投稿なら年間で約50分程度の消費なので余裕)
- cronが使える 毎週・毎日など定期実行をYAMLで宣言するだけ
- Secretsで認証情報を安全管理 APIキーをコードに書かずに利用できる
トリガーの種類と使い分け
設定を始める前に、どのトリガーを使うかを先に決めておくと全体の見通しが立ちやすくなります。
| トリガー | 用途 | 設定例 |
|---|---|---|
push |
ファイル追加時に即投稿 | on: push: paths: ["articles/*.md"] |
schedule |
曜日・時刻で定期実行 | on: schedule: - cron: "0 23 * * 0" |
workflow_dispatch(手動実行トリガー) |
ボタン1つで手動実行 | on: workflow_dispatch: |
pull_request |
PRマージ時に投稿 | on: pull_request: types: [closed] |
選び方の目安: 記事のストックが少ないうちは push トリガーだけで十分です。目安として4〜6本ストックできた段階で schedule への移行を検討してください。本番運用では schedule + workflow_dispatch の組み合わせがおすすめです。定期実行をベースにしつつ、臨時投稿したいときは手動実行ボタンで対応できます。
はてなブログAPIキーの取得
設定を始める前に、はてなブログのAPIキーを手元に用意しておきましょう。
- はてなブログの管理画面にログイン
- 設定 → 詳細設定 → AtomPub(はてなブログが外部ツールと連携するためのAPI規格)を開く
- 「APIキー」欄に表示されている文字列をコピーする
はてなIDとブログIDはURLから確認できます。たとえば yourid.hatenablog.com であれば、yourid がはてなID、yourid.hatenablog.com 全体がブログIDです。
コピーしたAPIキーは次のセクションでGitHub Secretsに登録します。メモしておくか、ブラウザのタブを開いたままにしておきましょう。
はてなブログ自動投稿のワークフロー設定(YAMLサンプル付き)
この仕組みは 「push → Actions起動 → API投稿」 という流れで動きます。それぞれのステップを順番に設定していきましょう。
ローカル実行(記事02・03)との違い:ローカルは手動トリガー、GitHub Actionsは自動トリガーです。スクリプト自体は同じものを使い回せます。
ワークフローは .github/workflows/ に置くYAMLファイルで定義します。まずは「articles/ フォルダにMarkdownを追加したら自動投稿する」シンプルな構成から始めましょう。
この設定を入れると、articles/ フォルダに .md ファイルを追加して mainブランチにpushしたタイミングで、自動的に記事が投稿されます。
# .github/workflows/post-on-push.yml name: Post to Hatena on Push on: push: branches: - main paths: - "articles/*.md" # articles/ 以下の.md変更時だけ発火 jobs: post: runs-on: ubuntu-latest steps: - name: リポジトリをチェックアウト uses: actions/checkout@v4 # リポジトリのファイルを作業環境に取得する標準ステップ - name: Pythonをセットアップ uses: actions/setup-python@v5 with: python-version: "3.11" - name: 依存パッケージをインストール run: pip install -r requirements.txt - name: はてなブログに投稿 env: HATENA_ID: ${{ secrets.HATENA_ID }} HATENA_API_KEY: ${{ secrets.HATENA_API_KEY }} HATENA_BLOG_ID: ${{ secrets.HATENA_BLOG_ID }} run: python post_to_hatena.py
ポイント: paths フィルターを設定しておくと、READMEや設定ファイルの変更では投稿スクリプトが走りません。誤投稿を防ぐための重要な設定です。
post_to_hatena.py と requirements.txt について
上記YAMLが参照している post_to_hatena.py は、はてなブログのAtomPub APIを叩いてMarkdownを投稿するPythonスクリプトです(requests ライブラリを使用)。requirements.txt はスクリプトが使うライブラリの一覧ファイルで、requests など最低限のHTTPライブラリを記載します。
詳しい実装は記事03で解説していますが、スクリプトが手元にある前提でこのまま読み進めてください。
Secretsの安全な管理方法
APIキーなどの認証情報は絶対にYAMLにハードコードしないこと。GitHubのSecretsに登録すればコードに書かずに済みます。
⚠️ ここをミスるとAPIキーが漏洩します(最重要ポイント) GitHub Actionsは権限設定ミスが攻撃対象になりやすい点が指摘されています。Secretsを使わずYAMLに直書きすると、リポジトリが公開状態の場合は全世界に晒されます。必ずSecretsで管理してください。
登録手順
- GitHubリポジトリの Settings → Secrets and variables → Actions
- New repository secret をクリック
- 以下の3つを登録
| Name | Value の例 |
|---|---|
HATENA_ID |
your-hatena-id |
HATENA_API_KEY |
xxxxxxxxxxxx |
HATENA_BLOG_ID |
yourblog.hatenablog.com |
登録したSecretはYAMLから ${{ secrets.SECRET_NAME }} で参照できます。値はログに表示されず、*** にマスクされます。
Secretsの登録が完了したら、次はいよいよ定期実行の設定です。
cronで定期実行する方法
毎週月曜朝8時(JST)に自動実行するには、on.schedule にcron式を書きます。GitHubのcronはUTC基準なので、JST(UTC+9)の8時はUTC 23時(前日) になります。
# .github/workflows/weekly-post.yml name: Weekly Blog Post on: schedule: - cron: "0 23 * * 0" # 毎週日曜23:00 UTC = 月曜08:00 JST workflow_dispatch: # 手動実行ボタンも追加 permissions: contents: write # git pushに必要な権限 jobs: post: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.11" - run: pip install -r requirements.txt - name: 未投稿記事を1件投稿 env: HATENA_ID: ${{ secrets.HATENA_ID }} HATENA_API_KEY: ${{ secrets.HATENA_API_KEY }} HATENA_BLOG_ID: ${{ secrets.HATENA_BLOG_ID }} run: python post_to_hatena.py --next-unpublished - name: 投稿履歴をコミット run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git add posted/ series/ git diff --staged --quiet || git commit -m "chore: update post history [skip ci]" git push
[skip ci] をコミットメッセージに含めると、このコミット自体がワークフローを再トリガーしません。無限ループ防止に必須です。
実際のログの読み方
GitHub Actions のログは Actions タブ → 該当のワークフロー実行 から確認できます。
成功時のログ例:
✅ Post to Hatena on Push # ジョブ名
✅ Set up job # 環境構築
✅ Checkout # リポジトリ取得
✅ Setup Python # Python インストール
✅ Install dependencies # pip install
✅ はてなブログに投稿 # メインのスクリプト
> 投稿成功: https://example.hatenablog.com/entry/...
✅ 投稿履歴をコミット
失敗時のログ例:
❌ はてなブログに投稿 > requests.exceptions.HTTPError: 403 Client Error: Forbidden
各ステップの左側が ✅(緑)なら成功、❌(赤)なら失敗です。ログを読むときはまず ❌ のステップを探し、クリックして展開してください。エラーメッセージは展開後の最後の数行に集中していることが多いので、下にスクロールして確認しましょう。
よくあるミス(まずここを確認)
設定後に動かないときは、以下の4点を先にチェックしてください。
- [ ] Secrets名が一致していない(YAML側と登録名の大文字小文字を確認)
- [ ] main以外のブランチにpushしている(
branches: [main]で絞り込み済みの場合) - [ ] XML生成でエスケープ漏れ(記事本文に
<>&が含まれていないか) - [ ] APIキーが無効になっている(はてなブログ側でキーが再発行された可能性)
GitHub Actionsブログ自動化でよくあるエラーと対処法
1. 権限エラー(403 Forbidden)
Error: 403 Forbidden - authentication failed
SecretのAPIキーの入力ミスや未登録が主な原因です。Settings → Secrets で値を再確認・再登録してください。
2. git push 失敗(403 / remote rejected)
remote: Permission to ... denied to github-actions[bot].
ワークフローからのpushにGITHUB_TOKENのwrite権限がないケースです。週次投稿のYAMLサンプルには既に permissions: contents: write を含めていますが、独自に作成したワークフローに不足している場合は追加してください。
3. cron が実行されない
原因1: リポジトリのデフォルトブランチにアクティビティが60日以上ない場合、scheduleが自動停止されます。Actions タブから手動で有効化するか、定期的にコミットを入れておきましょう。
原因2: GitHubのサーバー負荷による遅延が発生することがあります。筆者の6ヶ月の運用では、1時間以上の遅延は2〜3回程度でした。8時ちょうどの配信にこだわらなければ実用上の問題はありません。厳密な時刻指定が必要な場合は別サービスの利用を検討してください。
4. 多重投稿
原因: push と schedule が同タイミングで重なった場合や、paths フィルターが未設定の場合に発生します。
対処: 投稿済みかどうかをスクリプト側でチェックするロジックを追加しましょう。posted/ フォルダに投稿済みファイル名を記録する方法が一般的です。以下のようなコードをスクリプトに組み込むだけで多重投稿を防げます。
import os def is_already_posted(filename): posted_path = f"posted/{filename}" return os.path.exists(posted_path) def mark_as_posted(filename): os.makedirs("posted", exist_ok=True) with open(f"posted/{filename}", "w") as f: f.write("") # 投稿済みマーカーとして空ファイルを作成
まとめ:GitHub ActionsでブログのCI/CDを構築しよう
この記事では、GitHub Actionsを使ってブログを定期自動投稿する仕組みを作りました。投稿スクリプト・cron・Secretsの3点セットを設定すれば、ブログ更新の"忘れ"はゼロになります。
schedule+workflow_dispatchの組み合わせで「週1定期+手動」を両立- SecretsでAPIキーを安全に管理し、コードに認証情報を書かない
[skip ci]でワークフローの無限ループを防止- ログは
❌ステップの末尾数行を最初に確認する - 多重投稿防止のため、
posted/フォルダへの記録ロジックをスクリプトに組み込む
これで「記事を書く → pushするだけ」の状態が完成です。 ブログ更新の"忘れ"はゼロになり、あとは記事を書くだけで資産が積み上がります。
まだ設定が終わっていない方は、workflow_dispatch で手動実行して動作確認してみてください。Actionsタブに「Run workflow」ボタンが表示されているはずです。
次の記事(記事05)では、Claude API × GitHub Actionsを組み合わせて「記事の生成から投稿まで完全自動化」する構成を解説します。Claude APIでブログ記事を自動生成する方法も参考にしながら、Markdownファイルを1本 articles/ に置くだけで、AIが内容をチェック・補完してから自動投稿される仕組みを作り上げましょう。
このシリーズの前後の記事: - ← 前の記事:はてなブログにAPIで投稿する方法(AtomPub完全ガイド) - → 次の記事:Claude API×GitHub Actionsで完全自動化する構成(近日公開)
シリーズ一覧:ブログ運用を半自動化する実践ロードマップ