Skip to content

Commit a8db7ee

Browse files
committed
add fix_outdated script from Matthias Bernt
1 parent 753f3e3 commit a8db7ee

File tree

2 files changed

+118
-0
lines changed

2 files changed

+118
-0
lines changed

requirements.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@ bioblend
22
ephemeris
33
pykwalify
44
PyYAML
5+
6+
# for fix_uninstallable.py
7+
mercurial
8+
galaxy-tool-util

scripts/fix_outdated.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Check all revisions in the lockfile if they are installable.
2+
# Remove if not and add the next installable revision.
3+
#
4+
# Only updates the lock file and does not install
5+
# or uninstall any tools from a Galaxy instance.
6+
#
7+
# Backgroud: for each tool version there can be only one revision installed
8+
# (multiple revisions with the same version happen e.g. if the version
9+
# is not bumped but some files are updated)
10+
#
11+
# Revisions that became not-installable are treated as a safe update
12+
# because the author claims the tool did not change its behavior from
13+
# the reproducibility perspective.
14+
#
15+
# The script queries the TS to get_ordered_installable_revisions
16+
# and clones (to /tmp/) the mercurial repos to get all revisions
17+
# (the later is only done for tools with revisions that are not
18+
# installable).
19+
20+
import argparse
21+
import subprocess
22+
import os.path
23+
import yaml
24+
25+
from bioblend import toolshed
26+
from galaxy.tool_util.loader_directory import load_tool_sources_from_path
27+
28+
29+
def clone(toolshed_url, name, owner, repo_path):
30+
if not os.path.exists(repo_path):
31+
print(f"Cloning {toolshed_url} {owner} {name} {repo_path}")
32+
cmd = [
33+
"hg",
34+
"clone",
35+
f"{toolshed_url}/repos/{owner}/{name}",
36+
repo_path,
37+
]
38+
subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
39+
40+
41+
def get_all_revisions(toolshed_url, name, owner):
42+
repo_path = f"/tmp/toolshed-{owner}-{name}"
43+
clone(toolshed_url, name, owner, repo_path)
44+
cmd = ["hg", "log", "--template", "{node|short}\n"]
45+
result = subprocess.run(cmd, cwd=repo_path, capture_output=True, text=True)
46+
return list(reversed(result.stdout.splitlines()))
47+
48+
49+
def fix_uninstallable(lockfile_name, toolshed_url):
50+
ts = toolshed.ToolShedInstance(url=toolshed_url)
51+
52+
with open(lockfile_name) as f:
53+
lockfile = yaml.safe_load(f)
54+
tools = lockfile["tools"]
55+
56+
for i, tool in enumerate(tools):
57+
name = tool["name"]
58+
owner = tool["owner"]
59+
print(f"Checking {toolshed_url} {owner} {name} ")
60+
# get ordered_installable_revisions from oldest to newest
61+
ordered_installable_revisions = (
62+
ts.repositories.get_ordered_installable_revisions(name, owner)
63+
)
64+
65+
if len(set(tool["revisions"]) - set(ordered_installable_revisions)):
66+
all_revisions = get_all_revisions(toolshed_url, name, owner)
67+
68+
to_remove = []
69+
to_append = []
70+
for cur in tool["revisions"]:
71+
if cur in ordered_installable_revisions:
72+
continue
73+
if cur not in all_revisions:
74+
print(f"Removing {cur} -- it is not a valid revision of {name} {owner}")
75+
to_remove.append(cur)
76+
continue
77+
start = all_revisions.index(cur)
78+
nxt = None
79+
for i in range(start, len(all_revisions)):
80+
if all_revisions[i] in ordered_installable_revisions:
81+
nxt = all_revisions[i]
82+
break
83+
if nxt:
84+
print(f"Removing {cur} in favor of {nxt} {name} {owner}")
85+
to_remove.append(cur)
86+
if nxt not in tool["revisions"]:
87+
print(f"Adding {nxt} which was absent so far {name} {owner}")
88+
to_append.append(nxt)
89+
else:
90+
print(f"Could not determine the next revision for {cur} {name} {owner}")
91+
92+
for r in to_remove:
93+
tool["revisions"].remove(r)
94+
tool["revisions"].extend(to_append)
95+
96+
# maintaing unified sorting standard
97+
tool["revisions"] = sorted(list(set(map(str, tool['revisions']))))
98+
99+
with open(lockfile_name, "w") as handle:
100+
yaml.dump(lockfile, handle, default_flow_style=False)
101+
102+
103+
if __name__ == "__main__":
104+
parser = argparse.ArgumentParser()
105+
parser.add_argument(
106+
"lockfile", type=argparse.FileType("r"), help="Tool.yaml.lock file"
107+
)
108+
parser.add_argument(
109+
"--toolshed",
110+
default="https://toolshed.g2.bx.psu.edu",
111+
help="Toolshed to test against",
112+
)
113+
args = parser.parse_args()
114+
fix_uninstallable(args.lockfile.name, args.toolshed)

0 commit comments

Comments
 (0)