Zenn記事をXに自動投稿するGitHub Actions設定手順


Zennで記事を公開したら、手動でXにシェアしていませんか?

「記事を書き終えた達成感で満足してしまい、Xへの投稿を忘れる」「毎回URLをコピペするのが地味に面倒」——そんな経験があるなら、GitHub Actionsで自動化してしまうのがおすすめです。

一度設定すれば、Zennへのpushをトリガーにして、自動でXに投稿が流れるようになります。

仕組みをざっくり理解する

この自動化は、3つの要素を組み合わせています。

  1. Zenn + GitHub連携:記事をGitHubリポジトリで管理し、pushすると自動で公開される
  2. GitHub Actions:pushや定期実行をトリガーに処理を走らせる
  3. X API:プログラムからXに投稿する

流れとしては「GitHubにpush → Actionsが起動 → X APIで投稿」というシンプルな構成です。

事前に必要なもの

Zenn側の準備

ZennとGitHubリポジトリを連携済みであることが前提です。まだの場合は、Zenn公式の「Zenn CLIで記事・本を管理する方法」を参考に設定してください。

記事はarticles/ディレクトリにMarkdownファイルとして配置し、Front Matterでpublished: trueにすると公開対象になります。

---
title: "記事タイトル"
emoji: "📝"
type: "tech"
topics: ["github", "automation"]
published: true
---

X API側の準備

X(旧Twitter)APIを使うには、開発者アカウントとアプリの登録が必要です。

  1. X Developer Portalでアプリを作成
  2. OAuth 1.0aのアクセストークンを取得(API Key、API Secret、Access Token、Access Token Secretの4つ)

この4つの値は、後でGitHub Secretsに登録します。

GitHub Actionsのワークフロー設定

リポジトリに.github/workflows/post-to-x.ymlを作成します。

name: Post to X

on:
  push:
    branches:
      - main
    paths:
      - 'articles/*.md'

jobs:
  post:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 2

      - name: Get changed files
        id: changed
        run: |
          CHANGED=$(git diff --name-only HEAD~1 HEAD -- 'articles/*.md' | head -1)
          echo "file=$CHANGED" >> $GITHUB_OUTPUT

      - name: Extract article info
        if: steps.changed.outputs.file != ''
        id: article
        run: |
          FILE="${{ steps.changed.outputs.file }}"
          SLUG=$(basename "$FILE" .md)
          TITLE=$(grep -m1 '^title:' "$FILE" | sed 's/title: *"\(.*\)"/\1/')
          echo "slug=$SLUG" >> $GITHUB_OUTPUT
          echo "title=$TITLE" >> $GITHUB_OUTPUT

      - name: Post to X
        if: steps.article.outputs.slug != ''
        env:
          API_KEY: ${{ secrets.X_API_KEY }}
          API_SECRET: ${{ secrets.X_API_SECRET }}
          ACCESS_TOKEN: ${{ secrets.X_ACCESS_TOKEN }}
          ACCESS_TOKEN_SECRET: ${{ secrets.X_ACCESS_TOKEN_SECRET }}
        run: |
          TWEET_TEXT="${{ steps.article.outputs.title }} を公開しました!
          https://zenn.dev/あなたのユーザー名/articles/${{ steps.article.outputs.slug }}"
          
          # ここでX APIを呼び出す(実際にはOAuth署名が必要)
          echo "投稿内容: $TWEET_TEXT"

GitHub Secretsの登録

リポジトリの「Settings → Secrets and variables → Actions」から、以下の4つを登録します。

  • X_API_KEY
  • X_API_SECRET
  • X_ACCESS_TOKEN
  • X_ACCESS_TOKEN_SECRET

Secretsに保存した値はログに出力されないため、安全に管理できます。

実装時のポイント

X APIの認証について

上記のサンプルは構造を示すための簡略版です。実際にX APIへ投稿するには、OAuth 1.0aの署名付きリクエストを送る必要があります。

Node.jsならtwitter-api-v2、Pythonならtweepyなどのライブラリを使うと、署名処理を任せられて楽です。

// Node.jsの例(twitter-api-v2使用)
const { TwitterApi } = require('twitter-api-v2');

const client = new TwitterApi({
  appKey: process.env.API_KEY,
  appSecret: process.env.API_SECRET,
  accessToken: process.env.ACCESS_TOKEN,
  accessSecret: process.env.ACCESS_TOKEN_SECRET,
});

await client.v2.tweet('記事を公開しました! https://zenn.dev/...');

scheduleトリガーを使う場合の注意

pushではなく定期実行(on: schedule)で動かす場合、いくつか知っておくべき点があります。

  • cronはUTC指定。日本時間の午前9時に実行したいなら0 0 * * *(UTC 0時 = JST 9時)
  • 毎時0分は混雑しやすく、遅延やスキップが起きることがある。5分や15分にずらすのが無難
  • 公開リポジトリで60日間アクティビティがないと、scheduleが自動で無効化される
on:
  schedule:
    - cron: '15 0 * * *'  # 毎日JST 9:15に実行

Zennのデプロイがスキップされるケース

コミットメッセージに[ci skip][skip ci]が含まれていると、Zenn側のデプロイが走りません。自動コミットを組み込む場合は、このキーワードを避けるようにしてください。

うまく動かないときのチェックリスト

  • X APIのアクセストークンは「Read and Write」権限になっているか
  • GitHub Secretsの名前にタイポはないか
  • pathsフィルタで対象ファイルが正しく指定されているか
  • ZennのユーザーURLは正しいか

Actionsの実行ログは「Actions」タブから確認できます。エラーが出ていれば、そこにヒントがあるはずです。


設定は最初だけ少し手間ですが、一度動き始めると「記事を書いてpushするだけ」で告知まで完了します。

記事を書くこと自体に集中できるようになるので、ぜひ試してみてください。

参考リンク