Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions TESTING_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ Use `docs/test-doc-maintenance-plan.md` before moving oversized suites or adding
- `CliNonDatabaseErrorContractTests.cs`
Table-driven empty, not-found, invalid-argument, invalid-config, and hook-platform failures. Keep JSON assertions focused on the stable versioned envelope and sanitized optional context, pair every scenario with its coded human `Error` / `Hint` / `Usage` form, and restore current-directory and hook filesystem seams after each case.
- `GitHelperTests.cs`, `GitProcessRunnerTests.cs`, `HookCommandRunnerTests.cs`
Git-specific behavior, including worktrees, commit-based updates, direct git process runner diagnostics, cancellation of git subprocesses, portable absolute executable overrides, and fail-closed metadata-write boundaries. Hook install coverage keeps `installed`, `updated`, and `already_installed` distinct, proves an exact executable UTF-8/no-BOM rerun preserves the hook write time, repairs non-executable and differently encoded managed hooks, and exercises dry-run create, managed replacement, custom-hook chain, blocked, and no-op plans without creating the missing hooks directory or changing either hook file. Keep executable-override fixtures owner-only-writable, owned by the current test user, under trusted ancestors (including an explicit root-owned sticky `/tmp` case), and able to return `git version` from `--version` unless the test is asserting a rejection reason. Symlinked `.git` directory/file, intermediate metadata components, hard-linked metadata files, worktree target, `commondir`, `info`, `exclude`, `hooks`, and hook-file coverage must prove resolution is rejected before metadata is written outside the project boundary. Tests that create real repositories or launch real/fake git subprocesses use `ExternalProcessFactAttribute` / `ExternalProcessTheoryAttribute` and run only on the `net8.0` test target; keep pure `.git` metadata parsing, trust diagnostics, and trusted-candidate enumeration cross-target. Timeout and cancellation wall-clock assertions should stay below the fake git scripts' natural completion while leaving room for macOS CI scheduling and process-cleanup overhead. Fake git scripts that run after commit-ref validation should echo the verified commit argument for `rev-parse --verify <ref>^{commit}` so timeout tests reach the intended git command.
Git-specific behavior, including worktrees, commit-based updates, direct git process runner diagnostics, cancellation of git subprocesses, portable absolute executable overrides, and fail-closed metadata-write boundaries. Hook install coverage keeps `installed`, `updated`, and `already_installed` distinct, proves an exact executable UTF-8/no-BOM rerun preserves the hook write time, and repairs non-executable or differently encoded managed hooks. Hook dry-run coverage keeps successful preview status at `dry_run`, asserts `filesystem_mutation: false`, and validates state plus hash/provenance-bearing `planned_changes` for install create/replacement/chain/no-op and uninstall managed-block removal/chained restoration/force/blocked/idempotent plans without changing content, mode, or write time. Keep unmanaged/conflicted marker and symlink cases fail-closed. Keep executable-override fixtures owner-only-writable, owned by the current test user, under trusted ancestors (including an explicit root-owned sticky `/tmp` case), and able to return `git version` from `--version` unless the test is asserting a rejection reason. Symlinked `.git` directory/file, intermediate metadata components, hard-linked metadata files, worktree target, `commondir`, `info`, `exclude`, `hooks`, and hook-file coverage must prove resolution is rejected before metadata is written outside the project boundary. Tests that create real repositories or launch real/fake git subprocesses use `ExternalProcessFactAttribute` / `ExternalProcessTheoryAttribute` and run only on the `net8.0` test target; keep pure `.git` metadata parsing, trust diagnostics, and trusted-candidate enumeration cross-target. Timeout and cancellation wall-clock assertions should stay below the fake git scripts' natural completion while leaving room for macOS CI scheduling and process-cleanup overhead. Fake git scripts that run after commit-ref validation should echo the verified commit argument for `rev-parse --verify <ref>^{commit}` so timeout tests reach the intended git command.
- `WorkspaceMetadataEnricherTests.cs`
Workspace freshness and git metadata enrichment behavior.
- `DiagnosticRedactorTests.cs`, `SuggestionStoreTests.cs`
Expand Down Expand Up @@ -1515,7 +1515,7 @@ dotnet test --filter "FullyQualifiedName~GitHelperTests"
- `CliNonDatabaseErrorContractTests.cs`
empty、not-found、不正な引数、不正な設定、hook platform failure を表駆動で検証します。JSON assertion は安定したバージョン付き envelope と sanitization 済みの任意 context に集中させ、各 scenario で code 付き human `Error` / `Hint` / `Usage` も対にして検証し、current directory と hook filesystem seam を各 case の後に復元してください。
- `GitHelperTests.cs`、`GitProcessRunnerTests.cs`、`HookCommandRunnerTests.cs`
worktree、commit ベース更新、direct git process runner diagnostics、git subprocess cancellation、portable な absolute executable override、fail-closed な metadata-write boundary を含む Git まわりのテスト。hook install coverage では `installed`、`updated`、`already_installed` を区別し、同一かつ実行可能な UTF-8/no-BOM の内容での再実行が hook の write time を維持すること、実行不可または異なる encoding の managed hook を修復すること、および dry-run の create、managed replacement、custom-hook chain、blocked、no-op plan が存在しない hooks directory を作成せず、どちらの hook file も変更しないことを検証します。executable override fixture は rejection reason 自体を検証する場合を除き owner-only-writable、test user 所有、trusted ancestor 配下(root 所有の sticky `/tmp` case を明示的に含む)とし、`--version` で `git version` を返せるようにします。symlinked `.git` directory / file、中間 metadata component、hard-linked metadata file、worktree target、`commondir`、`info`、`exclude`、`hooks`、hook file の coverage は、project boundary 外へ metadata を書く前に解決が拒否されることを証明してください。実 repo を作る、または real/fake git subprocess を起動するテストは `ExternalProcessFactAttribute` / `ExternalProcessTheoryAttribute` を使い、`net8.0` test target だけで実行します。純粋な `.git` metadata parsing、trust diagnostics、trusted-candidate enumeration は cross-target のままにしてください。Timeout と cancellation の wall-clock assertion は fake git script の自然完了より短く保ちつつ、macOS CI の scheduling や process cleanup の遅れを許容する余裕を持たせます。commit-ref validation 後に使う fake git script は `rev-parse --verify <ref>^{commit}` の検証対象 commit 引数を返し、timeout テストが意図した git command まで到達するようにします。
worktree、commit ベース更新、direct git process runner diagnostics、git subprocess cancellation、portable な absolute executable override、fail-closed な metadata-write boundary を含む Git まわりのテスト。hook install coverage では `installed`、`updated`、`already_installed` を区別し、同一かつ実行可能な UTF-8/no-BOM の内容での再実行が hook の write time を維持すること、および実行不可または異なる encoding の managed hook を修復することを検証します。hook の dry-run coverage では、成功したプレビューの status を `dry_run` に保ち、`filesystem_mutation: false` を検証します。また、install の create / replacement / chain / no-op と uninstall の managed block 除去 / chained 復元 / force / blocked / idempotent plan について、state と hash / provenance 付き `planned_changes` を検証し、content、mode、write time が変化しないことを確認します。unmanaged / conflicted marker と symlink の case は fail-closed に保ってください。executable override fixture は rejection reason 自体を検証する場合を除き owner-only-writable、test user 所有、trusted ancestor 配下(root 所有の sticky `/tmp` case を明示的に含む)とし、`--version` で `git version` を返せるようにします。symlinked `.git` directory / file、中間 metadata component、hard-linked metadata file、worktree target、`commondir`、`info`、`exclude`、`hooks`、hook file の coverage は、project boundary 外へ metadata を書く前に解決が拒否されることを証明してください。実 repo を作る、または real/fake git subprocess を起動するテストは `ExternalProcessFactAttribute` / `ExternalProcessTheoryAttribute` を使い、`net8.0` test target だけで実行します。純粋な `.git` metadata parsing、trust diagnostics、trusted-candidate enumeration は cross-target のままにしてください。Timeout と cancellation の wall-clock assertion は fake git script の自然完了より短く保ちつつ、macOS CI の scheduling や process cleanup の遅れを許容する余裕を持たせます。commit-ref validation 後に使う fake git script は `rev-parse --verify <ref>^{commit}` の検証対象 commit 引数を返し、timeout テストが意図した git command まで到達するようにします。
- `WorkspaceMetadataEnricherTests.cs`
ワークスペース鮮度と git メタデータ付与のテスト。
- `DiagnosticRedactorTests.cs`、`SuggestionStoreTests.cs`
Expand Down
12 changes: 8 additions & 4 deletions USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,13 +216,15 @@ local index automatically:
|---|---|
| `cdidx hooks install [--dry-run]` | Install the optional git pre-commit hook, or preview it without writing. |
| `cdidx hooks status` | Show whether the hook is installed. |
| `cdidx hooks uninstall` | Remove the hook. |
| `cdidx hooks uninstall [--dry-run]` | Remove the cdidx hook, or preview managed-block removal/chained-hook restoration without writing. |

| Hook behavior | Detail |
|---|---|
| Refresh command | The installed hook runs `cdidx index <selected-project-path> --quiet` before the commit completes. When `--project` is omitted, the selected path is the current directory at install time. |
| Quiet mode | `--quiet` suppresses normal progress and success output for hook contexts while still printing indexing errors to stderr and returning a non-zero exit code. |
| Install preview | `cdidx hooks install --dry-run` does not create or change hook files. Human output prints the planned managed hook, while JSON adds `dry_run: true`, `planned_action` (`create`, `replace_managed`, `chain_existing`, `none`, or `blocked`), and `managed_hook_preview`. |
| Preview contract | `cdidx hooks install --dry-run` and `cdidx hooks uninstall --dry-run` return `status: dry_run` for a successful preview, `dry_run: true`, `filesystem_mutation: false`, `planned_action`, `hook_state`, `chained_hook_state`, and `planned_changes`. Each planned change carries its action/path/source, content hashes, executable-mode transition, and provenance. A blocked preview keeps `status: error` while retaining the same non-mutating plan fields. |
| Install preview | Install plans use `create`, `replace_managed`, `chain_existing`, `none`, or `blocked`; `managed_hook_preview` contains the generated managed hook. Preview never creates the missing hooks directory or rewrites an existing file. |
| Uninstall preview | Uninstall plans use `delete_managed`, `remove_managed_block`, `restore_chained`, `force_delete_unmanaged`, `force_restore_chained`, `none`, or `blocked`. A well-formed managed block is removed while preserving surrounding custom hook content; a chained backup is reported as the restoration source. Unmanaged or conflicted marker content is blocked unless `--force` is explicit. |
| Install result | A real install returns `status: installed` when it creates the hook, `status: updated` when it replaces managed content, repairs a non-executable or non-UTF-8/no-BOM managed hook, or chains an existing custom hook, and `status: already_installed` without rewriting when the exact executable UTF-8/no-BOM managed hook is already present. |
| Status JSON diagnostics | `cdidx hooks status --json` keeps `project_path`, `hook_path`, and `chained_hook_path` for compatibility, and also emits `diagnostic_project_path`, `diagnostic_hook_path`, and `diagnostic_chained_hook_path` with path-sanitized values for logs and support bundles. |
| Existing hooks | If `.git/hooks/pre-commit` already exists, `cdidx hooks install` moves it to `.git/hooks/pre-commit.cdidx-chain` and calls it after the cdidx refresh, preserving tools such as Husky, pre-commit, and lefthook. |
Expand Down Expand Up @@ -3486,13 +3488,15 @@ pre-commit hook をインストールします:
|---|---|
| `cdidx hooks install [--dry-run]` | 任意の git pre-commit hook をインストール、または書き込まずにプレビュー。 |
| `cdidx hooks status` | hook のインストール状態を表示。 |
| `cdidx hooks uninstall` | hook を削除。 |
| `cdidx hooks uninstall [--dry-run]` | cdidx hook を削除、または managed block の除去/chained hook の復元を書き込まずにプレビュー。 |

| hook の動作 | 詳細 |
|---|---|
| 更新コマンド | インストールされた hook はコミット完了前に `cdidx index <selected-project-path> --quiet` を実行します。`--project` を省略した場合、選択パスはインストール時のカレントディレクトリです。 |
| quiet mode | `--quiet` は hook 環境向けに通常の進捗・成功出力を抑制しつつ、indexing エラーは引き続き stderr に出力し、非ゼロの終了コードを返します。 |
| インストールのプレビュー | `cdidx hooks install --dry-run` は hook ファイルを作成・変更しません。human 出力では予定される managed hook を表示し、JSON には `dry_run: true`、`planned_action`(`create`、`replace_managed`、`chain_existing`、`none`、`blocked` のいずれか)、`managed_hook_preview` を追加します。 |
| プレビュー契約 | `cdidx hooks install --dry-run` と `cdidx hooks uninstall --dry-run` は、成功時に `status: dry_run`、`dry_run: true`、`filesystem_mutation: false`、`planned_action`、`hook_state`、`chained_hook_state`、`planned_changes` を返します。各変更予定には、操作、対象/変更元のパス、内容のハッシュ、実行モードの遷移、由来が含まれます。実行できないプレビューは `status: error` を維持しつつ、同じ非変更の計画フィールドを返します。 |
| インストールのプレビュー | インストール計画は `create`、`replace_managed`、`chain_existing`、`none`、`blocked` のいずれかで、`managed_hook_preview` に生成予定の管理対象 hook を含めます。プレビューは存在しない hooks ディレクトリを作成せず、既存ファイルも再書き込みしません。 |
| アンインストールのプレビュー | アンインストール計画は `delete_managed`、`remove_managed_block`、`restore_chained`、`force_delete_unmanaged`、`force_restore_chained`、`none`、`blocked` のいずれかです。正しい管理対象 block は周囲の独自 hook 内容を保持して除去し、連結されたバックアップがある場合は復元元として報告します。管理対象外の内容や marker が競合した内容は、明示的な `--force` がない限り実行できません。 |
| インストール結果 | 実際のインストールでは、hook を新規作成した場合は `status: installed`、managed content の置換、実行不可または UTF-8/no-BOM 以外になった managed hook の修復、あるいは既存 custom hook の chain 化を行った場合は `status: updated`、同一かつ実行可能な UTF-8/no-BOM の managed hook がすでに存在して再書き込みを行わなかった場合は `status: already_installed` を返します。 |
| status JSON 診断 | `cdidx hooks status --json` は互換性のため `project_path`、`hook_path`、`chained_hook_path` を維持しつつ、ログやサポートバンドル向けに path をサニタイズした `diagnostic_project_path`、`diagnostic_hook_path`、`diagnostic_chained_hook_path` も出力します。 |
| 既存 hook の扱い | リポジトリに `.git/hooks/pre-commit` がある場合、`cdidx hooks install` はそれを `.git/hooks/pre-commit.cdidx-chain` に移動し、cdidx の更新後に呼び出すため、Husky、pre-commit、lefthook などのツールも維持されます。 |
Expand Down
21 changes: 21 additions & 0 deletions changelog.d/unreleased/4883.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
category: fixed
issues:
- 4883
affected:
- src/CodeIndex/Cli/HookCommandRunner.cs
- src/CodeIndex/Cli/CliFlagSchema.cs
- src/CodeIndex/Cli/JsonOutputContracts.cs
- tests/CodeIndex.Tests/HookCommandRunnerTests.cs
- tests/CodeIndex.Tests/CliFlagSchemaTests.cs
- USER_GUIDE.md
- TESTING_GUIDE.md
---

## English

- **Hook dry-runs now report truthful install and uninstall plans (#4883)** — successful previews use `status: dry_run` with `filesystem_mutation: false` and hash/provenance-bearing planned changes, while uninstall can preview managed-block removal, chained-hook restoration, forced unmanaged removal, blocked conflicts, and no-op states without changing hook content or metadata.

## 日本語

- **hook の dry-run が install と uninstall の正確な計画を報告するようになりました (#4883)** — 成功したプレビューは `status: dry_run`、`filesystem_mutation: false`、hash / provenance 付きの変更予定を返します。また uninstall では、hook の内容や metadata を変更せずに managed block の除去、chained hook の復元、unmanaged hook の強制削除、競合による停止、no-op 状態をプレビューできます。
4 changes: 2 additions & 2 deletions src/CodeIndex/Cli/CliFlagSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -613,11 +613,11 @@ private static IReadOnlyList<CliFlag> BuildAll()
new()
{
Name = "--dry-run",
Description = "Preview without writing; hooks supports it only for install",
Description = "Preview without writing; hooks supports install and uninstall",
PrimaryCommands = Set("index", "hooks", "backfill-fold", "optimize", "vacuum"),
CompletionSubcommands = new Dictionary<string, IReadOnlySet<string>>(StringComparer.Ordinal)
{
["hooks"] = Set("install"),
["hooks"] = Set("install", "uninstall"),
},
Safety = CliOptionSafety.Preview,
},
Expand Down
Loading
Loading