ガンダムマーカー メッキシルバーがメッキ調にならなくなった

買ったときはめちゃくちゃメッキ調になってすごいと思った「ガンダムマーカー EX ガンダムメッキシルバー」ですが、4 年ぐらい経って使おうとしたら全くメッキ調にならなくなりました。

www.mr-hobby.com

新品を買ってきたので新旧比較、全然違う。

かなり使用期限の短い商品なのかも……

悲しい

技術書典 20 サークル参加決定

技術書典 20 にオンライン&オンサイトでサークル参加します。

techbookfest.org

日時と場所は

  • オンラインは 2026.04.11-26
  • オンサイトは 2026.04.12 池袋サンシャインシティ 展示ホール D

下記の書物を持っていく予定です。

よろしくね!

Nix Flake モジュールのオプションからドキュメントを自動生成する

Cspell というものがあります。ソースコード中の単語のスペルをチェックしてくれるツールです。うっかりタイポして面倒なエラーになるのは嫌なので自分がソースコードを書くときはチェックをしています。

cspell.org

それを nix flake check コマンドの中でやってしまおうというのが cspell-nix です。11 月ごろに作りました。

github.com

cspell-nix は Flake Parts のモジュールとして提供しています。

flake.parts

引数などはモジュール定義を見れば書いてあるのですが、わざわざそのためにソースコードを見るのは不要な部分も多いですし Commonmark などで書いてレンダーされた結果を見れるようにしたいです。しかし手でドキュメントを書いていくのはソースコードとの乖離などに注意する必要があります。そこでドキュメントを自動生成できるようにしました。

pkgs.nixosOptionsDoc

pkgs から nixosOptionsDoc という関数が定義されていて、これでドキュメントの生成ができます。できるのですが、なかなかつまづきました。

まず、nixosOptionsDoc にコメントが書かれていないためソースコードを見て使い方を推定する必要があります。ソースコードを辿ると nixpkgs/nixos/lib/make-options-doc/default.nix にコメントが書かれていてこれがヒントになります。

  Generates documentation for [nix modules](https://nix.dev/tutorials/module-system/index.html).

  It uses the declared `options` to generate documentation in various formats.

  # Outputs

  This function returns an attribute set with the following entries.

  ## optionsCommonMark

  Documentation in CommonMark text format.

  ## optionsJSON

  All options in a JSON format suitable for further automated processing.

  `example.json`
  ```json
  {
    ...
    "fileSystems.<name>.options": {
      "declarations": ["nixos/modules/tasks/filesystems.nix"],
      "default": {
        "_type": "literalExpression",
        "text": "[\n  \"defaults\"\n]"
      },
      "description": "Options used to mount the file system.",
      "example": {
        "_type": "literalExpression",
        "text": "[\n  \"data=journal\"\n]"
      },
      "loc": ["fileSystems", "<name>", "options"],
      "readOnly": false,
      "type": "non-empty (list of string (with check: non-empty))"
      "relatedPackages": "- [`pkgs.tmux`](\n    https://search.nixos.org/packages?show=tmux&sort=relevance&query=tmux\n  )\n",
    },
    ...
  }
  ```

  ## optionsAsciiDoc

  Documentation rendered as AsciiDoc. This is useful for e.g. man pages.

  > Note: NixOS itself uses this output to to build the configuration.nix man page"

  ## optionsNix

  All options as a Nix attribute set value, with the same schema as `optionsJSON`.

  # Example

  ## Example: NixOS configuration

  ```nix
  let
    # Evaluate a NixOS configuration
    eval = import (pkgs.path + "/nixos/lib/eval-config.nix") {
      modules = [
        ./module.nix
      ];
    };
  in
    pkgs.nixosOptionsDoc {
      inherit (eval) options;
    }
  ```

  ## Example: non-NixOS modules

  `nixosOptionsDoc` can also be used to build documentation for non-NixOS modules.

  ```nix
  let
    eval = lib.evalModules {
      modules = [
        ./module.nix
      ];
    };
  in
    pkgs.nixosOptionsDoc {
      inherit (eval) options;
    }
  ```

次に Flake モジュールを評価する必要があるのですが、flake-parts.lib.evalFlakeModule では評価後の perSystem のモジュールの options を評価するとよく分からないエラーになりました1。また、flake-parts.lib.evalFlakeModule を使用すると Flake Parts が提供するオプションまでドキュメントになってしまいます。これは要りません。なので自分のモジュールだけ単品で lib.evalModules で評価することにしました。しかし、これだと flake.apps などを参照する config の方がエラーになるので options だけのモジュールに分割して lib.evalModules に渡す必要がありました。

この辺りを乗り越えるとモジュールのオプションを Commonmark や Asciidoc に変換できるようになります。

出力したドキュメントはこんな感じになりました。

おしまい


  1. モジュール引数に pkgs がないというエラーなのですが、そんなことはないはず……

2025 年のふりかえり

例年ふりかえり記事を書いていないのですが、なんとなく書いてみるかという気になったので書いてみます。

健康

人生で初めて入院しました。

一時的な症状でよかったですが、またなるのはこりごりです。

抗鬱薬は続けて飲んでいます。

しごと

変わらず Herp からの業務委託を受けています。

Nix を書いてることが多い感じがします。

今のペースで続けていきたいです。

生活

妻と仲よく暮らしています。

昨年は庭できゅうりがめちゃくちゃ成りました。295 本。

屋外の水道管が破損して水漏れしました。賃借家屋なので管理会社経由で修理してもらいました。こういうとき水道局の減免制度があるから利用しよう。

同人

モナドドリル』を発行しました。

ドリルという性質上解説をたくさん入れるわけにはいかないので構成に頭を悩ましました。2 回はちゃぶ台返しをした気がします。

技術書典 18 と 19 で販売しました。

購入はこちらからどうぞ。

趣味

写真

写真熱が冷めています。フィルムの期限が切れてるのでもっと撮った方がいいですね。歩くと体力も付くし。

カメラはちょっと増えました。

旅行

ハーベストの丘」でめちゃくちゃわんちゃんにモテました。ネネちゃんというらしい。かわいい。寒かった。1 月。

f:id:kakkun61:20260105091312j:image

「第27回プログラミングおよびプログラミング言語ワークショップ」に参加しに三谷温泉へ行きました。おもしろい話がいっぱい聞けて楽しい。温泉も入れる。3 月。

f:id:kakkun61:20260105092116j:image

寝台特急「ウェストエクスプレス銀河」の座席が取れたので初めて出雲大社に行ってきました。「銀河」の乗客はみんな一癖ある感じ。出雲はその日だけ寒かった。帰りの HOT7000 系の一番前の座席が展望ですごくよかった。4 月。

f:id:kakkun61:20260105092439j:imagef:id:kakkun61:20260105092739j:image

「大阪関西万博」は 5 月に行ってきました。個別館はドイツにしか入られないぐらい混んでたんですけど、秋はもっとすごかったみたいですね。

f:id:kakkun61:20260105093250j:image

東京の知り合いと現地合流して「静岡ホビーショー」に行ってきました。これもすごい混みようでモデラー展示はちょっとしか見られなかったです。メーカー展示は見たいところだいたい見られてよかった。5 月。

f:id:kakkun61:20260105093643j:image

このあと 6 月に技術書典で東京に行って、5 月 6 月の遠出の疲れのせいか冒頭のめまい症になりました。

めまいから復帰し「関数型まつり」へ。来年あるなら何か話せればいいなあ。

f:id:kakkun61:20260105094223j:image

プログラミング

doujin.kakkun61.com のメンテナンスをしたり、envar というツールを作ったりしました。

今年中に動くところまでできたらいいなあというツールも作っています。

鉄道模型向けの自作デジタルコマンドコントロール(DCC)の実装は中断されています。再開したい。

また、Kubernetes とか触ってみるかとサーバー用途にするために GMK Tech の G3 という Intel N150 機を買ってみました。なんとか昨日に Raspberry Pi 2 B も込みのクラスターを組めました。いやあこの辺り何も分からん。

定理証明支援系である Lean をさわり始めました。Language Server がすごくちゃんと動いていてすごいです。Haskell Language Server も安定して使えたらいいのになという思いが強くなりました。

ゼロから始めるLean言語入門 ― 手を動かして学ぶ形式数学ライブラリ開発

模型

鉄道模型がまた増えました。なんで……? せめて部屋の線路を開通させねばならないです。一旦は開通させたものの出入りが難しくなるので跳ね上げ式にしようとした結果走れない状態になっています。Z ゲージのショーティー用のジオラマを作ったのは、小さくても満足感あるなという発見がありました。

プラモデルを組むのはいいけど塗装をしようとすると急に腰が重くなるので、それが簡単になるようなんかしくみを作りたいですね。

今年は

幸運であれば娘が生まれる予定で、しかも染色体異常があることが分かっているため、生まれた場合どういう生活になるのかまだ分からず、いろいろと予定が立てづらい状況です。無事に生まれてくれるといいなあ。

それでもこれらぐらいはしたいなと思っています。

  • 開発中のツールをリリースする
  • 鉄道模型のレイアウトを開通させる
  • 自作 DCC の実装を進める

2026 年もよろしくお願いします。

Envar — ディレクトリーごとに切り替える環境変数を一元管理する

Envar というコマンドラインツールを作りました。

github.com

これは環境変数の値をディレクトリーごとに切り替えるツールです。

例えば FOO という環境変数について、ディレクトリー path/to/A では hoge という値にして、ディレクトリー path/to/B では fuga という値にする、ということができます。下記のような vars.yaml ファイルでその設定をします。

FOO:
  path/to/A: hoge
  path/to/B: fuga

また、環境変数の値を別のコマンドの出力値にするということもできます。

この例は自分が使っている設定ファイルから抜粋したもので、gh コマンドで使用するアカウントを私用・しごとで切り替えるものです。

# vars.yaml
GH_TOKEN:
  /home/kazuki/Projects/Work:
    gh: kakkun61_work
  /home/kazuki/Projects:
    gh: kakkun61
# execs.yaml
gh: gh auth token --user %s

よくあるかもしれない質問

direnv と何が違うの?

direnv はそのディレクトリーで設定したい環境変数をそのディレクトリーに置いた設定ファイルに書きます。主にプロジェクトを使う人みんなに必要になる環境変数を設定する用途です。プロジェクト都合の環境変数です。

それに対して envar は設定ファイルはユーザーの設定ディレクトリーに置きます(例えば ~/.config/envar)。上記例のアカウントの切り替えなど個人の都合の環境変数を設定する想定です。

対応しているシェルは?

今のところ bash だけです。需要があれば zsh とかも対応するかも。

飛行機の中で Haskell プロジェクトをビルドする

いや別に飛行機の中でビルドするのが主題なわけではないのですが、オフラインモードのことを機内モードと言いますからね。最近の飛行機は Wi-Fi の提供があったりするらしいですが。

さて、あなたの Haskell プロジェクトをオフラインモードでビルドすることができますか? まあ、今時オフライン環境も珍しいですし「そんな必要あるのか?」という感覚もあるかもしれません。Nix ではビルド再現性のためにオフライン環境でビルドできることが求められます1

Nix で Haskell プロジェクトをビルドするには Input Output(iohk.io)の作成した haskell.nix が使用されるのが普通です。

github.com

これは cabal ファイルや stack ファイル、もしくは plan ファイルを入力とし、Cabal のパッケージひとつにつきひとつの Nix デリベーションに翻訳するシステムです2。Cabal がやってることを Nix(と Haskell)で再実装したと言ってもいいんじゃないかと思います。

いやあ、これが「結構大掛かりなシステムだなあ」と思うわけですよね。「もうちょっと cabal-install に頼ってビルドできないかなあ」と前々から思っていて、とりあえず Proof of Concept が動いたので紹介しようと思います。

コンセプト

自分のアイデアの肝腎は Cabal の「ローカルリポジトリー」を使うことです。普通はローカルリポジトリーを使うことはありません。Hackage つまりリモートリポジトリーを使いますから。ですが、Cabal は Hackage 以外のリポジトリーも使えるように作られています。一般的には ~/.config/cabal/config にある設定ファイルには下記のように書かれています3

repository hackage.haskell.org
    url: http://hackage.haskell.org/ 

このファイルは、cabal update の実行時にこのファイルがなければデフォルトの内容で生成されます。そのデフォルトの内容に Hackage の URL が記載されているので、みんなは Hackage からパッケージを取ってこられます。なので、やろうと思えば社内リポジトリーを立てて社内ライブラリーを配布したりすることもできます(認証は秘密鍵でできるっぽい)4。そして、この URL の部分は file+nocache というスキーマの URL も書くことができます5

repository my-local-repository
    url: file+noindex:///absolute/path/to/directory

/absolute/path/to/directoryディレクトリーにはパッケージの sdist ファイルを配置します。

/absolute/path/to/directory/
    foo-0.1.0.0.tar.gz
    bar-0.2.0.0.tar.gz

ここまで準備できれば後は簡単で、下記ステップでオフラインビルドができるようになります。

  1. ローカルリポジトリーのパスを書いた config ファイルを用意する
  2. 依存ライブラリーの sdist ファイルをローカルリポジトリーに配置する
  3. 環境変数コマンドライン引数で、用意した config ファイルを参照するようにする
  4. cabal build する

ツール

さて「ここまで準備できれば後は簡単」と書きましたが、依存ライブラリーの sdist を取得するはちょっと邪魔くさいです。間接依存や依存間のバージョン制約もあります。なので plan ファイルを元に sdist を取得するちょっとしたスクリプトを用意しました。

github.com

dependencies=$(cabal-plan topo --hide-builtin | grep -v ' ')for d in $dependencies
do
  url=https://hackage.haskell.org/package/$d/$d.tar.gz
  if [ $verbose -eq 0 ]
  then
    wget --quiet "$url"
  else
    wget --no-verbose "$url"
  fi
done

cabal-plan で依存のリストを取得して Hackage から wget でダウンロードするようにしています。

あとは flake.nixbuildPhase が肝腎なので気になる人は見ておくといいと思います6

github.com

runHook preBuild

# `cabal build` writes a file at a local repository,
# and so it must be writable.
cp -r --no-preserve=all $src/.local-repository .
# This cabal.config file declares using the local repository.
export CABAL_CONFIG=$src/cabal.config
# Set a writable directory for cabal
export CABAL_DIR=$TMPDIR/cabal
cabal="cabal --project-dir=$src --builddir=$TMPDIR --verbose"
$cabal v2-build --only-dependencies all
$cabal v2-build all

runHook postBuild

まだ確認できていないこと

cabal.project ファイルに source-repository-package が記載されていたらどうなるのか確認できていません。もしかすると cabal.project ファイルも切り替えないといけないかもしれないです。cabal.project.local に何か記載すれば source-repository-package を無効化できたりしないかなあ。誰か教えてください。


  1. 厳密に言うと Nix の用意する環境の中がオフラインで、Nix の処理系自体はネットワークで情報を取ってこれる。これは「Haskell の言語の中では副作用禁止だが、Haskell 処理系は副作用できる」みたいな話と似ているように思う。
  2. もしかしたらデリベーションは、コンポーネントつまり lib とか exe とか test とかと対応するのかも。
  3. ファイルの場所は環境や環境変数で変わる。詳しくはセクション 4.1.3. Directories に記載がある。
  4. 詳しくはセクション 4.1.4. Repository specification に記載がある。
  5. 詳しくはセクション 4.1.4.2. Local no-index repositories に記載がある。
  6. 記事を書いていたら「If the directory is not writable, you can append #shared-cache fragment to the URI, then the cache will be stored inside the remote-repo-cache directory.」というのを見つけたので、ローカルリポジトリーをコピーするステップはなくすことができそう。