GitHub Actions × Google Drive × Astro Workers でブログ同期を自動化した話
目次
このレポートについて
Astro で構築した技術ブログを Cloudflare Pages で公開している環境で、以下を満たす「ブログ記事同期の自動化フロー」を構築したときの記録です。
- 情報整理は ChatGPT で行い、最終成果物は Markdown(md)として保存したい
- 下書きや下準備は Google Drive に置きたい(PC/スマホ共通の置き場)
- 最終的には GitHub リポジトリの
src/content/blogに md を自動配置してほしい - Astro サイト側から「同期ボタン」を押すと GitHub Actions が走るようにしたい
特に、Astro + @astrojs/cloudflare(Workers モード)と Cloudflare Pages Functions の相性問題でかなりハマったので、そのあたりを重点的に残しています。
やりたかったこと
目的
- 調査メモ → 記事構造化 → Markdown 化までを ChatGPT で行う
- 生成した Markdown ファイルを、まずは Google Drive に配置
- GitHub Actions が Google Drive から md を取得してリポジトリに反映
- GitHub への push をトリガーに Cloudflare Pages が再デプロイ
- 必要なときだけ、ブログ側 UI から「同期ボタン」で GitHub Actions を任意実行
背景
- Astro 製の技術ブログを
antigravity-techblogリポジトリで運用中 - Cloudflare Pages でホスティングし、
@astrojs/cloudflareアダプタを使用 - ログイン必須のページやミドルウェアもあり、Workers モード(SSR)を維持したい
- ただし更新作業が「調査 → md 手打ち → Git commit → push」でダルい
最終的な構成概要
全体フロー
- ChatGPT でテンプレートに沿った md を生成してローカル保存
- Google Drive(指定フォルダ)にその md を格納
- GitHub Actions の workflow(
drive-sync.yml)で以下を実行- Node スクリプト(
scripts/fetch-from-drive.mjs)を呼び出し - Google Drive API(サービスアカウント)で md を一覧取得
- 新規 md を
src/content/blog/にコピー - 既に存在するファイル名はスキップ(重複生成を防止)
git add/commit/pushで main ブランチに反映
- Node スクリプト(
- GitHub の push をトリガーに Cloudflare Pages が自動デプロイ
- Astro サイトのトップに配置した「ブログ同期ボタン」から
/api/run-drive-syncに POST- その API が GitHub の
workflow_dispatchを叩き、3 の処理を即時起動
技術要素
- Astro(@astrojs/cloudflare アダプタ)
- Cloudflare Pages(Workers モード / _worker.js)
- GitHub Actions(
workflow_dispatch+ PAT) - Google Drive API(サービスアカウント + フォルダ ID)
- Node.js スクリプト(Drive → ローカル → Git 操作)
- Astro API Route(
src/pages/api/run-drive-sync.ts)
手順と実装の詳細
1. Google Drive 側の準備
- サービスアカウントを作成し、JSON キーを取得
- Drive 上で「ブログ同期用」フォルダを用意
- そのフォルダをサービスアカウントのメールアドレスと共有
- フォルダ ID を GitHub Actions から参照できるようにリポジトリ Secrets に保存
この時点で、サービスアカウント経由で「特定フォルダ内の md を一覧取得」できる状態にする。
2. Node スクリプト scripts/fetch-from-drive.mjs
目的:
- Google Drive フォルダ内の
.mdファイルを取得 - ローカルの
src/content/blog/に保存 - 既に同名ファイルが存在する場合はスキップ
- 新規ファイルがあれば
git add/commitする
ざっくりとした処理フロー:
- Google API クライアントで Drive の一覧を取得
mimeType = text/markdown相当のファイルのみ対象にする- ローカルに存在するファイル名を事前にセット化
- 存在しないファイルのみダウンロード対象とする
src/content/blog/<filename>.mdに保存- 新規ファイルが 1 件以上あれば git コマンドを実行
git add src/content/bloggit commit -m "chore: import blog markdown from Google Drive YYYY-MM-DD"
※ GitHub Actions 内で動くため、push は Actions 用 PAT で行う。
3. GitHub Actions ワークフロー drive-sync.yml
役割:
- 手動実行(
workflow_dispatch)と夜間自動実行(cron)の両方に対応 - Node をセットアップし、
npm install→npm run fetch-from-driveを実行
例(概略):
name: Drive Sync
on:
workflow_dispatch:
schedule:
- cron: "0 12 * * *" # JST 夜に相当するよう調整
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
- name: Install dependencies
run: npm ci
- name: Fetch markdown from Google Drive
env:
GOOGLE_SERVICE_ACCOUNT_JSON: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_JSON }}
DRIVE_FOLDER_ID: ${{ secrets.DRIVE_FOLDER_ID }}
run: npm run fetch-from-drive
- name: Push changes
env:
GITHUB_TOKEN: ${{ secrets.PERSONAL_GITHUB_PAT }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git push
ポイント:
workflow_dispatchを必ず定義しておく(API から叩くため)- push 用には
workflow権限を含む PAT(PERSONAL_GITHUB_PAT)を使用 GOOGLE_SERVICE_ACCOUNT_JSONには JSON キーの中身をそのまま保存
4. Astro × Cloudflare Pages 特有のハマりどころ
4-1. Pages Functions が機能しない原因
当初は Cloudflare Pages の functions/ ディレクトリを使って
functions/api/run-drive-sync.ts→/api/run-drive-sync
という構成を狙ったが、動作しなかった。
ログから分かったこと:
- Astro の設定で
adapter: @astrojs/cloudflare+output: "server"を使用 - ビルド時に
dist/_worker.jsが生成され、Cloudflare はそれを優先採用 Found _worker.js in output directory. Uploading.というログが出ている場合、- Pages Functions ではなくカスタム Worker モード
functions/ディレクトリは一切見られない
このため、いくら functions/api/*.ts を置いても /api/... が 404 のままだった。
4-2. Build output と functions の共存トライ
一時的に以下も試した:
- Build output を空にしてリポジトリルートをそのまま配信
dist/*をルートにコピーするカスタム build スクリプト(build:cf)functions/hello.tsを追加して/helloをテスト
結果として、/hello が動くケースと / が 404 になるケースが混在し、
最終的に _worker.js が存在する限り functions ルートは使えないという結論に至った。
5. 最終解決策:Astro Workers(API ルート)で実装
ミドルウェアによるログインチェックを維持したかったため、 Workers モード(@astrojs/cloudflare)を維持しつつ、Astro の API ルートで GitHub Actions を叩く構成に切り替えた。
5-1. API ルートの実装
src/pages/api/run-drive-sync.ts を新規作成:
- ルート:
POST /api/run-drive-sync - やること:
- Cloudflare の env から
GITHUB_TOKENを取得 - GitHub REST API
actions/workflows/{workflow_file}/dispatchesを叩く ref: mainでdrive-sync.ymlを起動
- Cloudflare の env から
実装のポイント:
- Cloudflare Pages ×
@astrojs/cloudflareでは、env はcontext.locals.runtime.envから取得 - GitHub REST API は
User-Agentヘッダが必須 - エラー時には GitHub のレスポンスボディを一度確認し、403/404/422 のどれかで切り分ける
5-2. User-Agent ヘッダでハマった点
最初に出たエラー:
{
"ok": false,
"status": 403,
"body": "\r\nRequest forbidden by administrative rules. Please make sure your request has a User-Agent header ..."
}
GitHub REST API v3 は User-Agent が必須のため、以下をヘッダに追加して解決:
'User-Agent': 'noinet-antigravity-techblog-sync'
5-3. ページ側のトリガーボタン
Astro 側では、ブログトップに「ブログ同期」ボタンを設置し、
ハンドラ内で /api/run-drive-sync に POST するだけのシンプルな実装とした。
- ログイン状態は既存の middleware で判定
- 成功時/失敗時のトースト表示などはフロント側で制御
実際に動かしてみた結果
良かった点
- ChatGPT → md → Google Drive → GitHub → Cloudflare Pages という 「調査から公開までの一本のレール」が完成した
- 手元で md を Drive に置くだけでそのうち公開されるため、 「Git の細かい操作を毎回意識しなくて良い」状態になった
- 手動トリガー(API 経由)も用意したので、
- 新しい記事をすぐ出したいとき
- 夜のバッチ実行を待ちたくないとき に即座に Actions を起動できる
気をつけるべき点
- GitHub PAT の権限設定を最低限にすること(
repo+workflow) workflow_dispatchを忘れると API から起動できない- Google Drive 側のフォルダ構造を増やしすぎると、 スクリプト側のフィルタ条件が複雑になる可能性がある
- md の frontmatter はテンプレートを守る(キー欠損でビルド失敗する)
今後やりたい改善
- GitHub Actions の実行結果をフロント側にフィードバックする仕組み
- 成功/失敗を UI で分かるようにする
- Cloudflare Pages のデプロイ完了をポーリングして、 「公開完了まで」を一連の UX として見せる
- Google Drive から取得した md に対して lint / フォーマットチェックを追加し、 表示崩れや frontmatter ミスを早期検知する
まとめ
- Astro を Cloudflare Pages(Workers モード)で動かしている場合、
dist/_worker.jsが生成される- その状態では Cloudflare Pages の
functions/ディレクトリは使えない
- ログインやミドルウェアを維持しつつ API を追加したい場合は、
src/pages/api/*.tsとして Astro の API ルートを実装するのが素直
- GitHub Actions と Google Drive API を組み合わせれば、
- 「ローカル or スマホから md を Drive に置くだけでブログに反映」
- 「必要なときだけ UI から同期ボタンで即時反映」 という運用が実現できる。
今回構築したフローをベースに、 他のコンテンツ(テンプレート集、Tips 系記事、調査ノートなど)も 同じレールに乗せていくことで、 「調査したことを md にして残す」コストをさらに下げていきたい。