Skip to content

Commit

Permalink
Merge branch 'dev' into fix-typos
Browse files Browse the repository at this point in the history
  • Loading branch information
mribeirodantas authored Oct 19, 2024
2 parents 9b078db + deab2db commit f82a7bf
Show file tree
Hide file tree
Showing 26 changed files with 1,718 additions and 1,581 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/update-textual-snapshots.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:
- name: Run pytest to update snapshots
id: pytest
run: |
python3 -m pytest tests/test_create_app.py --snapshot-update --color=yes --durations=0
python3 -m pytest tests/pipelines/test_create_app.py --snapshot-update --color=yes --durations=0
continue-on-error: true

# indication that the run has finished
Expand Down
2 changes: 1 addition & 1 deletion .gitpod.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
image: nfcore/gitpod:latest
image: nfcore/gitpod:dev
tasks:
- name: install current state of nf-core/tools and setup pre-commit
command: |
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.9
rev: v0.7.0
hooks:
- id: ruff # linter
args: [--fix, --exit-non-zero-on-fix] # sort imports and fix
Expand Down
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
# nf-core/tools: Changelog

## v3.0.3dev

### Template

- Keep pipeline name in version.yml file ([#3223](https://github.com/nf-core/tools/pull/3223))
- Fix Manifest DOI text ([#3224](https://github.com/nf-core/tools/pull/3224))
- Do not assume pipeline name is url ([#3225](https://github.com/nf-core/tools/pull/3225))

### Download

### Linting

### Modules

### Subworkflows

### General

- Include .nf-core.yml in `nf-core pipelines bump-version` ([#3220](https://github.com/nf-core/tools/pull/3220))
- create: add shortcut to toggle all switches ([#3226](https://github.com/nf-core/tools/pull/3226))
- chore(deps): update pre-commit hook astral-sh/ruff-pre-commit to v0.7.0 ([#3229](https://github.com/nf-core/tools/pull/3229))

## [v3.0.2 - Titanium Tapir Patch](https://github.com/nf-core/tools/releases/tag/3.0.2) - [2024-10-11]

### Template
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ Optionally followed by the description that you want to add to the changelog.

- Update Textual snapshots:

If the Textual snapshots (run by `tests/test_crate_app.py`) fail, an HTML report is generated and uploaded as an artifact.
If the Textual snapshots (run by `tests/pipelines/test_crate_app.py`) fail, an HTML report is generated and uploaded as an artifact.
If you are sure that these changes are correct, you can automatically update the snapshots form the PR by posting a comment with the magic words:

```
Expand Down
8 changes: 4 additions & 4 deletions nf_core/pipeline-template/nextflow.config
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ validation {
defaultIgnoreParams = ["genomes"]
help {
enabled = true
command = "nextflow run $manifest.name -profile <docker/singularity/.../institute> --input samplesheet.csv --outdir <OUTDIR>"
command = "nextflow run {{ name }} -profile <docker/singularity/.../institute> --input samplesheet.csv --outdir <OUTDIR>"
fullParameter = "help_full"
showHiddenParameter = "show_hidden"
{% if is_nfcore -%}
Expand All @@ -283,15 +283,15 @@ validation {
\033[0;34m |\\ | |__ __ / ` / \\ |__) |__ \033[0;33m} {\033[0m
\033[0;34m | \\| | \\__, \\__/ | \\ |___ \033[0;32m\\`-._,-`-,\033[0m
\033[0;32m`._,._,\'\033[0m
\033[0;35m ${manifest.name} ${manifest.version}\033[0m
\033[0;35m {{ name }} ${manifest.version}\033[0m
-\033[2m----------------------------------------------------\033[0m-
"""
afterText = """${manifest.doi ? "* The pipeline\n" : ""}${manifest.doi.tokenize(",").collect { " https://doi.org/${it.trim().replace('https://doi.org/','')}"}.join("\n")}${manifest.doi ? "\n" : ""}
afterText = """${manifest.doi ? "\n* The pipeline\n" : ""}${manifest.doi.tokenize(",").collect { " https://doi.org/${it.trim().replace('https://doi.org/','')}"}.join("\n")}${manifest.doi ? "\n" : ""}
* The nf-core framework
https://doi.org/10.1038/s41587-020-0439-x
* Software dependencies
https://github.com/${manifest.name}/blob/master/CITATIONS.md
https://github.com/{{ name }}/blob/master/CITATIONS.md
"""{% endif %}
}{% if is_nfcore %}
summary {
Expand Down
2 changes: 1 addition & 1 deletion nf_core/pipeline-template/workflows/pipeline.nf
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ workflow {{ short_name|upper }} {
softwareVersionsToYAML(ch_versions)
.collectFile(
storeDir: "${params.outdir}/pipeline_info",
name: {% if is_nfcore %}'nf_core_' {% else %} '' {% endif %} + 'pipeline_software_' + {% if multiqc %} 'mqc_' {% else %} '' {% endif %} + 'versions.yml',
name: {% if is_nfcore %}'nf_core_' + {% endif %} '{{ short_name }}_software_' {% if multiqc %} + 'mqc_' {% endif %} + 'versions.yml',
sort: true,
newLine: true
).set { ch_collated_versions }
Expand Down
182 changes: 130 additions & 52 deletions nf_core/pipelines/bump_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
import logging
import re
from pathlib import Path
from typing import List, Tuple, Union
from typing import List, Optional, Tuple, Union

import rich.console
from ruamel.yaml import YAML

import nf_core.utils
from nf_core.utils import Pipeline
Expand Down Expand Up @@ -60,6 +61,7 @@ def bump_pipeline_version(pipeline_obj: Pipeline, new_version: str) -> None:
f"/releases/tag/{new_version}",
)
],
yaml_key=["report_comment"],
)
if multiqc_current_version != "dev" and multiqc_new_version == "dev":
update_file_version(
Expand All @@ -71,6 +73,7 @@ def bump_pipeline_version(pipeline_obj: Pipeline, new_version: str) -> None:
"/tree/dev",
)
],
yaml_key=["report_comment"],
)
if multiqc_current_version == "dev" and multiqc_new_version != "dev":
update_file_version(
Expand All @@ -82,6 +85,7 @@ def bump_pipeline_version(pipeline_obj: Pipeline, new_version: str) -> None:
f"/releases/tag/{multiqc_new_version}",
)
],
yaml_key=["report_comment"],
)
update_file_version(
Path("assets", "multiqc_config.yml"),
Expand All @@ -92,6 +96,7 @@ def bump_pipeline_version(pipeline_obj: Pipeline, new_version: str) -> None:
f"/{multiqc_new_version}/",
),
],
yaml_key=["report_comment"],
)
# nf-test snap files
pipeline_name = pipeline_obj.nf_config.get("manifest.name", "").strip(" '\"")
Expand All @@ -107,6 +112,20 @@ def bump_pipeline_version(pipeline_obj: Pipeline, new_version: str) -> None:
)
],
)
# .nf-core.yml - pipeline version
# update entry: version: 1.0.0dev, but not `nf_core_version`, or `bump_version`
update_file_version(
".nf-core.yml",
pipeline_obj,
[
(
current_version,
new_version,
)
],
required=False,
yaml_key=["template", "version"],
)


def bump_nextflow_version(pipeline_obj: Pipeline, new_version: str) -> None:
Expand Down Expand Up @@ -147,10 +166,11 @@ def bump_nextflow_version(pipeline_obj: Pipeline, new_version: str) -> None:
# example:
# NXF_VER:
# - "20.04.0"
rf"- \"{re.escape(current_version)}\"",
f'- "{new_version}"',
current_version,
new_version,
)
],
yaml_key=["jobs", "test", "strategy", "matrix", "NXF_VER"],
)

# README.md - Nextflow version badge
Expand All @@ -161,70 +181,128 @@ def bump_nextflow_version(pipeline_obj: Pipeline, new_version: str) -> None:
(
rf"nextflow%20DSL2-%E2%89%A5{re.escape(current_version)}-23aa62.svg",
f"nextflow%20DSL2-%E2%89%A5{new_version}-23aa62.svg",
),
(
# example: 1. Install [`Nextflow`](https://www.nextflow.io/docs/latest/getstarted.html#installation) (`>=20.04.0`)
rf"1\.\s*Install\s*\[`Nextflow`\]\(https:\/\/www\.nextflow\.io\/docs\/latest\/getstarted\.html#installation\)\s*\(`>={re.escape(current_version)}`\)",
f"1. Install [`Nextflow`](https://www.nextflow.io/docs/latest/getstarted.html#installation) (`>={new_version}`)",
),
)
],
)


def update_file_version(filename: Union[str, Path], pipeline_obj: Pipeline, patterns: List[Tuple[str, str]]) -> None:
"""Updates the version number in a requested file.
def update_file_version(
filename: Union[str, Path],
pipeline_obj: Pipeline,
patterns: List[Tuple[str, str]],
required: bool = True,
yaml_key: Optional[List[str]] = None,
) -> None:
"""
Updates a file with a new version number.
Args:
filename (str): File to scan.
pipeline_obj (nf_core.pipelines.lint.PipelineLint): A PipelineLint object that holds information
about the pipeline contents and build files.
pattern (str): Regex pattern to apply.
Raises:
ValueError, if the version number cannot be found.
filename (str): The name of the file to update.
pipeline_obj (nf_core.utils.Pipeline): A `Pipeline` object that holds information
about the pipeline contents.
patterns (List[Tuple[str, str]]): A list of tuples containing the regex patterns to
match and the replacement strings.
required (bool, optional): Whether the file is required to exist. Defaults to `True`.
yaml_key (Optional[List[str]], optional): The YAML key to update. Defaults to `None`.
"""
# Load the file
fn = pipeline_obj._fp(filename)
content = ""
try:
with open(fn) as fh:
content = fh.read()
except FileNotFoundError:
fn: Path = pipeline_obj._fp(filename)

if not fn.exists():
log.warning(f"File not found: '{fn}'")
return

replacements = []
for pattern in patterns:
found_match = False
if yaml_key:
update_yaml_file(fn, patterns, yaml_key, required)
else:
update_text_file(fn, patterns, required)

newcontent = []
for line in content.splitlines():
# Match the pattern
matches_pattern = re.findall(rf"^.*{pattern[0]}.*$", line)
if matches_pattern:
found_match = True

# Replace the match
newline = re.sub(pattern[0], pattern[1], line)
newcontent.append(newline)
def update_yaml_file(fn: Path, patterns: List[Tuple[str, str]], yaml_key: List[str], required: bool):
"""
Updates a YAML file with a new version number.
# Save for logging
replacements.append((line, newline))
Args:
fn (Path): The name of the file to update.
patterns (List[Tuple[str, str]]): A list of tuples containing the regex patterns to
match and the replacement strings.
yaml_key (List[str]): The YAML key to update.
required (bool): Whether the file is required to exist.
"""
yaml = YAML()
yaml.preserve_quotes = True
with open(fn) as file:
yaml_content = yaml.load(file)

try:
target = yaml_content
for key in yaml_key[:-1]:
target = target[key]

# No match, keep line as it is
last_key = yaml_key[-1]
current_value = target[last_key]

new_value = current_value
for pattern, replacement in patterns:
# check if current value is list
if isinstance(current_value, list):
new_value = [re.sub(pattern, replacement, item) for item in current_value]
else:
newcontent.append(line)
new_value = re.sub(pattern, replacement, current_value)

if found_match:
content = "\n".join(newcontent) + "\n"
else:
log.error(f"Could not find version number in {filename}: `{pattern}`")
if new_value != current_value:
target[last_key] = new_value
with open(fn, "w") as file:
yaml.dump(yaml_content, file)
log.info(f"Updated version in YAML file '{fn}'")
log_change(str(current_value), str(new_value))
except KeyError as e:
handle_error(f"Could not find key {e} in the YAML structure of {fn}", required)

log.info(f"Updated version in '{filename}'")
for replacement in replacements:
stderr.print(f" [red] - {replacement[0].strip()}", highlight=False)
stderr.print(f" [green] + {replacement[1].strip()}", highlight=False)
stderr.print("\n")

with open(fn, "w") as fh:
fh.write(content)
def update_text_file(fn: Path, patterns: List[Tuple[str, str]], required: bool):
"""
Updates a text file with a new version number.
Args:
fn (Path): The name of the file to update.
patterns (List[Tuple[str, str]]): A list of tuples containing the regex patterns to
match and the replacement strings.
required (bool): Whether the file is required to exist.
"""
with open(fn) as file:
content = file.read()

updated = False
for pattern, replacement in patterns:
new_content, count = re.subn(pattern, replacement, content)
if count > 0:
log_change(content, new_content)
content = new_content
updated = True
log.info(f"Updated version in '{fn}'")
log.debug(f"Replaced pattern '{pattern}' with '{replacement}' {count} times")
elif required:
handle_error(f"Could not find version number in {fn}: `{pattern}`", required)

if updated:
with open(fn, "w") as file:
file.write(content)


def handle_error(message: str, required: bool):
if required:
raise ValueError(message)
else:
log.info(message)


def log_change(old_content: str, new_content: str):
old_lines = old_content.splitlines()
new_lines = new_content.splitlines()

for old_line, new_line in zip(old_lines, new_lines):
if old_line != new_line:
stderr.print(f" [red] - {old_line.strip()}", highlight=False)
stderr.print(f" [green] + {new_line.strip()}", highlight=False)

stderr.print("\n")
17 changes: 13 additions & 4 deletions nf_core/pipelines/create/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
"""A Textual app to create a pipeline."""

import logging
from pathlib import Path

import click
import yaml
from textual.app import App
from textual.widgets import Button
from textual.widgets import Button, Switch

import nf_core
from nf_core.pipelines.create import utils
from nf_core.pipelines.create.basicdetails import BasicDetails
from nf_core.pipelines.create.custompipeline import CustomPipeline
Expand Down Expand Up @@ -46,6 +43,7 @@ class PipelineCreateApp(App[utils.CreateConfig]):
BINDINGS = [
("d", "toggle_dark", "Toggle dark mode"),
("q", "quit", "Quit"),
("a", "toggle_all", "Toggle all"),
]
SCREENS = {
"welcome": WelcomeScreen(),
Expand Down Expand Up @@ -105,3 +103,14 @@ def on_button_pressed(self, event: Button.Pressed) -> None:
def action_toggle_dark(self) -> None:
"""An action to toggle dark mode."""
self.dark: bool = not self.dark

def action_toggle_all(self) -> None:
"""An action to toggle all Switches."""
switches = self.query(Switch)
if not switches:
return # No Switches widgets found
# Determine the new state based on the first switch
new_state = not switches.first().value if switches.first() else True
for switch in switches:
switch.value = new_state
self.refresh()
Loading

0 comments on commit f82a7bf

Please sign in to comment.