Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ development source code and as such may not be routinely kept up to date.

([#501](https://github.com/nextstrain/cli/pull/501))

* The `build` and `run` commands now run `snakemake` with the
`--benchmark-extended` option for more detailed benchmark files.
([#467](https://github.com/nextstrain/cli/issues/467))

# 10.4.2 (7 January 2026)

## Improvements
Expand Down
4 changes: 4 additions & 0 deletions doc/changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ development source code and as such may not be routinely kept up to date.

([#501](https://github.com/nextstrain/cli/pull/501))

* The `build` and `run` commands now run `snakemake` with the
`--benchmark-extended` option for more detailed benchmark files.
([#467](https://github.com/nextstrain/cli/issues/467))

(v10-4-2)=
## 10.4.2 (7 January 2026)

Expand Down
35 changes: 30 additions & 5 deletions nextstrain/cli/command/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from ..argparse import add_extended_help_flags, AppendOverwriteDefault, SKIP_AUTO_DEFAULT_IN_HELP
from ..debug import debug
from ..errors import UsageError, UserError
from ..runner import docker, singularity, aws_batch
from ..runner import conda, docker, singularity, aws_batch
from ..util import byte_quantity, runner_name, split_image_name, warn
from ..volume import NamedVolume

Expand Down Expand Up @@ -206,7 +206,7 @@ def register_parser(subparser):
nargs = "?")

# Register runner flags and arguments
runner.register_runners(parser, exec = ["snakemake", "--printshellcmds", ...])
runner.register_runners(parser, exec = ["snakemake", ...])

return parser

Expand Down Expand Up @@ -243,12 +243,21 @@ def run(opts):
opts.volumes.append(build_volume) # for Docker, Singularity, and AWS Batch


# Automatically pass thru appropriate resource options to Snakemake to
# avoid the user having to repeat themselves (once for us, once for
# snakemake).
if opts.exec == "snakemake":
opts.default_exec_args += [
# Useful to see what's going on; see also 08ffc925.
"--printshellcmds",

# Useful to have additional information in benchmark files.
*(["--benchmark-extended"]
if supports_benchmark_extended(opts) else []),
]

snakemake_opts = parse_snakemake_args(opts.extra_exec_args)

# Automatically pass thru appropriate resource options to Snakemake to
# avoid the user having to repeat themselves (once for us, once for
# snakemake).
if not snakemake_opts["--cores"]:
if opts.cpus:
opts.extra_exec_args += ["--cores=%d" % opts.cpus]
Expand Down Expand Up @@ -308,6 +317,22 @@ def run(opts):
return runner.run(opts, working_volume = working_volume, cpus = opts.cpus, memory = opts.memory)


def supports_benchmark_extended(opts) -> bool:
"""
Check if the runner's image or environment supports Snakemake's
``--benchmark-extended`` option (requires Snakemake ≥8.11.0).
"""
if opts.__runner__ is docker:
return docker.image_supports(docker.IMAGE_FEATURE.benchmark_extended, opts.image)
if opts.__runner__ is aws_batch:
return docker.image_supports(docker.IMAGE_FEATURE.benchmark_extended, opts.image)
if opts.__runner__ is singularity:
return docker.image_supports(docker.IMAGE_FEATURE.benchmark_extended, opts.image.removeprefix("docker://"))
if opts.__runner__ is conda:
return conda.env_supports(conda.ENV_FEATURE.benchmark_extended)
return False


def assert_overlay_volumes_support(opts):
"""
Check that runtime overlays are supported, if given.
Expand Down
3 changes: 3 additions & 0 deletions nextstrain/cli/command/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,9 @@ def run(opts):
# Useful to see what's going on; see also 08ffc925.
"--printshellcmds",

# Useful to have additional information in benchmark files.
"--benchmark-extended",

# In our experience,¹ it's rarely useful to fail on incomplete outputs
# (Snakemake's default behaviour) instead of automatically regenerating
# them.
Expand Down
23 changes: 23 additions & 0 deletions nextstrain/cli/runner/conda.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
usable for the platform.
"""

from enum import Enum
import json
import os
import platform
Expand Down Expand Up @@ -173,6 +174,28 @@
}


class ENV_FEATURE(Enum):
# --benchmark-extended was introduced in Snakemake 8.11.0.
# FIXME: find the earliest supported image
benchmark_extended = "20251126T221614Z"


def env_supports(feature: ENV_FEATURE) -> bool:
"""
Test if the conda environment supports a *feature*, i.e. by version
comparison of the nextstrain-base package against the feature's first
release.
If the nextstrain-base package is not found, it is assumed to not have
support for the feature.
"""
meta = package_meta(NEXTSTRAIN_BASE)
if not meta:
return False
version = meta.get("version", "0")
return parse_version_lax(version) >= parse_version_lax(feature.value)


def register_arguments(parser) -> None:
"""
No-op. No arguments necessary.
Expand Down
4 changes: 4 additions & 0 deletions nextstrain/cli/runner/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ class IMAGE_FEATURE(Enum):
# file overwriting) in ZIP extraction.
aws_batch_overlays = "build-20250321T184358Z"

# --benchmark-extended was introduced in Snakemake 8.11.0.
# FIXME: find the earliest supported image
benchmark_extended = "build-20260310T234712Z"


def register_arguments(parser) -> None:
# Docker development options
Expand Down
Loading