Zenn Publication ProとGitHub連携で構築する企業向け技術ブログ執筆環境
PKSHA TechnologyのZenn、はじめました
PKSHA Technology ソフトウェアエンジニア/EMの矢嶋です。
今まで PKSHA ではテックブログを他の記事とあわせて note で書いていたのですが、より多くのエンジニアに届けるため、またより良い執筆環境を実現するため、Zenn を始めました!
PKSHA の Zenn は Publication Pro プランと GitHub 連携を利用して執筆しています。
この記事では Zenn の執筆環境について紹介します。
この記事について
Zenn Publication Pro と GitHub 連携を活用して、PKSHA Technology の技術ブログ執筆環境を構築しました。本記事では、pnpm workspace による複数執筆者の管理、textlint と AI レビューによる品質管理、GitHub Actions を使った自動チェックの仕組みを紹介します。
Publication ProとGitHub連携を選んだ理由
Zenn Publication Pro には、Web エディタでの執筆と GitHub 連携の 2 つの方法がありますが、私たちは GitHub 連携に絞って運用しています。
Publication Pro + GitHub 連携の利点:
- 執筆経路を GitHub に絞り、プルリクベースでレビューと執筆ステータス管理ができる
- Pro プランのみ、Organization の単一レポジトリで複数人が執筆できる
- 誤って Web エディタを使わないよう、GitHub 連携以外の執筆を禁止するポリシーが設定できる
- GitHub Actions で自動チェックを実行できる
- VS Code、Cursor 等、好みのエディタで執筆できる
Publication には他にも PR 掲載、記事のピン留め、ダッシュボードなど様々な機能がありますが、執筆関連で私たちにとって重要なのは上記のポイントです。
私たちが利用していない機能として Web エディタ上でのレビュー機能や AI レビュー機能がありますが、GitHub 中心のフローを優先して、そのうえで AI レビューは自前で構築することにしました。
執筆環境の構築
ディレクトリ構成
Publication Pro の GitHub 連携では、各執筆者の Zenn ID でディレクトリを作り、その配下に記事を配置します。
個人レポジトリで Zenn と連携するときの構造をそのまま執筆者ディレクトリ内へ構築する形になります。
ドキュメントに明記されていませんが、個人利用時と同様に images ディレクトリを配置することで画像ファイルも管理できます。
Zenn の執筆体験の魅力のひとつとして Zenn CLI によるプレビュー機能がありますが、これも執筆者ディレクトリ内でコマンドを実行することで利用可能です。
.
├── README.md
├── authors.md # 執筆者一覧
├── create-user.sh # 新規執筆者追加スクリプト
├── .textlintrc.json # textlint設定
├── pnpm-workspace.yaml # pnpmワークスペース設定
└── {Zenn ID}/ # 執筆者ごとのディレクトリ
├── README.md
├── articles/ # 記事
├── books/
├── images/ # 画像
└── package.json # zenn-cli設定
pnpm workspaceによるパッケージ管理
各執筆者ディレクトリでの Zenn CLI の多重管理を避けるため、pnpm の workspace 機能でモノレポ構成にして、プロジェクトルートで管理しています。
それに合わせて、後述 textlint などもプロジェクトルートに構築しています。
また、昨今問題になっているサプライチェーン攻撃対策のひとつとして pnpm の minimumReleaseAge を設定しています。
執筆者追加の手順化
執筆者を追加する際は、執筆者ごとに上記のディレクトリの作成などの作業が必要になります。
また Zenn 特有の困りごととして、Zenn ID と氏名、GitHub ID の対応がレポジトリを見ただけでは判別しにくいという問題があります。
これらに対する工夫として、以下を自動で行うスクリプトを用意しました。
- 執筆者ディレクトリ作成
- 執筆者ごとの README に執筆者の情報を記載
- プロジェクトルートの
authors.md(執筆者一覧)の更新
create-user.sh:
#!/usr/bin/env bash
set -euo pipefail
# Zenn ID、氏名、メールアドレスを入力
read -r -p "Zenn ID: " zenn_user_id
read -r -p "氏名(例: Yamada Taro): " name
read -r -p "メールアドレス: " email
# authors.mdに追記
printf "| %s | %s | %s |\n" "$zenn_user_id" "$name" "$email" >> authors.md
# ディレクトリ作成
mkdir -p "$zenn_user_id"
# package.json作成
cat > "$zenn_user_id/package.json" << EOF
{
"name": "$zenn_user_id",
"scripts": {
"preview": "zenn preview",
"new:article": "zenn new:article"
}
}
EOF
# 依存関係インストールとZenn初期化
pnpm install
cd "$zenn_user_id"
pnpm zenn init
mkdir -p images
textlintとAIによる自動レビュー
記事の品質改善とレビュー負荷低減のため、textlint と AI による自動レビューを導入しています。
textlintによる文章チェック
textlint は日本語の校正ツールで、表記の統一や基本的な文章チェックを自動化できます。機械的に適用できるルールなら --fixオプションを付けることで自動修正が可能です。
--fix できない場合でも、今なら Claude Code や Cursor などの AI ツールで手軽に修正案を得ることができます。
以下は適用しているルールの抜粋です。
すべての指摘を解消することは求めておらず、あくまで参考程度のものとして導入しています。
.textlintrc.json(一部抜粋):
{
"rules": {
// 日本語技術文書プリセット
"preset-ja-technical-writing": {
"sentence-length": {"max": 120},
"no-exclamation-question-mark": false
},
// 全角半角間のスペース統一
"preset-ja-spacing": {
"ja-space-between-half-and-full-width": {
"space": "always"
}
},
// 類義語の検出
"@textlint-ja/no-synonyms": true,
// AWSサービス名チェック
"textlint-rule-aws-service-name": {},
// AIっぽい記述を検出するプリセット
// 実際は severity : info としてもっと細かく設定してますが長くなるため割愛
"@textlint-ja/textlint-rule-preset-ai-writing": true,
// カスタム辞書
"prh": {
"rulePaths": ["./prh-rules.yml"]
}
}
}
使い方:
# textlint の実行
pnpm run textlint {記事のパス}
# 自動修正できる指摘の修正実行
pnpm run textlint:fix {記事のパス}
Claude Codeによる AI レビュー
記事の AI レビューについて、初期構築と運用の負担を考慮した初期案として Claude Code のサブエージェントを利用しています。
サブエージェントのコンテキストはメインコンテキストと独立するため、それまでの執筆や修正をあえて考慮せず、第三者的なレビューができます。
明示的に呼び出しやすいように、サブエージェントを呼び出すためのカスタムコマンドとセットで運用しています。
構築したサブエージェント:
- tech-blog-reviewer: Zenn 執筆ガイドラインとの照合、包括的なレビュー
- textlint-reviewer: textlint 実行と修正提案
使い方:
# Claude Code 上で実行
# tech-blog-reviewerエージェントが記事をレビュー
/review-article @{Zenn ID}/articles/xxxxx.md
# textlint-reviewerエージェントがlintチェックと修正提案
/lint-article @{Zenn ID}/articles/xxxxx.md
GitHub ActionsによるCI/CD
上記の textlint と Claude Code レビューを GitHub Actions でも実行できるようにしています。
textlint については article ディレクトリ内の Markdown ファイルに差分がある場合のみ、そのファイルに対して実行されるようにしています。
Claude Code によるレビューは実行コストとの兼ね合いで自動実行は行わず、 @claude で呼び出す形で運用しています。
textlint CI:
PR で変更された記事ファイルに対して textlint を自動実行します。
※ファイル検知と textlint 実行部分だけを抜粋しています。
- name: Get changed markdown files
id: changed-files
run: |
CHANGED_FILES=$(git diff --name-only --diff-filter=ACMRT \
origin/${{ github.base_ref }}...HEAD | \
grep '\.md$' | grep '/articles/' || true)
echo "files=$CHANGED_FILES" >> $GITHUB_OUTPUT
- name: Run textlint on changed files
if: steps.changed-files.outputs.files != ''
run: |
echo "${{ steps.changed-files.outputs.files }}" | xargs -r pnpm run textlint
公開フローに応じたPRテンプレート
記事公開時の確認漏れを防ぐため、PR テンプレートにチェックリストを設けています。
細かい工夫ですが、執筆者、レビュワー、Zenn 運営メンバーの負荷を少しでも下げて、特に意識しなくても執筆フローに沿って進められるようにしています。
この記事では割愛しますが、ブログネタの起票やリリース予定日などは別途 Notion で管理しています。
PR テンプレート:
## 記事公開チェックリスト
- [ ] previewコマンドで正しく表示されることを確認した
- [ ] `published = true`になっている // Zenn CLIの初期値はfalseで非公開記事になる
- [ ] textlintを実行した ※全ての指摘に対応する必要はありません
- [ ] AIレビューを実行した ※全ての指摘に対応する必要はありません
- [ ] レビューが完了した
おわりに
PKSHA の Zenn 始動にあたり、Publication Pro と GitHub を中心として、textlint、Claude Code 等を利用した執筆環境についてご紹介しました。
実は 2024 年末に「技術広報元年」という note を出していたのですが、この記事の中で次のように書いています。
PKSHA は「未来のソフトウェアを形にする」というミッションのもと、最先端技術の社会実装に重きをおいているのですが、その裏返しとして技術広報が後回しになってしまっている部分があります。
技術発信の土台を作った 2024 年を元年として、2025 年は各種イベントやカンファレンスへの参加、そしてこの Zenn 運用のスタートと、技術発信が大きく前進した 1 年となりました。
外から見える PKSHA よりも社内からみる PKSHA の方が圧倒的に進んでいて、まだまだ発信したいネタがたくさん眠っている
しかしこの状況はまだまだ変わらず、PKSHA にはまだ表に出せていない面白いネタや優秀なエンジニアがたくさん隠れています。
Zenn でのテックブログを通じて、PKSHA の魅力をより広く届けていきます!
Discussion