Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor passing of command line arguments to nf-core commands #1145

Merged
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/create-lint-wf.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
run: nf-core --log-file log.txt create -n testpipeline -d "This pipeline is for testing" -a "Testing McTestface"

- name: nf-core lint
run: nf-core --log-file log.txt lint nf-core-testpipeline --fail-ignored
run: nf-core --log-file log.txt lint --dir nf-core-testpipeline --fail-ignored

- name: nf-core list
run: nf-core --log-file log.txt list
Expand All @@ -41,19 +41,19 @@ jobs:
# run: nf-core --log-file log.txt licences nf-core-testpipeline

- name: nf-core sync
run: nf-core --log-file log.txt sync nf-core-testpipeline/
run: nf-core --log-file log.txt sync --dir nf-core-testpipeline/

- name: nf-core schema
run: nf-core --log-file log.txt schema build nf-core-testpipeline/ --no-prompts
run: nf-core --log-file log.txt schema build --dir nf-core-testpipeline/ --no-prompts

- name: nf-core bump-version
run: nf-core --log-file log.txt bump-version nf-core-testpipeline/ 1.1
run: nf-core --log-file log.txt bump-version --dir nf-core-testpipeline/ 1.1

- name: nf-core lint in release mode
run: nf-core --log-file log.txt lint nf-core-testpipeline --fail-ignored --release
run: nf-core --log-file log.txt lint --dir nf-core-testpipeline --fail-ignored --release

- name: nf-core modules install
run: nf-core --log-file log.txt modules install nf-core-testpipeline/ --tool fastqc --force --latest
run: nf-core --log-file log.txt modules install fastqc --dir nf-core-testpipeline/ --force --latest

- name: Upload log file artifact
if: ${{ always() }}
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
* Updated `nf-core modules list` to show versions of local modules
* Improved exit behavior by replacing `sys.exit` with exceptions
* Updated `nf-core modules remove` to remove module entry in `modules.json` if module directory is missing
* Refactored passing of command line arguments to `nf-core` commands and subcommands
KevinMenden marked this conversation as resolved.
Show resolved Hide resolved

#### Sync

Expand Down
90 changes: 58 additions & 32 deletions nf_core/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ def create(name, description, author, version, no_git, force, outdir):


@nf_core_cli.command(help_priority=6)
@click.argument("pipeline_dir", type=click.Path(exists=True), required=True, metavar="<pipeline directory>")
@click.option("-d", "--dir", type=click.Path(exists=True), default=".", help="Pipeline directory. Defaults to CWD")
@click.option(
"--release",
is_flag=True,
Expand All @@ -312,7 +312,7 @@ def create(name, description, author, version, no_git, force, outdir):
@click.option("-i", "--fail-ignored", is_flag=True, help="Convert ignored tests to failures")
@click.option("--markdown", type=str, metavar="<filename>", help="File to write linting results to (Markdown)")
@click.option("--json", type=str, metavar="<filename>", help="File to write linting results to (JSON)")
def lint(pipeline_dir, release, fix, key, show_passed, fail_ignored, markdown, json):
def lint(dir, release, fix, key, show_passed, fail_ignored, markdown, json):
"""
Check pipeline code against nf-core guidelines.

Expand All @@ -324,14 +324,24 @@ def lint(pipeline_dir, release, fix, key, show_passed, fail_ignored, markdown, j
See the documentation for details.
"""

# Check if pipeline directory is a pipeline
try:
nf_core.utils.is_pipeline_directory(dir)
except UserWarning as e:
log.error(e)
sys.exit(1)

# Run the lint tests!
try:
lint_obj = nf_core.lint.run_linting(pipeline_dir, release, fix, key, show_passed, fail_ignored, markdown, json)
lint_obj = nf_core.lint.run_linting(dir, release, fix, key, show_passed, fail_ignored, markdown, json)
if len(lint_obj.failed) > 0:
sys.exit(1)
except AssertionError as e:
log.critical(e)
sys.exit(1)
except UserWarning as e:
log.error(e)
sys.exit(1)


## nf-core module subcommands
Expand Down Expand Up @@ -361,9 +371,9 @@ def modules(ctx, repository, branch):

@modules.command(help_priority=1)
@click.pass_context
@click.argument("pipeline_dir", type=click.Path(exists=True), required=False, metavar="(<pipeline directory>)")
@click.option("-i", "--installed", type=click.Path(exists=True), help="List modules installed in local directory")
@click.option("-j", "--json", is_flag=True, help="Print as JSON to stdout")
def list(ctx, pipeline_dir, json):
def list(ctx, installed, json):
"""
List available software modules.

Expand All @@ -372,29 +382,29 @@ def list(ctx, pipeline_dir, json):
If no pipeline directory is given, lists all currently available
software wrappers in the nf-core/modules repository.
"""
module_list = nf_core.modules.ModuleList(pipeline_dir)
module_list = nf_core.modules.ModuleList(installed)
module_list.modules_repo = ctx.obj["modules_repo_obj"]
print(module_list.list_modules(json))


@modules.command(help_priority=2)
@click.pass_context
@click.argument("pipeline_dir", type=click.Path(exists=True), required=True, metavar="<pipeline directory>")
@click.option("-t", "--tool", type=str, metavar="<tool> or <tool/subtool>")
@click.argument("tool", type=str, required=False, metavar="<tool> or <tool/subtool>")
@click.option("-d", "--dir", type=click.Path(exists=True), default=".", help="Pipeline directory. Defaults to CWD")
@click.option("-l", "--latest", is_flag=True, default=False, help="Install the latest version of the module")
@click.option(
"-f", "--force", is_flag=True, default=False, help="Force installation of module if module already exists"
)
@click.option("-s", "--sha", type=str, metavar="<commit sha>", help="Install module at commit SHA")
def install(ctx, pipeline_dir, tool, latest, force, sha):
def install(ctx, tool, dir, latest, force, sha):
"""
Add a DSL2 software wrapper module to a pipeline.

Finds the relevant files in nf-core/modules and copies to the pipeline,
along with associated metadata.
"""
try:
module_install = nf_core.modules.ModuleInstall(pipeline_dir, force=force, latest=latest, sha=sha)
module_install = nf_core.modules.ModuleInstall(dir, force=force, latest=latest, sha=sha)
module_install.modules_repo = ctx.obj["modules_repo_obj"]
module_install.install(tool)
except UserWarning as e:
Expand All @@ -404,14 +414,14 @@ def install(ctx, pipeline_dir, tool, latest, force, sha):

@modules.command(help_priority=3)
@click.pass_context
@click.argument("pipeline_dir", type=click.Path(exists=True), required=True, metavar="<pipeline directory>")
@click.option("-t", "--tool", type=str, metavar="<tool> or <tool/subtool>")
def remove(ctx, pipeline_dir, tool):
@click.argument("tool", type=str, required=False, metavar="<tool> or <tool/subtool>")
@click.option("-d", "--dir", type=click.Path(exists=True), default=".", help="Pipeline directory. Defaults to CWD")
def remove(ctx, dir, tool):
"""
Remove a software wrapper from a pipeline.
"""
try:
module_remove = nf_core.modules.ModuleRemove(pipeline_dir)
module_remove = nf_core.modules.ModuleRemove(dir)
module_remove.modules_repo = ctx.obj["modules_repo_obj"]
module_remove.remove(tool)
except UserWarning as e:
Expand Down Expand Up @@ -567,7 +577,7 @@ def validate(pipeline, params):


@schema.command(help_priority=2)
@click.argument("pipeline_dir", type=click.Path(exists=True), required=True, metavar="<pipeline directory>")
@click.option("-d", "--dir", type=click.Path(exists=True), default=".", help="Pipeline directory. Defaults to CWD")
@click.option("--no-prompts", is_flag=True, help="Do not confirm changes, just update parameters and exit")
@click.option("--web-only", is_flag=True, help="Skip building using Nextflow config, just launch the web tool")
@click.option(
Expand All @@ -576,7 +586,7 @@ def validate(pipeline, params):
default="https://nf-co.re/pipeline_schema_builder",
help="Customise the builder URL (for development work)",
)
def build(pipeline_dir, no_prompts, web_only, url):
def build(dir, no_prompts, web_only, url):
"""
Interactively build a pipeline schema from Nextflow params.

Expand All @@ -588,8 +598,12 @@ def build(pipeline_dir, no_prompts, web_only, url):
https://nf-co.re website where you can annotate and organise parameters.
Listens for this to be completed and saves the updated schema.
"""
schema_obj = nf_core.schema.PipelineSchema()
if schema_obj.build_schema(pipeline_dir, no_prompts, web_only, url) is False:
try:
schema_obj = nf_core.schema.PipelineSchema()
if schema_obj.build_schema(dir, no_prompts, web_only, url) is False:
sys.exit(1)
except UserWarning as e:
log.error(e)
sys.exit(1)


Expand Down Expand Up @@ -619,12 +633,12 @@ def lint(schema_path):


@nf_core_cli.command("bump-version", help_priority=9)
@click.argument("pipeline_dir", type=click.Path(exists=True), required=True, metavar="<pipeline directory>")
@click.argument("new_version", required=True, metavar="<new version>")
@click.option("-d", "--dir", type=click.Path(exists=True), default=".", help="Pipeline directory. Defaults to CWD")
@click.option(
"-n", "--nextflow", is_flag=True, default=False, help="Bump required nextflow version instead of pipeline version"
)
def bump_version(pipeline_dir, new_version, nextflow):
def bump_version(new_version, dir, nextflow):
"""
Update nf-core pipeline version number.

Expand All @@ -637,24 +651,31 @@ def bump_version(pipeline_dir, new_version, nextflow):

As well as the pipeline version, you can also change the required version of Nextflow.
"""
# Make a pipeline object and load config etc
pipeline_obj = nf_core.utils.Pipeline(pipeline_dir)
pipeline_obj._load()

# Bump the pipeline version number
if not nextflow:
nf_core.bump_version.bump_pipeline_version(pipeline_obj, new_version)
else:
nf_core.bump_version.bump_nextflow_version(pipeline_obj, new_version)
try:
# Check if pipeline directory contain necessary files
ErikDanielsson marked this conversation as resolved.
Show resolved Hide resolved
nf_core.utils.is_pipeline_directory(dir)

# Make a pipeline object and load config etc
pipeline_obj = nf_core.utils.Pipeline(dir)
pipeline_obj._load()

# Bump the pipeline version number
if not nextflow:
nf_core.bump_version.bump_pipeline_version(pipeline_obj, new_version)
else:
nf_core.bump_version.bump_nextflow_version(pipeline_obj, new_version)
except UserWarning as e:
log.error(e)
sys.exit(1)


@nf_core_cli.command("sync", help_priority=10)
@click.argument("pipeline_dir", required=True, type=click.Path(exists=True), metavar="<pipeline directory>")
@click.option("-d", "--dir", type=click.Path(exists=True), default=".", help="Pipeline directory. Defaults to CWD")
@click.option("-b", "--from-branch", type=str, help="The git branch to use to fetch workflow vars.")
@click.option("-p", "--pull-request", is_flag=True, default=False, help="Make a GitHub pull-request with the changes.")
@click.option("-r", "--repository", type=str, help="GitHub PR: target repository.")
@click.option("-u", "--username", type=str, help="GitHub PR: auth username.")
def sync(pipeline_dir, from_branch, pull_request, repository, username):
def sync(dir, from_branch, pull_request, repository, username):
"""
Sync a pipeline TEMPLATE branch with the nf-core template.

Expand All @@ -667,9 +688,14 @@ def sync(pipeline_dir, from_branch, pull_request, repository, username):
the pipeline. It is run automatically for all pipelines when ever a
new release of nf-core/tools (and the included template) is made.
"""
# Check if pipeline directory contain necessary files
ErikDanielsson marked this conversation as resolved.
Show resolved Hide resolved
try:
nf_core.utils.is_pipeline_directory(dir)
except UserWarning:
raise

# Sync the given pipeline dir
sync_obj = nf_core.sync.PipelineSync(pipeline_dir, from_branch, pull_request, repository, username)
sync_obj = nf_core.sync.PipelineSync(dir, from_branch, pull_request, repository, username)
try:
sync_obj.sync()
except (nf_core.sync.SyncException, nf_core.sync.PullRequestException) as e:
Expand Down
7 changes: 3 additions & 4 deletions nf_core/bump_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ def bump_pipeline_version(pipeline_obj, new_version):
log.warning("Stripping leading 'v' from new version number")
new_version = new_version[1:]
if not current_version:
log.error("Could not find config variable 'manifest.version'")
sys.exit(1)
raise UserWarning("Could not find config variable 'manifest.version'")

log.info("Changing version number from '{}' to '{}'".format(current_version, new_version))

# nextflow.config - workflow manifest version
Expand Down Expand Up @@ -60,8 +60,7 @@ def bump_nextflow_version(pipeline_obj, new_version):
current_version = re.sub(r"^[^0-9\.]*", "", current_version)
new_version = re.sub(r"^[^0-9\.]*", "", new_version)
if not current_version:
log.error("Could not find config variable 'manifest.nextflowVersion'")
sys.exit(1)
raise UserWarning("Could not find config variable 'manifest.nextflowVersion'")
log.info("Changing Nextlow version number from '{}' to '{}'".format(current_version, new_version))

# nextflow.config - manifest minimum nextflowVersion
Expand Down
5 changes: 4 additions & 1 deletion nf_core/lint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,10 @@ def __init__(self, wf_path, release_mode=False, fix=(), key=(), fail_ignored=Fal
"""Initialise linting object"""

# Initialise the parent object
super().__init__(wf_path)
try:
super().__init__(wf_path)
except UserWarning:
raise

self.lint_config = {}
self.release_mode = release_mode
Expand Down
2 changes: 1 addition & 1 deletion nf_core/modules/module_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,4 +309,4 @@ def get_repo_type(dir):
elif os.path.exists(os.path.join(dir, "software")):
return "modules"
else:
raise LookupError("Could not determine repository type of {}".format(dir))
raise LookupError("Could not determine repository type of '{}'".format(dir))
11 changes: 7 additions & 4 deletions nf_core/modules/modules_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@ def __init__(self, dir):
self.modules_repo = ModulesRepo()
self.dir = dir
self.module_names = []
if self.dir:
self.repo_type = nf_core.modules.module_utils.get_repo_type(self.dir)
else:
self.repo_type = None
try:
if self.dir:
self.repo_type = nf_core.modules.module_utils.get_repo_type(self.dir)
else:
self.repo_type = None
except LookupError as e:
raise UserWarning(e)

def get_pipeline_modules(self):
"""Get list of modules installed in the current directory"""
Expand Down
6 changes: 6 additions & 0 deletions nf_core/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,12 @@ def make_skeleton_schema(self):
def build_schema(self, pipeline_dir, no_prompts, web_only, url):
"""Interactively build a new pipeline schema for a pipeline"""

# Check if supplied pipeline directory really is one
try:
nf_core.utils.is_pipeline_directory(pipeline_dir)
except UserWarning:
raise

if no_prompts:
self.no_prompts = True
if web_only:
Expand Down
17 changes: 17 additions & 0 deletions nf_core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,23 @@ def _fp(self, fn):
return os.path.join(self.wf_path, fn)


def is_pipeline_directory(wf_path):
"""
Checks if the specified directory have the minimum required files
('main.nf', 'nextflow.config') for a pipeline directory

Args:
wf_path (str): The directory to be inspected

Raises:
UserWarning: If one of the files are missing
"""
for fn in ["main.nf", "nextflow.config"]:
path = os.path.join(wf_path, fn)
if not os.path.isfile(path):
raise UserWarning(f"'{wf_path}' is not a pipeline - '{fn}' is missing")


def fetch_wf_config(wf_path):
"""Uses Nextflow to retrieve the the configuration variables
from a Nextflow workflow.
Expand Down