- name
- publish-qiita
- description
- Qiita の下書き URL と著者名を入力に、hexiita 変換 → フロントマター整備 → 本文の整形(見出し・段落内の改行・箇条書き)→ カテゴリ・タグ見直し → 連載の索引記事の表を埋める → textlint / markdownlint 対応 → mermaid SVG 生成 → 検証 → PR 作成まで行う記事公開手順。「この Qiita 記事を公開して」と頼まれたときに使う。
# Qiita 記事の公開
入力: Qiita 記事 URL(限定共有 `/private/` 可)と著者名(ブログでの表記。Qiita ID ではない)。
連載に属する場合は連載名も聞く。
## 1. 変換(hexiita)
- `source/_posts/<年>/` で同日の既存記事を確認し、postid(a, b, c…)を決める
- リポジトリルートで実行する。カレントの `source/` 配下に記事 MD と画像
(`source/images/<年>/<記事ID>/`、thumbnail 含む)が出力される
```sh
hexiita <qiita url> YYYYMMDD
```
- 連載記事は `-series "連載名"`、索引記事はさらに `-index`。
フラグは URL より**前**に置く(Go の flag は最初の非フラグ引数で解釈を止める)
- **v0.7.0 以降**を使う。それ以前はフロントマターのキーが `tag:` / `category:`(単数)で
出て、Hexo が読まずタグもカテゴリも無い記事になる(ビルドは通るので気づきにくい)。
出力の先頭を見て複数形かどうかを確かめ、古ければ入れ直す
```sh
go install github.com/ma91n/hexiita@v0.7.0
```
版を明示するのは、`@latest` だとタグを push した直後にプロキシが追いつかず
古い版が入ることがあるため。必要な最低版を書いておけば、入れ直す人も迷わない
## 2. フロントマター整備
- `author:` は Qiita ID のまま出力されるので、指定された著者名に書き換える
- `lede:` の先頭に `![...]` 画像記法の残骸が混入することがあるので除去する。
中身が冒頭の自己紹介(「TIGの○○です」)のままになっていることも多い。
`lede` は一覧と OGP に出る概要文なので、**記事本文の文を使って要約に書き換える**
(何の記事かが1行で分かる形。書いていないことを補わない)
- 同日複数投稿なら `date:` の時刻を postid 順にずらす(a=00:00:00、b=00:00:01。#2055)
- 著者が `_profile.yml` 未登録でも、**記事本文に自己紹介が無いなら追記しない**。
about は記事冒頭の自己紹介から作るもので、書く材料が無いなら空欄にも推測にもせず登録を見送る。
未登録の著者は `get_profile` が空文字を返すだけで表示は壊れない(登録済みは2名だけ)。
自己紹介がある場合は追記し、twitter_id / github_id は確実に分かる場合だけ書く(推測で埋めない)
- **書籍の出版や技術誌への寄稿の記事なら `books:` / `magazines:` を書く**(#2912)。
`/specials/publications/` の一覧はこのフロントマターから作るので、ここに書かないと載らない。
タグに `出版` を付けるだけでは載らない
```yaml
books:
- name: "Real World HTTP 第3版"
url: https://www.oreilly.co.jp/books/9784814400669/
magazines:
- name: "Software Design 2026年1月号"
url: https://gihyo.jp/magazine/SD/archive/2026/202601
work: "アルゴリズム特集"
```
- `name` は書名・号の名前、`url` は**出版社の商品ページ**(Amazon ではなく、
オライリー・技術評論社・KADOKAWA など。Amazon しか無いならそれでよい)
- `work` は雑誌だけ。その号の**特集・章の名前**。一覧は「号+`work`」で1行にまとまるので、
同じ特集に複数人が寄稿しているなら**同じ文字列**にする(章番号や章タイトルまで
書き分けると号が2行に分かれる)。書籍には書かない
- `cover` は書籍・雑誌の両方に書ける。**表紙を一覧のパネルの左に出すので、
記事のサムネイルが表紙でないときだけ**(本棚や机の写真)画像のパスを書く。
省くと `thumbnail` が使われる。ただし**1つの記事で複数の号を紹介するときは
サムネイルを借りない**(どの号のものでもないため)ので、表紙を出したい号には
`cover` を書く。書かなければ空き枠になる
- 号の表紙が手元に無いときは、**出版社のページの書影を
`source/images/publications/<誌名>-<号>.jpg` に保存**して指す
(`sd-202601.jpg`。幅300pxのJPEGに縮める)。記事ごとのディレクトリに
置かないのは、同じ号を複数の記事が指すため
- `by` は**記事の著者と書いた人が違うときだけ**書く(イベント参加レポートなど)。
省くと記事の `author` が使われる。連名は `、` で区切る。
**一覧には出ない**(1名しか出ないと共著が単著に見えるため)。記録として残す欄
- **1つの号に複数の寄稿があるなら、その記事が扱う分だけ書く。** 同じ号を書いた記事が
既にあっても気にしなくてよい。一覧は号ごとに1行へまとめ、寄稿をその行の中に積む
- **参考として挙げた他者の本は書かない。** 一覧は「フューチャーの社員が書いたもの」で、
本文に出てくる書籍リンクをすべて拾う場所ではない
- 並びは `url` から取る号の年月なので、記事の日付とずれていてよい
## 3. 画像
- ファイル名が NFD(濁点が分解された形)で出ることがある。`node normalize.mjs` で
NFC 化する。見た目が同じでも文字列としては一致しないので、置換や grep が
当たらず気づきにくい
- `alt` にファイル名が入るので空にする(本文で説明済みの装飾画像として扱う)
- 冒頭画像は `top.png` のように意味のある名前へ改める。Qiita 側の名前は
誤字のことがある(実例: `sumbnail.png`)
- 白地の画像(スライド・白背景のスクリーンショット)は本文と地続きに見えて
どこまでが画像か分からない。`<img class="bordered" ...>` を付けて縁取る
(CSS は `theme-styles.styl` の `.article-entry img.bordered`)。
枠を内蔵した画像や写真では二重線になるので一律には付けない。
対象は四隅の画素が白かどうかで機械的に判定できる
## 4. 本文の整形
Qiita の書き方をそのまま出すと崩れるものを直す。lint より先にやる(文が変わると指摘も変わる)。
- **見出しを `##` 始まりへ1段下げる。** Qiita は `#` で節を切るため、そのままだと
記事タイトルと同じ大きさの h1 が本文に並ぶ(markdownlint の MD025 でも出る)
- **段落内の改行を詰めて、ひとまとまりの段落は1行にする。**
`_config.yml` に `breaks` の指定が無く hexo-renderer-marked の既定(`breaks: true`)が
効くので、段落内の単純な改行がそのまま `<br>` になる。Qiita 記事は1文ごとに
改行して書かれることが多く、意図しない強制改行が本文中に何十個も入る
(実例: AI Dev Day 記事で26個)
- 連結するのはプレーンな本文行だけ。`-` `#` `<` `|` `>` で始まる行と
コードフェンスの中は触らない。`**強調**` で始まる行も本文の続きなら連結する
- 確認は生成 HTML の `<br>` を数える。コードブロック内の行末 `<br>` は
highlight の出力なので残る
- **列挙は箇条書きに組み替える。** 目安は本文が数を名乗っている箇所
(「挙がったのは3つでした」「デプロイ先は3つ」)と、読点でつないだ長い列挙。
後者は `max-ten` にも効く
- 導入文が名乗る数と項目数を一致させる。項目末に句点は打たない
(箇条書きの中は `ja-no-mixed-period` の対象外)
- 本文に書いていない項目を作らない。段落を分解して並べ替えるだけにする
- **言語指定の無いコードフェンスに言語を付ける。** 基準は CLAUDE.md
(端末の画面は `console`、実行するコードは `sh` / `bash`、数式や素のテキストは `text`)
## 5. カテゴリ・タグ見直し
- カテゴリは CLAUDE.md の既存語彙から。境界ルール(IaC / Infrastructure / DevOps、
AIDD / DataScience)に照らして確認する
- 同じ催しの連作(登壇レポート等)が既にあれば、カテゴリとタグはそれに揃える。
読者から見て同じ系統の記事が別のカテゴリに散るのを防ぐ
- タグは具体タグだけ付ける(抽象への接続はオントロジーが肩代わり)。
`source/_data/tag_ontology.yml` に未登録のタグが増えたら **tag-maintenance スキル**の
増分追記手順で broader を判断して追記し、`check.mjs` を ERROR ゼロで通す
- 参加レポートのように製品名が大量に出る記事は、**専用セクション(`##`)の主題に
なっているものだけ**タグにする。通りがかりの言及は付けない(全部拾うと二桁になる)
- 1記事だけの具体タグは避けなくてよい。全タグの6%(701件中49件)が1記事で、
`themes/future/layout/_partial/post/tag.ejs` が `tag.length > 1` で分岐し、
1件なら非リンクのテキストで描くので空のタグページへ読者を送らない。
削る基準は「活発でない×汎用語」なので、製品名はそもそも対象外
## 6. 連載の索引記事(`series` を書いたときだけ)
**`series` を足したら索引記事の表も埋める。** 記事側の連載ナビは `series` から
自動で出るが、**索引記事の表は手書き**(予定表なので `series` から生成していない。
CLAUDE.md #2790)。ここが抜けると、連載の入口に来た読者が公開済みの記事へ辿れない。
- 索引記事は同じ `series` を持ち `インデックス` タグが付いた記事
```sh
grep -rl 'series: "<連載名>"' source/_posts/<年>/ | xargs grep -l インデックス
```
- 自分の行(「調整中」「〜(仮)」になっている)を、**日付のセルの先頭に
`{% thumb <記事ID> %}`、タイトルの列にリンク**へ差し替える
```markdown
| {% thumb 20260907a %} 9/7(月) | 永井優斗 | [タイトル](/articles/20260907a/) |
```
- **日付と執筆者の列は触らない。行の並びも入れ替えない**(予定表であって
公開順の一覧ではない)
- サムネイル用の列を足さない(日付のセルに畳む #2790)
- 索引記事も PR の変更ファイルに入る
## 7. lint
```sh
node_modules/.bin/textlint --fix <path> # 自動修正
node_modules/.bin/textlint <path> # 残エラー確認。ゼロになるまで
node_modules/.bin/markdownlint-cli2 --fix "<path>" # 見出し表記ゆれ(heading-vocab)等
```
- `--fix` は2周回す。1周目で直した箇所の後ろに残る指摘があり、`二つ` → `2つ` のような
自動修正でも取りきれないことがある。fix → 手修正 → fix → 素の実行で確認、の順
- `--fix` の exit 0 は lint クリーンを意味しない。max-ten(読点4つ以上)と
no-doubled-joshi(同一助詞の重複)は残り、放置すると reviewdog が PR の全行に
コメントする。意味を変えない語順調整で手修正する
- max-ten は読点1つの削除か文の分割が安全
- 罠: 読点区切りの列挙は助詞重複が許容されるが、読点を `・` に替えると
no-doubled-joshi が新たに発火する
- 誤検知(固有名詞等)は `.textlintrc` の `filters.allowlist.allow` に追加して黙らせる。
発表タイトルのように書き換えられない語に含まれる助詞重複もこれで通す
- markdownlint の MD025(複数の h1)は Qiita 由来の記事で必ず出る。Qiita は `#` で
節を切るため。4. の1段下げが済んでいれば消える
## 8. mermaid 図(記事にある場合のみ)
```sh
make mermaid # Docker 必須
```
`source/_mermaid/*.svg` が生成されるので、記事とあわせてコミットする。
## 9. 検証
- ローカルサーバを立てて、その URL を `curl` で叩いて確認する。保存すると
再描画されるので **`hexo generate` は回さない**(何も変えていなくても1分半以上かかる)。
4001〜4004 は並行セッションが使っていることがあるので、`ss -tln` で
空きポートを確認してから `hexo server -p <port>` する
- `curl http://localhost:<port>/articles/<記事ID>/` で見るもの:
- author・タグ・カテゴリ・thumbnail(`og:image`)
- 画像の `src` と空の `alt`、見出しが `h2` / `h3` になっているか
- 段落内の `<br>` が消えているか
- mermaid 図がインライン SVG になっているか(`<svg id="mermaid-`)
- プロフィールを追記した場合は著者ページ `/authors/<著者名>/`
- 連載記事なら索引記事 `curl http://localhost:<port>/articles/<索引記事ID>/` も見る。
自分の行にサムネイル(72×48)とリンクが入っているか
- 同じ URL をユーザーの目視確認用に伝える。作業が終わったらサーバを止める
## 10. PR
- コミット対象: 記事 MD / 画像ディレクトリ / **連載の索引記事 MD** / `source/_mermaid/*.svg` /
(変更していれば)`_profile.yml` / `.textlintrc` / `tag_ontology.yml`
- worktree では `gh pr create` に `--head <branch> --base main` を明示する
(省略すると push 済みでも "must first push the current branch" で失敗する)。
PR 本文は `--body-file` で渡す
- マージ確認を待ち、レビュー指摘は同じ PR に追いコミットで対応する(force push しない)
عرض على GitHub