99from commands .analyze_kinds import analyze_kinds , print_summary_table
1010from commands .analyze_entity_fields import analyze_field_contributions , print_field_summary
1111from commands .cleanup_expired import cleanup_expired
12+ from commands .drive_sync import push_to_drive , pull_from_drive
1213
13- app = typer .Typer (help = "Utilities for analyzing and managing local Datastore/Firestore (Datastore mode)" , no_args_is_help = True )
14+ app = typer .Typer (
15+ help = "Utilities for analyzing and managing local Datastore/Firestore (Datastore mode)" ,
16+ no_args_is_help = True ,
17+ )
1418
1519# Aliases with flags only — no defaults here
1620ConfigOpt = Annotated [Optional [str ], typer .Option ("--config" , help = "Path to config.yaml" )]
1721ProjectOpt = Annotated [Optional [str ], typer .Option ("--project" , help = "GCP/Emulator project id" )]
18- EmulatorHostOpt = Annotated [Optional [str ], typer .Option ("--emulator-host" , help = "Emulator host, e.g. localhost:8010" )]
22+ EmulatorHostOpt = Annotated [
23+ Optional [str ], typer .Option ("--emulator-host" , help = "Emulator host, e.g. localhost:8010" )
24+ ]
1925LogLevelOpt = Annotated [Optional [str ], typer .Option ("--log-level" , help = "Logging level" )]
2026KindsOpt = Annotated [
2127 Optional [List [str ]],
22- typer .Option ("--kind" , "-k" , help = "Kinds to process (omit or empty to process all in each namespace)" )
28+ typer .Option (
29+ "--kind" , "-k" , help = "Kinds to process (omit or empty to process all in each namespace)"
30+ ),
31+ ]
32+ SingleKindOpt = Annotated [
33+ Optional [str ], typer .Option ("--kind" , "-k" , help = "Kind to analyze (falls back to config.kind)" )
2334]
24- SingleKindOpt = Annotated [ Optional [ str ], typer . Option ( "--kind" , "-k" , help = "Kind to analyze (falls back to config.kind)" )]
35+
2536
2637def _load_cfg (
2738 config_path : Optional [str ],
@@ -38,6 +49,7 @@ def _load_cfg(
3849 overrides ["log_level" ] = log_level
3950 return load_config (config_path , overrides )
4051
52+
4153@app .command ("analyze-kinds" )
4254def cmd_analyze_kinds (
4355 config : ConfigOpt = None ,
@@ -64,17 +76,31 @@ def cmd_analyze_kinds(
6476 else :
6577 print_summary_table (rows )
6678
79+
6780@app .command ("analyze-fields" )
6881def cmd_analyze_fields (
6982 kind : SingleKindOpt = None ,
70- namespace : Annotated [Optional [str ], typer .Option ("--namespace" , "-n" , help = "Namespace to query (omit to use all)" )] = None ,
71- group_by : Annotated [Optional [str ], typer .Option ("--group-by" , help = "Group results by this field value (falls back to config.group_by_field)" )] = None ,
72- only_field : Annotated [Optional [List [str ]], typer .Option ("--only-field" , help = "Only consider these fields" )] = None ,
83+ namespace : Annotated [
84+ Optional [str ],
85+ typer .Option ("--namespace" , "-n" , help = "Namespace to query (omit to use all)" ),
86+ ] = None ,
87+ group_by : Annotated [
88+ Optional [str ],
89+ typer .Option (
90+ "--group-by" ,
91+ help = "Group results by this field value (falls back to config.group_by_field)" ,
92+ ),
93+ ] = None ,
94+ only_field : Annotated [
95+ Optional [List [str ]], typer .Option ("--only-field" , help = "Only consider these fields" )
96+ ] = None ,
7397 config : ConfigOpt = None ,
7498 project : ProjectOpt = None ,
7599 emulator_host : EmulatorHostOpt = None ,
76100 log_level : LogLevelOpt = None ,
77- output_json : Annotated [Optional [str ], typer .Option ("--output-json" , help = "Write raw JSON results to file" )] = None ,
101+ output_json : Annotated [
102+ Optional [str ], typer .Option ("--output-json" , help = "Write raw JSON results to file" )
103+ ] = None ,
78104):
79105 cfg = _load_cfg (config , project , emulator_host , log_level )
80106
@@ -100,17 +126,32 @@ def cmd_analyze_fields(
100126 else :
101127 print_field_summary (result )
102128
129+
103130@app .command ("cleanup" )
104131def cmd_cleanup (
105132 config : ConfigOpt = None ,
106133 project : ProjectOpt = None ,
107134 emulator_host : EmulatorHostOpt = None ,
108135 log_level : LogLevelOpt = None ,
109136 kind : KindsOpt = None ,
110- ttl_field : Annotated [Optional [str ], typer .Option ("--ttl-field" , help = "TTL field name (falls back to config.ttl_field)" )] = None ,
111- delete_missing_ttl : Annotated [Optional [bool ], typer .Option ("--delete-missing-ttl" , help = "Delete when TTL field is missing (falls back to config.delete_missing_ttl)" )] = None ,
112- batch_size : Annotated [Optional [int ], typer .Option ("--batch-size" , help = "Delete batch size (falls back to config.batch_size)" )] = None ,
113- dry_run : Annotated [bool , typer .Option ("--dry-run" , help = "Only report counts; do not delete" )] = False ,
137+ ttl_field : Annotated [
138+ Optional [str ],
139+ typer .Option ("--ttl-field" , help = "TTL field name (falls back to config.ttl_field)" ),
140+ ] = None ,
141+ delete_missing_ttl : Annotated [
142+ Optional [bool ],
143+ typer .Option (
144+ "--delete-missing-ttl" ,
145+ help = "Delete when TTL field is missing (falls back to config.delete_missing_ttl)" ,
146+ ),
147+ ] = None ,
148+ batch_size : Annotated [
149+ Optional [int ],
150+ typer .Option ("--batch-size" , help = "Delete batch size (falls back to config.batch_size)" ),
151+ ] = None ,
152+ dry_run : Annotated [
153+ bool , typer .Option ("--dry-run" , help = "Only report counts; do not delete" )
154+ ] = False ,
114155):
115156 cfg = _load_cfg (config , project , emulator_host , log_level )
116157
@@ -127,6 +168,46 @@ def cmd_cleanup(
127168 deleted_sum = sum (totals .values ())
128169 typer .echo (f"Total entities { 'to delete' if dry_run else 'deleted' } : { deleted_sum } " )
129170
171+
172+ @app .command ("push" )
173+ def cmd_push (
174+ version : Annotated [
175+ Optional [str ], typer .Argument (help = "Version name (defaults to today's date YYYY-mm-DD)" )
176+ ] = None ,
177+ overwrite : Annotated [
178+ bool , typer .Option ("-o" , "--overwrite" , help = "Overwrite existing file with same name" )
179+ ] = False ,
180+ local_db : Annotated [
181+ Optional [str ],
182+ typer .Option (
183+ "--local-db" , help = "Path to local-db binary (falls back to config.local_db_path)"
184+ ),
185+ ] = None ,
186+ config : ConfigOpt = None ,
187+ log_level : LogLevelOpt = None ,
188+ ):
189+ cfg = _load_cfg (config , None , None , log_level )
190+ push_to_drive (cfg , version , overwrite , local_db )
191+
192+
193+ @app .command ("pull" )
194+ def cmd_pull (
195+ version : Annotated [
196+ Optional [str ], typer .Argument (help = "Version name (omit to download latest)" )
197+ ] = None ,
198+ local_db : Annotated [
199+ Optional [str ],
200+ typer .Option (
201+ "--local-db" , help = "Path to local-db binary (falls back to config.local_db_path)"
202+ ),
203+ ] = None ,
204+ config : ConfigOpt = None ,
205+ log_level : LogLevelOpt = None ,
206+ ):
207+ cfg = _load_cfg (config , None , None , log_level )
208+ pull_from_drive (cfg , version , local_db )
209+
210+
130211if __name__ == "__main__" :
131212 import sys
132213
0 commit comments