feat: follow the branch a port was read from

This commit is contained in:
Abdessamad Derraz committed 2026-08-10 17:55:35 +02:00
1 parent 87087c74f0
commit 2b7c5f9a64
5 files changed
+503 -124

No files matched your search

+2 -1
View File
@@ -2,7 +2,8 @@ emulator: mkxp-z
type: libretro
core_classification: game_engine
source: "https://github.com/white-axe/mkxp-z"
source_ref: "branch: libretro (PR #255 to mkxp-z/mkxp-z)"
source_branch: libretro
source_ref: "PR #255 to mkxp-z/mkxp-z"
upstream: "https://github.com/mkxp-z/mkxp-z"
profiled_date: "2026-03-24"
source_commit: "f4bc5aacee7875526ac7053cb61c0dc2357cac17"
+458 -115
View File
@@ -4,21 +4,42 @@
"description": "One YAML per emulator core or engine, documenting every external file the code loads. Field reference: https://abdess.github.io/retrobios/wiki/profiling/",
"type": "object",
"additionalProperties": false,
"required": ["emulator", "type"],
"required": [
"emulator",
"type"
],
"allOf": [
{
"comment": "An alias profile points at another profile and carries no files of its own.",
"if": {
"properties": { "type": { "const": "alias" } },
"required": ["type"]
"properties": {
"type": {
"const": "alias"
}
},
"required": [
"type"
]
},
"then": { "required": ["alias_of"] },
"else": { "required": ["files"] }
"then": {
"required": [
"alias_of"
]
},
"else": {
"required": [
"files"
]
}
}
],
"properties": {
"emulator": { "type": "string" },
"display_name": { "type": "string" },
"emulator": {
"type": "string"
},
"display_name": {
"type": "string"
},
"type": {
"description": "Operational: resolve_platform_cores() filters on it. Not a classification.",
"type": "string",
@@ -49,135 +70,384 @@
"other"
]
},
"source": { "type": ["string", "object"] },
"upstream": { "type": ["string", "object", "null"] },
"source_commit": { "type": "string" },
"upstream_commit": { "type": "string" },
"source": {
"type": [
"string",
"object"
]
},
"upstream": {
"type": [
"string",
"object",
"null"
]
},
"source_commit": {
"type": "string"
},
"upstream_commit": {
"type": "string"
},
"profiled_date": {
"description": "Quote it: an unquoted YAML date parses to a date object.",
"type": ["string", "object"]
"type": [
"string",
"object"
]
},
"core_version": {
"type": [
"string",
"number",
"object",
"null"
]
},
"alias_of": {
"type": "string"
},
"core_version": { "type": ["string", "number", "object", "null"] },
"alias_of": { "type": "string" },
"cores": {
"description": "Upstream names the core is known by. A dict maps each name to per-variant metadata. Names are strings: an unquoted 81 or 2048 parses as a number and stops matching the buildbot name.",
"oneOf": [
{ "type": "array", "items": { "type": "string", "minLength": 1 }, "uniqueItems": true },
{ "type": "object", "propertyNames": { "minLength": 1 } }
{
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"uniqueItems": true
},
{
"type": "object",
"propertyNames": {
"minLength": 1
}
}
]
},
"systems": { "type": "array", "items": { "type": "string" } },
"mode": { "type": "string", "enum": ["standalone", "libretro", "both"] },
"verification": { "type": "string", "enum": ["existence", "md5", "sha1", "crc32"] },
"exclusion_note": { "type": "string" },
"analysis": { "type": "object" },
"analysis_commit": { "type": "string" },
"analysis_date": { "type": "string" },
"archive_prefix": { "type": "string" },
"author": { "type": "string" },
"based_on": { "type": "string" },
"bios_directory": { "type": "string" },
"bios_identical_to": { "type": "string" },
"bios_mode": { "type": "string" },
"bios_size": { "type": "integer", "minimum": 0 },
"builtin_boot_roms": { "type": "object" },
"core": { "type": "string" },
"core_name": { "type": "string" },
"systems": {
"type": "array",
"items": {
"type": "string"
}
},
"mode": {
"type": "string",
"enum": [
"standalone",
"libretro",
"both"
]
},
"verification": {
"type": "string",
"enum": [
"existence",
"md5",
"sha1",
"crc32"
]
},
"exclusion_note": {
"type": "string"
},
"analysis": {
"type": "object"
},
"analysis_commit": {
"type": "string"
},
"analysis_date": {
"type": "string"
},
"archive_prefix": {
"type": "string"
},
"author": {
"type": "string"
},
"based_on": {
"type": "string"
},
"bios_directory": {
"type": "string"
},
"bios_identical_to": {
"type": "string"
},
"bios_mode": {
"type": "string"
},
"bios_size": {
"type": "integer",
"minimum": 0
},
"builtin_boot_roms": {
"type": "object"
},
"core": {
"type": "string"
},
"core_name": {
"type": "string"
},
"data_directories": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["ref"],
"required": [
"ref"
],
"properties": {
"ref": { "type": "string" },
"destination": { "type": "string" },
"source_ref": { "type": "string" }
"ref": {
"type": "string"
},
"destination": {
"type": "string"
},
"source_ref": {
"type": "string"
}
}
}
},
"dev_flash": { "type": "object" },
"dev_flash2": { "type": "object" },
"dev_flash3": { "type": "object" },
"firmware_titles": { "type": "array", "items": { "type": "object" } },
"game_count": { "type": "integer", "minimum": 0 },
"logo": { "type": "string" },
"machine_directories": { "type": "object" },
"machine_properties": { "type": "object" },
"mame_version": { "type": "string" },
"memory_layout": { "type": "object" },
"mlc_structure": { "type": "array", "items": { "type": "string" } },
"model_kickstart_map": { "type": "object" },
"note": { "type": "string" },
"notes": { "type": ["string", "array", "object"] },
"pack_structure": { "type": "object" },
"platform_details": { "type": "object" },
"resources_directory": { "type": "string" },
"rom_path": { "type": "string" },
"source_ref": { "type": "string" },
"valid_bios_crc32": { "type": "object" },
"whdload_kickstarts": { "type": "array", "items": { "type": "string" } },
"dev_flash": {
"type": "object"
},
"dev_flash2": {
"type": "object"
},
"dev_flash3": {
"type": "object"
},
"firmware_titles": {
"type": "array",
"items": {
"type": "object"
}
},
"game_count": {
"type": "integer",
"minimum": 0
},
"logo": {
"type": "string"
},
"machine_directories": {
"type": "object"
},
"machine_properties": {
"type": "object"
},
"mame_version": {
"type": "string"
},
"memory_layout": {
"type": "object"
},
"mlc_structure": {
"type": "array",
"items": {
"type": "string"
}
},
"model_kickstart_map": {
"type": "object"
},
"note": {
"type": "string"
},
"notes": {
"type": [
"string",
"array",
"object"
]
},
"pack_structure": {
"type": "object"
},
"platform_details": {
"type": "object"
},
"resources_directory": {
"type": "string"
},
"rom_path": {
"type": "string"
},
"source_ref": {
"type": "string"
},
"valid_bios_crc32": {
"type": "object"
},
"whdload_kickstarts": {
"type": "array",
"items": {
"type": "string"
}
},
"files": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["name"],
"required": [
"name"
],
"properties": {
"name": { "type": "string" },
"required": { "type": "boolean" },
"system": { "type": "string" },
"path": { "type": ["string", "null"] },
"destination": { "type": "string" },
"aliases": { "type": "array", "items": { "type": "string" } },
"name": {
"type": "string"
},
"required": {
"type": "boolean"
},
"system": {
"type": "string"
},
"path": {
"type": [
"string",
"null"
]
},
"destination": {
"type": "string"
},
"aliases": {
"type": "array",
"items": {
"type": "string"
}
},
"archive": {
"description": "ROM set the file lives in, for arcade entries.",
"type": "string"
},
"category": {
"type": "string",
"enum": ["bios", "game_data", "bios_zip"]
"enum": [
"bios",
"game_data",
"bios_zip"
]
},
"mode": {
"type": "string",
"enum": [
"standalone",
"libretro",
"both"
]
},
"mode": { "type": "string", "enum": ["standalone", "libretro", "both"] },
"storage": {
"type": "string",
"enum": ["embedded", "external", "user_provided", "large_file", "release"]
"enum": [
"embedded",
"external",
"user_provided",
"large_file",
"release"
]
},
"hle_fallback": {
"type": "boolean"
},
"adler32_byteswap": {
"type": "boolean"
},
"agnostic": {
"type": "boolean"
},
"bundled": {
"type": "boolean"
},
"config_key": {
"type": "string"
},
"embedded": {
"type": "boolean"
},
"fast_boot": {
"type": "string"
},
"has_builtin": {
"type": "boolean"
},
"load_from": {
"type": "string"
},
"priority": {
"type": "integer"
},
"region_check": {
"type": "boolean"
},
"size_note": {
"type": "string"
},
"size_options": {
"type": "array",
"items": {
"type": "integer"
}
},
"standalone_path": {
"type": "string"
},
"type": {
"type": "string"
},
"variant_group": {
"type": "string",
"minLength": 1
},
"size": {
"type": [
"integer",
"array",
"null"
]
},
"min_size": {
"type": "integer"
},
"max_size": {
"type": "integer"
},
"hle_fallback": { "type": "boolean" },
"adler32_byteswap": { "type": "boolean" },
"agnostic": { "type": "boolean" },
"bundled": { "type": "boolean" },
"config_key": { "type": "string" },
"embedded": { "type": "boolean" },
"fast_boot": { "type": "string" },
"has_builtin": { "type": "boolean" },
"load_from": { "type": "string" },
"priority": { "type": "integer" },
"region_check": { "type": "boolean" },
"size_note": { "type": "string" },
"size_options": { "type": "array", "items": { "type": "integer" } },
"standalone_path": { "type": "string" },
"type": { "type": "string" },
"variant_group": { "type": "string", "minLength": 1 },
"size": { "type": ["integer", "array", "null"] },
"min_size": { "type": "integer" },
"max_size": { "type": "integer" },
"md5": {
"oneOf": [
{ "type": "null" },
{ "type": "string", "pattern": "^[a-fA-F0-9]{32}$" },
{
"type": "null"
},
{
"type": "string",
"pattern": "^[a-fA-F0-9]{32}$"
},
{
"type": "array",
"items": { "type": "string", "pattern": "^[a-fA-F0-9]{32}$" }
"items": {
"type": "string",
"pattern": "^[a-fA-F0-9]{32}$"
}
}
]
},
"sha1": {
"oneOf": [
{ "type": "string", "pattern": "^[a-fA-F0-9]{40}$" },
{
"type": "string",
"pattern": "^[a-fA-F0-9]{40}$"
},
{
"type": "array",
"items": { "type": "string", "pattern": "^[a-fA-F0-9]{40}$" }
"items": {
"type": "string",
"pattern": "^[a-fA-F0-9]{40}$"
}
}
]
},
@@ -185,13 +455,28 @@
"type": "string",
"pattern": "^[a-fA-F0-9]{64}(,[a-fA-F0-9]{64})*$"
},
"crc32": { "type": "string", "pattern": "^[a-fA-F0-9]{8}$" },
"known_hash_adler32": { "type": ["string", "integer"] },
"crc32": {
"type": "string",
"pattern": "^[a-fA-F0-9]{8}$"
},
"known_hash_adler32": {
"type": [
"string",
"integer"
]
},
"validation": {
"description": "Checks the code performs. A dict splits core from upstream when they diverge.",
"oneOf": [
{ "type": "array", "items": { "type": "string" } },
{ "type": "object" }
{
"type": "array",
"items": {
"type": "string"
}
},
{
"type": "object"
}
]
},
"region": {
@@ -200,14 +485,40 @@
"items": {
"enum": [
"world",
"north-america", "canada",
"latin-america", "brazil", "mexico", "argentina",
"europe", "uk", "france", "germany", "italy", "spain",
"netherlands", "portugal", "greece", "poland", "russia",
"sweden", "norway", "denmark", "finland",
"asia", "japan", "south-korea", "china", "taiwan",
"hong-kong", "singapore", "india", "asia-ntsc", "asia-pal",
"oceania", "australia", "new-zealand"
"north-america",
"canada",
"latin-america",
"brazil",
"mexico",
"argentina",
"europe",
"uk",
"france",
"germany",
"italy",
"spain",
"netherlands",
"portugal",
"greece",
"poland",
"russia",
"sweden",
"norway",
"denmark",
"finland",
"asia",
"japan",
"south-korea",
"china",
"taiwan",
"hong-kong",
"singapore",
"india",
"asia-ntsc",
"asia-pal",
"oceania",
"australia",
"new-zealand"
]
}
},
@@ -216,26 +527,58 @@
"items": {
"type": "object",
"additionalProperties": false,
"required": ["name"],
"required": [
"name"
],
"properties": {
"name": { "type": "string", "minLength": 1 },
"size": { "type": "integer", "minimum": 0 },
"sha1": { "type": "string", "pattern": "^[a-fA-F0-9]{40}$" },
"crc32": { "type": "string", "pattern": "^[a-fA-F0-9]{8}$" },
"description": { "type": "string" },
"bad_dump": { "type": "boolean" }
"name": {
"type": "string",
"minLength": 1
},
"size": {
"type": "integer",
"minimum": 0
},
"sha1": {
"type": "string",
"pattern": "^[a-fA-F0-9]{40}$"
},
"crc32": {
"type": "string",
"pattern": "^[a-fA-F0-9]{8}$"
},
"description": {
"type": "string"
},
"bad_dump": {
"type": "boolean"
}
}
}
},
"source_ref": {
"description": "A dict splits core from upstream when the two diverge.",
"type": ["string", "array", "object"]
"type": [
"string",
"array",
"object"
]
},
"description": { "type": "string" },
"note": { "type": "string" },
"unsourceable": { "type": "string" }
"description": {
"type": "string"
},
"note": {
"type": "string"
},
"unsourceable": {
"type": "string"
}
}
}
},
"source_branch": {
"type": "string",
"description": "Branch the port was read from, when it is not the default one."
}
}
}
+6 -1
View File
@@ -687,6 +687,9 @@ def select_views(
"""
views: list[RepoView] = []
seen: set[tuple[str, str]] = set()
# A port living on a branch of a fork is absent from the default tip, so
# the branch it was read from is followed when the profile names one.
branch = str(profile.get("source_branch") or "") or None
for field in ("source", "upstream"):
repo = upstream.parse_repo(str(profile.get(field) or ""))
if repo is None or (repo.host, repo.slug) in seen:
@@ -694,7 +697,9 @@ def select_views(
pin, origin = resolve_pin(profile, repo, cache_dir, offline, field)
if not pin:
continue
head = upstream.resolve_head(repo, cache_dir, offline)
head = upstream.resolve_head(
repo, cache_dir, offline, branch if field == "source" else None
)
if not head:
continue
seen.add((repo.host, repo.slug))
+21 -7
View File
@@ -262,16 +262,22 @@ def _project(repo: Repo) -> str:
return urllib.parse.quote(f"{repo.owner}/{repo.name}", safe="")
def _commits_url(repo: Repo, date: str | None) -> str:
def _commits_url(repo: Repo, date: str | None, branch: str | None = None) -> str:
if repo.family == "github":
base = f"{repo.api_base}/repos/{repo.slug}/commits?per_page=1"
if branch:
base += f"&sha={urllib.parse.quote(branch, safe='')}"
elif repo.family == "gitlab":
base = (
f"{repo.api_base}/projects/{_project(repo)}"
f"/repository/commits?per_page=1"
)
if branch:
base += f"&ref_name={urllib.parse.quote(branch, safe='')}"
else:
base = f"{repo.api_base}/repos/{repo.slug}/commits?limit=1"
if branch:
base += f"&sha={urllib.parse.quote(branch, safe='')}"
return f"{base}&until={date}T23:59:59Z" if date else base
@@ -283,16 +289,24 @@ def _first_sha(payload: object) -> str | None:
return None
def resolve_head(repo: Repo, cache_dir: str, offline: bool = False) -> str | None:
"""Sha of the default branch tip."""
return _first_sha(_api(_commits_url(repo, None), cache_dir, offline))
def resolve_head(
repo: Repo, cache_dir: str, offline: bool = False, branch: str | None = None
) -> str | None:
"""Sha of a branch tip, the default branch unless one is named.
A libretro port often lives on a branch of a fork rather than on the
default branch, so the code a profile documents can be absent from the
tip the forge serves by default.
"""
return _first_sha(_api(_commits_url(repo, None, branch), cache_dir, offline))
def resolve_commit_at(
repo: Repo, date: str, cache_dir: str, offline: bool = False
repo: Repo, date: str, cache_dir: str, offline: bool = False,
branch: str | None = None,
) -> str | None:
"""Last default-branch commit on or before a date."""
return _first_sha(_api(_commits_url(repo, date), cache_dir, offline))
"""Last commit on or before a date, on the default branch or a named one."""
return _first_sha(_api(_commits_url(repo, date, branch), cache_dir, offline))
def _tags_url(repo: Repo) -> str:
+16
View File
@@ -1108,6 +1108,22 @@ class TestBuildReport(unittest.TestCase):
report = build_report("mame", profile, self.dir)
self.assertEqual(report.entries[0].status, "ANCHORED")
def test_a_declared_branch_is_followed_for_head(self):
seen = {}
def _head(repo, cache_dir, offline=False, branch=None):
seen[repo.slug] = branch
return "headsha"
profile_sync.upstream.resolve_head = _head
profile = self._profile(["a.c:1"])
profile["upstream"] = "https://github.com/o/up"
profile["source_branch"] = "libretro"
build_report("test", profile, self.dir)
# Only the port carries the branch; the upstream keeps its own tip.
self.assertEqual(seen["o/n"], "libretro")
self.assertIsNone(seen["o/up"])
def test_pin_on_the_declared_version_tag_is_flagged(self):
self.files[("pinsha", "a.c")] = ["x", "hit"]
self.files[("headsha", "a.c")] = ["x", "hit"]