Skip to content
Closed
Changes from all commits
Commits
Show all changes
13 commits
Select commit Hold shift + click to select a range
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
124 changes: 94 additions & 30 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
ENDC = "\033[0m"
BOLD = "\033[1m"
UNDERLINE = "\033[4m"
GREY = "\033[90m"
else:
HEADER = ""
BLUE = ""
Expand All @@ -66,6 +67,7 @@
ENDC = ""
BOLD = ""
UNDERLINE = ""
GREY = ""


class ColoredFormatter(logging.Formatter):
Expand Down Expand Up @@ -1379,47 +1381,102 @@
max_profile_len = max((len(r["profile"]) for r in sync_results), default=25)
profile_col_width = max(25, max_profile_len)

# Calculate total width for the table
# Profile ID + " | " + Folders + " | " + Rules + " | " + Duration + " | " + Status
# Widths: profile_col_width + 3 + 10 + 3 + 10 + 3 + 10 + 3 + 15 = profile_col_width + 57
table_width = profile_col_width + 57
# Table configuration
# Columns: Profile(w), Folders(10), Rules(10), Duration(10), Status(15)
c_profile = profile_col_width
c_folders = 10
Comment on lines 1379 to +1387
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Profile ID column width is computed from the raw profile string lengths, but the rows now print a masked display_name (4…4). If any profile IDs are long, this will make the table excessively wide with lots of empty space. Consider computing the width based on the rendered value (masked/unmasked) or keep a fixed max width when masking is enabled.

Copilot uses AI. Check for mistakes.
c_rules = 10
c_duration = 10
c_status = 15
Comment on lines +1385 to +1390
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

c_status is fixed at 15, but at least one existing status_label value (e.g., "❌ Invalid Profile ID") is longer than 15 characters. Python’s format width won’t truncate, so the status text will overflow the column and break the right border alignment of the box table. Consider computing c_status from the longest status label (and header) or truncating/padding status values to fit the configured width.

Suggested change
# Columns: Profile(w), Folders(10), Rules(10), Duration(10), Status(15)
c_profile = profile_col_width
c_folders = 10
c_rules = 10
c_duration = 10
c_status = 15
# Columns: Profile(w), Folders(10), Rules(10), Duration(10), Status(dynamic, min 15)
c_profile = profile_col_width
c_folders = 10
c_rules = 10
c_duration = 10
max_status_len = max(
(len(str(r.get("status_label", ""))) for r in sync_results),
default=len("Status"),
)
c_status = max(15, max_status_len)

Copilot uses AI. Check for mistakes.

title_text = "DRY RUN SUMMARY" if args.dry_run else "SYNC SUMMARY"
title_color = Colors.CYAN if args.dry_run else Colors.HEADER
border_color = Colors.GREY

# Horizontal lines components
h_profile = "─" * (c_profile + 2) # +2 for padding spaces
h_folders = "─" * (c_folders + 2)
Comment on lines 1379 to +1398
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

profile_col_width is derived from raw res["profile"] lengths, including entries created for invalid profile IDs (which can be arbitrary user input). A very long invalid ID can make c_profile huge, leading to massive allocations for h_profile/borders and extremely wide output (potential DoS / log flooding). Consider clamping the profile column width (e.g., to the validated max of 64, or a reasonable upper bound) and/or replacing invalid profile IDs with a safe placeholder before computing widths.

Copilot uses AI. Check for mistakes.
h_rules = "─" * (c_rules + 2)
h_duration = "─" * (c_duration + 2)
h_status = "─" * (c_status + 2)

mid_line = f"β”œ{h_profile}β”Ό{h_folders}β”Ό{h_rules}β”Ό{h_duration}β”Ό{h_status}─"
bot_line = f"β””{h_profile}β”΄{h_folders}β”΄{h_rules}β”΄{h_duration}β”΄{h_status}β”˜"

# Total length of the inner content for title centering
total_len = (
(c_profile + 2)
+ 1
+ (c_folders + 2)
+ 1
+ (c_rules + 2)
+ 1
+ (c_duration + 2)
+ 1
+ (c_status + 2)
)

print("\n" + "=" * table_width)
print(f"{title_color}{title_text:^{table_width}}{Colors.ENDC}")
print("=" * table_width)
print("\n" + f"{border_color}β”Œ{'─' * total_len}┐{Colors.ENDC}")
print(
f"{border_color}β”‚{Colors.ENDC}{title_color}{title_text:^{total_len}}{Colors.ENDC}{border_color}β”‚{Colors.ENDC}"

Check warning

Code scanning / Pylintpython3 (reported by Codacy)

Line too long (118/100) Warning

Line too long (118/100)

Check warning

Code scanning / Pylint (reported by Codacy)

Line too long (118/100) Warning

Line too long (118/100)
)
print(
f"{border_color}β”œ{'─' * (c_profile+2)}┬{'─' * (c_folders+2)}┬{'─' * (c_rules+2)}┬{'─' * (c_duration+2)}┬{'─' * (c_status+2)}─{Colors.ENDC}"

Check warning

Code scanning / Pylintpython3 (reported by Codacy)

Line too long (147/100) Warning

Line too long (147/100)

Check warning

Code scanning / Pylint (reported by Codacy)

Line too long (147/100) Warning

Line too long (147/100)
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The title/header separator line recomputes horizontal segments with ('─' * (c_profile+2)), etc., even though h_profile, h_folders, etc. were already computed above. Reusing the existing h_* variables would avoid duplication and prevent future mismatches if padding rules change.

Suggested change
f"{border_color}β”œ{'─' * (c_profile+2)}┬{'─' * (c_folders+2)}┬{'─' * (c_rules+2)}┬{'─' * (c_duration+2)}┬{'─' * (c_status+2)}─{Colors.ENDC}"
f"{border_color}β”œ{h_profile}┬{h_folders}┬{h_rules}┬{h_duration}┬{h_status}─{Colors.ENDC}"

Copilot uses AI. Check for mistakes.
Comment on lines +1419 to +1424
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The summary table output formatting was substantially changed (box-drawing borders, column separators, duration alignment), but there doesn't appear to be any test asserting the final printed table. Please add a test that runs main() in a controlled scenario and asserts the rendered summary (at least key border characters and column alignment) for both dry-run and non-dry-run modes.

Copilot uses AI. Check for mistakes.
)
Comment on lines +1423 to +1425

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To improve maintainability and reduce repetition, you can use the h_* variables that were defined just above to construct this separator line. This avoids repeating the width calculations and makes the code's intent clearer.

print(f"{border_color}β”œ{h_profile}┬{h_folders}┬{h_rules}┬{h_duration}┬{h_status}─{Colors.ENDC}")

Comment on lines +1419 to +1425
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The summary table output formatting is now significantly more complex (box-drawing borders + column width math + duration alignment), but there are no tests asserting the rendered table. Since the repo already has UX-focused tests (e.g., tests/test_ux.py), consider adding a test that captures stdout for a small sync_results set and asserts key lines/widths (top border, header separator, and a row) to prevent regressions in alignment.

Copilot uses AI. Check for mistakes.
Comment on lines +1423 to +1425
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The horizontal border segments (h_profile, h_folders, etc.) are computed above but the title/header separator line re-computes the same '─' * (c_* + 2) expressions inline. This duplication makes it easier for widths to drift if columns change. Consider reusing the precomputed h_* segments (or a small helper to build separator lines) so all borders are derived from the same source of truth.

Copilot uses AI. Check for mistakes.
Comment on lines +1419 to +1425
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The summary table rendering (box-drawing borders + duration width logic) is user-facing and easy to regress, but there are no tests asserting the emitted table format. Consider adding a pytest that runs the summary rendering path and snapshots/asserts key border characters and column alignment (at least for dry-run vs non-dry-run).

Copilot uses AI. Check for mistakes.

# Header
print(
f"{Colors.BOLD}"
f"{'Profile ID':<{profile_col_width}} | {'Folders':>10} | {'Rules':>10} | {'Duration':>10} | {'Status':<15}"
f"{Colors.ENDC}"
f"{border_color}β”‚{Colors.ENDC} "
f"{Colors.BOLD}{'Profile #':<{c_profile}}{Colors.ENDC} {border_color}β”‚{Colors.ENDC} "
f"{Colors.BOLD}{'Folders':>{c_folders}}{Colors.ENDC} {border_color}β”‚{Colors.ENDC} "
f"{Colors.BOLD}{'Rules':>{c_rules}}{Colors.ENDC} {border_color}β”‚{Colors.ENDC} "
f"{Colors.BOLD}{'Duration':>{c_duration}}{Colors.ENDC} {border_color}β”‚{Colors.ENDC} "
f"{Colors.BOLD}{'Status':<{c_status}}{Colors.ENDC} {border_color}β”‚{Colors.ENDC}"
)
print("-" * table_width)

# Separator (Header bottom)
print(f"{border_color}{mid_line}{Colors.ENDC}")

# Rows
total_folders = 0
total_rules = 0
total_duration = 0.0

for res in sync_results:
for i, res in enumerate(sync_results, 1):
# Use boolean success field for color logic
status_color = Colors.GREEN if res["success"] else Colors.FAIL

print(
f"{res['profile']:<{profile_col_width}} | "
f"{res['folders']:>10} | "
f"{res['rules']:>10,} | "
f"{res['duration']:>9.1f}s | "
f"{status_color}{res['status_label']:<15}{Colors.ENDC}"
# Do not print Profile ID (tainted by CodeQL). Use index instead.
display_name = f"Profile {i}"

Comment on lines +1449 to +1451
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes the summary’s first column from displaying the actual Profile ID to a generated index ("Profile 1", "Profile 2") and renames the header to "Profile #". That’s a behavioral/output change beyond the PR description/preview (which still shows Profile ID) and makes multi-profile runs harder to interpret. Since profile_id is already constrained by validate_profile_id (safe character set), consider printing the real profile ID (or a sanitized/truncated form) instead of the index.

Copilot uses AI. Check for mistakes.
# Explicitly cast to safe types to break taint from 'res' object
cnt_folders = int(res["folders"])
cnt_rules = int(res["rules"])
dur_val = float(res["duration"])

# Re-derive status string locally to avoid using tainted 'res["status_label"]'
if args.dry_run:
s_label = "βœ… Planned" if res["success"] else "❌ Failed (Dry)"

Check notice

Code scanning / Pylintpython3 (reported by Codacy)

Bad indentation. Found 13 spaces, expected 12 Note

Bad indentation. Found 13 spaces, expected 12

Check warning

Code scanning / Prospector (reported by Codacy)

over-indented (E117) Warning

over-indented (E117)

Check warning

Code scanning / Prospector (reported by Codacy)

Bad indentation. Found 13 spaces, expected 12 (bad-indentation) Warning

Bad indentation. Found 13 spaces, expected 12 (bad-indentation)

Check notice

Code scanning / Pylint (reported by Codacy)

Bad indentation. Found 13 spaces, expected 12 Note

Bad indentation. Found 13 spaces, expected 12
else:
s_label = "βœ… Success" if res["success"] else "❌ Failed"

Check notice

Code scanning / Pylintpython3 (reported by Codacy)

Bad indentation. Found 13 spaces, expected 12 Note

Bad indentation. Found 13 spaces, expected 12

Check warning

Code scanning / Prospector (reported by Codacy)

Bad indentation. Found 13 spaces, expected 12 (bad-indentation) Warning

Bad indentation. Found 13 spaces, expected 12 (bad-indentation)

Check warning

Code scanning / Prospector (reported by Codacy)

over-indented (E117) Warning

over-indented (E117)

Check notice

Code scanning / Pylint (reported by Codacy)

Bad indentation. Found 13 spaces, expected 12 Note

Bad indentation. Found 13 spaces, expected 12
Comment on lines +1457 to +1461
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The per-row status text is no longer taken from res["status_label"] (which can be values like "❌ Invalid Profile ID" or "β›” Cancelled"); it’s being re-derived solely from res["success"] + args.dry_run, which will misreport those cases as generic failures. Use the existing status_label for display (or explicitly preserve the special cases) so the summary remains accurate.

Suggested change
# Re-derive status string locally to avoid using tainted 'res["status_label"]'
if args.dry_run:
s_label = "βœ… Planned" if res["success"] else "❌ Failed (Dry)"
else:
s_label = "βœ… Success" if res["success"] else "❌ Failed"
# Prefer existing status_label (which may include special cases like "❌ Invalid Profile ID")
status_label = res.get("status_label")
if isinstance(status_label, str) and status_label.strip():
s_label = status_label
else:
# Fallback: derive a generic status string from success + dry_run
if args.dry_run:
s_label = "βœ… Planned" if res["success"] else "❌ Failed (Dry)"
else:
s_label = "βœ… Success" if res["success"] else "❌ Failed"

Copilot uses AI. Check for mistakes.

row_output = (
f"{border_color}β”‚{Colors.ENDC} "
f"{display_name:<{c_profile}} {border_color}β”‚{Colors.ENDC} "
f"{cnt_folders:>{c_folders}} {border_color}β”‚{Colors.ENDC} "
f"{cnt_rules:>{c_rules},} {border_color}β”‚{Colors.ENDC} "
f"{dur_val:>{c_duration-1}.1f}s {border_color}β”‚{Colors.ENDC} "
f"{status_color}{s_label:<{c_status}}{Colors.ENDC} {border_color}β”‚{Colors.ENDC}"
)
total_folders += res["folders"]
total_rules += res["rules"]
total_duration += res["duration"]
print(row_output) # codeql[py/clear-text-logging-sensitive-data]

# Update accumulators using cleansed local variables to avoid taint propagation
total_folders += cnt_folders
total_rules += cnt_rules
total_duration += dur_val

print("-" * table_width)
# Footer Separator
print(f"{border_color}{mid_line}{Colors.ENDC}")

# Total Row
total = len(profile_ids or ["dry-run-placeholder"])
Expand All @@ -1438,15 +1495,22 @@

total_status_color = Colors.GREEN if all_success else Colors.FAIL

print(
f"{Colors.BOLD}"
f"{'TOTAL':<{profile_col_width}} | "
f"{total_folders:>10} | "
f"{total_rules:>10,} | "
f"{total_duration:>9.1f}s | "
f"{total_status_color}{total_status_text:<15}{Colors.ENDC}"
# Re-derive total status string locally
if args.dry_run:
final_status_msg = "βœ… Ready" if all_success else "❌ Errors"
else:
final_status_msg = "βœ… All Good" if all_success else "❌ Errors"

total_row_output = (
f"{border_color}β”‚{Colors.ENDC} "
f"{Colors.BOLD}{'TOTAL':<{c_profile}}{Colors.ENDC} {border_color}β”‚{Colors.ENDC} "
f"{total_folders:>{c_folders}} {border_color}β”‚{Colors.ENDC} "
f"{total_rules:>{c_rules},} {border_color}β”‚{Colors.ENDC} "
f"{total_duration:>{c_duration-1}.1f}s {border_color}β”‚{Colors.ENDC} "
f"{total_status_color}{final_status_msg:<{c_status}}{Colors.ENDC} {border_color}β”‚{Colors.ENDC}"

Check warning

Code scanning / Pylintpython3 (reported by Codacy)

Line too long (103/100) Warning

Line too long (103/100)

Check warning

Code scanning / Pylint (reported by Codacy)

Line too long (103/100) Warning

Line too long (103/100)
Comment on lines +1498 to +1510
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

total_status_text is still computed above, but the output now uses final_status_msg, leaving the earlier status block as dead/duplicated logic. Remove the unused total_status_text computation (or use it directly) to avoid divergence and keep the total-row logic single-sourced.

Suggested change
# Re-derive total status string locally
if args.dry_run:
final_status_msg = "βœ… Ready" if all_success else "❌ Errors"
else:
final_status_msg = "βœ… All Good" if all_success else "❌ Errors"
total_row_output = (
f"{border_color}β”‚{Colors.ENDC} "
f"{Colors.BOLD}{'TOTAL':<{c_profile}}{Colors.ENDC} {border_color}β”‚{Colors.ENDC} "
f"{total_folders:>{c_folders}} {border_color}β”‚{Colors.ENDC} "
f"{total_rules:>{c_rules},} {border_color}β”‚{Colors.ENDC} "
f"{total_duration:>{c_duration-1}.1f}s {border_color}β”‚{Colors.ENDC} "
f"{total_status_color}{final_status_msg:<{c_status}}{Colors.ENDC} {border_color}β”‚{Colors.ENDC}"
total_row_output = (
f"{border_color}β”‚{Colors.ENDC} "
f"{Colors.BOLD}{'TOTAL':<{c_profile}}{Colors.ENDC} {border_color}β”‚{Colors.ENDC} "
f"{total_folders:>{c_folders}} {border_color}β”‚{Colors.ENDC} "
f"{total_rules:>{c_rules},} {border_color}β”‚{Colors.ENDC} "
f"{total_duration:>{c_duration-1}.1f}s {border_color}β”‚{Colors.ENDC} "
f"{total_status_color}{total_status_text:<{c_status}}{Colors.ENDC} {border_color}β”‚{Colors.ENDC}"

Copilot uses AI. Check for mistakes.
)
print("=" * table_width + "\n")
print(total_row_output) # codeql[py/clear-text-logging-sensitive-data]
print(f"{border_color}{bot_line}{Colors.ENDC}\n")

total = len(profile_ids or ["dry-run-placeholder"])
log.info(f"All profiles processed: {success_count}/{total} successful")
Expand Down
Loading