Skip to content

Commit 5e365bf

Browse files
committed
perf(installomator): gracefully handle problematic labels (#28)
1 parent 48ee2a1 commit 5e365bf

File tree

4 files changed

+52
-15
lines changed

4 files changed

+52
-15
lines changed

src/patcher/cli.py

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import asyncio
22
import inspect
33
import sys
4+
import warnings
45
from pathlib import Path
56
from typing import Optional, Tuple, Union
67

@@ -16,7 +17,7 @@
1617
from .client.ui_manager import UIConfigManager
1718
from .utils.animation import Animation
1819
from .utils.data_manager import DataManager
19-
from .utils.exceptions import APIResponseError, PatcherError
20+
from .utils.exceptions import APIResponseError, InstallomatorWarning, PatcherError
2021
from .utils.logger import LogMe, PatcherLog
2122

2223
DATE_FORMATS = {
@@ -30,8 +31,6 @@
3031
# Context settings to enable both ``-h`` and ``--help`` for help output
3132
CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
3233

33-
sys.excepthook = PatcherLog.custom_excepthook # Log unhandled exceptions
34-
3534

3635
def setup_logging(debug: bool) -> None:
3736
"""Configures global logging based on the debug flag."""
@@ -85,6 +84,15 @@ def initialize_cache(cache_dir: Path) -> None:
8584
return
8685

8786

87+
def warning_format(message, category, filename, lineno, file=None, line=None):
88+
return f"{category.__name__}: {message}\n"
89+
90+
91+
sys.excepthook = PatcherLog.custom_excepthook # Log unhandled exceptions
92+
warnings.simplefilter("always", InstallomatorWarning) # Show warnings in CLI
93+
warnings.formatwarning = warning_format
94+
95+
8896
# Entry
8997
@click.group(context_settings=CONTEXT_SETTINGS, options_metavar="<options>")
9098
@click.version_option(version=__version__)

src/patcher/models/label.py

+15-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import Any, Dict
22

3-
from pydantic import field_validator
3+
from pydantic import Field, field_validator
44

55
from ..utils.exceptions import PatcherError
66
from . import Model
@@ -39,7 +39,7 @@ class Label(Model):
3939

4040
name: str
4141
type: str
42-
expectedTeamID: str
42+
expectedTeamID: str = Field(..., description="Expected Team ID (must be exactly 10 characters)")
4343
installomatorLabel: str # fragmentName - ".sh"
4444
downloadURL: str
4545

@@ -70,13 +70,25 @@ def __str__(self):
7070

7171
@field_validator("type", mode="before")
7272
def validate_type(cls, v):
73-
allowed_types = ["dmg", "pkg", "zip", "tbz", "pkgInDmg", "pkgInZip", "appInDmgInZip"]
73+
allowed_types = [
74+
"dmg",
75+
"pkg",
76+
"zip",
77+
"tbz",
78+
"pkgInDmg",
79+
"pkgInZip",
80+
"appInDmgInZip",
81+
"appindmg",
82+
"bz2",
83+
]
7484
if v not in allowed_types:
7585
raise PatcherError(f"Type must be one of {allowed_types}", type=v)
7686
return v
7787

7888
@field_validator("expectedTeamID", mode="before")
7989
def validate_team_id(cls, v):
90+
if v in "Software Update":
91+
return v # Apple software/tools
8092
if len(v) != 10:
8193
raise PatcherError("expectedTeamID must be a 10-character string", team_id=v)
8294
return v

src/patcher/utils/exceptions.py

+6
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,9 @@ class TokenError(PatcherError):
4848
"""Raised when there is an error fetching, saving or retrieving a bearer token from Jamf API."""
4949

5050
pass
51+
52+
53+
class InstallomatorWarning(Warning):
54+
"""Custom warning to indicate Installomator-related issues."""
55+
56+
pass

src/patcher/utils/installomator.py

+20-9
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import fnmatch
33
import json
44
import re
5+
import warnings
56
from pathlib import Path
67
from typing import Any, Dict, List, Optional
78

@@ -13,9 +14,11 @@
1314
from ..models.fragment import Fragment
1415
from ..models.label import Label
1516
from ..models.patch import PatchTitle
16-
from .exceptions import APIResponseError, PatcherError, ShellCommandError
17+
from .exceptions import APIResponseError, InstallomatorWarning, PatcherError, ShellCommandError
1718
from .logger import LogMe
1819

20+
IGNORED_TEAMS = ["Frydendal", "Media", "LL3KBL2M3A"] # "LL3KBL2M3A" - lcadvancedvpnclient
21+
1922

2023
class Installomator:
2124
def __init__(self, concurrency: Optional[int] = 5):
@@ -149,20 +152,28 @@ async def _build_labels(self) -> List[Label]:
149152
for file_path in self.label_path.glob("*.sh"):
150153
content = file_path.read_text()
151154
fragment_dict = self._parse(content)
155+
156+
expected_team_id = fragment_dict.get("expectedTeamID")
157+
if expected_team_id in IGNORED_TEAMS:
158+
self.log.warning(
159+
f"Skipping label {file_path.stem} (ignored Team ID: {expected_team_id})"
160+
)
161+
warnings.warn(
162+
f"⚠️ Skipping {file_path.stem} due to ignored Team ID.",
163+
InstallomatorWarning,
164+
)
165+
continue # skip this label entirely
166+
152167
try:
153168
label = Label.from_dict(
154169
fragment_dict, installomatorLabel=file_path.stem.split(".")[0]
155170
)
156171
labels.append(label)
157-
except ValueError as e:
158-
raise PatcherError(
159-
"Failed to create Label object from fragment.",
160-
fragment=file_path.name,
161-
error_msg=str(e),
172+
except ValidationError as e:
173+
self.log.warning(
174+
f"Skipping invalid Installomator label: {file_path.name} due to validation error: {e}"
162175
)
163-
164-
# Cache labels
165-
self._labels = labels
176+
continue # Skip problematic label but continue
166177

167178
return labels
168179

0 commit comments

Comments
 (0)