32
32
from fetchcode .package_util import GitHubSource
33
33
from fetchcode .package_util import MiniupnpPackagesGitHubSource
34
34
from fetchcode .package_util import OpenSSLGitHubSource
35
+ from fetchcode .package_util import construct_cocoapods_package
36
+ from fetchcode .package_util import get_cocoapod_tags
35
37
from fetchcode .packagedcode_models import Package
38
+ from fetchcode .utils import get_hashed_path
36
39
from fetchcode .utils import get_response
37
40
38
41
router = Router ()
39
42
40
43
41
44
def info (url ):
42
45
"""
43
- Return data according to the `url` string
44
- `url` string can be purl too
46
+ Return package metadata for a URL or PURL.
47
+ Return None if there is no URL, or the URL or PURL is not supported.
45
48
"""
46
49
if url :
47
50
try :
@@ -83,13 +86,7 @@ def get_cargo_data_from_purl(purl):
83
86
crate = response .get ("crate" ) or {}
84
87
homepage_url = crate .get ("homepage" )
85
88
code_view_url = crate .get ("repository" )
86
- yield Package (
87
- homepage_url = homepage_url ,
88
- api_url = api_url ,
89
- code_view_url = code_view_url ,
90
- download_url = download_url ,
91
- ** purl .to_dict (),
92
- )
89
+
93
90
versions = response .get ("versions" , [])
94
91
for version in versions :
95
92
version_purl = PackageURL (type = purl .type , name = name , version = version .get ("num" ))
@@ -100,6 +97,9 @@ def get_cargo_data_from_purl(purl):
100
97
download_url = None
101
98
declared_license = version .get ("license" )
102
99
100
+ if purl .version and version_purl .version != purl .version :
101
+ continue
102
+
103
103
yield Package (
104
104
homepage_url = homepage_url ,
105
105
api_url = api_url ,
@@ -109,6 +109,9 @@ def get_cargo_data_from_purl(purl):
109
109
** version_purl .to_dict (),
110
110
)
111
111
112
+ if purl .version :
113
+ break
114
+
112
115
113
116
@router .route ("pkg:npm/.*" )
114
117
def get_npm_data_from_purl (purl ):
@@ -120,39 +123,30 @@ def get_npm_data_from_purl(purl):
120
123
name = purl .name
121
124
version = purl .version
122
125
api_url = f"{ base_path } /{ name } "
126
+
123
127
response = get_response (api_url )
124
128
vcs_data = response .get ("repository" ) or {}
125
129
bugs = response .get ("bugs" ) or {}
126
-
127
130
download_url = f"{ base_path } /{ name } /-/{ name } -{ version } .tgz" if version else None
128
131
vcs_url = vcs_data .get ("url" )
129
132
bug_tracking_url = bugs .get ("url" )
130
133
license = response .get ("license" )
131
134
homepage_url = response .get ("homepage" )
132
135
133
- yield Package (
134
- homepage_url = homepage_url ,
135
- api_url = api_url ,
136
- vcs_url = vcs_url ,
137
- bug_tracking_url = bug_tracking_url ,
138
- download_url = download_url ,
139
- declared_license = license ,
140
- ** purl .to_dict (),
141
- )
142
-
143
136
versions = response .get ("versions" , [])
144
- tags = []
145
137
for num in versions :
146
138
version = versions [num ]
147
139
version_purl = PackageURL (type = purl .type , name = name , version = version .get ("version" ))
148
140
repository = version .get ("repository" ) or {}
149
141
bugs = response .get ("bugs" ) or {}
150
142
dist = version .get ("dist" ) or {}
151
- licenses = version .get ("licenses" ) or [{}]
152
143
vcs_url = repository .get ("url" )
153
144
download_url = dist .get ("tarball" )
154
145
bug_tracking_url = bugs .get ("url" )
155
- declared_license = licenses [0 ].get ("type" )
146
+ declared_license = license
147
+
148
+ if purl .version and version_purl .version != purl .version :
149
+ continue
156
150
157
151
yield Package (
158
152
homepage_url = homepage_url ,
@@ -164,6 +158,9 @@ def get_npm_data_from_purl(purl):
164
158
** version_purl .to_dict (),
165
159
)
166
160
161
+ if purl .version :
162
+ break
163
+
167
164
168
165
@router .route ("pkg:pypi/.*" )
169
166
def get_pypi_data_from_purl (purl ):
@@ -172,6 +169,7 @@ def get_pypi_data_from_purl(purl):
172
169
"""
173
170
purl = PackageURL .from_string (purl )
174
171
name = purl .name
172
+
175
173
base_path = "https://pypi.org/pypi"
176
174
api_url = f"{ base_path } /{ name } /json"
177
175
response = get_response (api_url )
@@ -182,19 +180,14 @@ def get_pypi_data_from_purl(purl):
182
180
project_urls = info .get ("project_urls" ) or {}
183
181
code_view_url = get_pypi_codeview_url (project_urls )
184
182
bug_tracking_url = get_pypi_bugtracker_url (project_urls )
185
- yield Package (
186
- homepage_url = homepage_url ,
187
- api_url = api_url ,
188
- bug_tracking_url = bug_tracking_url ,
189
- code_view_url = code_view_url ,
190
- declared_license = license ,
191
- ** purl .to_dict (),
192
- )
183
+
193
184
for num in releases :
194
185
version_purl = PackageURL (type = purl .type , name = name , version = num )
195
186
release = releases .get (num ) or [{}]
196
187
release = release [0 ]
197
188
download_url = release .get ("url" )
189
+ if purl .version and version_purl .version != purl .version :
190
+ continue
198
191
yield Package (
199
192
homepage_url = homepage_url ,
200
193
api_url = api_url ,
@@ -205,6 +198,9 @@ def get_pypi_data_from_purl(purl):
205
198
** version_purl .to_dict (),
206
199
)
207
200
201
+ if purl .version :
202
+ break
203
+
208
204
209
205
@router .route ("pkg:github/.*" )
210
206
def get_github_data_from_purl (purl ):
@@ -291,24 +287,24 @@ def get_bitbucket_data_from_purl(purl):
291
287
bitbucket_url = "https://bitbucket.org"
292
288
bug_tracking_url = f"{ bitbucket_url } /{ namespace } /{ name } /issues"
293
289
code_view_url = f"{ bitbucket_url } /{ namespace } /{ name } "
294
- yield Package (
295
- api_url = api_url ,
296
- bug_tracking_url = bug_tracking_url ,
297
- code_view_url = code_view_url ,
298
- ** purl .to_dict (),
299
- )
290
+
300
291
links = response .get ("links" ) or {}
301
292
tags_url = links .get ("tags" ) or {}
302
293
tags_url = tags_url .get ("href" )
303
294
if not tags_url :
304
295
return []
305
296
tags_data = get_response (tags_url )
306
297
tags = tags_data .get ("values" ) or {}
298
+
307
299
for tag in tags :
308
300
version = tag .get ("name" ) or ""
309
301
version_purl = PackageURL (type = purl .type , namespace = namespace , name = name , version = version )
310
302
download_url = f"{ base_path } /{ namespace } /{ name } /downloads/{ name } -{ version } .tar.gz"
311
303
code_view_url = f"{ bitbucket_url } /{ namespace } /{ name } /src/{ version } "
304
+
305
+ if purl .version and version_purl .version != purl .version :
306
+ continue
307
+
312
308
yield Package (
313
309
api_url = api_url ,
314
310
bug_tracking_url = bug_tracking_url ,
@@ -317,6 +313,9 @@ def get_bitbucket_data_from_purl(purl):
317
313
** version_purl .to_dict (),
318
314
)
319
315
316
+ if purl .version :
317
+ break
318
+
320
319
321
320
@router .route ("pkg:rubygems/.*" )
322
321
def get_rubygems_data_from_purl (purl ):
@@ -325,22 +324,38 @@ def get_rubygems_data_from_purl(purl):
325
324
"""
326
325
purl = PackageURL .from_string (purl )
327
326
name = purl .name
328
- api_url = f"https://rubygems.org/api/v1/gems/{ name } .json"
329
- response = get_response (api_url )
330
- declared_license = response .get ("licenses" ) or None
331
- homepage_url = response .get ("homepage_uri" )
332
- code_view_url = response .get ("source_code_uri" )
333
- bug_tracking_url = response .get ("bug_tracker_uri" )
334
- download_url = response .get ("gem_uri" )
335
- yield Package (
336
- homepage_url = homepage_url ,
337
- api_url = api_url ,
338
- bug_tracking_url = bug_tracking_url ,
339
- code_view_url = code_view_url ,
340
- declared_license = declared_license ,
341
- download_url = download_url ,
342
- ** purl .to_dict (),
343
- )
327
+ all_versions_url = f"https://rubygems.org/api/v1/versions/{ name } .json"
328
+ all_versions = get_response (all_versions_url )
329
+
330
+ for vers in all_versions :
331
+ version_purl = PackageURL (type = purl .type , name = name , version = vers .get ("number" ))
332
+
333
+ if purl .version and version_purl .version != purl .version :
334
+ continue
335
+
336
+ number = vers .get ("number" )
337
+ version_api = f"https://rubygems.org/api/v2/rubygems/{ name } /versions/{ number } .json"
338
+ version_api_response = get_response (version_api )
339
+ declared_license = version_api_response .get ("licenses" ) or None
340
+ homepage_url = version_api_response .get ("homepage_uri" )
341
+ code_view_url = version_api_response .get ("source_code_uri" )
342
+ bug_tracking_url = version_api_response .get ("bug_tracker_uri" )
343
+ download_url = version_api_response .get ("gem_uri" )
344
+ repository_homepage_url = version_api_response .get ("project_uri" )
345
+
346
+ yield Package (
347
+ homepage_url = homepage_url ,
348
+ api_url = version_api ,
349
+ bug_tracking_url = bug_tracking_url ,
350
+ code_view_url = code_view_url ,
351
+ declared_license = declared_license ,
352
+ download_url = download_url ,
353
+ repository_homepage_url = repository_homepage_url ,
354
+ ** version_purl .to_dict (),
355
+ )
356
+
357
+ if purl .version :
358
+ break
344
359
345
360
346
361
@router .route ("pkg:gnu/.*" )
@@ -354,6 +369,45 @@ def get_gnu_data_from_purl(purl):
354
369
yield from extract_packages_from_listing (purl , source_archive_url , version_regex , [])
355
370
356
371
372
+ @router .route ("pkg:cocoapods/.*" )
373
+ def get_cocoapods_data_from_purl (purl ):
374
+ purl = PackageURL .from_string (purl )
375
+ name = purl .name
376
+ cocoapods_org_url = f"https://cocoapods.org/pods/{ name } "
377
+ api = "https://cdn.cocoapods.org"
378
+ hashed_path = get_hashed_path (name )
379
+ hashed_path_underscore = hashed_path .replace ("/" , "_" )
380
+ file_prefix = "all_pods_versions_"
381
+ spec = f"{ api } /{ file_prefix } { hashed_path_underscore } .txt"
382
+ data_list = get_cocoapod_tags (spec , name )
383
+
384
+ for tag in data_list :
385
+ version_purl = PackageURL (type = purl .type , name = name , version = tag )
386
+ if purl .version and version_purl .version != purl .version :
387
+ continue
388
+
389
+ gh_repo_owner = None
390
+ gh_repo_name = name
391
+ podspec_api_url = f"https://raw.githubusercontent.com/CocoaPods/Specs/master/Specs/{ hashed_path } /{ name } /{ tag } /{ name } .podspec.json"
392
+ podspec_api_response = get_response (podspec_api_url )
393
+ podspec_homepage = podspec_api_response .get ("homepage" )
394
+
395
+ if podspec_homepage .startswith ("https://github.com/" ):
396
+ podspec_homepage_remove_gh_prefix = podspec_homepage .replace ("https://github.com/" , "" )
397
+ podspec_homepage_split = podspec_homepage_remove_gh_prefix .split ("/" )
398
+ gh_repo_owner = podspec_homepage_split [0 ]
399
+ gh_repo_name = podspec_homepage_split [- 1 ]
400
+
401
+ tag_pkg = construct_cocoapods_package (
402
+ version_purl , name , hashed_path , cocoapods_org_url , gh_repo_owner , gh_repo_name , tag
403
+ )
404
+
405
+ yield tag_pkg
406
+
407
+ if purl .version :
408
+ break
409
+
410
+
357
411
@dataclasses .dataclass
358
412
class DirectoryListedSource :
359
413
source_url : str = dataclasses .field (
0 commit comments