@@ -16,6 +16,7 @@ class AttachmentStatus:
1616 SUCCESS = "✓ Recorded successfully"
1717 FAILED = "⚠ Failed to record"
1818 SKIPPED_NON_TEXT = "⚠ Skipped: not a valid text file"
19+ SKIPPED_DUPLICATE = "⚠ Skipped: duplicate"
1920
2021
2122@click .command ()
@@ -66,8 +67,12 @@ def attachment(
6667 [zip_info .filename , AttachmentStatus .SKIPPED_NON_TEXT ])
6768 continue
6869
69- file_name = normalize_filename (zip_info .filename )
70- file_name = get_unique_filename (file_name , used_filenames )
70+ file_name = get_unique_filename (zip_info .filename , used_filenames )
71+ if not file_name :
72+ summary_rows .append (
73+ [zip_info .filename , AttachmentStatus .SKIPPED_DUPLICATE ])
74+ continue
75+
7176 status = post_attachment (
7277 client , session , file_content , file_name )
7378 summary_rows .append ([file_name , status ])
@@ -93,8 +98,12 @@ def attachment(
9398 [tar_info .name , AttachmentStatus .SKIPPED_NON_TEXT ])
9499 continue
95100
96- file_name = normalize_filename (tar_info .name )
97- file_name = get_unique_filename (file_name , used_filenames )
101+ file_name = get_unique_filename (tar_info .name , used_filenames )
102+ if not file_name :
103+ summary_rows .append (
104+ [tar_info .name , AttachmentStatus .SKIPPED_DUPLICATE ])
105+ continue
106+
98107 status = post_attachment (
99108 client , session , file_content , file_name )
100109 summary_rows .append ([file_name , status ])
@@ -108,8 +117,12 @@ def attachment(
108117 [a , AttachmentStatus .SKIPPED_NON_TEXT ])
109118 continue
110119
111- file_name = normalize_filename (a )
112- file_name = get_unique_filename (file_name , used_filenames )
120+ file_name = get_unique_filename (a , used_filenames )
121+ if not file_name :
122+ summary_rows .append (
123+ [a , AttachmentStatus .SKIPPED_DUPLICATE ])
124+ continue
125+
113126 status = post_attachment (client , session , file_content , file_name )
114127 summary_rows .append ([file_name , status ])
115128
@@ -119,40 +132,37 @@ def attachment(
119132 display_summary_as_table (summary_rows )
120133
121134
122- def get_unique_filename (filepath : str , used_filenames : Set [str ]) -> str :
135+ def get_unique_filename (filepath : str , used_filenames : Set [str ]) -> Optional [ str ] :
123136 """
124- Get a unique filename by extracting the basename and prepending parent folder if needed.
137+ Get a unique filename by extracting the basename and prepending parent folders if needed.
125138 Strategy:
126139 1. First occurrence: use basename (e.g., app.log)
127- 2. Duplicate: prepend parent folder (e.g., nested-app.log)
128- 3. Still duplicate: append .1, .2, etc. (e.g., nested-app.1.log)
140+ 2. Duplicate: prepend parent directories until unique
129141 """
130- filename = os .path .basename (filepath )
142+ # Normalize path separators to forward slash (archives always use forward slash in both linux, and windows)
143+ normalized_path = filepath .replace (os .sep , '/' )
144+ normalized_path = normalize_filename (normalized_path )
145+
146+ basename = normalized_path .split ('/' )[- 1 ]
131147
132148 # If basename is not used, return it
133- if filename not in used_filenames :
134- used_filenames .add (filename )
135- return filename
136-
137- # Try prepending the parent directory name
138- parent_dir = os .path .basename (os .path .dirname (filepath ))
139- if parent_dir : # Has a parent directory
140- name , ext = os .path .splitext (filename )
141- filename = f"{ parent_dir } -{ name } { ext } "
142-
143- if filename not in used_filenames :
144- used_filenames .add (filename )
145- return filename
146-
147- # If still duplicate, append numbers
148- name , ext = os .path .splitext (filename )
149- counter = 1
150- while True :
151- filename = f"{ name } .{ counter } { ext } "
152- if filename not in used_filenames :
153- used_filenames .add (filename )
154- return filename
155- counter += 1
149+ if basename not in used_filenames :
150+ used_filenames .add (basename )
151+ return basename
152+
153+ # Try prepending parents from nearest to farthest
154+ path_parts = normalized_path .split ('/' )
155+ parent_parts = [p for p in path_parts [:- 1 ] if p ]
156+
157+ prefixed_name = basename
158+ for parent in reversed (parent_parts ):
159+ prefixed_name = f"{ parent } /{ prefixed_name } "
160+
161+ if prefixed_name not in used_filenames :
162+ used_filenames .add (prefixed_name )
163+ return prefixed_name
164+
165+ return None
156166
157167
158168def matches_include_patterns (filename : str , include_patterns : Tuple [str , ...]) -> bool :
0 commit comments