Warning
This document is for an in-development version of Galaxy. You can alternatively view this page in the latest release if it exists or view the top of the latest release's documentation.
Source code for galaxy.tool_util.deps.mulled.mulled_build_channel
#!/usr/bin/env python
"""Build a mulled images for all recent conda recipe updates that don't have existing images.
Examples:
Build mulled images for recent bioconda changes with:
mulled-build-channel build
Build, test, and publish images with the follow command:
mulled-build-channel all
See recent changes that would be built with:
mulled-build-channel list
"""
from __future__ import annotations
import os
import subprocess
import time
from typing import Protocol
from galaxy.util import requests
from ._cli import arg_parser
from .mulled_build import (
add_build_arguments,
args_to_mull_targets_kwds,
build_target,
conda_versions,
docker_platform_tag_suffix,
docker_platform_to_conda_subdir,
get_affected_packages,
mull_targets,
)
from .util import (
quay_versions,
version_sorted,
)
class _FetchRepoDataArgs(Protocol):
channel: str
repo_data: str
class _ChannelPackagesArgs(Protocol):
recipes_dir: str
diff_hours: str
class _RunChannelArgs(_FetchRepoDataArgs, _ChannelPackagesArgs, Protocol):
force_rebuild: bool
namespace: str
def _fetch_repo_data(args: _FetchRepoDataArgs) -> str:
repo_data = args.repo_data
channel = args.channel
if not os.path.exists(repo_data):
platform_tag = docker_platform_to_conda_subdir(getattr(args, "target_platform", None))
subprocess.check_call(
[
"wget",
"--quiet",
f"https://conda.anaconda.org/{channel}/{platform_tag}/repodata.json.bz2",
"-O",
f"{repo_data}.bz2",
]
)
subprocess.check_call(["bzip2", "-d", f"{repo_data}.bz2"])
return repo_data
def _unpublished_versions(quay: list[str], conda: list[str], platform_suffix: str | None = None) -> list[str]:
"""Calculate the versions that are in conda but not on quay.io."""
squay = set(quay) if quay else set()
if platform_suffix is None:
return [v for v in conda if v not in squay]
suffix = f"-{platform_suffix}"
# Unsuffixed legacy tags represent amd64 builds and must not suppress
# publication of the requested non-amd64 variant.
return [v for v in conda if f"{v}{suffix}" not in squay]
def run_channel(args: _RunChannelArgs, build_last_n_versions: int = 1) -> None:
"""Build list of involucro commands (as shell snippet) to run."""
session = requests.Session()
for pkg_name, pkg_tests in get_affected_packages(args):
repo_data = _fetch_repo_data(args)
c = conda_versions(pkg_name, repo_data)
# only package the most recent N versions
c = version_sorted(c)[:build_last_n_versions]
if not args.force_rebuild:
time.sleep(1)
q = quay_versions(args.namespace, pkg_name, session)
versions = _unpublished_versions(q, c, docker_platform_tag_suffix(getattr(args, "target_platform", None)))
else:
versions = c
for tag in versions:
target = build_target(pkg_name, tag=tag)
targets = [target]
mull_targets(targets, test=pkg_tests, **args_to_mull_targets_kwds(args))
def get_pkg_names(args: _ChannelPackagesArgs) -> None:
"""Print package names that would be affected."""
print("\n".join(pkg_name for pkg_name, pkg_tests in get_affected_packages(args)))
def add_channel_arguments(parser):
"""Add arguments only used if running mulled over a whole conda channel."""
parser.add_argument(
"--channel",
dest="channel",
default="bioconda",
help="Conda channel to fetch repodata from. Default: bioconda",
)
parser.add_argument(
"--repo-data",
dest="repo_data",
required=True,
help="Published repository data. Will be auto-downloaded from --channel if file does not exist.",
)
parser.add_argument(
"--diff-hours",
dest="diff_hours",
default="25",
help="If finding all recently changed recipes, use this number of hours.",
)
parser.add_argument("--recipes-dir", dest="recipes_dir", default="./bioconda-recipes")
parser.add_argument(
"--force-rebuild", dest="force_rebuild", action="store_true", help="Rebuild package even if already published."
)
[docs]
def main(argv=None):
"""Main entry-point for the CLI tool."""
parser = arg_parser(argv, globals())
add_channel_arguments(parser)
add_build_arguments(parser)
parser.add_argument("command", metavar="COMMAND", help="Command (list, build-and-test, build, all)")
parser.add_argument(
"--targets", dest="targets", default=None, help="Build a single container with specific package(s)."
)
parser.add_argument(
"--repository-name",
dest="repository_name",
default=None,
help="Name of a single container (leave blank to auto-generate based on packages).",
)
args = parser.parse_args()
if args.command == "list":
get_pkg_names(args)
else:
run_channel(args)
__all__ = ("main",)
if __name__ == "__main__":
main()