Skip to content

Add markdown output mode to lint command#6715

Merged
bentsherman merged 2 commits intomasterfrom
nextflow-lint-markdown
Jan 13, 2026
Merged

Add markdown output mode to lint command#6715
bentsherman merged 2 commits intomasterfrom
nextflow-lint-markdown

Conversation

@ewels
Copy link
Member

@ewels ewels commented Jan 13, 2026

I was building some automation around lint results and wanted to generate some nice markdown. I figured instead of writing that in my crappy Python script, I should do it upstream in Nextflow itself, then anyone can use it.

  • Add a new -o markdown option to the nextflow lint command
  • Generate markdown-formatted output for lint results with code snippets and caret highlighting
  • Update CLI documentation
PR details

The markdown output includes:

  • Header with title "Nextflow lint results"
  • Metadata: generation timestamp (ISO 8601), Nextflow version, summary counts
  • Errors section with :x: emoji (omitted if no errors)
  • Warnings section with :warning: emoji (omitted if no warnings)
  • Each entry shows filename:line:column, message, and code snippet with caret highlighting (^^^)

Errors and warnings are sorted by filename then by position.

Example output

# Nextflow lint results

- Generated: 2026-01-13T07:57:21.823435Z
- Nextflow version: 25.12.0-edge
- Summary: 2 errors

## :x: Errors

- Error: `/tmp/test_lint_a.nf:2:13`: `undefined_var` is not defined

    ```nextflow
        script: undefined_var
                ^^^^^^^^^^^^^
    ```

- Error: `/tmp/test_lint_b.nf:3:13`: `unknown` is not defined

    ```nextflow
        script: unknown
                ^^^^^^^
    ```

🤖 Generated with Claude Code

Example output:
nextflow lint -o markdown .

Nextflow lint results

  • Generated: 2026-01-13T08:03:30.620642Z
  • Nextflow version: 25.12.0-edge
  • Summary: 7 errors, 17 warnings

❌ Errors

  • Error: nextflow.config:293:28: manifest is not defined

    \033[0;35m  nf-core/demo ${manifest.version}\033[0m
                               ^^^^^^^^
  • Error: nextflow.config:296:26: manifest is not defined

            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" : ""}
                             ^^^^^^^^
  • Error: nextflow.config:296:69: manifest is not defined

            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" : ""}
                                                                        ^^^^^^^^
  • Error: nextflow.config:296:186: manifest is not defined

            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" : ""}
                                                                                                                                                                                             ^^^^^^^^
  • Error: nextflow.config:306:22: validation is not defined

            beforeText = validation.help.beforeText
                         ^^^^^^^^^^
  • Error: nextflow.config:307:21: validation is not defined

            afterText = validation.help.afterText
                        ^^^^^^^^^^
  • Error: nf-test.config:1:1: Config settings must be assigned with an equals sign (=)

    config {
    ^

⚠️ Warnings

  • Warning: nextflow.config:296:129: Implicit closure parameter is deprecated, declare an explicit parameter instead

            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" : ""}
                                                                                                                                    ^^
  • Warning: subworkflows/local/utils_nfcore_demo_pipeline/main.nf:31:5: Parameter was not used -- prefix with _ to suppress warning

        monochrome_logs   // boolean: Do not use coloured log outputs
        ^^^^^^^^^^^^^^^
  • Warning: subworkflows/local/utils_nfcore_demo_pipeline/main.nf:34:5: Parameter was not used -- prefix with _ to suppress warning

        input             //  string: Path to input samplesheet
        ^^^^^
  • Warning: subworkflows/local/utils_nfcore_demo_pipeline/main.nf:38:19: The use of Channel to access channel factories is deprecated -- use channel instead

        ch_versions = Channel.empty()
                      ^^^^^^^
  • Warning: subworkflows/local/utils_nfcore_demo_pipeline/main.nf:75:5: The use of Channel to access channel factories is deprecated -- use channel instead

        Channel
        ^^^^^^^
  • Warning: subworkflows/nf-core/utils_nfcore_pipeline/main.nf:101:98: The use of Channel to access channel factories is deprecated -- use channel instead

        return ch_versions.unique().map { version -> processVersionsFromYAML(version) }.unique().mix(Channel.of(workflowVersionToYAML()))
                                                                                                     ^^^^^^^
  • Warning: workflows/demo.nf:26:19: The use of Channel to access channel factories is deprecated -- use channel instead

        ch_versions = Channel.empty()
                      ^^^^^^^
  • Warning: workflows/demo.nf:27:24: The use of Channel to access channel factories is deprecated -- use channel instead

        ch_multiqc_files = Channel.empty()
                           ^^^^^^^
  • Warning: workflows/demo.nf:34:68: Implicit closure parameter is deprecated, declare an explicit parameter instead

        ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]})
                                                                       ^^
  • Warning: workflows/demo.nf:44:9: Variable was declared but not used

            ch_trimmed  = SEQTK_TRIM.out.reads
            ^^^^^^^^^^
  • Warning: workflows/demo.nf:63:32: The use of Channel to access channel factories is deprecated -- use channel instead

        ch_multiqc_config        = Channel.fromPath(
                                   ^^^^^^^
  • Warning: workflows/demo.nf:66:9: The use of Channel to access channel factories is deprecated -- use channel instead

            Channel.fromPath(params.multiqc_config, checkIfExists: true) :
            ^^^^^^^
  • Warning: workflows/demo.nf:67:9: The use of Channel to access channel factories is deprecated -- use channel instead

            Channel.empty()
            ^^^^^^^
  • Warning: workflows/demo.nf:69:9: The use of Channel to access channel factories is deprecated -- use channel instead

            Channel.fromPath(params.multiqc_logo, checkIfExists: true) :
            ^^^^^^^
  • Warning: workflows/demo.nf:70:9: The use of Channel to access channel factories is deprecated -- use channel instead

            Channel.empty()
            ^^^^^^^
  • Warning: workflows/demo.nf:74:27: The use of Channel to access channel factories is deprecated -- use channel instead

        ch_workflow_summary = Channel.value(paramsSummaryMultiqc(summary_params))
                              ^^^^^^^
  • Warning: workflows/demo.nf:80:45: The use of Channel to access channel factories is deprecated -- use channel instead

        ch_methods_description                = Channel.value(
                                                ^^^^^^^

# Nextflow lint results

- Generated: 2026-01-13T08:03:30.620642Z
- Nextflow version: 25.12.0-edge
- Summary: 7 errors, 17 warnings

## :x: Errors

- Error: `nextflow.config:293:28`: `manifest` is not defined

    ```nextflow
    \033[0;35m  nf-core/demo ${manifest.version}\033[0m
                               ^^^^^^^^
    ```

- Error: `nextflow.config:296:26`: `manifest` is not defined

    ```nextflow
            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" : ""}
                             ^^^^^^^^
    ```

- Error: `nextflow.config:296:69`: `manifest` is not defined

    ```nextflow
            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" : ""}
                                                                        ^^^^^^^^
    ```

- Error: `nextflow.config:296:186`: `manifest` is not defined

    ```nextflow
            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" : ""}
                                                                                                                                                                                             ^^^^^^^^
    ```

- Error: `nextflow.config:306:22`: `validation` is not defined

    ```nextflow
            beforeText = validation.help.beforeText
                         ^^^^^^^^^^
    ```

- Error: `nextflow.config:307:21`: `validation` is not defined

    ```nextflow
            afterText = validation.help.afterText
                        ^^^^^^^^^^
    ```

- Error: `nf-test.config:1:1`: Config settings must be assigned with an equals sign (`=`)

    ```nextflow
    config {
    ^
    ```


## :warning: Warnings

- Warning: `nextflow.config:296:129`: Implicit closure parameter is deprecated, declare an explicit parameter instead

    ```nextflow
            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" : ""}
                                                                                                                                    ^^
    ```

- Warning: `subworkflows/local/utils_nfcore_demo_pipeline/main.nf:31:5`: Parameter was not used -- prefix with `_` to suppress warning

    ```nextflow
        monochrome_logs   // boolean: Do not use coloured log outputs
        ^^^^^^^^^^^^^^^
    ```

- Warning: `subworkflows/local/utils_nfcore_demo_pipeline/main.nf:34:5`: Parameter was not used -- prefix with `_` to suppress warning

    ```nextflow
        input             //  string: Path to input samplesheet
        ^^^^^
    ```

- Warning: `subworkflows/local/utils_nfcore_demo_pipeline/main.nf:38:19`: The use of `Channel` to access channel factories is deprecated -- use `channel` instead

    ```nextflow
        ch_versions = Channel.empty()
                      ^^^^^^^
    ```

- Warning: `subworkflows/local/utils_nfcore_demo_pipeline/main.nf:75:5`: The use of `Channel` to access channel factories is deprecated -- use `channel` instead

    ```nextflow
        Channel
        ^^^^^^^
    ```

- Warning: `subworkflows/nf-core/utils_nfcore_pipeline/main.nf:101:98`: The use of `Channel` to access channel factories is deprecated -- use `channel` instead

    ```nextflow
        return ch_versions.unique().map { version -> processVersionsFromYAML(version) }.unique().mix(Channel.of(workflowVersionToYAML()))
                                                                                                     ^^^^^^^
    ```

- Warning: `workflows/demo.nf:26:19`: The use of `Channel` to access channel factories is deprecated -- use `channel` instead

    ```nextflow
        ch_versions = Channel.empty()
                      ^^^^^^^
    ```

- Warning: `workflows/demo.nf:27:24`: The use of `Channel` to access channel factories is deprecated -- use `channel` instead

    ```nextflow
        ch_multiqc_files = Channel.empty()
                           ^^^^^^^
    ```

- Warning: `workflows/demo.nf:34:68`: Implicit closure parameter is deprecated, declare an explicit parameter instead

    ```nextflow
        ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]})
                                                                       ^^
    ```

- Warning: `workflows/demo.nf:44:9`: Variable was declared but not used

    ```nextflow
            ch_trimmed  = SEQTK_TRIM.out.reads
            ^^^^^^^^^^
    ```

- Warning: `workflows/demo.nf:63:32`: The use of `Channel` to access channel factories is deprecated -- use `channel` instead

    ```nextflow
        ch_multiqc_config        = Channel.fromPath(
                                   ^^^^^^^
    ```

- Warning: `workflows/demo.nf:66:9`: The use of `Channel` to access channel factories is deprecated -- use `channel` instead

    ```nextflow
            Channel.fromPath(params.multiqc_config, checkIfExists: true) :
            ^^^^^^^
    ```

- Warning: `workflows/demo.nf:67:9`: The use of `Channel` to access channel factories is deprecated -- use `channel` instead

    ```nextflow
            Channel.empty()
            ^^^^^^^
    ```

- Warning: `workflows/demo.nf:69:9`: The use of `Channel` to access channel factories is deprecated -- use `channel` instead

    ```nextflow
            Channel.fromPath(params.multiqc_logo, checkIfExists: true) :
            ^^^^^^^
    ```

- Warning: `workflows/demo.nf:70:9`: The use of `Channel` to access channel factories is deprecated -- use `channel` instead

    ```nextflow
            Channel.empty()
            ^^^^^^^
    ```

- Warning: `workflows/demo.nf:74:27`: The use of `Channel` to access channel factories is deprecated -- use `channel` instead

    ```nextflow
        ch_workflow_summary = Channel.value(paramsSummaryMultiqc(summary_params))
                              ^^^^^^^
    ```

- Warning: `workflows/demo.nf:80:45`: The use of `Channel` to access channel factories is deprecated -- use `channel` instead

    ```nextflow
        ch_methods_description                = Channel.value(
                                                ^^^^^^^
    ```

Add a new `-o markdown` option to the `nextflow lint` command that
generates markdown-formatted output for lint results. The markdown
output includes:

- Header with title "Nextflow lint results"
- Metadata: generation timestamp (ISO 8601), Nextflow version, summary
- Errors section with `:x:` emoji (omitted if no errors)
- Warnings section with `:warning:` emoji (omitted if no warnings)
- Each entry shows filename:line:column, message, and code snippet
  with caret highlighting

Errors and warnings are sorted by filename then by position.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Phil Ewels <phil.ewels@seqera.io>
@ewels ewels requested a review from a team as a code owner January 13, 2026 07:59
@netlify
Copy link

netlify bot commented Jan 13, 2026

Deploy Preview for nextflow-docs-staging ready!

Name Link
🔨 Latest commit fddbccd
🔍 Latest deploy log https://app.netlify.com/projects/nextflow-docs-staging/deploys/69667272c49ad900083b40bd
😎 Deploy Preview https://deploy-preview-6715--nextflow-docs-staging.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@bentsherman
Copy link
Member

Tested it on nf-core/rnaseq and it works wonderfully. Well done Phil 👍

@bentsherman bentsherman merged commit dad7c5d into master Jan 13, 2026
25 checks passed
@bentsherman bentsherman deleted the nextflow-lint-markdown branch January 13, 2026 16:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants