Skip to content

Commit 08efa37

Browse files
committed
update to the fix script from matthias
1 parent f6f77eb commit 08efa37

File tree

1 file changed

+122
-40
lines changed

1 file changed

+122
-40
lines changed

scripts/fix_outdated.py

Lines changed: 122 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -15,86 +15,154 @@
1515
# The script queries the TS to get_ordered_installable_revisions
1616
# and clones (to /tmp/) the mercurial repos to get all revisions
1717
# (the later is only done for tools with revisions that are not
18-
# installable).
18+
# installable)
19+
#
20+
# For each revision cur that has been replaced by nxt
21+
# - check that the tool versions of the revisons are really the same
22+
# - if cur and nxt are in the lock file cur is removed
23+
# - if a Galaxy URL is given it is checked that cur is not installed
24+
# - if only cur in in the list then cur is removed and nxt is added
1925

2026
import argparse
27+
import logging
2128
import subprocess
2229
import os.path
2330
import yaml
24-
25-
from bioblend import toolshed
31+
from typing import (
32+
Dict,
33+
List,
34+
Optional,
35+
Set,
36+
Tuple,
37+
)
38+
39+
import bioblend
40+
from bioblend import galaxy, toolshed
2641
from galaxy.tool_util.loader_directory import load_tool_sources_from_path
2742

2843

29-
def clone(toolshed_url, name, owner, repo_path):
44+
logger = logging.getLogger()
45+
46+
47+
def clone(toolshed_url: str, name: str, owner: str, repo_path: str) -> None:
3048
if not os.path.exists(repo_path):
31-
print(f"Cloning {toolshed_url} {owner} {name} {repo_path}")
49+
logger.info(f"Cloning {toolshed_url} {owner} {name} {repo_path}")
3250
cmd = [
3351
"hg",
3452
"clone",
3553
f"{toolshed_url}/repos/{owner}/{name}",
3654
repo_path,
3755
]
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}"
56+
proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
57+
else:
58+
cmd = ["hg", "pull", "-u"]
59+
proc = subprocess.run(cmd, cwd = repo_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
60+
assert proc.returncode == 0, f"failed {' '.join(cmd)} in {repo_path}"
61+
62+
def get_all_revisions(toolshed_url: str, name: str, owner: str) -> List[str]:
63+
repo_path = f"/tmp/repos/{os.path.basename(toolshed_url)}-{owner}-{name}"
4364
clone(toolshed_url, name, owner, repo_path)
65+
cmd = ["hg", "update", "tip"]
66+
proc = subprocess.run(cmd, cwd=repo_path, capture_output=True, text=True)
67+
assert proc.returncode == 0, f"failed {' '.join(cmd)} in {repo_path}"
4468
cmd = ["hg", "log", "--template", "{node|short}\n"]
69+
assert proc.returncode == 0, f"failed {' '.join(cmd)} in {repo_path}"
4570
result = subprocess.run(cmd, cwd=repo_path, capture_output=True, text=True)
4671
return list(reversed(result.stdout.splitlines()))
4772

4873

49-
def fix_uninstallable(lockfile_name, toolshed_url):
74+
def get_all_versions(
75+
toolshed_url: str, name: str, owner: str, revisions: List[str]
76+
) -> Dict[str, Set[Tuple[str, str]]]:
77+
repo_path = f"/tmp/repos/{os.path.basename(toolshed_url)}-{owner}-{name}"
78+
clone(toolshed_url, name, owner, repo_path)
79+
80+
versions: Dict[str, Set[Tuple[str, str]]] = {}
81+
for r in revisions:
82+
cmd = ["hg", "update", r]
83+
subprocess.run(cmd, cwd=repo_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
84+
85+
versions[r] = set()
86+
for _, tool in load_tool_sources_from_path(repo_path):
87+
versions[r].add((tool.parse_id(), tool.parse_version()))
88+
assert len(versions[r]) > 0
89+
90+
return versions
91+
92+
93+
def fix_uninstallable(lockfile_name: str, toolshed_url: str, galaxy_url: Optional[str] = None) -> None:
5094
ts = toolshed.ToolShedInstance(url=toolshed_url)
95+
installed_tools = {}
96+
if galaxy_url:
97+
gi = galaxy.GalaxyInstance(url=galaxy_url, key=None)
98+
for t in gi.toolshed.get_repositories():
99+
if (t['name'], t['owner']) not in installed_tools:
100+
installed_tools[(t['name'], t['owner'])] = set()
101+
# TODO? could also check for 'status': 'Installed'
102+
if t['deleted'] or t['uninstalled']:
103+
continue
104+
installed_tools[(t['name'], t['owner'])].add(t['changeset_revision'])
51105

52106
with open(lockfile_name) as f:
53107
lockfile = yaml.safe_load(f)
54-
tools = lockfile["tools"]
108+
locked_tools = lockfile["tools"]
55109

56-
for i, tool in enumerate(tools):
57-
name = tool["name"]
58-
owner = tool["owner"]
59-
print(f"Checking {toolshed_url} {owner} {name} ")
110+
for i, locked_tool in enumerate(locked_tools):
111+
name = locked_tool["name"]
112+
owner = locked_tool["owner"]
113+
logger.info(f"Checking {toolshed_url} {owner} {name} ")
60114
# 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)):
115+
try:
116+
ordered_installable_revisions = (
117+
ts.repositories.get_ordered_installable_revisions(name, owner)
118+
)
119+
except bioblend.ConnectionError:
120+
logger.warning(f"Could not determine intstallable revisions for {name} {owner}")
121+
continue
122+
123+
if len(set(locked_tool["revisions"]) - set(ordered_installable_revisions)):
66124
all_revisions = get_all_revisions(toolshed_url, name, owner)
125+
try:
126+
all_versions = get_all_versions(toolshed_url, name, owner, all_revisions)
127+
except:
128+
logger.warning(f"Could not determine versions for {name} {owner}")
129+
continue
67130

68131
to_remove = []
69132
to_append = []
70-
for cur in tool["revisions"]:
133+
for cur in locked_tool["revisions"]:
71134
if cur in ordered_installable_revisions:
72135
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
136+
assert cur in all_revisions, f"{cur} is not a valid revision of {name} {owner}"
77137
start = all_revisions.index(cur)
78138
nxt = None
79139
for i in range(start, len(all_revisions)):
80140
if all_revisions[i] in ordered_installable_revisions:
81141
nxt = all_revisions[i]
82142
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}")
91143

92-
for r in to_remove:
93-
tool["revisions"].remove(r)
94-
tool["revisions"].extend(to_append)
144+
if not nxt:
145+
logger.warning(f"Could not determine the next revision for {cur} {name} {owner}")
146+
continue
147+
148+
if all_versions[cur] != all_versions[nxt]:
149+
logger.warning(f"{name},{owner} {cur} {nxt} have unequal versions")
150+
continue
151+
152+
if nxt not in locked_tool["revisions"]:
153+
logger.info(f"Adding {nxt} which was absent so far {name} {owner}")
154+
to_append.append(nxt)
155+
elif galaxy_url:
156+
assert (name, owner) in installed_tools
157+
if cur in installed_tools[(name, owner)]:
158+
logger.warning(f"{name},{owner} {cur} still installed on {galaxy_url}")
159+
continue
160+
logger.info(f"remove {cur} in favor of {nxt} {name} {owner}")
161+
to_remove.append(cur)
95162

96-
# maintaing unified sorting standard
97-
tool["revisions"] = sorted(list(set(map(str, tool['revisions']))))
163+
for r in to_remove:
164+
locked_tool["revisions"].remove(r)
165+
locked_tool["revisions"].extend(to_append)
98166

99167
with open(lockfile_name, "w") as handle:
100168
yaml.dump(lockfile, handle, default_flow_style=False)
@@ -110,5 +178,19 @@ def fix_uninstallable(lockfile_name, toolshed_url):
110178
default="https://toolshed.g2.bx.psu.edu",
111179
help="Toolshed to test against",
112180
)
181+
parser.add_argument('--galaxy_url', default=None, required=False, help="Galaxy instance to check")
113182
args = parser.parse_args()
114-
fix_uninstallable(args.lockfile.name, args.toolshed)
183+
184+
185+
logger.setLevel(logging.DEBUG)
186+
logging.getLogger('urllib3').setLevel(logging.WARNING)
187+
logging.getLogger('bioblend').setLevel(logging.WARNING)
188+
logging.getLogger('PIL.Image').setLevel(logging.WARNING)
189+
# otherwise tool loading errors (of there are other xml files that can't be parsed?) are reported
190+
logging.getLogger('galaxy.tool_util.loader_directory').disabled = True
191+
handler = logging.StreamHandler()
192+
logger.addHandler(handler)
193+
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
194+
handler.setFormatter(formatter)
195+
196+
fix_uninstallable(args.lockfile.name, args.toolshed, args.galaxy_url)

0 commit comments

Comments
 (0)