-
Notifications
You must be signed in to change notification settings - Fork 1
π¨ Palette: Upgrade CLI Summary Table to Box-Drawing Characters #159
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
789e138
8259f8f
500f764
fd12a2e
66b8d08
dc4aefa
5b8a630
03e2b16
cfb5dd7
23c53be
da308c9
a128e3b
7c90d44
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -56,6 +56,7 @@ | |||||||||||||||||||||||||||||||||||||||||
| ENDC = "\033[0m" | ||||||||||||||||||||||||||||||||||||||||||
| BOLD = "\033[1m" | ||||||||||||||||||||||||||||||||||||||||||
| UNDERLINE = "\033[4m" | ||||||||||||||||||||||||||||||||||||||||||
| GREY = "\033[90m" | ||||||||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||||||||
| HEADER = "" | ||||||||||||||||||||||||||||||||||||||||||
| BLUE = "" | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -66,6 +67,7 @@ | |||||||||||||||||||||||||||||||||||||||||
| ENDC = "" | ||||||||||||||||||||||||||||||||||||||||||
| BOLD = "" | ||||||||||||||||||||||||||||||||||||||||||
| UNDERLINE = "" | ||||||||||||||||||||||||||||||||||||||||||
| GREY = "" | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| class ColoredFormatter(logging.Formatter): | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -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 | ||||||||||||||||||||||||||||||||||||||||||
| c_rules = 10 | ||||||||||||||||||||||||||||||||||||||||||
| c_duration = 10 | ||||||||||||||||||||||||||||||||||||||||||
| c_status = 15 | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1385
to
+1390
|
||||||||||||||||||||||||||||||||||||||||||
| # 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
AI
Feb 3, 2026
There was a problem hiding this comment.
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.
Check warning
Code scanning / Pylintpython3 (reported by Codacy)
Line too long (118/100) Warning
Check warning
Code scanning / Pylint (reported by Codacy)
Line too long (118/100) Warning
Check warning
Code scanning / Pylintpython3 (reported by Codacy)
Line too long (147/100) Warning
Check warning
Code scanning / Pylint (reported by Codacy)
Line too long (147/100) Warning
Copilot
AI
Feb 3, 2026
There was a problem hiding this comment.
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.
| 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
AI
Feb 3, 2026
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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}")
Copilot
AI
Feb 3, 2026
There was a problem hiding this comment.
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
AI
Feb 3, 2026
There was a problem hiding this comment.
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
AI
Feb 3, 2026
There was a problem hiding this comment.
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
AI
Feb 3, 2026
There was a problem hiding this comment.
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.
Check notice
Code scanning / Pylintpython3 (reported by Codacy)
Bad indentation. Found 13 spaces, expected 12 Note
Check warning
Code scanning / Prospector (reported by Codacy)
over-indented (E117) Warning
Check warning
Code scanning / Prospector (reported by Codacy)
Bad indentation. Found 13 spaces, expected 12 (bad-indentation) Warning
Check notice
Code scanning / Pylint (reported by Codacy)
Bad indentation. Found 13 spaces, expected 12 Note
Check notice
Code scanning / Pylintpython3 (reported by Codacy)
Bad indentation. Found 13 spaces, expected 12 Note
Check warning
Code scanning / Prospector (reported by Codacy)
Bad indentation. Found 13 spaces, expected 12 (bad-indentation) Warning
Check warning
Code scanning / Prospector (reported by Codacy)
over-indented (E117) Warning
Check notice
Code scanning / Pylint (reported by Codacy)
Bad indentation. Found 13 spaces, expected 12 Note
Copilot
AI
Feb 3, 2026
There was a problem hiding this comment.
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.
| # 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" |
Fixed
Show fixed
Hide fixed
Check warning
Code scanning / Pylintpython3 (reported by Codacy)
Line too long (103/100) Warning
Check warning
Code scanning / Pylint (reported by Codacy)
Line too long (103/100) Warning
Copilot
AI
Feb 3, 2026
There was a problem hiding this comment.
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.
| # 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}" |
There was a problem hiding this comment.
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.