3535 TextColumn ,
3636 TimeElapsedColumn ,
3737)
38- from rich .table import Table
3938
4039
4140class IDPPublisher :
@@ -107,7 +106,7 @@ def print_usage(self):
107106 """Print usage information with Rich formatting"""
108107 self .console .print ("\n [bold cyan]Usage:[/bold cyan]" )
109108 self .console .print (
110- " python3 publish.py <cfn_bucket_basename> <cfn_prefix> <region> [public] [--max-workers N]"
109+ " python3 publish.py <cfn_bucket_basename> <cfn_prefix> <region> [public] [--max-workers N] [--verbose] "
111110 )
112111
113112 self .console .print ("\n [bold cyan]Parameters:[/bold cyan]" )
@@ -125,6 +124,9 @@ def print_usage(self):
125124 self .console .print (
126125 " Use 1 for sequential processing, higher numbers for more concurrency"
127126 )
127+ self .console .print (
128+ " [yellow][--verbose, -v][/yellow]: Optional. Enable verbose output for debugging"
129+ )
128130
129131 def check_parameters (self , args ):
130132 """Check and validate input parameters"""
@@ -179,6 +181,9 @@ def check_parameters(self, args):
179181 )
180182 self .print_usage ()
181183 sys .exit (1 )
184+ elif arg in ["--verbose" , "-v" ]:
185+ # Verbose flag is already handled by Typer, just acknowledge it here
186+ pass
182187 else :
183188 self .console .print (
184189 f"[yellow]Warning: Unknown argument '{ arg } ' ignored[/yellow]"
@@ -263,6 +268,22 @@ def check_prerequisites(self):
263268 )
264269 sys .exit (1 )
265270
271+ def ensure_aws_sam_directory (self ):
272+ """Ensure .aws-sam directory exists"""
273+ aws_sam_dir = ".aws-sam"
274+
275+ if not os .path .exists (aws_sam_dir ):
276+ self .console .print (
277+ "[yellow].aws-sam directory not found. Creating it...[/yellow]"
278+ )
279+ os .makedirs (aws_sam_dir , exist_ok = True )
280+ self .console .print (
281+ "[green]✅ Successfully created .aws-sam directory[/green]"
282+ )
283+ else :
284+ if self .verbose :
285+ self .console .print ("[dim].aws-sam directory already exists[/dim]" )
286+
266287 def version_compare (self , version1 , version2 ):
267288 """Compare two version strings. Returns -1 if v1 < v2, 0 if equal, 1 if v1 > v2"""
268289
@@ -365,6 +386,7 @@ def get_directory_checksum(self, directory):
365386 ".git" ,
366387 ".vscode" ,
367388 ".idea" ,
389+ "test-reports" , # Exclude test report directories
368390 }
369391
370392 exclude_file_patterns = {
@@ -379,9 +401,19 @@ def get_directory_checksum(self, directory):
379401 ".coverage" ,
380402 ".DS_Store" ,
381403 "Thumbs.db" ,
404+ "coverage.xml" , # Coverage reports
405+ "test-results.xml" , # Test result reports
406+ ".gitkeep" , # Git placeholder files
382407 }
383408
384- exclude_file_suffixes = (".pyc" , ".pyo" , ".pyd" , ".so" , ".coverage" )
409+ exclude_file_suffixes = (
410+ ".pyc" ,
411+ ".pyo" ,
412+ ".pyd" ,
413+ ".so" ,
414+ ".coverage" ,
415+ ".log" , # Log files
416+ )
385417 exclude_dir_suffixes = (".egg-info" ,)
386418
387419 def should_exclude_dir (dir_name ):
@@ -405,7 +437,12 @@ def should_exclude_file(file_name):
405437 return True
406438 # Exclude test files for library checksum only
407439 if "lib" in directory and (
408- file_name .startswith ("test_" ) or file_name .endswith ("_test.py" )
440+ file_name .startswith ("test_" )
441+ or file_name .endswith ("_test.py" )
442+ or file_name == "nodeids" # pytest cache files
443+ or file_name == "lastfailed" # pytest cache files
444+ or file_name
445+ in ["coverage.xml" , "test-results.xml" ] # specific test report files
409446 ):
410447 return True
411448 return False
@@ -458,6 +495,17 @@ def get_stored_checksum(self, directory):
458495
459496 def needs_rebuild (self , * paths ):
460497 """Check if any of the paths have changed since last build"""
498+ # Special case for ./lib directory - use .lib_checksum in root and get_directory_checksum
499+ if len (paths ) == 1 and paths [0 ] == "./lib" :
500+ current_checksum = self .get_directory_checksum ("./lib" )
501+ checksum_file = ".lib_checksum"
502+ if os .path .exists (checksum_file ):
503+ with open (checksum_file , "r" ) as f :
504+ stored_checksum = f .read ().strip ()
505+ return current_checksum != stored_checksum
506+ return True
507+
508+ # For all other cases, use get_checksum
461509 current_checksum = self .get_checksum (* paths )
462510
463511 # For single directory, check its stored checksum
@@ -1164,30 +1212,7 @@ def print_outputs(self):
11641212 encoded_template_url = quote (template_url , safe = ":/?#[]@!$&'()*+,;=" )
11651213 launch_url = f"https://{ self .region } .console.aws.amazon.com/cloudformation/home?region={ self .region } #/stacks/create/review?templateURL={ encoded_template_url } &stackName=IDP"
11661214
1167- # First, display URLs in plain text to avoid Rich formatting issues
1168- self .console .print ("\n [bold green]Deployment Outputs[/bold green]" )
1169- self .console .print ("[cyan]Template URL (use to update existing stack):[/cyan]" )
1170- self .console .print (f"{ template_url } " )
1171- self .console .print (
1172- "\n [cyan]1-Click Launch URL (use to launch new stack):[/cyan]"
1173- )
1174- self .console .print (f"{ launch_url } " )
1175-
1176- # Also display in a table with clickable links
1177- table = Table (title = "[bold green]Quick Links[/bold green]" , show_lines = True )
1178- table .add_column ("Link" , style = "cyan" , no_wrap = False , width = 60 )
1179- table .add_column ("Description" , style = "yellow" , width = 40 )
1180-
1181- # Create clickable links using Rich's link syntax
1182- template_link = f"[link={ template_url } ]Template URL[/link]"
1183- launch_link = f"[link={ launch_url } ]1-Click Launch[/link]"
1184-
1185- table .add_row (template_link , "Go to cloudformation and update existing stack" )
1186- table .add_row (launch_link , "Use to launch new stack" )
1187-
1188- self .console .print (table )
1189-
1190- # Additional information for troubleshooting
1215+ # Display deployment information first
11911216 self .console .print ("\n [bold cyan]Deployment Information:[/bold cyan]" )
11921217 self .console .print (f" • Region: [yellow]{ self .region } [/yellow]" )
11931218 self .console .print (f" • Bucket: [yellow]{ self .bucket } [/yellow]" )
@@ -1198,6 +1223,15 @@ def print_outputs(self):
11981223 f" • Public Access: [yellow]{ 'Yes' if self .public else 'No' } [/yellow]"
11991224 )
12001225
1226+ # Then display URLs
1227+ self .console .print ("\n [bold green]Deployment Outputs[/bold green]" )
1228+ self .console .print ("[cyan]Template URL (use to update existing stack):[/cyan]" )
1229+ self .console .print (f"{ template_url } " )
1230+ self .console .print (
1231+ "\n [cyan]1-Click Launch URL (use to launch new stack):[/cyan]"
1232+ )
1233+ self .console .print (f"{ launch_url } " )
1234+
12011235 def run (self , args ):
12021236 """Main execution method"""
12031237 try :
@@ -1210,6 +1244,9 @@ def run(self, args):
12101244 # Check prerequisites
12111245 self .check_prerequisites ()
12121246
1247+ # Ensure .aws-sam directory exists
1248+ self .ensure_aws_sam_directory ()
1249+
12131250 # Set up S3 bucket
12141251 self .setup_artifacts_bucket ()
12151252
0 commit comments