Repositories / agent-snapshot.git
agent-snapshot.git
Clone (read-only): git clone http://git.guha-anderson.com/git/agent-snapshot.git
@@ -585,6 +585,23 @@ let write_manifest (out : string) (command : string list) (exit_status : int) : in Json.to_file ~std:true (concat_path out "manifest.json") (Manifest_json.to_yojson manifest) +let operation_was_recorded (recd : file_record) (operation : string) : bool = + Hashtbl.mem recd.operations operation + +let print_snapshot_summary () : unit = + let updated_files = ref 0 in + let uncommitted_read_files = ref 0 in + Hashtbl.iter + (fun _ recd -> + if + (operation_was_recorded recd "write" || operation_was_recorded recd "delete") + && (Option.is_some recd.after.blob || recd.after.tombstone) + then incr updated_files; + if operation_was_recorded recd "read" && Option.is_some recd.before.blob then incr uncommitted_read_files) + files; + Printf.eprintf "Worked in %d repositories. Saved %d updated files. Saved %d read files in the snapshot that were not committed.\n%!" + (Hashtbl.length repos) !updated_files !uncommitted_read_files + (** Resolve relative syscall paths against cwd or a directory fd as required by *at syscalls. *) let resolve_path (proc : proc_state) (dirfd : int) (path : string) : string = if is_absolute path then normalize_path path @@ -769,6 +786,7 @@ let run_snapshot (output : string option) (command : string list) : int = finalize_records (); close_blob_writer ()); write_manifest output command 0; + print_snapshot_summary (); 0 open Cmdliner
@@ -338,6 +338,22 @@ def test_traced_command_options_do_not_need_separator(tmp_path): assert Snapshot(out).manifest["command"] == [PYTHON, "-c", "print('direct command')"] +def test_run_prints_stderr_summary(tmp_path): + out = tmp_path / "snapshot" + + completed = subprocess.run( + [str(BIN), "--snapshot-dir", str(out), "/bin/true"], + cwd=ROOT, + text=True, + check=True, + capture_output=True, + ) + + assert completed.stderr.endswith( + "Worked in 0 repositories. Saved 0 updated files. Saved 0 read files in the snapshot that were not committed.\n" + ) + + def test_rename_records_source_tombstone_and_destination_content(tmp_path): # Rename is not just a write: a replay-equivalent snapshot needs to know that # the source path stopped existing and that the destination path acquired the