"""Which Archipelago version a run tests against.""" from archipelago_tester.core.state.keys import StateKeys from archipelago_tester.pipeline.build.archipelago import ArchipelagoBuild from archipelago_tester.pipeline.build.docker import Docker class Version: """Resolve the version, and build it if this machine lacks it.""" def __init__(self, config, paths, store): self.paths = paths self.store = store self.build = ArchipelagoBuild(config, paths) def resolve(self): """The version to test, and where that choice came from. A pin is a decision already made, so nothing else revisits it. Failing that the recorded version wins, then whichever image is on this machine - a test runs entirely inside the container, so an image already here is everything it needs. """ pinned = self.build.pinned_version if pinned: return pinned, "pinned in config.json" recorded = self.store.load().get(StateKeys.ARCHIPELAGO_TAG) if recorded: return recorded, "from state.json" local = next(iter(self.build.local_tags()), None) return (local, "newest image on this machine") if local else (None, None) def ensure_built(self, tag): """Build what this version needs, if anything is missing. Building needs the Archipelago source, which is the one job the checkout has - so this is also the only path that clones. """ if tag is None: print("no Archipelago image on this machine - cloning the " "newest release to build one (several minutes) ...") built, _, _, _ = self.build.update_and_build(self.store) return built, "just built" if not Docker.image_exists(self.build.image_tag(tag)): print(f"Archipelago {tag} is not built on this machine - " "cloning and building it (several minutes) ...") self.build.build_version(tag) return tag, None def note_others(self, tag, source): """Say so when an unpinned run is not using the newest image. An unpinned tag can come from an image that happens to be lying around, so a run can quietly answer for a version that is not the current one. A pinned tag was chosen deliberately. """ local = self.build.local_tags() if self.build.pinned_version or not local or tag == local[0]: return source return f"{source}, but {local[0]} is also built here" def update(self): """Resolve the newest release, build it, and record it. Never automatic. A new version invalidates every cached result, so the full re-test it forces is the caller's decision, not a side effect of running. A pinned version stays pinned: the resolution behind this honours the pin, so an update then only ensures that version is built. """ tag, previous, changed, _ = self.build.update_and_build(self.store) if self.build.pinned_version: return tag, "pinned in config.json" if not changed: return tag, "already the newest release" if previous is None: return tag, "newest release, recorded for the first time" return tag, f"updated from {previous}" def prepare(self, update=False): """The version to test against, built if it is missing.""" if update: return self.update() tag, source = self.resolve() tag, built = self.ensure_built(tag) return tag, built or self.note_others(tag, source)