88 lines
3.7 KiB
Python
88 lines
3.7 KiB
Python
"""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)
|