feat: read per-mode sources and pins

This commit is contained in:
Abdessamad Derraz committed 2026-08-10 18:18:09 +02:00
1 parent 4a69b96922
commit 49f50f51b5
4 files changed
+74 -20

No files matched your search

+8 -6
View File
@@ -3,10 +3,12 @@ type: standalone + libretro
core_classification: community_fork core_classification: community_fork
source: source:
standalone: "https://github.com/StrikerX3/Ymir" standalone: "https://github.com/StrikerX3/Ymir"
libretro: "https://github.com/warmenhoven/Ymir/tree/libretro" libretro: "https://github.com/warmenhoven/Emir/tree/libretro"
upstream: "https://github.com/StrikerX3/Ymir" upstream: "https://github.com/StrikerX3/Ymir"
profiled_date: "2026-03-26" profiled_date: "2026-03-26"
source_commit: "5e9310410baa47cb7da5b068f399c9e4a34eedbc" source_commit:
standalone: "5e9310410baa47cb7da5b068f399c9e4a34eedbc"
libretro: "5d99cbbf813206736f38b3574ecca7a8aedc07e5"
core_version: core_version:
standalone: "v0.2.1" standalone: "v0.2.1"
libretro: "PR#746" libretro: "PR#746"
@@ -54,7 +56,7 @@ files:
description: "Saturn IPL ROM (BIOS). Any region accepted." description: "Saturn IPL ROM (BIOS). Any region accepted."
source_ref: source_ref:
standalone: "apps/ymir-sdl3/src/util/rom_loader.cpp:9-22, apps/ymir-sdl3/src/app/rom_manager.cpp:14-56, libs/ymir-core/include/ymir/sys/memory_defs.hpp:9" standalone: "apps/ymir-sdl3/src/util/rom_loader.cpp:9-22, apps/ymir-sdl3/src/app/rom_manager.cpp:14-56, libs/ymir-core/include/ymir/sys/memory_defs.hpp:9"
libretro: "libretro.cpp:260-263,534-558" libretro: "apps/ymir-libretro/src/libretro.cpp:260-263,534-558"
- name: "cdb/cdb_rom.bin" - name: "cdb/cdb_rom.bin"
system: sega-saturn system: sega-saturn
@@ -65,7 +67,7 @@ files:
description: "SH1 CD Block ROM for low-level CD drive emulation. Scans cdb/ subdirectory for any matching file." description: "SH1 CD Block ROM for low-level CD drive emulation. Scans cdb/ subdirectory for any matching file."
source_ref: source_ref:
standalone: "apps/ymir-sdl3/src/util/rom_loader.cpp:25-38, apps/ymir-sdl3/src/app/rom_manager.cpp:58-93, libs/ymir-core/include/ymir/hw/sh1/sh1_defs.hpp:7" standalone: "apps/ymir-sdl3/src/util/rom_loader.cpp:25-38, apps/ymir-sdl3/src/app/rom_manager.cpp:58-93, libs/ymir-core/include/ymir/hw/sh1/sh1_defs.hpp:7"
libretro: "libretro.cpp:567-591" libretro: "apps/ymir-libretro/src/libretro.cpp:565-591"
- name: "mpr-18811-mx.ic1" - name: "mpr-18811-mx.ic1"
system: sega-saturn system: sega-saturn
@@ -76,7 +78,7 @@ files:
description: "ROM cartridge for The King of Fighters '95." description: "ROM cartridge for The King of Fighters '95."
source_ref: source_ref:
standalone: "apps/ymir-sdl3/src/app/rom_manager.cpp:96-131, libs/ymir-core/include/ymir/db/rom_cart_db.hpp:19-22" standalone: "apps/ymir-sdl3/src/app/rom_manager.cpp:96-131, libs/ymir-core/include/ymir/db/rom_cart_db.hpp:19-22"
libretro: "libretro.cpp:619-620,597-616" libretro: "apps/ymir-libretro/src/libretro.cpp:621, 598-618"
- name: "mpr-19367-mx.ic1" - name: "mpr-19367-mx.ic1"
system: sega-saturn system: sega-saturn
@@ -87,4 +89,4 @@ files:
description: "ROM cartridge for Ultraman: Hikari no Kyojin Densetsu." description: "ROM cartridge for Ultraman: Hikari no Kyojin Densetsu."
source_ref: source_ref:
standalone: "apps/ymir-sdl3/src/app/rom_manager.cpp:96-131, libs/ymir-core/include/ymir/db/rom_cart_db.hpp:25-28" standalone: "apps/ymir-sdl3/src/app/rom_manager.cpp:96-131, libs/ymir-core/include/ymir/db/rom_cart_db.hpp:25-28"
libretro: "libretro.cpp:619,621,597-616" libretro: "apps/ymir-libretro/src/libretro.cpp:622, 598-618"
+18 -2
View File
@@ -84,10 +84,26 @@
] ]
}, },
"source_commit": { "source_commit": {
"type": "string" "oneOf": [
{
"type": "string"
},
{
"type": "object"
}
],
"description": "Revision the profile was read at; an object keys it by build mode when the builds live in different repositories."
}, },
"upstream_commit": { "upstream_commit": {
"type": "string" "oneOf": [
{
"type": "string"
},
{
"type": "object"
}
],
"description": "Revision the profile was read at; an object keys it by build mode when the builds live in different repositories."
}, },
"profiled_date": { "profiled_date": {
"description": "Quote it: an unquoted YAML date parses to a date object.", "description": "Quote it: an unquoted YAML date parses to a date object.",
+46 -10
View File
@@ -659,20 +659,28 @@ def select_repo(profile: dict) -> upstream.Repo | None:
def resolve_pin( def resolve_pin(
profile: dict, repo, cache_dir: str, offline: bool, field: str = "source" profile: dict, repo, cache_dir: str, offline: bool, field: str = "source",
mode: str = "", branch: str | None = None,
) -> tuple[str | None, str | None]: ) -> tuple[str | None, str | None]:
"""Commit the profile was written at, and how it was obtained. """Commit the profile was written at, and how it was obtained.
`field` selects which declared pin applies: `source_commit` for the port `field` selects which declared pin applies: `source_commit` for the port
the profile was read from, `upstream_commit` for the original project. the profile was read from, `upstream_commit` for the original project.
A profile whose builds live in different repositories keys its pin by
build mode, the way `ymir` keys `source` and `core_version`.
""" """
pinned = profile.get(f"{field}_commit") pinned = profile.get(f"{field}_commit")
if isinstance(pinned, dict):
pinned = pinned.get(mode)
origin = f"{field}_commit[{mode}]"
else:
origin = f"{field}_commit"
if pinned: if pinned:
return str(pinned), f"{field}_commit" return str(pinned), origin
date = str(profile.get("profiled_date") or "") date = str(profile.get("profiled_date") or "")
if not date: if not date:
return None, None return None, None
sha = upstream.resolve_commit_at(repo, date, cache_dir, offline) sha = upstream.resolve_commit_at(repo, date, cache_dir, offline, branch)
return sha, (f"profiled_date {date}" if sha else None) return sha, (f"profiled_date {date}" if sha else None)
@@ -689,17 +697,18 @@ def select_views(
seen: set[tuple[str, str]] = set() seen: set[tuple[str, str]] = set()
# A port living on a branch of a fork is absent from the default tip, so # 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. # the branch it was read from is followed when the profile names one.
branch = str(profile.get("source_branch") or "") or None declared = str(profile.get("source_branch") or "") or None
for field in ("source", "upstream"): for field, mode, url in declared_repositories(profile):
repo = upstream.parse_repo(str(profile.get(field) or "")) repo = upstream.parse_repo(url)
if repo is None or (repo.host, repo.slug) in seen: if repo is None or (repo.host, repo.slug) in seen:
continue continue
pin, origin = resolve_pin(profile, repo, cache_dir, offline, field) branch = branch_in_url(url) or (declared if field == "source" else None)
pin, origin = resolve_pin(
profile, repo, cache_dir, offline, field, mode, branch
)
if not pin: if not pin:
continue continue
head = upstream.resolve_head( head = upstream.resolve_head(repo, cache_dir, offline, branch)
repo, cache_dir, offline, branch if field == "source" else None
)
if not head: if not head:
continue continue
seen.add((repo.host, repo.slug)) seen.add((repo.host, repo.slug))
@@ -707,6 +716,33 @@ def select_views(
return views return views
def declared_repositories(profile: dict) -> list[tuple[str, str, str]]:
"""Every repository URL the profile declares, source before upstream.
`source` and `upstream` are usually plain strings. `ymir` keys them by
build mode instead, because its standalone and libretro builds live in
different repositories.
"""
urls: list[tuple[str, str, str]] = []
for field in ("source", "upstream"):
value = profile.get(field)
if isinstance(value, dict):
urls.extend((field, str(k), str(v)) for k, v in value.items() if v)
elif value:
urls.append((field, "", str(value)))
return urls
def branch_in_url(url: str) -> str | None:
"""Branch named by a forge URL pointing inside a repository.
A profile can cite a fork as `.../Ymir/tree/libretro`, which names both
the repository and the branch the port lives on.
"""
_, sep, tail = url.partition("/tree/")
return tail.strip("/") or None if sep else None
SELF_CHECK_CONTEXT = 2 SELF_CHECK_CONTEXT = 2
+2 -2
View File
@@ -1242,7 +1242,7 @@ class TestRunResilience(unittest.TestCase):
self._orig = profile_sync.upstream.resolve_commit_at self._orig = profile_sync.upstream.resolve_commit_at
self.seen: list[str] = [] self.seen: list[str] = []
def flaky(repo, date, cache_dir, offline=False): def flaky(repo, date, cache_dir, offline=False, branch=None):
self.seen.append(repo.slug) self.seen.append(repo.slug)
if repo.slug == "o/bad": if repo.slug == "o/bad":
raise profile_sync.upstream.UpstreamError("HTTP 401") raise profile_sync.upstream.UpstreamError("HTTP 401")
@@ -1265,7 +1265,7 @@ class TestRunResilience(unittest.TestCase):
build_report("bad", profile, self.tmp.name) build_report("bad", profile, self.tmp.name)
def test_rate_limit_still_propagates(self): def test_rate_limit_still_propagates(self):
def limited(repo, date, cache_dir, offline=False): def limited(repo, date, cache_dir, offline=False, branch=None):
raise profile_sync.upstream.RateLimitError("HTTP 403") raise profile_sync.upstream.RateLimitError("HTTP 403")
profile_sync.upstream.resolve_commit_at = limited profile_sync.upstream.resolve_commit_at = limited