agentic.el には、作業ディレクトリに .mount を置いておくと、それを読んで docker run --volume を組み立てる仕組みがある。これが思ったより便利だった。
一方で、HTTP サーバや各種デバッグ用プロセスをコンテナ内で立ち上げるときは、ポート公開も毎回ほしくなる。ここだけコマンドライン引数で別管理になっているのは、少しだけ落ち着かない。
そこで、 .mount と同じ発想で .port も読めるようにした。コンテナ起動に必要なローカル設定は、なるべくディレクトリ内のファイルに寄せておきたい。
何がよいか
.port を使えるようにすると、起動オプションの一部が「その場限りの引数」ではなく「そのディレクトリの設定」になる。
たとえば、ある作業ディレクトリでは常に 8000 番を公開したい、別のディレクトリでは 3000 番を公開したい、ということがある。そういう差分をシェル履歴や個別の alias に逃がさず、ディレクトリ側に閉じ込められる。
このやり方のよいところは、 .mount と運用を揃えられることでもある。マウントとポートの両方を「このディレクトリでコンテナをどう起動するか」という設定ファイルとして扱えるので、考える場所が散らばらない。
使い方
たとえば次のように .port を置いておく。
8000
127.0.0.1:8000:8000
agentic.el は .port を 1 行ずつ読んで、各行を --publish に変換する。空行と # で始まるコメント行は無視される。
8000 のようにコロンを含まない書き方をした場合は、内部で 8000:8000 として扱われる。すでに 127.0.0.1:8000:8000 のような完全な形式で書いてある行は、そのまま --publish に渡される。単純なケースは短く書けて、必要なら厳密にも書けるので、この仕様はちょうどよい。
複数ポートを公開したければ、 .mount と同じ感覚で行を追加すればよい。設定の置き場所がファイルに揃っているので、あとで見返したときにも分かりやすい。
コード
.port= の読み込み部分はほぼ .mount の対になる形になっている。
(defun agentic--read-port-file ()
(let ((port-file (expand-file-name ".port" default-directory))
(ports ""))
(when (file-readable-p port-file)
(with-temp-buffer
(insert-file-contents port-file)
(dolist (line (split-string (buffer-string) "\n" t))
(let ((port (string-trim line)))
(when (and (not (string-empty-p port))
(not (string-prefix-p "#" port)))
(setq ports (concat ports " --publish "
(if (string-match-p ":" port)
port
(concat port ":" port)))))))))
ports))
コロンの有無で 8000 を 8000:8000 に補完する、という仕様はこの分岐そのままである。空行とコメント行を読み飛ばす作りも .mount と揃っている。
呼び出し側も単純で、 docker run の文字列を組み立てるところで agentic--read-mount-file の直後に差し込んでいる。
(concat
"docker run -it "
" --volume " project-dir ":" project-dir " "
" --workdir " project-dir " "
(agentic--read-mount-file)
(agentic--read-port-file)
" sximada/agentic/all:202605.dev1 "
" bash ")
.mount と .port の両方を同じ層で組み立てているので、実装としても使い方としても筋がよい。
まとめ
.mount が便利なら、 .port も同じくらい自然にあってよい。今回の修正で、 agentic.el のコンテナ起動設定をもう少し素直にファイルベースで扱えるようになった。
細かい引数を毎回手で足すより、作業ディレクトリに設定として置いておける方が気楽だし、長く使うほど効いてくる。