Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/3375.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Show project homepages alongside repository links in the Hydra Landscape.
7 changes: 5 additions & 2 deletions tools/landscape/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,11 @@ the decision ledger.
```

5. Record maintainer decisions in `data/decisions.json`. Every included
decision must contain its complete public `listing`. Regenerate the public
data directly when needed:
decision must contain its complete public `listing`. A listing may also
carry an optional `homepage`, which must use HTTPS and must differ from
`listing.url`; the review queue reports each repository's homepage from
GitHub metadata so it can be copied in. Regenerate the public data directly
when needed:

```bash
.venv/bin/python tools/landscape/build_public_landscape.py
Expand Down
3 changes: 3 additions & 0 deletions tools/landscape/build_landscape_review.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ def maintenance_summary(
"fork": bool(metadata.get("fork")),
"parent": metadata.get("parent"),
"url": metadata.get("url") or analysis.get("project_url"),
"homepage": metadata.get("homepage"),
"evidence": [
item
for item in analysis.get("evidence", [])
Expand Down Expand Up @@ -395,10 +396,12 @@ def maintenance_markdown(maintenance: dict[str, Any]) -> str:
else "none"
)
stars = maintenance["stars"] if maintenance["stars"] is not None else "unknown"
homepage = maintenance.get("homepage")
return (
f"- **Maintenance:** {maintenance['assessment']} · "
f"default-branch commit: [{committed_at}]({commit_url}) · "
f"latest release: {release} · stars: {stars} · "
f"homepage: {homepage or 'none'} · "
f"metadata fetched: {maintenance['metadata_fetched_at']}"
)

Expand Down
21 changes: 19 additions & 2 deletions tools/landscape/build_public_landscape.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"type",
"url",
}
OPTIONAL_LISTING_FIELDS = {"homepage"}
REPOSITORY_RE = re.compile(r"^[\w.-]+/[\w.-]+$")
TAG_RE = re.compile(r"^[a-z][a-z0-9_]*$")
DATE_RE = re.compile(r"^\d{4}-\d{2}-\d{2}$")
Expand Down Expand Up @@ -103,8 +104,13 @@ def build_project(decision: dict[str, Any]) -> dict[str, Any]:
listing = decision.get("listing")
if not isinstance(listing, dict):
raise ValueError(f"{repo}: included decisions must contain a listing")
if set(listing) != LISTING_FIELDS:
raise ValueError(f"{repo}: listing fields must be {sorted(LISTING_FIELDS)!r}")
unknown = set(listing) - LISTING_FIELDS - OPTIONAL_LISTING_FIELDS
missing = LISTING_FIELDS - set(listing)
if unknown or missing:
raise ValueError(
f"{repo}: listing fields must be {sorted(LISTING_FIELDS)!r} "
f"with optional {sorted(OPTIONAL_LISTING_FIELDS)!r}"
)

kind = require_string(listing.get("kind"), field="listing.kind", repo=repo)
if kind not in ALLOWED_KINDS:
Expand All @@ -126,10 +132,21 @@ def build_project(decision: dict[str, Any]) -> dict[str, Any]:
if not url.startswith("https://"):
raise ValueError(f"{repo}: listing.url must use HTTPS")

homepage = None
if "homepage" in listing:
homepage = require_string(
listing.get("homepage"), field="listing.homepage", repo=repo
)
if not homepage.startswith("https://"):
raise ValueError(f"{repo}: listing.homepage must use HTTPS")
if homepage == url:
raise ValueError(f"{repo}: listing.homepage must differ from listing.url")

return {
"repository": repo,
"name": require_string(listing.get("name"), field="listing.name", repo=repo),
"url": url,
**({"homepage": homepage} if homepage is not None else {}),
"description": require_string(
listing.get("description"), field="listing.description", repo=repo
),
Expand Down
Loading
Loading