-
Notifications
You must be signed in to change notification settings - Fork 0
Enhanced multi-arch status handling and results table visual enhancements #14
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
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 | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -161,10 +161,11 @@ def find_key_recursive(obj, key): | |||||||
|
|
||||||||
| def format_status(status_str): | ||||||||
| """Returns a rich-formatted status string.""" | ||||||||
| if status_str == "Completed": return f"[green]{status_str}[/green] ✅" | ||||||||
| if status_str == "In Progress": return f"[yellow]{status_str}[/yellow] ⏳" | ||||||||
| if status_str == "Quarantined": return f"[red]{status_str}[/red] ☠️" | ||||||||
| if status_str == "Failed": return f"[bold red]{status_str}[/bold red] ❌" | ||||||||
| if status_str == "Completed": return f"[green]{status_str}[/green]" | ||||||||
| if status_str == "In Progress": return f"[yellow]{status_str}[/yellow]" | ||||||||
| if status_str == "Quarantined": return f"[red]{status_str}[/red]" | ||||||||
| if status_str == "Partial Quarantine": return f"[bold yellow]{status_str}[/bold yellow]" | ||||||||
| if status_str == "Failed": return f"[bold red]{status_str}[/bold red]" | ||||||||
| return status_str | ||||||||
|
|
||||||||
| def batch_delete_packages(workspace, repo, slugs): | ||||||||
|
|
@@ -266,7 +267,7 @@ def get_digest_data(workspace, repo, img, digest, ntag_display, platform="unknow | |||||||
| "is_child": True | ||||||||
| } | ||||||||
|
|
||||||||
| def fetch_tag_data(workspace, repo, img, ntag, detailed=False, all=False): | ||||||||
| def fetch_tag_data(workspace, repo, img, ntag, detailed=False, include_all=False): | ||||||||
| """Fetches the manifest list for a tag and returns a list of data dicts.""" | ||||||||
|
|
||||||||
| manifest_url = f"{CLOUDSMITH_URL}/v2/{workspace}/{repo}/{img}/manifests/{ntag}" | ||||||||
|
|
@@ -279,7 +280,7 @@ def fetch_tag_data(workspace, repo, img, ntag, detailed=False, all=False): | |||||||
| is_list = 'manifests' in manifest_json | ||||||||
|
|
||||||||
| # Removed to allow single images to be processed by default | ||||||||
| # if not is_list and not all: | ||||||||
| # if not is_list and not include_all: | ||||||||
| # return [] | ||||||||
|
|
||||||||
| children = [] | ||||||||
|
|
@@ -292,7 +293,8 @@ def fetch_tag_data(workspace, repo, img, ntag, detailed=False, all=False): | |||||||
| arch = p.get('architecture', 'unknown') | ||||||||
| plat = f"{os_name}/{arch}" | ||||||||
|
|
||||||||
| if d and arch.lower() != 'unknown': | ||||||||
| # Removed strict arch check to prevent filtering valid images with missing metadata | ||||||||
| if d: | ||||||||
| children.append({'digest': d, 'platform': plat}) | ||||||||
| else: | ||||||||
| # Fallback | ||||||||
|
|
@@ -306,12 +308,23 @@ def fetch_tag_data(workspace, repo, img, ntag, detailed=False, all=False): | |||||||
| # Process children | ||||||||
| children_data = [] | ||||||||
| total_downloads = 0 | ||||||||
| derived_status = None | ||||||||
|
|
||||||||
| if is_list: | ||||||||
| for child in children: | ||||||||
| data = get_digest_data(workspace, repo, img, child['digest'], ntag, platform=child['platform']) | ||||||||
| children_data.append(data) | ||||||||
| total_downloads += data['downloads'] | ||||||||
|
|
||||||||
| # Check quarantine status of children | ||||||||
| if children_data: | ||||||||
| quarantined_count = sum(1 for c in children_data if "Quarantined" in c.get('status', '')) | ||||||||
| count = len(children_data) | ||||||||
|
|
||||||||
| if quarantined_count == count: | ||||||||
| derived_status = "Quarantined" | ||||||||
| elif quarantined_count > 0: | ||||||||
| derived_status = "Partial Quarantine" | ||||||||
|
|
||||||||
| # Fetch parent package info | ||||||||
| api_url = f"https://api.cloudsmith.io/v1/packages/{workspace}/{repo}/?query=version:{ntag}" | ||||||||
|
|
@@ -338,6 +351,10 @@ def fetch_tag_data(workspace, repo, img, ntag, detailed=False, all=False): | |||||||
| if arch and arch.lower() != 'unknown': | ||||||||
| parent_platform = arch | ||||||||
|
|
||||||||
| # Override parent status if derived from children | ||||||||
| if derived_status: | ||||||||
| parent_status = derived_status | ||||||||
|
|
||||||||
| # Fallback: Fetch config blob for single images to determine platform | ||||||||
| if not is_list and (parent_platform == "unknown" or not parent_platform): | ||||||||
| # 1. Check for Schema 1 top-level architecture | ||||||||
|
|
@@ -626,47 +643,24 @@ def get_untagged_images(workspace, repo, img, delete=False, detailed=False, prog | |||||||
|
|
||||||||
| return groups | ||||||||
|
|
||||||||
| # filepath: /Users/cmoynes/dev/support-engineering/Docker/Cloudsmith Sonar/sonar.py | ||||||||
| # filepath: /Users/cmoynes/dev/support-engineering/Docker/Sonar/sonar.py | ||||||||
| # --- Core Logic --- | ||||||||
|
|
||||||||
| def get_image_analysis(workspace, repo, img_name, delete_all=False, delete_tag=None, detailed=False, progress=None, all=False): | ||||||||
| # Switch to Cloudsmith API to avoid upstream tags and allow filtering | ||||||||
| api_url = f"https://api.cloudsmith.io/v1/packages/{workspace}/{repo}/" | ||||||||
|
|
||||||||
| # Construct query: format:docker AND name:{img_name} (if provided) | ||||||||
| query_parts = ["format:docker"] | ||||||||
| if img_name: | ||||||||
| query_parts.append(f"name:{img_name}") | ||||||||
|
|
||||||||
| query = urlencode({'query': " AND ".join(query_parts)}) | ||||||||
| next_url = f"{api_url}?{query}" | ||||||||
| def get_image_analysis(workspace, repo, img_name, delete_all=False, delete_tag=None, detailed=False, progress=None, include_all=False): | ||||||||
| # Fetch all tags (including untagged if requested, but logic handled separately) | ||||||||
| api_url = f"https://api.cloudsmith.io/v1/packages/{workspace}/{repo}/?query=name:{img_name}" | ||||||||
|
||||||||
| api_url = f"https://api.cloudsmith.io/v1/packages/{workspace}/{repo}/?query=name:{img_name}" | |
| query_params = urlencode({"query": f"name:{img_name}"}) | |
| api_url = f"https://api.cloudsmith.io/v1/packages/{workspace}/{repo}/?{query_params}" |
Copilot
AI
Jan 7, 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 pagination logic has been removed, limiting results to the first page only (default page size varies by API). For images with many packages/tags, this will silently skip data. The original implementation correctly handled pagination via the Link header to retrieve all results across multiple pages.
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 string matching logic
"Quarantined" in c.get('status', '')will incorrectly match the new "Partial Quarantine" status as quarantined. This causes inaccurate quarantine counting when aggregating child statuses. Use exact string comparison:c.get('status', '') == "Quarantined"instead.