スケジュール実行で実現 - Qiita記事の予約投稿システム(第3回:予約投稿編)
⚠️ 注意事項
免責事項:
- この記事の内容は 2025-12-30 時点の情報に基づいています
- GitHub Actionsの仕様は変更される可能性があります
- cron式のタイムゾーンはUTCです(JSTとの時差に注意)
- 予約投稿の動作を事前にテストしてください
📝 TL;DR
- 課題: 記事を書き溜めても、毎回手動で投稿するのが面倒
- 解決策: GitHub ActionsのScheduled Workflowで予約投稿を実現
- 効果: 週次投稿を完全自動化(手動作業0分)
- 導入工数: 約 3時間
📊 改善効果サマリー
| 項目 | 改善前 | 改善後 | 削減率 |
|---|---|---|---|
| 週次投稿作業時間 | 10分 | 0分 | 100%削減 |
| 投稿忘れリスク | 高 | なし | 100%削減 |
| 導入コスト | - | 3時間 | - |
| 学習コスト | - | 中 | - |
🎯 背景
なぜこの改善に取り組んだか
第1回でGitHub Actionsによる手動投稿、第2回でIFTTT連携によるX(Twitter)自動投稿を実現しましたが、まだ投稿タイミングは手動でした。
- 記事を書き溜めても、投稿は手動で行う必要がある
- 「今週はまだ投稿していない」と思い出す必要がある
- 最適な投稿タイミング(日曜朝など)に投稿したい
- 記事を事前に準備して、スケジュール通りに投稿したい
対象読者
- Qiita記事を定期的に投稿したい方
- GitHub Actionsのcron式を学びたい方
- 予約投稿機能を実装したい方
- ブログの運用を自動化したい方
😓 課題
画像
従来の業務フロー
1. 記事を執筆(平日に書き溜め)
2. 投稿タイミングを思い出す(週末)
3. GitHub Actionsで手動実行(1分)
4. 投稿確認(1分)
合計: 約2分/記事(投稿タイミングを逃すリスクあり)
具体的な問題点
-
投稿忘れのリスク
- 詳細: 投稿タイミングを思い出す必要がある
- 影響: 週次投稿の習慣が崩れる
- 頻度: 月1回程度発生
-
最適な投稿タイミングに投稿できない
- 詳細: 日曜朝6:00に投稿したいが、手動では難しい
- 影響: 読まれる可能性が下がる
-
記事の書き溜めができない
- 詳細: 投稿日に記事を準備する必要がある
- 影響: 計画的な記事執筆ができない
定量的な影響
- 投稿忘れリスク: 月1回 × 12ヶ月 = 年12回
- 投稿タイミングのずれ: 毎回
→ 定期投稿の習慣が崩れるリスク
💡 解決策
採用したアプローチ
GitHub ActionsのScheduled Workflowとファイル名ベースの予約投稿システムを構築しました。
仕組み:
- 記事ファイル名に公開日を含める(例:
2025-12-31-記事タイトル.md) - GitHub Actionsが毎週日曜6:00に自動実行
- 公開日が今日以前の記事を自動的に
private: falseに変更 - 変更をコミット & プッシュ → Qiitaに自動投稿
選定理由:
- ✅ 完全自動化(手動作業0)
- ✅ ファイル名で予約日を管理(シンプル)
- ✅ GitHub Actionsの無料枠で十分
- ✅ いつでも手動実行可能
代替案との比較:
| 項目 | GitHub Actions | Qiita予約投稿 | WordPress予約投稿 |
|---|---|---|---|
| 導入コスト | 中(3時間) | なし | 高(WordPress必要) |
| カスタマイズ性 | 高 | 低 | 中 |
| Gitでの管理 | 可能 | 不可 | 不可 |
| 料金 | 無料 | なし(機能なし) | 有料プランが多い |
改善後の業務フロー
1. 記事を執筆(平日に書き溜め)
2. ファイル名に公開日を設定(例: 2025-12-31-記事タイトル.md)
3. Gitにコミット & プッシュ
4. 毎週日曜6:00に自動チェック & 自動投稿
合計: 約0分/記事(完全自動化)
アーキテクチャ図
🔧 実装
開発環境
| 項目 | 詳細 |
|---|---|
| OS | Windows 11 |
| Git | 2.43.0 |
| GitHub Actions | ubuntu-latest |
| シェル | Bash |
セットアップ手順
1. Scheduled Publishワークフローの作成
.github/workflows/weekly-scheduled-publish.yml を作成します。
name: Weekly Scheduled Publish (毎週日曜 6:00)
# 毎週日曜日 6:00 JST(土曜日 21:00 UTC)に実行
# 2026-01-03 から有効化
on:
# 毎週日曜日 6:00 JST(土曜日 21:00 UTC)に実行
schedule:
- cron: '0 21 * * 6' # 土曜 21:00 UTC = 日曜 6:00 JST
# 手動実行も可能
workflow_dispatch:
permissions:
contents: write
jobs:
update-publish-status:
runs-on: ubuntu-latest
steps:
- name: 📅 有効化日をチェック
id: activation_check
run: |
TODAY=$(date -u +%Y-%m-%d)
echo "Today: $TODAY"
# 有効化日: 2026-01-03
if [[ "$TODAY" < "2026-01-03" ]]; then
echo "⚠️ このワークフローは 2026-01-03 から有効化されます"
echo "SKIP=true" >> $GITHUB_OUTPUT
exit 0
fi
echo "✅ ワークフロー有効化済み"
echo "SKIP=false" >> $GITHUB_OUTPUT
- name: Checkout repository
if: steps.activation_check.outputs.SKIP != 'true'
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Update article publish status
if: steps.activation_check.outputs.SKIP != 'true'
id: update
run: |
set -euo pipefail
shopt -s nullglob
CHANGED=0
TODAY=$(date -u +%Y-%m-%d)
echo "## 📝 Weekly Scheduled Publish - $TODAY" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "📅 Today: $TODAY" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# qiita/public/ 配下のMarkdownファイルを処理
for file in qiita/public/*.md; do
if [ ! -f "$file" ]; then
continue
fi
# ファイル名から日付を抽出(YYYY-MM-DD形式)
filename=$(basename "$file")
if [[ $filename =~ ^([0-9]{4}-[0-9]{2}-[0-9]{2})- ]]; then
publish_date="${BASH_REMATCH[1]}"
echo "📝 Processing: $filename" >> $GITHUB_STEP_SUMMARY
echo " Publish date: $publish_date" >> $GITHUB_STEP_SUMMARY
# 公開日が今日以前かチェック
if [[ "$publish_date" < "$TODAY" ]] || [[ "$publish_date" == "$TODAY" ]]; then
# private: true を private: false に変更
if grep -q "^private: true" "$file"; then
sed -i 's/^private: true/private: false/' "$file"
echo " ✅ Changed to public" >> $GITHUB_STEP_SUMMARY
CHANGED=1
else
echo " ℹ️ Already public or no private field" >> $GITHUB_STEP_SUMMARY
fi
else
echo " ⏰ Not yet (publish date: $publish_date)" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
fi
done
echo "CHANGED=$CHANGED" >> $GITHUB_OUTPUT
echo "---" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ $CHANGED -eq 1 ]; then
echo "✅ **Some articles were updated to public status**" >> $GITHUB_STEP_SUMMARY
else
echo "ℹ️ **No articles to update**" >> $GITHUB_STEP_SUMMARY
fi
- name: Commit and push if changed
if: steps.activation_check.outputs.SKIP != 'true' && steps.update.outputs.CHANGED == '1'
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add qiita/public/*.md
git commit -m "🗓️ Auto-update article publish status [skip ci]"
git push
2. Publish Articlesワークフローの更新
workflow_run トリガーを追加して、Scheduled Publish完了後に自動実行されるようにします。
on:
push:
branches:
- main
paths:
- 'qiita/public/**'
# Scheduled Publishワークフロー完了後に自動実行
workflow_run:
workflows: ["Weekly Scheduled Publish (毎週日曜 6:00)"]
types:
- completed
workflow_dispatch:
実装詳細
ステップ1: cron式の理解
GitHub Actionsのcron式はUTCで動作します。
# cron式の形式
# ┌───────────── 分 (0 - 59)
# │ ┌───────────── 時 (0 - 23)
# │ │ ┌───────────── 日 (1 - 31)
# │ │ │ ┌───────────── 月 (1 - 12)
# │ │ │ │ ┌───────────── 曜日 (0 - 6) (日曜=0, 月曜=1, ..., 土曜=6)
# │ │ │ │ │
# * * * * *
# 例: 毎週日曜 6:00 JST = 土曜 21:00 UTC
'0 21 * * 6' # 土曜 21:00 UTC
ポイント:
- UTCとJSTの時差は9時間
- 日曜 6:00 JST = 土曜 21:00 UTC
- 曜日は0(日曜)〜6(土曜)
ステップ2: ファイル名から日付を抽出
Bash正規表現でファイル名から日付を抽出します。
filename="2025-12-31-記事タイトル.md"
# 正規表現で日付を抽出
if [[ $filename =~ ^([0-9]{4}-[0-9]{2}-[0-9]{2})- ]]; then
publish_date="${BASH_REMATCH[1]}" # 2025-12-31
echo "Publish date: $publish_date"
fi
ポイント:
-
^([0-9]{4}-[0-9]{2}-[0-9]{2})-で YYYY-MM-DD 形式を抽出 -
BASH_REMATCH[1]で最初のキャプチャグループを取得
注意点:
- ファイル名は必ず
YYYY-MM-DD-で始める必要がある - 日付形式が間違っていると無視される
ステップ3: private: true を private: false に変更
sed コマンドでフロントマターを更新します。
# private: true を private: false に変更
sed -i 's/^private: true/private: false/' "$file"
# 確認
if grep -q "^private: true" "$file"; then
echo "Still private"
else
echo "Changed to public"
fi
ポイント:
-
-iオプションでファイルを直接編集 -
^private: trueで行頭のprivate: trueのみマッチ
ステップ4: workflow_run でワークフロー連鎖
Scheduled Publish完了後にPublish Articlesを自動実行します。
workflow_run:
workflows: ["Weekly Scheduled Publish (毎週日曜 6:00)"]
types:
- completed
ポイント:
-
workflowsにはワークフロー名を指定 -
types: completedで成功/失敗に関わらず実行 - 成功時のみ実行したい場合は、条件分岐を追加
if: ${{ github.event.workflow_run.conclusion == 'success' }}
📈 効果測定
定量的な効果
作業時間の削減
| 作業 | 改善前 | 改善後 | 削減時間 |
|---|---|---|---|
| 投稿タイミングを思い出す | 1分 | 0分 | 1分 |
| GitHub Actionsで手動実行 | 1分 | 0分 | 1分 |
| 投稿確認 | 1分 | 0分 | 1分 |
| 合計 | 3分 | 0分 | 3分 (100%削減) |
年間削減効果の試算
1回あたりの削減時間: 3分
月間投稿回数: 4回
年間投稿回数: 4回 × 12ヶ月 = 48回
年間削減時間: 48回 × 3分 = 144分
= 約 2.4時間の工数削減
※ 投稿忘れリスクもゼロに
定性的な効果
-
✅ 完全自動化
- 週次投稿が完全に自動化された
-
✅ 投稿忘れリスクがゼロ
- スケジュール通りに必ず投稿される
-
✅ 最適な投稿タイミング
- 日曜朝6:00という最適なタイミングで投稿
-
✅ 記事の書き溜めが可能
- 事前に記事を準備して、計画的に投稿できる
-
✅ 心理的負担の軽減
- 「投稿しなきゃ」というプレッシャーがなくなった
🚀 導入方法
導入の流れ
-
準備フェーズ(30分)
- cron式の学習
- タイムゾーン(UTC/JST)の理解
- ファイル命名規則の決定
-
実装フェーズ(2時間)
- Scheduled Publishワークフローの作成
- Publish Articlesワークフローの更新
- テスト記事の作成
-
検証フェーズ(30分)
- 手動実行でテスト
- ログの確認
- 3日間のテスト運用
必要なリソース
人的リソース
- エンジニア: GitHub Actions、Bash、cron式の知識
技術リソース
- GitHub: 無料プラン(Actionsの無料枠)
- Git: バージョン管理
時間リソース
- 初期導入: 約 3時間
- 学習期間: 約 2時間(cron式、Bash)
- メンテナンス: 月1回(10分程度)
⚠️ 留意点・制約事項
技術的な制約
-
cron式の実行タイミング
- 内容: GitHub Actionsのcronは数分遅れる場合がある
- 対策: 余裕を持ったスケジュール設定
-
UTCとJSTの時差
- 内容: cron式はUTCで動作(JSTとの時差9時間)
- 対策: 時差計算を間違えないように注意
-
ファイル名の命名規則
- 内容:
YYYY-MM-DD-で始める必要がある - 対策: テンプレートファイルを用意
- 内容:
運用上の注意点
- 手動実行でテスト: 本番運用前に必ずテスト
- ログの確認: GitHub ActionsのSummaryでログを確認
- 緊急時の対応: ワークフローを一時停止可能
想定されるリスクと対策
| リスク | 影響度 | 発生確率 | 対策 |
|---|---|---|---|
| cron実行の遅延 | 低 | 中 | 余裕を持ったスケジュール |
| 時差計算ミス | 高 | 低 | テスト運用で確認 |
| ファイル名ミス | 中 | 低 | テンプレート使用 |
こんな場合は不向き
- ❌ 毎日投稿したい(cron式で対応可能だが、記事の準備が大変)
- ❌ 投稿タイミングを柔軟に変えたい(手動実行で対応可能)
- ❌ 記事数が少ない(手動投稿で十分)
🔄 今後の展開
短期的な改善計画(1〜3ヶ月)
- 投稿前の自動プレビュー生成
- 投稿後の自動通知(Slack、Discord)
- 投稿統計の自動集計
中長期的な構想(6ヶ月〜1年)
- AIによる記事の自動校正
- SEO最適化の自動チェック
- 画像の自動最適化
💬 まとめ
この改善で実現できたこと
- ✅ 週次投稿を完全自動化(手動作業0分)
- ✅ 投稿忘れリスクをゼロに
- ✅ 記事の書き溜めが可能に
学んだこと・気づき
-
cron式は強力
- 定期実行で様々な自動化が可能
- UTCとJSTの時差に注意
-
ファイル名ベースの管理はシンプル
- データベース不要
- Gitで管理が完結
-
ワークフロー連鎖で複雑な処理も可能
-
workflow_runで連鎖実行 - 段階的な自動化が実現
-
推奨する人
- ✅ Qiita記事を定期的に投稿したい方
- ✅ 記事を書き溜めたい方
- ✅ GitHub Actionsで自動化したい方
最後に
3回シリーズで、Qiita記事の投稿を完全自動化しました。
- 第1回: GitHub Actionsで手動投稿
- 第2回: IFTTT連携でX(Twitter)自動投稿
- 第3回: スケジュール実行で予約投稿
これで、記事執筆に集中でき、投稿は完全に自動化されました。
補足:
予約投稿の動作を事前にテストしてください。特に、cron式のタイムゾーン(UTC/JST)に注意してください。
📚 参考資料
公式ドキュメント
- GitHub Actions - Scheduled Events
- Crontab Guru - cron式の確認ツール
関連記事
📝 更新履歴
- 2025-12-30: 初版公開
タグ: #GitHub #GitHubActions #Qiita #自動化 #cron

