Source code for snakedeploy.client

#!/usr/bin/env python

__author__ = "Vanessa Sochat"
__copyright__ = "Copyright 2020-2021, Vanessa Sochat"
__license__ = "MPL 2.0"

import argparse
import sys
from pathlib import Path
from snakedeploy.conda import pin_conda_envs, update_conda_envs

from snakedeploy.logger import setup_logger
from snakedeploy.deploy import deploy
from snakedeploy.collect_files import collect_files
import snakedeploy
from snakedeploy.exceptions import UserError
from snakedeploy.scaffold_plugins import scaffold_plugin
from .snakemake_wrappers import update_snakemake_wrappers


[docs] def get_parser(): parser = argparse.ArgumentParser( description="Snakedeploy: deployment and maintenance related toolbox for Snakemake.", formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) parser.add_argument( "--version", dest="version", help="print the version and exit.", default=False, action="store_true", ) parser.add_argument( "--quiet", dest="quiet", help="Minimize output.", default=False, action="store_true", ) # Logging logging_group = parser.add_argument_group("LOGGING") logging_group.add_argument( "--verbose", dest="verbose", help="Verbose output.", default=False, action="store_true", ) logging_group.add_argument( "--log-disable-color", dest="disable_color", default=False, help="Disable color for snakedeploy logging.", action="store_true", ) subparsers = parser.add_subparsers(title="Subcommands", dest="subcommand") deploy_workflow_parser = subparsers.add_parser( "deploy-workflow", description="Deploy a workflow from a git repository.", help="Deploy a workflow from a git repository.", ) deploy_group = deploy_workflow_parser.add_argument_group("DEPLOY") deploy_workflow_parser.add_argument( "repo", help="Workflow repository to use.", ) deploy_workflow_parser.add_argument( "dest", help="Path to create the deploying workflow in.", ) deploy_workflow_parser.add_argument( "--tag", help="Git tag to deploy from (e.g. a certain release).", ) deploy_workflow_parser.add_argument( "--branch", help="Git branch to deploy from.", ) deploy_group.add_argument( "--name", help="The name for the module in the resulting Snakefile (default: repository name).", ) deploy_workflow_parser.add_argument( "--force", action="store_true", help="Enforce overwriting of already present files.", ) collect_files = subparsers.add_parser( "collect-files", description="Collect files into a tabular structure, given input from " "STDIN formats glob patterns defined in a config sheet.", help="Collect files into a tabular structure, given input from " "STDIN formats glob patterns defined in a config sheet.", ) collect_files.add_argument( "config", help="A TSV file containing two columns input_pattern and glob_pattern. " "The input pattern is a Python regular expression that selects lines from STDIN, " "and extracts values from it (as groups; example: 'S888_Nr(?P<nr>[0-9]+)'). " "If possible such extracted values are automatically converted to integers. " "The glob pattern is formatted (via the Python format minilanguage) with the values from the " "input pattern and used to glob files from the filesystem. " "The globbed files are printed as TSV next to the matching input value taken from STDIN. " "If the globbing does not return any files for one STDIN input, an error is thrown. " "If one STDIN input is not matched by any of the provided stdin patterns, an error is thrown. " "If one STDIN input is matched by multiple of the provided stdin patterns, an error is thrown.", ) pin_conda_envs = subparsers.add_parser( "pin-conda-envs", help="Pin/lock given conda envrionments to compatible package URLs at the time of invocation.", description="Pin/lock given conda environment definition files (in YAML format) " "into a list of explicit package URLs including checksums, stored in a file " "<prefix>.<platform>.pin.txt with prefix being the path to the original definition file and " "<platform> being the name of the platform the pinning was performed on (e.g. linux-64). " "The resulting file will be automatically used by Snakemake to restore exactly the pinned " "environment. Also you can use it manually, e.g. with 'conda create -f <path-to-pin-file> -n <env-name>'.", ) pin_conda_envs.add_argument( "envfiles", nargs="+", help="Environment definition YAML files to pin." ) pin_conda_envs.add_argument( "--conda-frontend", choices=["mamba", "conda"], default="conda", help="Conda frontend to use.", ) pin_conda_envs.add_argument( "--create-prs", action="store_true", help="Create pull request for each updated environment. " "Requires GITHUB_TOKEN and GITHUB_REPOSITORY (the repo name) and one of GITHUB_REF_NAME or GITHUB_BASE_REF " "(preferring the latter, representing the target branch, e.g. main or master) environment " "variables to be set (the latter three are available when running as github action). " "In order to enable further actions (e.g. checks) to run on the generated PRs, the " "provided GITHUB_TOKEN may not be the default github actions token. See here for " "options: https://github.com/peter-evans/create-pull-request/blob/main/docs/concepts-guidelines.md#triggering-further-workflow-runs.", ) pin_conda_envs.add_argument( "--entity-regex", help="Regular expression for deriving an entity name from the environment file name " "(will be used for adding a label and for title and description). Has to contain a group 'entity' " "(e.g. '(?P<entity>.+)/environment.yaml').", ) pin_conda_envs.add_argument( "--pr-add-label", action="store_true", help="Add a label to the PR. Has to be used in combination with --entity-regex.", ) pin_conda_envs.add_argument( "--warn-on-error", action="store_true", help="Only warn if conda env evaluation fails and go on with the other envs.", ) def add_create_pr_args(subparser, entity: str): subparser.add_argument( "--create-prs", action="store_true", help=f"Create pull request for each updated {entity}. " "Requires GITHUB_TOKEN and GITHUB_REPOSITORY (the repo name) and one of GITHUB_REF_NAME or GITHUB_BASE_REF " "(preferring the latter, representing the target branch, e.g. main or master) environment " "variables to be set (the latter three are available when running as github action). " "In order to enable further actions (e.g. checks) to run on the generated PRs, the " "provided GITHUB_TOKEN may not be the default github actions token. See here for " "options: https://github.com/peter-evans/create-pull-request/blob/main/docs/concepts-guidelines.md#triggering-further-workflow-runs.", ) subparser.add_argument( "--entity-regex", help=f"Regular expression for deriving an entity name from the {entity} file name " "(will be used for adding a label and for title and description). Has to contain a group 'entity' " "(e.g. '(?P<entity>.+)/environment.yaml').", ) subparser.add_argument( "--pr-add-label", action="store_true", help="Add a label to the PR. Has to be used in combination with --entity-regex.", ) update_conda_envs = subparsers.add_parser( "update-conda-envs", help="Update given conda environment definition files (in YAML format) " "so that all contained packages are set to the latest feasible versions.", description="Update given conda environment definition files (in YAML format) " "so that all contained packages are set to the latest feasible versions.", ) update_conda_envs.add_argument( "envfiles", nargs="+", help="Environment definition YAML files to update." ) update_conda_envs.add_argument( "--conda-frontend", choices=["mamba", "conda"], default="conda", help="Conda frontend to use.", ) update_conda_envs.add_argument( "--pin-envs", action="store_true", help="also pin the updated environments (see pin-conda-envs subcommand).", ) add_create_pr_args(update_conda_envs, "environment") update_conda_envs.add_argument( "--warn-on-error", action="store_true", help="Only warn if conda env evaluation fails and go on with the other envs.", ) update_snakemake_wrappers = subparsers.add_parser( "update-snakemake-wrappers", help="Update all snakemake wrappers in given Snakefiles to their latest versions.", description="Update all snakemake wrappers in given Snakefiles to their latest versions.", ) update_snakemake_wrappers.add_argument( "snakefiles", nargs="+", help="Paths to Snakefiles which should be updated." ) add_create_pr_args(update_snakemake_wrappers, "snakefile") update_snakemake_wrappers.add_argument( "--per-snakefile-prs", action="store_true", help="Create one PR per Snakefile instead of a single PR for all.", ) scaffold_snakemake_plugin = subparsers.add_parser( "scaffold-snakemake-plugin", help="Scaffold a snakemake plugin by adding recommended dependencies and code snippets.", description="Scaffold a snakemake plugin by adding recommended dependencies and code snippets.", ) scaffold_snakemake_plugin.add_argument( "plugin_type", choices=[ "executor", "report", "scheduler", "storage", "software-deployment", "logger", ], help="Type of the plugin to scaffold.", ) return parser
[docs] def main(): """main entrypoint for snakedeploy""" parser = get_parser() def help(return_code=0): """print help, including the software version and active client and exit with return code. """ version = snakedeploy.__version__ print("\nSnakeDeploy Python v%s" % version) parser.print_help() sys.exit(return_code) # If the user didn't provide both arguments, show the full help if len(sys.argv) < 2: help() # If an error occurs while parsing the arguments, the interpreter will exit with value 2 args, extra = parser.parse_known_args() # Show the version and exit if args.version: print(snakedeploy.__version__) sys.exit(0) setup_logger( quiet=args.quiet, nocolor=args.disable_color, debug=args.verbose, ) from snakedeploy.logger import logger try: if args.subcommand == "deploy-workflow": if not (args.tag or args.branch): raise UserError("Please specify either --tag or --branch") deploy( args.repo, name=args.name, tag=args.tag, branch=args.branch, dest_path=Path(args.dest), force=args.force, ) elif args.subcommand == "collect-files": collect_files(config_sheet_path=args.config) elif args.subcommand == "pin-conda-envs": pin_conda_envs( args.envfiles, conda_frontend=args.conda_frontend, create_prs=args.create_prs, entity_regex=args.entity_regex, pr_add_label=args.pr_add_label, warn_on_error=args.warn_on_error, ) elif args.subcommand == "update-conda-envs": update_conda_envs( args.envfiles, conda_frontend=args.conda_frontend, create_prs=args.create_prs, pin_envs=args.pin_envs, entity_regex=args.entity_regex, pr_add_label=args.pr_add_label, warn_on_error=args.warn_on_error, ) elif args.subcommand == "update-snakemake-wrappers": update_snakemake_wrappers( args.snakefiles, create_prs=args.create_prs, per_snakefile_prs=args.per_snakefile_prs, entity_regex=args.entity_regex, pr_add_label=args.pr_add_label, ) elif args.subcommand == "scaffold-snakemake-plugin": scaffold_plugin(args.plugin_type) except UserError as e: logger.error(e) sys.exit(1)
if __name__ == "__main__": main()