Skip to content

Commit 1f1dca5

Browse files
authored
Merge pull request #150 from baszalmstra/fix/populate-package-repository
fix: populate package repository metadata
2 parents afec056 + 631d64a commit 1f1dca5

6 files changed

Lines changed: 184 additions & 13 deletions

File tree

vinca/distro.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,18 @@ def is_archive_url(url):
3838
return path.endswith(ARCHIVE_SUFFIXES)
3939

4040

41+
def is_bloom_release_repository_url(url):
42+
"""Return True when url names a Bloom-generated release repository."""
43+
path = urllib.parse.urlparse(url).path.rstrip("/")
44+
repository_name = posixpath.basename(path).removesuffix(".git").lower()
45+
return repository_name.endswith(("-release", "_release"))
46+
47+
48+
def _strip_git_suffix(url):
49+
"""Return a repository URL without its optional ``.git`` suffix."""
50+
return url[:-4] if url.lower().endswith(".git") else url
51+
52+
4153
def _normalize_member(name):
4254
"""Return an archive member name without its './' prefix and trailing slash."""
4355
name = name.strip("/")
@@ -314,6 +326,38 @@ def get_released_repo(self, pkg_name):
314326
release_tag = get_release_tag(repo, pkg_name)
315327
return repo.url, release_tag, "tag"
316328

329+
def get_repository_url(self, pkg_name, package_urls=()):
330+
"""Return the best declared upstream repository for a package."""
331+
pkg_info = self._get_snapshot_package_info(pkg_name)
332+
if pkg_info is not None:
333+
if repository := pkg_info.get("repository"):
334+
return _strip_git_suffix(repository)
335+
336+
additional_info = (self.additional_packages_snapshot or {}).get(pkg_name)
337+
if additional_info is not None:
338+
if repository := additional_info.get("repository"):
339+
return _strip_git_suffix(repository)
340+
else:
341+
package = self._distro.release_packages.get(pkg_name)
342+
if package is not None:
343+
repository = self._distro.repositories[package.repository_name]
344+
source_repository = repository.source_repository
345+
if (
346+
source_repository is not None
347+
and source_repository.url
348+
and not is_bloom_release_repository_url(source_repository.url)
349+
):
350+
return _strip_git_suffix(source_repository.url)
351+
352+
for package_url in package_urls:
353+
if (
354+
package_url.type == "repository"
355+
and package_url.url
356+
and not is_bloom_release_repository_url(package_url.url)
357+
):
358+
return _strip_git_suffix(package_url.url)
359+
return None
360+
317361
def check_package(self, pkg_name):
318362
# If the package is in the additional_packages_snapshot, it is always considered valid
319363
# even if it is not in the released packages, as it is an additional

vinca/main.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -479,14 +479,12 @@ def parse_package(pkg, distro, vinca_conf, path):
479479
recipe["about"]["maintainers"].append(name)
480480

481481
for u in pkg["urls"]:
482-
# if u.type == 'repository' :
483-
# recipe['source']['git'] = u.url
484-
# recipe['source']['tag'] = recipe['package']['version']
485482
if u.type == "website":
486483
recipe["about"]["homepage"] = u.url
487484

488-
# if u.type == 'bugtracker' :
489-
# recipe['about']['url_issues'] = u.url
485+
repository = distro.get_repository_url(pkg.name, pkg["urls"])
486+
if repository:
487+
recipe["about"]["repository"] = repository
490488

491489
if not recipe["source"].get("git", None):
492490
aux = path.split("/")

vinca/recipes.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -286,14 +286,17 @@ def _requirement_sort_key(requirement):
286286
)
287287

288288

289-
def _add_metadata(output: dict[str, Any], package: Any, shortname: str) -> None:
290-
"""Populate ``about`` from the package.xml URLs, license and description."""
289+
def _add_metadata(
290+
output: dict[str, Any], package: Any, shortname: str, distro: Distro
291+
) -> None:
292+
"""Populate ``about`` from package.xml and rosdistro metadata."""
291293
about = output["about"] = {}
292294
for url in package.urls:
293295
if url.type == "website":
294296
about["homepage"] = url.url
295-
elif url.type == "repository":
296-
about["repository"] = url.url
297+
repository = distro.get_repository_url(shortname, package.urls)
298+
if repository:
299+
about["repository"] = repository
297300
if package.licenses:
298301
license_expression = convert_to_spdx_license(
299302
[str(license) for license in package.licenses], package_name=shortname
@@ -459,5 +462,5 @@ def generate_output(
459462
_adjust_requirements(output["requirements"], package_prefix)
460463
if dependencies_only:
461464
return output["requirements"]
462-
_add_metadata(output, package, shortname)
465+
_add_metadata(output, package, shortname, distro)
463466
return output

vinca/snapshot.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ def main():
7979
continue
8080

8181
output[dep] = {"url": url, "version": version, "tag": tag}
82+
if repository := distro.get_repository_url(dep):
83+
output[dep]["repository"] = repository
8284

8385
if not args.quiet:
8486
print("{0:{2}} {1}".format(dep, version, max_len + 2))

vinca/test_recipes.py

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,22 @@ def package_xml(name, build_type="ament_cmake", depends=()):
4040
class FakeDistro:
4141
name = "rolling"
4242

43-
def __init__(self, xml_by_name, ros1=False):
43+
def __init__(self, xml_by_name, ros1=False, repository_by_name=None):
4444
self._xml_by_name = xml_by_name
4545
self._ros1 = ros1
46+
self._repository_by_name = repository_by_name or {}
4647

4748
def get_release_package_xml(self, name):
4849
return self._xml_by_name.get(name)
4950

51+
def get_repository_url(self, name, package_urls=()):
52+
if repository := self._repository_by_name.get(name):
53+
return repository.removesuffix(".git")
54+
for package_url in package_urls:
55+
if package_url.type == "repository":
56+
return package_url.url.removesuffix(".git")
57+
return None
58+
5059
def check_ros1(self):
5160
return self._ros1
5261

@@ -90,7 +99,10 @@ def build(
9099
dependencies_only=False,
91100
**overrides,
92101
):
93-
distro = FakeDistro({name: package_xml(name, build_type, depends)})
102+
distro = FakeDistro(
103+
{name: package_xml(name, build_type, depends)},
104+
repository_by_name={name: f"https://github.com/ros2/{name}.git"},
105+
)
94106
return generate_output(
95107
name,
96108
make_config(name, **overrides),
@@ -154,13 +166,32 @@ def test_generate_output_produces_a_complete_recipe():
154166
},
155167
"about": {
156168
"homepage": "https://example.org/demo",
157-
"repository": "https://github.com/example/demo",
169+
"repository": "https://github.com/ros2/demo",
158170
"license": "Apache-2.0",
159171
"summary": "Description of demo.",
160172
},
161173
}
162174

163175

176+
def test_generate_output_uses_rosdistro_repository_instead_of_manifest_url():
177+
distro = FakeDistro(
178+
{"demo": package_xml("demo")},
179+
repository_by_name={"demo": "https://github.com/ros2/demo.git"},
180+
)
181+
182+
output = generate_output("demo", make_config("demo"), distro, "1.2.3")
183+
184+
assert output["about"]["repository"] == "https://github.com/ros2/demo"
185+
186+
187+
def test_generate_output_uses_manifest_repository_as_fallback():
188+
distro = FakeDistro({"demo": package_xml("demo")})
189+
190+
output = generate_output("demo", make_config("demo"), distro, "1.2.3")
191+
192+
assert output["about"]["repository"] == "https://github.com/example/demo"
193+
194+
164195
def test_cmake_is_build_only_on_emscripten():
165196
output = build("demo", depends=[("exec_depend", "cmake")])
166197

vinca/test_snapshot_metadata.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,13 @@ def make_snapshot_distro(monkeypatch):
3333
snapshot = {
3434
"snapshot_package": {
3535
"url": "https://github.com/example/snapshot-package-release.git",
36+
"repository": "https://github.com/example/snapshot-package.git",
3637
"version": "1.0.0",
3738
"tag": "release/rolling/snapshot_package/1.0.0-1",
3839
},
3940
"snapshot_dependency": {
4041
"url": "https://github.com/example/snapshot-dependency-release.git",
42+
"repository": "https://github.com/example/snapshot-dependency.git",
4143
"version": "1.0.0",
4244
"tag": "release/rolling/snapshot_dependency/1.0.0-1",
4345
},
@@ -83,6 +85,10 @@ def test_snapshot_package_xml_and_dependencies_do_not_follow_live_rosdistro(
8385
"release/rolling/snapshot_package/1.0.0-1",
8486
"tag",
8587
)
88+
assert (
89+
distro.get_repository_url("snapshot_package")
90+
== "https://github.com/example/snapshot-package"
91+
)
8692
assert distro.get_version("snapshot_package") == "1.0.0"
8793
assert "<version>1.0.0</version>" in package_xml_content
8894
assert "snapshot_dependency" in package_xml_content
@@ -173,6 +179,9 @@ def test_snapshot_metadata_generates_dependency_required_by_pinned_source(
173179
"name": "ros2-snapshot-package",
174180
"version": "1.0.0",
175181
}
182+
assert output["about"]["repository"] == (
183+
"https://github.com/example/snapshot-package"
184+
)
176185
assert "ros2-snapshot-dependency" in output["requirements"]["host"]
177186
assert "ros2-live-dependency" not in output["requirements"]["host"]
178187

@@ -189,6 +198,90 @@ def test_snapshot_is_authoritative_for_package_membership(monkeypatch):
189198
}
190199

191200

201+
def test_live_repository_url_requires_upstream_source_metadata():
202+
distro = Distro.__new__(Distro)
203+
distro.snapshot = None
204+
distro.additional_packages_snapshot = None
205+
distro._distro = Mock()
206+
distro._distro.release_packages = {
207+
"source_package": Mock(repository_name="source-package"),
208+
"release_only_package": Mock(repository_name="release-only-package"),
209+
"bloom_source_package": Mock(repository_name="bloom-source-package"),
210+
}
211+
distro._distro.repositories = {
212+
"source-package": Mock(
213+
source_repository=Mock(url="https://github.com/example/source.git"),
214+
release_repository=Mock(
215+
url="https://github.com/example/source-release.git"
216+
),
217+
),
218+
"release-only-package": Mock(
219+
source_repository=None,
220+
release_repository=Mock(
221+
url="https://github.com/example/release-only-release.git"
222+
),
223+
),
224+
"bloom-source-package": Mock(
225+
source_repository=Mock(
226+
url="https://github.com/example/bloom-source-release.git"
227+
),
228+
release_repository=Mock(
229+
url="https://github.com/ros2-gbp/bloom-source-release.git"
230+
),
231+
),
232+
}
233+
234+
assert (
235+
distro.get_repository_url("source_package")
236+
== "https://github.com/example/source"
237+
)
238+
assert distro.get_repository_url("release_only_package") is None
239+
assert (
240+
distro.get_repository_url(
241+
"release_only_package",
242+
[Mock(type="repository", url="https://github.com/example/upstream.git")],
243+
)
244+
== "https://github.com/example/upstream"
245+
)
246+
assert distro.get_repository_url("bloom_source_package") is None
247+
248+
249+
def test_additional_package_repository_must_be_explicit():
250+
distro = Distro.__new__(Distro)
251+
distro.snapshot = None
252+
distro.additional_packages_snapshot = {
253+
"explicit": {
254+
"url": "https://github.com/example/explicit-release.git",
255+
"repository": "https://github.com/example/explicit.git",
256+
},
257+
"source_only": {"url": "https://github.com/example/source-only.git"},
258+
}
259+
260+
assert (
261+
distro.get_repository_url("explicit") == "https://github.com/example/explicit"
262+
)
263+
assert distro.get_repository_url("source_only") is None
264+
assert (
265+
distro.get_repository_url(
266+
"source_only",
267+
[Mock(type="repository", url="https://github.com/example/source-only.git")],
268+
)
269+
== "https://github.com/example/source-only"
270+
)
271+
assert (
272+
distro.get_repository_url(
273+
"source_only",
274+
[
275+
Mock(
276+
type="repository",
277+
url="https://github.com/example/source-only-release.git",
278+
)
279+
],
280+
)
281+
is None
282+
)
283+
284+
192285
def test_empty_snapshot_keeps_live_rosdistro_behavior():
193286
distro = Distro.__new__(Distro)
194287
distro.snapshot = {}

0 commit comments

Comments
 (0)