Mercurial > repos > shellac > sam_consensus_v3
comparison env/lib/python3.9/site-packages/planemo/commands/cmd_container_register.py @ 0:4f3585e2f14b draft default tip
"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author | shellac |
---|---|
date | Mon, 22 Mar 2021 18:12:50 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4f3585e2f14b |
---|---|
1 """Module describing the planemo ``container_register`` command.""" | |
2 import os | |
3 import string | |
4 | |
5 import click | |
6 from galaxy.tool_util.deps.container_resolvers.mulled import targets_to_mulled_name | |
7 from galaxy.tool_util.deps.mulled.mulled_build import ( | |
8 base_image_for_targets, | |
9 DEFAULT_BASE_IMAGE, | |
10 ) | |
11 from galaxy.tool_util.deps.mulled.util import ( | |
12 conda_build_target_str, | |
13 v2_image_name, | |
14 ) | |
15 | |
16 from planemo import options | |
17 from planemo.cli import command_function | |
18 from planemo.conda import ( | |
19 best_practice_search, | |
20 build_conda_context, | |
21 collect_conda_target_lists_and_tool_paths | |
22 ) | |
23 from planemo.git import add, branch, commit, push | |
24 from planemo.github_util import ( | |
25 clone_fork_branch, | |
26 DEFAULT_REMOTE_NAME, | |
27 get_repository_object, | |
28 pull_request, | |
29 ) | |
30 from planemo.mulled import conda_to_mulled_targets | |
31 | |
32 REGISTRY_TARGET_NAME = "multi-package-containers" | |
33 REGISTRY_TARGET_PATH = "combinations" | |
34 REGISTRY_REPOSITORY = "BioContainers/multi-package-containers" | |
35 DEFAULT_MESSAGE = "Add container $hash.\n**Hash**: $hash\n\n**Packages**:\n$packages\nBase Image:$base_image\n\n" | |
36 DEFAULT_MESSAGE += "**For** :\n$tools\n\nGenerated with Planemo." | |
37 CONTENTS = "#targets\tbase_image\timage_build\n$targets\t$base_image\t$image_build\n" | |
38 BIOCONTAINERS_PLATFORM = 'linux-64' | |
39 | |
40 | |
41 @click.command('container_register') | |
42 @options.optional_tools_arg(multiple=True) | |
43 @options.recursive_option() | |
44 @options.mulled_namespace_option() | |
45 @options.conda_target_options() | |
46 @click.option( | |
47 "output_directory", | |
48 "--output_directory", | |
49 type=click.Path( | |
50 file_okay=False, | |
51 dir_okay=True, | |
52 resolve_path=True, | |
53 ), | |
54 default=None, | |
55 help=("Container registration directory (defaults to ~/.planemo/multi-package-containers."), | |
56 ) | |
57 @click.option( | |
58 "-m", | |
59 "--message", | |
60 default=DEFAULT_MESSAGE, | |
61 help="Commit and pull request message template for registration interactions." | |
62 ) | |
63 @click.option( | |
64 "--pull_request/--no_pull_request", | |
65 is_flag=True, | |
66 default=True, | |
67 help="Fork and create a pull request against %s for these changes." % REGISTRY_REPOSITORY | |
68 ) | |
69 @click.option( | |
70 "--force_push/--no_force_push", | |
71 is_flag=True, | |
72 default=False, | |
73 help="Force push branch for pull request in case it already exists.", | |
74 ) | |
75 @command_function | |
76 def cli(ctx, paths, **kwds): | |
77 """Register multi-requirement containers as needed. | |
78 | |
79 BioContainers publishes all Bioconda packages automatically as individual | |
80 container images. These however are not enough for tools with multiple | |
81 best-practice requirements. Such requirements should be recorded and published | |
82 so that a container can be created and registered for these tools. | |
83 """ | |
84 registry_target = RegistryTarget(ctx, **kwds) | |
85 conda_context = build_conda_context(ctx, **kwds) | |
86 | |
87 combinations_added = 0 | |
88 conda_targets_list, tool_paths_list = collect_conda_target_lists_and_tool_paths(ctx, paths, recursive=kwds["recursive"]) | |
89 for conda_targets, tool_paths in zip(conda_targets_list, tool_paths_list): | |
90 ctx.vlog("Handling conda_targets [%s]" % conda_targets) | |
91 mulled_targets = conda_to_mulled_targets(conda_targets) | |
92 mulled_targets_str = "- " + "\n- ".join(map(conda_build_target_str, mulled_targets)) | |
93 | |
94 if len(mulled_targets) < 1: | |
95 ctx.log("Skipping registration, no targets discovered.") | |
96 continue | |
97 | |
98 name = v2_image_name(mulled_targets) | |
99 tag = "0" | |
100 name_and_tag = "%s-%s" % (name, tag) | |
101 target_filename = os.path.join(registry_target.output_directory, "%s.tsv" % name_and_tag) | |
102 if os.path.exists(target_filename): | |
103 ctx.log("Target file '%s' already exists, skipping" % target_filename) | |
104 continue | |
105 | |
106 if targets_to_mulled_name(mulled_targets, hash_func='v2', namespace=kwds["mulled_namespace"]): | |
107 ctx.vlog("quay repository already exists, skipping") | |
108 continue | |
109 | |
110 if registry_target.has_pull_request_for(name): | |
111 ctx.vlog("Found matching open pull request for [%s], skipping" % name) | |
112 continue | |
113 | |
114 best_practice_requirements = True | |
115 for conda_target in conda_targets: | |
116 best_hit, exact = best_practice_search(conda_target, conda_context=conda_context, platform=BIOCONTAINERS_PLATFORM) | |
117 if not best_hit: | |
118 ctx.log("Target [%s] is not available in best practice channels - skipping" % conda_target) | |
119 best_practice_requirements = False | |
120 if not exact: | |
121 ctx.log("Target version [%s] for package [%s] is not available in best practice channels - please specify the full version", | |
122 conda_target.version, | |
123 conda_target.package) | |
124 | |
125 if not best_practice_requirements: | |
126 continue | |
127 | |
128 base_image = DEFAULT_BASE_IMAGE | |
129 for conda_target in conda_targets: | |
130 base_image = base_image_for_targets([conda_target], conda_context=conda_context) | |
131 if base_image != DEFAULT_BASE_IMAGE: | |
132 ctx.log("%s requires '%s' as base image" % (conda_target, base_image)) | |
133 break | |
134 | |
135 registry_target.write_targets(ctx, target_filename, mulled_targets, tag, base_image) | |
136 tools_str = "\n".join(map(lambda p: "- " + os.path.basename(p), tool_paths)) | |
137 registry_target.handle_pull_request(ctx, name, target_filename, mulled_targets_str, tools_str, base_image, **kwds) | |
138 combinations_added += 1 | |
139 | |
140 | |
141 class RegistryTarget(object): | |
142 """Abstraction around mulled container registry (both directory and Github repo).""" | |
143 | |
144 def __init__(self, ctx, **kwds): | |
145 output_directory = kwds["output_directory"] | |
146 pr_titles = [] | |
147 target_repository = None | |
148 self.remote_name = DEFAULT_REMOTE_NAME | |
149 do_pull_request = kwds.get("pull_request", True) | |
150 if output_directory is None: | |
151 target_repository = os.path.join(ctx.workspace, REGISTRY_TARGET_NAME) | |
152 output_directory = os.path.join(target_repository, REGISTRY_TARGET_PATH) | |
153 self.remote_name = clone_fork_branch( | |
154 ctx, | |
155 "https://github.com/%s" % REGISTRY_REPOSITORY, | |
156 target_repository, | |
157 fork=do_pull_request, | |
158 ) or self.remote_name | |
159 pr_titles = [pr.title for pr in open_prs(ctx)] | |
160 | |
161 self.do_pull_request = do_pull_request | |
162 self.pr_titles = pr_titles | |
163 self.output_directory = output_directory | |
164 self.target_repository = target_repository | |
165 | |
166 def has_pull_request_for(self, name): | |
167 has_pr = False | |
168 if self.do_pull_request: | |
169 if any([name in t for t in self.pr_titles]): | |
170 has_pr = True | |
171 | |
172 return has_pr | |
173 | |
174 def handle_pull_request(self, ctx, name, target_filename, packages_str, tools_str, base_image, **kwds): | |
175 if self.do_pull_request: | |
176 message = kwds["message"] | |
177 message = string.Template(message).safe_substitute({ | |
178 "hash": name, | |
179 "packages": packages_str, | |
180 "tools": tools_str, | |
181 "base_image": base_image, | |
182 }) | |
183 branch_name = name.replace(":", "-") | |
184 branch(ctx, self.target_repository, branch_name, from_branch="master") | |
185 add(ctx, self.target_repository, target_filename) | |
186 commit(ctx, self.target_repository, message=message) | |
187 force_push = kwds.get("force_push", False) | |
188 push(ctx, repo_path=self.target_repository, to=self.remote_name, branch=branch_name, force=force_push) | |
189 pull_request(ctx, self.target_repository, message=message, repo=REGISTRY_REPOSITORY) | |
190 | |
191 def write_targets(self, ctx, target_filename, mulled_targets, tag, base_image): | |
192 with open(target_filename, "w") as f: | |
193 targets = to_target_str(mulled_targets) | |
194 f.write(string.Template(CONTENTS).safe_substitute( | |
195 targets=targets, | |
196 base_image=base_image, | |
197 image_build=tag | |
198 ) | |
199 ) | |
200 ctx.log("Wrote requirements [%s] to file [%s]" % (targets, target_filename)) | |
201 | |
202 | |
203 def to_target_str(targets): | |
204 target_strings = [] | |
205 for target in targets: | |
206 if target.version: | |
207 target_str = "%s=%s" % (target.package_name, target.version) | |
208 else: | |
209 target_str = target.package_name | |
210 target_strings.append(target_str) | |
211 return ",".join(target_strings) | |
212 | |
213 | |
214 def open_prs(ctx): | |
215 repo = get_repository_object(ctx, REGISTRY_REPOSITORY) | |
216 prs = [pr for pr in repo.get_pulls()] | |
217 return prs |