once に `exec` コマンドを追加し、コンテナ内コマンド実行を可能に

basecamp/once

once CLI が提供するサブコマンドに exec が加わり、アプリケーションコンテナ内で任意のコマンドを直接実行できるようになりました。TTY の自動付与と終了コードの透過が実装され、スクリプトからの利用がシームレスになります。

背景

once ではコンテナ内部での一時的な操作が必要になるケースが増えていました。Issue #59 で提案されたように、開発者はアプリケーション固有の TUI や CLI をコンテナ内で起動したいと求めていましたが、既存の once コマンドセットにはその手段がなく、docker exec 等を別途呼び出すしかありませんでした。

このギャップを埋めることで、開発フロー全体の一貫性が向上しますonce exec が提供されれば、プロジェクト固有のツールを同じ CLI から直接呼び出せ、環境ごとのラッパーを書く必要がなくなります。

技術的な変更

once exec がコマンドツリーに追加されましたinternal/command/root.goNewRootCommand()r.cmd.AddCommand(newExecCommand().cmd) が挿入され、既存のサブコマンドと同様に利用できる形になっています。

実装は internal/command/exec.go に集中していますnewExecCommand() が Cobra コマンドを生成し、splitExecArgs<host> と実行コマンド列を分離します。run メソッドは名前空間とアプリケーションを解決し、app.Exec を呼び出すだけのシンプルなフローです。

コンテナ内部実行の本体は internal/docker/application.go に追加された Exec メソッドですexecAttachedInContainer を呼び出し、errdefs.IsConflict が返す場合は ErrApplicationNotRunning を返すことで、停止中コンテナへの実行試行を明示的にエラー化しています。

execAttachedInContainer の実装は internal/docker/container.go に拡張されました。TTY の自動付与や標準入出力の転送を行い、inspectExecExitCode で取得した終了コードを ExecResult に格納して返します。これにより、CLI が実際のコマンド結果を正確に取得できます。

CLI のエグジットコード伝搬が改善されましたcmd/once/main.gocommand.ExitCode(err) をチェックし、取得できた場合はそのコードでプロセスを終了します。これにより once exec が内部コマンドの終了コードをそのまま呼び出し元に返せます。

エラー型とユーティリティが internal/command/root.go に導入されましたexitCoder インタフェースと ExitCode, IsExitError 関数により、任意のエラーから終了コードを抽出できる共通ロジックが確立され、他のサブコマンドでも再利用可能です。

テストスイートが拡充され、動作が検証されていますintegration/docker_test.go では実際にコンテナを起動し、app.Execexit 0exit 7 を実行してコードが正しく伝搬することを確認しています。また、internal/command/exec_test.gosplitExecArgs の各パターン(フラグ付き引数や -- 区切り)を網羅的にテストしています。

設計判断

Cobra ベースのサブコマンドとして実装する選択が取られました。新たに外部バイナリを作るのではなく、既存 CLI の拡張として統一感を保ち、ユーザー体験を一貫させています。

終了コードをエラーに埋め込む設計が採用されていますexitCoder インタフェースにより Go のエラーハンドリングと UNIX のプロセス終了コードを橋渡しし、once exec が期待通りのステータスを返すことを保証しています。

コンテナが停止中の場合は専用エラー ErrApplicationNotRunning を返すことで、呼び出し側が状態判定しやすくなっています。エラー分類を細分化することで、スクリプト側でリトライロジックやユーザー向けメッセージを簡潔に実装できます。

まとめ

once exec コマンドの追加により、once CLI から直接コンテナ内部のコマンドを実行でき、TTY 自動付与と終了コード透過という重要な UX が実現しました。エラーハンドリングとコード構造が統一されたことで、今後の拡張やメンテナンスも容易になるでしょう。

記事メタデータ

Generated by:
gpt-oss-120b for DiffDaily
LLM Trace:
a64c7c6e

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
gpt-oss-120b for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文・背景・技術的な変更・設計判断・まとめの5部構成があり、総論→各論→結論の流れが明確です。背景セクションは結論的なまとめがなくても全体の結論がまとめに集約されているため問題なしと判断しました。

カスタムMarkdown構文 ⚠ WARNING

シンタックスハイライト・GitHubリンク記法の正確性

GitHubリンクが `[PR #75](URL)` の形式になっており、要件の `[#75](URL)` 形式とずれています。リンク記法は内容理解に支障はないものの、指定された記法に沿っていないため警告としました。コードブロックでのファイル名ハイライトは使用されていませんが、必須ではありません。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

対象は once CLI の開発者・保守者向けのエンジニアであり、専門用語や内部実装の説明が中心です。初心者向けの過度な解説はなく、適切です。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各段落はトピックセンテンスで始まり、1段落1トピックの構成が保たれています。段落長は6文未満で、空行で区切られているため読みやすく、要件を満たしています。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事中で言及されているファイル名・追加された関数・ロジックは、提供されたDiffの内容と一致しています。コード例は掲載されていませんが、説明は Diff の変更点を正確に反映しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

Cobra、namespace、ExecResult、exitCoder などの用語は PR と Diff で使用されている通り正しく使用されています。誤用は見られません。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

技術的な主張(TTY 自動付与、終了コード伝搬、エラーハンドリングの統一など)は Diff の変更点で裏付けられており、論理的に正しいです。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事の全ての事実は PR タイトル、説明、Diff に基づいており、根拠のない推測や外部知識の付与はありません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR 番号 #75、Issue #59 といった数値は正しく記載されています。その他の数値やバージョン情報はなく、誤りは見られません。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事タイトルは PR の "Add a `once exec ...` command" を日本語化したもので、内容と一致しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

LTS、EOL、リリース日程等の外部知識は記事に含まれていません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

時間表現の歪曲はなく、PR の記述と一致しています。