Skip to content
Published on

文書のなかのコードのスクリーンショットはいつから嘘になるのか — 画像をビルド成果物にする

シェア
Authors

半年経った文書のスクリーンショットがやっていること

ガイド文書の途中に、きれいなコードのスクリーンショットがひとつ埋まっています。暗い背景に丸い角、左上に赤と黄と緑の点が三つ。半年前に誰かが自分のエディタでキャプチャして貼ったものです。

そのあいだに関数名が変わりました。オプションがひとつ消えました。文書の本文は更新されました。画像はそのままです。いまやそのページは、上の段落で新しい名前を語り、下の画像で古い名前を見せています。そして初めて来た人はたいてい画像を信じます。文章より絵のほうが具体的に見えるからです。

問題は画像が間違っていることではありません。間違っているという事実に誰も気づけないことです。コードブロックだったら検索に引っかかったはずですが、画像は引っかかりません。

スクリーンショットは成果物なのにソースがありません

この問題を別の言い方にするとこうなります。文書リポジトリに入っているそのPNGファイルは成果物です。ところが、それをもう一度作る方法がリポジトリにありません。どのエディタで、どのテーマで、どのフォントサイズで、どの部分を切り取って撮ったのかが記録されていません。

私たちはこういう状況を、別の領域なら即座に問題と見なします。ビルド結果をソースなしでリポジトリにコミットすればレビューで指摘が出ます。ところが画像はなぜか例外になります。画像は文書の装飾であってコードではない、と考えるからです。

基準をひとつに揃えれば、やるべきことは明確になります。コマンド一行で作り直せないものはリポジトリに置かない。 画像も例外ではありません。

その前に一段先に問うべきことがあります。このコードはわざわざ画像である必要があるのか、です。文書のなかのコードはたいていコードブロックのほうが優れています。コピーでき、検索に引っかかり、スクリーンリーダーが読み、画面幅に合わせて折り返します。画像が正当な場合は狭いところです。強調色やエディタの表示のようにテキストへ移せない情報が一緒になければならないとき、発表スライドやソーシャルカードのようにテキストを入れられない媒体に載せるとき、そしてターミナルの色と揃えが説明の一部であるとき、くらいです。この三つに当てはまらないのに画像を使っていたなら、再生成の問題を解くより、コードブロックへ戻すほうが先です。

goshotがやること

goshotは、コードとターミナル出力を画像にするGoのライブラリ兼CLIです。READMEは自らをCarbonやSiliconに似たツールだと紹介します。違いはウェブページではなくコマンドラインで動くという点であり、この記事の論旨ではその違いがすべてです。

機能一覧で確認したものだけを書くと、構文強調はchromaを使って数百のテーマに対応し、ANSI色の入ったターミナル出力をそのままレンダリングし、ウィンドウ装飾はmacOSとWindows 11とGNOMEとKDE Breezeの見た目を選べます。背景は単色と七種類のグラデーションと画像に対応し、出力はPNGとJPEGとBMPで、クリップボードと標準出力へも出せます。そしてAPIキーとトークンとパスワードを自動で隠す機能が一覧に入っています。

基本的な使い方はこうです。

# ファイルひとつを画像に
goshot main.go -o main.png

# 標準入力を受けてクリップボードへ
cat main.go | goshot -c

# テーマとウィンドウの形と背景を指定
goshot main.go -t catppuccin-mocha -C gnome -b '#1e1e2e'

# 特定の行だけ強調
goshot main.go --highlight-lines 10..14

インストールはGoがあれば go install github.com/watzon/goshot/cmd/goshot@latest で、ArchのAURとUbuntuのPPAパッケージも案内されています。

ライブラリとして使うときの構造も見ておく価値があります。READMEはこれを小さなパイプラインとして説明します。内容を画像に描き、それをウィンドウ装飾で包み、背景の上に載せます。三つの段階がそれぞれ独立した概念なので、ひとつだけ変えるのが簡単です。文書ツールを自作するなら、この分離自体が参考になる設計です。テーマを変える仕事とウィンドウの形を変える仕事と余白を変える仕事が互いに干渉しなければ、あとでスタイルを一括変更するときに直す場所が一か所へ集まります。

コマンドになった瞬間に変わること

goshot自体が特別なツールだから、ではありません。画像の生成がコマンドになると、三つのことが付いてきます。

第一に、文書の更新と画像の更新が同じ作業になります。 コードを直して文書を直すときに、画像生成コマンドをもう一度回せば済みます。人がキャプチャツールを開いてウィンドウの大きさを合わせて切り取る過程が消えます。

第二に、差分が目に見えます。 画像がソースから決定論的に作られるなら、コードが変わっていないときは画像も変わりません。だから変更差分に画像ファイルが上がってきたということ自体が信号になります。

第三に、一貫性が規則になります。 テーマとフォントと余白がコマンドの引数として書かれるので、メンバーごとに違うエディタテーマで撮ったスクリーンショットが混ざることがなくなります。

ターミナル出力も同じ問題を抱えています

文書のなかでより頻繁に古くなるのは、コードよりターミナル出力です。コマンドの出力形式はツールのバージョンが上がると静かに変わるからです。goshotはコマンドを実行してその出力をそのまま画像にするサブコマンドを提供します。

# コマンドを実行して出力を画像に
goshot exec -A -p -- go test ./...

# タイトルバーの色を内容に合わせて自然に
goshot exec -A -p --title-bar-color auto -- ls -la

ここで重要なのは画像がきれいだという点ではなく、その出力が文書を作るときに実際に実行された結果だという点です。手で書き写した出力は、写す人が行を消したり整えたりする過程で原本と食い違い、その差をあとから検証する方法がありません。

秘密の隠しは付加機能ではありません

機能一覧のなかで実務でもっとも値打ちがあるのは、自動の隠しだと思います。スクリーンショットを通じた資格情報の流出が危険な理由は、流出そのものより検知が難しいという点にあります。リポジトリにコミットされたテキストの鍵は秘密スキャナーが捕まえます。画像のなかに入った鍵は捕まりません。人が目で見つけるまでそのまま残ります。

goshotはAPIキーとトークンとパスワードを自動で隠す機能を掲げ、隠し方も選べるようになっています。

goshot config.go --redact --redact-style blur

ただしここで一つを明確にしておく必要があります。自動の隠しは既知のパターンに反応する装置であり、社内システム固有の形式はパターンにない可能性があります。だからこの機能は最後の防衛線ではなく最初の網として置くのが正しいところです。本当の防衛線は、文書を作るときに実際の資格情報が入った環境を使わないことです。

スタイル設定をリポジトリに置く

goshotはよく使うフラグの既定値を設定ファイルに置けます。READMEによれば ~/.config/goshot/config.yaml にフラグ名と値を平坦な形で書き、コマンドラインで与えたフラグが常に勝ちます。

theme: catppuccin-mocha
chrome: mac
background: '#1e1e2e'
corner-radius: 12

このファイルがホームディレクトリにあると、人ごとに違う結果が出ます。だから文書を作るリポジトリなら、これらの値をホームではなくプロジェクトの中に置き、スクリプトから明示的に渡すほうが良いところです。そうすればスタイルの変更がコードレビューに上がり、ある日突然に文書全体の背景色が変わることも起きません。

今週付けられる最小構成

もっとも小さな形はこうです。画像のソースになるコード断片を別のファイルとして置き、そのファイルから画像を作る規則をひとつ書きます。

# docs/snippets/*.go から docs/images/*.png を作る
SNIPPETS := $(wildcard docs/snippets/*.go)
IMAGES   := $(patsubst docs/snippets/%.go,docs/images/%.png,$(SNIPPETS))

images: $(IMAGES)

docs/images/%.png: docs/snippets/%.go
	goshot $< -o $@ -t catppuccin-mocha -C mac --redact

こう置けばコード断片が実際のコンパイル対象になるので、スニペットが古くなればビルドやリントが先に教えてくれます。画像はそのあとに付いてきます。画像が古くなる問題を画像で解く代わりに、ソースが古くなる問題へ戻したことがこの構成の要点です。

ここで正直に書いておくことがあります。このMakefileは、私がgoshotをインストールして実際に回した結果ではありません。goshotのコマンドとフラグはリポジトリのREADMEで確認したもので、Makefileのパターン規則の部分は一般的なmakeの文法です。導入するときは小さなスニペットひとつでまず回してみてください。

まとめと出典

核心はツールの名前ではありません。文書に入る画像にも、ほかの成果物と同じ基準を当てようということです。コマンドで作り直せるか。作り直せないなら、その画像はいつか誰にも知られないまま間違ったことを語り始めます。

  • watzon/goshotリポジトリ — 機能一覧、CLIの例、設定ファイルの形式、ライセンス。本文のgoshotに関する記述はすべてこのリポジトリのREADMEで確認しました。
  • リポジトリのメタデータで確認した事項: ライセンスはMIT、主要言語はGo、リポジトリの作成は2024年11月です。一人の開発者が維持するプロジェクトであり、大企業が後ろ盾になっているツールではないという点を踏まえて導入の可否を判断してください。
  • 本文のCLIの例はREADMEに載っているものを移したもので、Makefileの例は実行して確認していません。