24
24
"""Classes and utility functions to generate a remotely hosted cache of all addon catalog entries.
25
25
Intended to be run by a server-side systemd timer to generate a file that is then loaded by the
26
26
Addon Manager in each FreeCAD installation."""
27
+ import enum
27
28
import xml .etree .ElementTree
28
29
from dataclasses import dataclass , asdict
29
30
from typing import List , Optional
45
46
"https://raw.githubusercontent.com/FreeCAD/FreeCAD-addons/master/AddonCatalog.json"
46
47
)
47
48
BASE_DIRECTORY = "./CatalogCache"
48
- MAX_COUNT = 10 # Do at most this many repos (for testing purposes)
49
+ MAX_COUNT = 10000 # Do at most this many repos (for testing purposes this can be made smaller )
49
50
50
51
# Repos that are too large, or that should for some reason not be cloned here
51
52
EXCLUDED_REPOS = ["parts_library" ]
@@ -62,6 +63,14 @@ class CacheEntry:
62
63
icon_data : str = ""
63
64
64
65
66
+ class GitRefType (enum .IntEnum ):
67
+ """Enum for the type of git ref (tag, branch, or hash)."""
68
+
69
+ TAG = 1
70
+ BRANCH = 2
71
+ HASH = 3
72
+
73
+
65
74
class CatalogFetcher :
66
75
"""Fetches the addon catalog from the given URL and returns an AddonCatalog object. Separated
67
76
from the main class for easy mocking during tests. Note that every instantiation of this class
@@ -181,10 +190,15 @@ def generate_cache_entry_from_package_xml(
181
190
return None
182
191
183
192
relative_icon_path = self .get_icon_from_metadata (metadata )
184
- absolute_icon_path = os .path .join (os .path .dirname (path_to_package_xml ), relative_icon_path )
185
- if os .path .exists (absolute_icon_path ):
186
- with open (absolute_icon_path , "rb" ) as f :
187
- cache_entry .icon_data = base64 .b64encode (f .read ()).decode ("utf-8" )
193
+ if relative_icon_path is not None :
194
+ absolute_icon_path = os .path .join (
195
+ os .path .dirname (path_to_package_xml ), relative_icon_path
196
+ )
197
+ if os .path .exists (absolute_icon_path ):
198
+ with open (absolute_icon_path , "rb" ) as f :
199
+ cache_entry .icon_data = base64 .b64encode (f .read ()).decode ("utf-8" )
200
+ else :
201
+ print (f"ERROR: Could not find icon file { absolute_icon_path } " )
188
202
return cache_entry
189
203
190
204
def create_local_copy_of_single_addon_with_git (
@@ -246,6 +260,8 @@ def clone_or_update(name: str, url: str, branch: str) -> None:
246
260
print (f"Updating { name } " , flush = True )
247
261
old_dir = os .getcwd ()
248
262
os .chdir (os .path .join (old_dir , name ))
263
+ # Determine if we are dealing with a tag, branch, or hash
264
+ git_ref_type = CacheWriter .determine_git_ref_type (name , url , branch )
249
265
command = ["git" , "fetch" ]
250
266
completed_process = subprocess .run (command )
251
267
if completed_process .returncode != 0 :
@@ -256,11 +272,12 @@ def clone_or_update(name: str, url: str, branch: str) -> None:
256
272
if completed_process .returncode != 0 :
257
273
os .chdir (old_dir )
258
274
raise RuntimeError (f"git checkout failed for { name } branch { branch } " )
259
- command = ["git" , "merge" , "--quiet" ]
260
- completed_process = subprocess .run (command )
261
- if completed_process .returncode != 0 :
262
- os .chdir (old_dir )
263
- raise RuntimeError (f"git merge failed for { name } branch { branch } " )
275
+ if git_ref_type == GitRefType .BRANCH :
276
+ command = ["git" , "merge" , "--quiet" ]
277
+ completed_process = subprocess .run (command )
278
+ if completed_process .returncode != 0 :
279
+ os .chdir (old_dir )
280
+ raise RuntimeError (f"git merge failed for { name } branch { branch } " )
264
281
os .chdir (old_dir )
265
282
266
283
def find_file (
@@ -293,6 +310,28 @@ def get_icon_from_metadata(metadata: addonmanager_metadata.Metadata) -> Optional
293
310
return icon
294
311
return None
295
312
313
+ @staticmethod
314
+ def determine_git_ref_type (name : str , url : str , branch : str ) -> GitRefType :
315
+ """Determine if the given branch, tag, or hash is a tag, branch, or hash. Returns the type
316
+ if determinable, otherwise raises a RuntimeError."""
317
+ command = ["git" , "show-ref" , "--verify" , f"refs/remotes/origin/{ branch } " ]
318
+ completed_process = subprocess .run (command )
319
+ if completed_process .returncode == 0 :
320
+ return GitRefType .BRANCH
321
+ command = ["git" , "show-ref" , "--tags" ]
322
+ completed_process = subprocess .run (command , capture_output = True )
323
+ completed_process_output = completed_process .stdout .decode ("utf-8" )
324
+ if branch in completed_process_output :
325
+ return GitRefType .TAG
326
+ command = ["git" , "rev-parse" , branch ]
327
+ completed_process = subprocess .run (command )
328
+ if completed_process .returncode == 0 :
329
+ return GitRefType .HASH
330
+ raise RuntimeError (
331
+ f"Could not determine if { branch } of { name } is a tag, branch, or hash. "
332
+ f"Output was: { completed_process_output } "
333
+ )
334
+
296
335
297
336
if __name__ == "__main__" :
298
337
writer = CacheWriter ()
0 commit comments