"""A GitHub releases page, the sheet's usual link.""" from archipelago_tester.pipeline.download.handlers.base import LinkHandler from archipelago_tester.pipeline.download.release_downloader import ( ReleaseDownloader, ) from archipelago_tester.pipeline.download.release_identity import ( ReleaseIdentity, ) from archipelago_tester.pipeline.download.release_picker import ReleasePicker from archipelago_tester.pipeline.download.walk_back import WalkBack class GitHubRelease(LinkHandler): """Resolve which release the link means, then fetch its apworld.""" def __init__(self, shapes, previous=None): super().__init__(shapes) self.previous = previous def parse(self, link): return self.shapes.releases(link) def resolve(self, parsed, context): """The release this link resolves to, or None. A hand-pinned tag prefix wins, then a link naming one exact tag, then the search term. Runs every pass: it is the one call that can report a new release, which is the only thing able to change a previously failed answer. """ owner, repository, term, tag = parsed picker = ReleasePicker(context, owner, repository, term) overrides = context.config.get("release_overrides") or {} prefix = overrides.get(context.game.name) if prefix: return picker.by_tag_prefix(prefix) if tag: return picker.by_tag(tag) return picker.pick() def fetch(self, parsed, context, release): """This release's apworld, walking back if it ships none. Only that one cause is walked back from. "Ships another game's apworld" must not be, since an older release of a multi-world repo is precisely the wrong answer there. """ owner, repository = parsed[0], parsed[1] downloader = ReleaseDownloader( context=context, release=release, # What this row's date already says, so a re-upload of # unchanged bytes keeps it instead of moving it forward. previous_published_at=(self.previous or {}).get("published_at"), ) downloaded, reason, info = downloader.run() if (downloaded is not None or context.identifier is None or downloader.cause != ReleaseDownloader.NO_APWORLD): return downloaded, reason, info fallback, reason = WalkBack( context=context, owner=owner, repository=repository, release=release, ).run(reason) return fallback if fallback is not None else (None, reason, info) def download(self, parsed, context): release = self.resolve(parsed, context) if release is None: return None, "no release matched the search term in the link", None identity = ReleaseIdentity(release) repeat = identity.repeated(self.previous) if repeat is not None: return None, repeat, identity.failure( {"published_at": None}, repeat) downloaded, reason, info = self.fetch(parsed, context, release) if downloaded is None: return None, reason, identity.failure(info, reason) return downloaded, reason, info