Lint and validation rules
Run sprocket explain <RULE> for one rule or sprocket explain --tag <TAG> for every lint rule with a tag. Disable selected rules with configuration or lint directives; see Exceptions.
Rules from wdl-lint are listed as Lint rules. Rules from wdl-analysis are listed as Validation rules. Validation rules do not expose lint tags.
Summary
| Rule | Kind | Tags | Description |
|---|---|---|---|
BashSetSyntax | Lint | Correctness | Ensures that all command sections start with a valid set command. |
CallInputKeyword | Lint | Style, Deprecated | Ensures that the input: keyword is not used in call statements when WDL version is 1.2 or later. |
CommandSectionIndentation | Validation | — | Ensures consistent indentation (no mixed spaces/tabs) within command sections. |
ConciseInput | Lint | Style | Ensures concise input assignments are used (implicit binding when available). |
ContainerUri | Lint | Clarity, Portability | Ensures that values for the container key within runtime/requirements sections are well-formed. |
DeclarationName | Lint | Naming, Style, Clarity | Ensures declaration names do not redundantly include their type name. |
DenyGlobStar | Lint | Clarity, Correctness | Ensures glob("*") is not used in output declarations. |
DeprecatedObject | Validation | — | Ensures that the deprecated Object types are not used. |
DeprecatedPlaceholder | Validation | — | Ensures that deprecated expression placeholder options are not used. |
DeprecatedRuntimeSection | Validation | — | Detects deprecated runtime sections. |
DescriptionLength | Lint | SprocketCompatibility | Ensures that description meta entries are not too long for display in Sprocket documentation. |
DocCommentTabs | Lint | Style, Clarity | Ensures that doc comments do not contain tab characters. |
DocMetaStrings | Lint | SprocketCompatibility | Ensures that reserved meta keys have string values. |
EmptyDocComment | Lint | Clarity, Documentation | Ensures that documentation comment blocks are not empty. |
EmptyOutputs | Lint | Completeness | Ensures tasks specify an output section. |
ExceptDirectiveValid | Validation | — | Ensures except directives are placed correctly to have the intended effect. |
ExpectedRuntimeKeys | Lint | Completeness, Deprecated | Ensures that runtime sections have the appropriate keys. |
HereDocCommands | Lint | Clarity, Correctness | Ensures that tasks use heredoc syntax in command sections. |
HostPathLiterals | Lint | Portability | Flags File/Directory declaration defaults that use absolute host paths. |
ImportPlacement | Lint | Clarity | Ensures that imports are placed between the version statement and any document items. |
InlineInstall | Lint | Portability, Correctness, Performance | Ensures inline installation of packages is not used in command sections. |
InputName | Lint | Naming, Style | Ensures input names are meaningful (e.g. not generic like 'input', 'in', or too short). |
KnownRules | Validation | — | Ensures only known rules are used in except directives. |
MatchingOutputMeta | Lint | Completeness, Documentation, SprocketCompatibility | Ensures that each output field is documented in the meta section under meta.outputs. |
MeaninglessLintDirective | Validation | — | Warns if an #@ except: comment doesn't actually suppress a lint. |
MetaDescription | Lint | Completeness, Documentation, SprocketCompatibility | Ensures the meta section contains a description key. |
MetaSections | Lint | Completeness, Clarity, Documentation | Ensures that tasks and workflows have the required meta and parameter_meta sections, or supplementary doc comments. |
MisleadingDeclarationOrder | Validation | — | Warns when a variable declaration is placed after a command block. |
OutputName | Lint | Naming, Style | Ensures output names are meaningful (e.g. not generic like 'output', 'out', or too short). |
ParameterDescription | Lint | Completeness, Documentation | Ensures that parameters and outputs have proper descriptions. |
ParameterMetaMatched | Lint | Completeness, Sorting, Documentation, SprocketCompatibility | Ensures that inputs have a matching entry in a parameter_meta section. |
PascalCase | Lint | Naming, Style, Clarity | Ensures that structs are defined with PascalCase names. |
RedundantNone | Lint | Style | Flags redundant assignment of None to optional inputs. |
RequirementsSection | Lint | Completeness, Portability | Ensures that tasks have a requirements section (for WDL v1.2 and beyond). |
RuntimeSection | Lint | Completeness, Portability | Ensures that tasks have a runtime section (for WDL v1.1 and prior). |
ShellCheck | Lint | Correctness | Ensures that command blocks are free of ShellCheck violations. |
SnakeCase | Lint | Naming, Style, Clarity | Ensures that tasks, workflows, and variables are defined with snake_case names. |
TodoComment | Lint | Style | Flags TODO statements in comments to ensure they are not forgotten. |
UnnecessaryFunctionCall | Validation | — | Ensures that function calls are necessary. |
UnusedCall | Validation | — | Ensures that outputs of a call statement are used in the declaring workflow. |
UnusedDeclaration | Validation | — | Ensures that private declarations in tasks or workspaces are used within the declaring task or workspace. |
UnusedDocComments | Lint | Documentation | Reports doc comments that are attached to WDL items that don't support them. |
UnusedImport | Validation | — | Ensures that import namespaces are used in the importing document. |
UnusedInput | Validation | — | Ensures that task or workspace inputs are used within the declaring task or workspace. |
UsingFallbackVersion | Validation | — | Warns if interpretation of a document with an unsupported version falls back to a default. |
Tags
- All:
BashSetSyntax,CallInputKeyword,ConciseInput,ContainerUri,DeclarationName,DenyGlobStar,DescriptionLength,DocCommentTabs,DocMetaStrings,EmptyDocComment,EmptyOutputs,ExpectedRuntimeKeys,HereDocCommands,HostPathLiterals,ImportPlacement,InlineInstall,InputName,MatchingOutputMeta,MetaDescription,MetaSections,OutputName,ParameterDescription,ParameterMetaMatched,PascalCase,RedundantNone,RequirementsSection,RuntimeSection,ShellCheck,SnakeCase,TodoComment,UnusedDocComments - Clarity:
ContainerUri,DeclarationName,DenyGlobStar,DocCommentTabs,EmptyDocComment,HereDocCommands,ImportPlacement,MetaSections,PascalCase,SnakeCase - Completeness:
EmptyOutputs,ExpectedRuntimeKeys,MatchingOutputMeta,MetaDescription,MetaSections,ParameterDescription,ParameterMetaMatched,RequirementsSection,RuntimeSection - Correctness:
BashSetSyntax,DenyGlobStar,HereDocCommands,InlineInstall,ShellCheck - Deprecated:
CallInputKeyword,ExpectedRuntimeKeys - Documentation:
EmptyDocComment,MatchingOutputMeta,MetaDescription,MetaSections,ParameterDescription,ParameterMetaMatched,UnusedDocComments - Naming:
DeclarationName,InputName,OutputName,PascalCase,SnakeCase - Performance:
InlineInstall - Portability:
ContainerUri,HostPathLiterals,InlineInstall,RequirementsSection,RuntimeSection - Sorting:
ParameterMetaMatched - Spacing: No rules.
- SprocketCompatibility:
DescriptionLength,DocMetaStrings,MatchingOutputMeta,MetaDescription,ParameterMetaMatched - Style:
CallInputKeyword,ConciseInput,DeclarationName,DocCommentTabs,InputName,OutputName,PascalCase,RedundantNone,SnakeCase,TodoComment
Definitions
WDL Document Structure
Preamble
The document preamble is defined as anything before the version declaration statement and the version declaration statement itself. Only comments and whitespace are permitted before the version declaration.
Comment Types
Lint Directives
Lint directives are special comments that begin with #@ except: followed by a comma-delimited list of rule IDs. These comments are used to disable specific lint rules for a section of the document. When a lint directive is encountered in the preamble, it will disable the specified rules for the entire document.
For example:
#@ except: DoubleQuotes, ConciseInputPreamble Comments
Preamble comments are special comments at the start of a WDL file that begin with double pound signs (##). These comments are used for documentation that doesn't fit within any of the WDL-defined documentation elements (i.e., meta and parameter_meta sections). They may provide context for a collection of tasks or structs, or they may provide a high-level overview of a workflow. Preamble comments can be formatted as Markdown text.
For example:
## # FlagFilter
##
## A struct to represent the filtering flags used in various `samtools` commands.BashSetSyntax
- Kind: Lint
- Tags: Correctness
Ensures that all command sections start with a valid set command.
Bash has many silent failure cases, which can produce invalid results and be difficult to debug. The set command should be used in all command sections to enforce stricter behavior.
Configuration
bash_set_options
- Default:
["errexit","nounset","pipefail"]
List of options to enforce in the bash set builtin for every command section.
Example
bash_set_options = ["errexit", "nounset", "pipefail"]Examples
Problem
version 1.3
task say_hello {
command <<<
echo "Hello, World!"
>>>
}Assuming the default configuration
version 1.2
task say_hello {
command <<<
set -euo pipefail
echo "Hello, World!"
>>>
}CallInputKeyword
- Kind: Lint
- Tags: Style, Deprecated
Ensures that the input: keyword is not used in call statements when WDL version is 1.2 or later.
Starting with WDL version 1.2, the input: keyword in call statements is optional. This specification change allows call inputs to be specified directly within the braces without the input: keyword, resulting in a cleaner and more concise syntax. This rule encourages adoption of the newer syntax when using WDL 1.2 or later.
Examples
Problem
version 1.2
workflow example {
# In versions prior to WDL v1.2, the `input:` keyword
# was necessary in `call` statements.
call say_hello { input:
name = "world",
}
}Revision
version 1.2
workflow example {
# This is correct for WDL v1.2 and later.
call say_hello {
name = "world",
}
}CommandSectionIndentation
- Kind: Validation
- Tags: None
Ensures consistent indentation (no mixed spaces/tabs) within command sections.
Mixing indentation (tab and space) characters within the command line causes leading whitespace stripping to be skipped. Commands may be whitespace sensitive, and skipping the whitespace stripping step may cause unexpected behavior.
Examples
Problem
version 1.3
task say_greetings {
input {
String name
}
command <<<
# this line is prefixed with tabs
echo "Hello, ~{name}!"
# this line is prefixed with spaces
echo "Goodbye, ~{name}!"
>>>
}Revision
version 1.3
task say_greetings {
input {
String name
}
command <<<
# this line is prefixed with spaces
echo "Hello, ~{name}!"
# this line is prefixed with spaces
echo "Goodbye, ~{name}!"
>>>
}ConciseInput
- Kind: Lint
- Tags: Style
Ensures concise input assignments are used (implicit binding when available).
Redundant input assignments can be shortened in WDL versions >=v1.1 with an implicit binding. For example, { input: a = a } can be shortened to { input: a }.
Examples
Problem
version 1.2
workflow hello {
input {
String name
}
# Since WDL v1.1, these explicit bindings can be shortened.
call say_hello {
name = name,
}
}
task say_hello {
input {
String name
}
command <<<
echo "Hello, ~{name}!"
>>>
}Revision
version 1.2
workflow hello {
input {
String name
}
# `name` can be passed in directly
call say_hello {
name,
}
}
task say_hello {
input {
String name
}
command <<<
echo "Hello, ~{name}!"
>>>
}ContainerUri
- Kind: Lint
- Tags: Clarity, Portability
Ensures that values for the container key within runtime/requirements sections are well-formed.
This rule checks the following:
- Containers should have a tag, as container URIs with no tags have no expectation that the behavior of the containers won't change between runs.
- Further, immutable containers tagged with SHA256 sums are preferred. This is due to the requirement from the WDL specification that tasks produce functionally equivalent output across runs. When a mutable tag is used, there is a risk that changes to the container will cause different behavior between runs.
- Use of the 'any' container URI (
*) within an array of container URIs is ambiguous and should be avoided. - Empty container URI arrays are not disallowed by the specification but are ambiguous and should be avoided.
- An array of container URIs with a single element should be changed to a single string value.
Examples
Problem
version 1.2
task say_hello {
input {
String name
}
command <<<
echo "Hello, ~{name}!"
>>>
# No tag
requirements {
container: "ubuntu"
}
}
task say_goodbye {
input {
String name
}
command <<<
echo "Goodbye, ~{name}!"
>>>
# Unnecessary array
requirements {
container: [
"ubuntu@sha256:cc925e589b7543b910fea57a240468940003fbfc0515245a495dd0ad8fe7cef1",
]
}
}Revision
version 1.2
task say_hello {
input {
String name
}
command <<<
echo "Hello, ~{name}!"
>>>
requirements {
container: "ubuntu@sha256:cc925e589b7543b910fea57a240468940003fbfc0515245a495dd0ad8fe7cef1"
}
}
task say_goodbye {
input {
String name
}
command <<<
echo "Goodbye, ~{name}!"
>>>
requirements {
container: "ubuntu@sha256:cc925e589b7543b910fea57a240468940003fbfc0515245a495dd0ad8fe7cef1"
}
}DeclarationName
- Kind: Lint
- Tags: Naming, Style, Clarity
- Related rules:
InputName,OutputName
Ensures declaration names do not redundantly include their type name.
Declaration names should not include their type. This makes the code more verbose and often redundant. For example, use 'counter' instead of 'counter_int' or 'is_active' instead of 'is_active_bool'. Exceptions are made for String, File, and user-defined struct types, which are not flagged by this rule.
Configuration
allowed_names
- Default:
[]
List of names to ignore in the SnakeCase and DeclarationName lints.
Example
allowed_names = ["Foo", "counter_int"]Examples
Problem
version 1.2
task example {
input {
Int total_count_int
}
}Revision
version 1.2
task example {
input {
Int total_count
}
}DenyGlobStar
- Kind: Lint
- Tags: Clarity, Correctness
Ensures glob("*") is not used in output declarations.
glob("*") captures all files; as a task grows, you may include unintended files and cause unnecessary aggregation. Prefer explicit patterns to opt in only to the files you need, keeping tasks easier to debug/reproduce.
Examples
Problem
version 1.2
task generate_files {
command <<<
touch foo.txt
touch bar.txt
>>>
output {
Array[File] files = glob("*")
}
}Revision
version 1.2
task generate_files {
command <<<
touch foo.txt
touch bar.txt
>>>
output {
# Specifically collect the .txt files
Array[File] files = glob("*.txt")
}
}DeprecatedObject
- Kind: Validation
- Tags: None
Ensures that the deprecated Object types are not used.
WDL Object types are officially deprecated and will be removed in the next major WDL release.
Objects existed prior to better containers, such as Maps and Structs, being introduced into the language. Unfortunately, though these better alternatives did exist at the time of the v1.0 release, the type was not removed. It was later decided that Objects overlapped with Maps and Structs in functionality, and the type was marked for removal.
See this issue for more details: https://github.com/openwdl/wdl/pull/228.
Examples
Problem
version 1.2
workflow example {
Object person = object {
name: "Jimmy",
age: 55,
}
}Consider switching to a Struct or Map
version 1.2
struct Person {
String name
Int age
}
workflow example {
Person person = Person {
name: "Jimmy",
age: 55,
}
}DeprecatedPlaceholder
- Kind: Validation
- Tags: None
Ensures that deprecated expression placeholder options are not used.
Expression placeholder options were deprecated in WDL v1.1 and will be removed in the next major WDL version.
sepplaceholder options should be replaced by thesep()standard library function.true/falseplaceholder options should be replaced withif/elsestatements.defaultplaceholder options should be replaced by theselect_first()standard library function.${}interpolation placeholders should be replaced by~{}interpolation placeholders.
This rule only evaluates for WDL V1 documents with a version of v1.1 or later, as this was the version where the deprecation was introduced.
Examples
Problem
version 1.2
workflow example {
Array[String] names = [
"James",
"Jimmy",
"John",
]
String names_separated = "~{sep="," names}"
String names_interpolated = "${names_separated}"
}Revision
version 1.2
workflow example {
Array[String] names = [
"James",
"Jimmy",
"John",
]
String names_separated = "~{sep(",", names)}"
String names_interpolated = "~{names_separated}"
}DeprecatedRuntimeSection
- Kind: Validation
- Tags: None
Detects deprecated runtime sections.
The runtime section is deprecated in WDL v1.2 and later. Replace it with a requirements section.
Examples
Problem
version 1.2
task say_hello {
input {
String name
}
command <<<
echo "Hello, ~{name}!"
>>>
runtime {
container: "ubuntu:latest"
}
}Revision
version 1.2
task say_hello {
input {
String name
}
command <<<
echo "Hello, ~{name}!"
>>>
requirements {
container: "ubuntu:latest"
}
}DescriptionLength
- Kind: Lint
- Tags: SprocketCompatibility
Ensures that description meta entries are not too long for display in Sprocket documentation.
Descriptions should be kept short so that they can always render in full. If a description is too long, it will be clipped in some documentation contexts. help meta entries are never clipped and may be a better place for long form text.
Examples
Problem
version 1.2
workflow example {
meta {
description: "This is an example workflow. It is very important for documentation purposes, as it conveys a real workflow document without having to provide any implementation."
}
}Revision
version 1.2
workflow example {
meta {
description: "This is an example workflow."
# The `help` key can be used for extended descriptions
help: "It is very important for documentation purposes, as it conveys a real workflow document without having to provide any implementation."
}
}DocCommentTabs
- Kind: Lint
- Tags: Style, Clarity
Ensures that doc comments do not contain tab characters.
Tabs render with different widths depending on the viewer. Doc comments should use spaces instead of tabs to ensure consistent rendering.
Examples
Problem
version 1.3
# Using tabs for alignment
## {
## "foo": 123,
## ^^^^^
## }
workflow example {
meta {
description: 123
}
}Revision
version 1.3
# Using spaces for alignment
## {
## "foo": 123,
## ^^^^^
## }
workflow example {
meta {
description: "123"
}
}DocMetaStrings
- Kind: Lint
- Tags: SprocketCompatibility
- Related rules:
MetaDescription,MetaSections,ParameterMetaMatched,ParameterDescription,DescriptionLength
Ensures that reserved meta keys have string values.
Sprocket's documentation command reserves certain keys in meta and parameter_meta sections for documentation generation. These keys (description, help, external_help, warning, category, and group) must have String values. Using non-String values will cause the documentation to be rendered incorrectly or not at all. This rule ensures all reserved keys have String values for proper documentation generation.
Examples
Problem
version 1.2
workflow example {
meta {
description: 123
}
}Revision
version 1.2
workflow example {
meta {
description: "123"
}
}EmptyDocComment
- Kind: Lint
- Tags: Clarity, Documentation
- Related rules:
UnusedDocComments
Ensures that documentation comment blocks are not empty.
Documentation comment blocks (consecutive lines starting with ##) where all lines are empty serve no purpose. Either add meaningful text to the documentation comment block or remove it entirely.
Examples
Problem
version 1.2
# This will render nothing!
##
struct Person {
String name
Int age
}EmptyOutputs
- Kind: Lint
- Tags: Completeness
Ensures tasks specify an output section.
A task without an output section may be a mistake. This lint may be overzealous, as there are some legitimate use cases for tasks without outputs (e.g. uploading to an external service). In that case, authors should suppress the diagnostic on that specific task, and document its behavior in the meta section.
Examples
Problem
version 1.2
task generate_files {
command <<<
touch foo.txt
>>>
}If the results are intended to be received by the caller
version 1.2
task generate_files {
command <<<
touch foo.txt
>>>
output {
File files = "foo.txt"
}
}ExceptDirectiveValid
- Kind: Validation
- Tags: None
Ensures except directives are placed correctly to have the intended effect.
When writing WDL, except directives are used to suppress certain rules. If an except directive is misplaced, it will have no effect. This rule flags misplaced except directives to ensure they are in the correct location.
Examples
Problem
version 1.3
# UsingFallbackVersion exceptions aren't valid
# in this context
#@ except: UsingFallbackVersion
workflow example {
}Revision
#@ except: UsingFallbackVersion
version 1.3
workflow example {
}ExpectedRuntimeKeys
- Kind: Lint
- Tags: Completeness, Deprecated
- Related rules:
DeprecatedObject,DeprecatedPlaceholder
Ensures that runtime sections have the appropriate keys.
The behavior of this rule is different depending on the WDL version:
For WDL v1.0 documents, the docker and memory keys are recommended, but the inclusion of any number of other keys is permitted.
For WDL v1.1 documents:
- A list of mandatory, reserved keywords will be recommended for inclusion if they are not present. Here, 'mandatory' refers to the requirement that all execution engines support this key—not that the key must be present in the
runtimesection. - Optional, reserved "hint" keys are also permitted but not flagged when they are missing (as their support in execution engines is not guaranteed).
- The WDL v1.1 specification deprecates the inclusion of non-reserved keys in a
runtimesection. As such, any non-reserved keys will be flagged for removal.
For WDL v1.2 documents and later, this rule does not evaluate because runtime sections were deprecated in this version.
Configuration
allowed_runtime_keys
- Default:
[]
List of keys to ignore in the ExpectedRuntimeKeys lint.
Example
allowed_runtime_keys = ["foo"]Examples
Example 1
The following is missing a mandatory key
version 1.1
task missing_required_keys {
runtime {
# Missing `container` key
}
}Example 2
The following has an unexpected key
version 1.1
task unexpected_runtime_key {
runtime {
container: "ubuntu"
foo: "bar"
}
}HereDocCommands
- Kind: Lint
- Tags: Clarity, Correctness
Ensures that tasks use heredoc syntax in command sections.
Curly command blocks are no longer considered idiomatic WDL. Idiomatic WDL code uses heredoc command blocks instead. This is because curly command blocks create ambiguity with Bash syntax.
Examples
Problem
version 1.2
task say_hello {
command {
echo "Hello, World!"
}
}Revision
version 1.2
task say_hello {
command <<<
echo "Hello, World!"
>>>
}HostPathLiterals
- Kind: Lint
- Tags: Portability
Flags File/Directory declaration defaults that use absolute host paths.
File and Directory declarations with absolute path defaults are not portable across environments. Use relative paths or supply values at runtime.
Examples
Problem
version 1.3
task run_tool {
input {
File data = "/etc/host/input.txt"
}
command <<<
echo "run"
>>>
}ImportPlacement
- Kind: Lint
- Tags: Clarity
Ensures that imports are placed between the version statement and any document items.
All import statements should follow the WDL version declaration with one empty line between the version and the first import statement.
Examples
Problem
version 1.2
workflow example {
}
import "example2.wdl"Revision
version 1.2
import "example2.wdl"
workflow example {
}InlineInstall
- Kind: Lint
- Tags: Portability, Correctness, Performance
Ensures inline installation of packages is not used in command sections.
All required software should be installed in the execution environment before the workflow is run. Inline installations can lead to lack of reproducibility, portability, and incur a performance cost, as software must be downloaded and installed every invocation.
Examples
Problem
version 1.3
task say_hello {
command <<<
sudo apt install python3
python3 -c "print('Hello, world!')"
>>>
requirements {
container: "debian:trixie"
}
}Consider using a dedicated container image
version 1.3
task say_hello {
command <<<
python3 -c "print('Hello, world!')"
>>>
requirements {
container: "python:trixie"
}
}InputName
- Kind: Lint
- Tags: Naming, Style
- Related rules:
OutputName,DeclarationName
Ensures input names are meaningful (e.g. not generic like 'input', 'in', or too short).
Any input name matching these regular expressions will be flagged: /^[iI]n[A-Z_]/, /^input/i or /^..?$/.
It is redundant and needlessly verbose to use an input's name to specify that it is an input. Input names should be short yet descriptive. Prefixing a name with in or input adds length to the name without adding clarity or context. Additionally, names with only 2 characters can lead to confusion and obfuscates the content of an input. Input names should be at least 3 characters long.
Examples
Problem
version 1.2
task say_hello {
input {
String input_name
}
command <<<
echo "Hello, ~{input_name}!"
>>>
}Revision
version 1.2
task say_hello {
meta {
description: "Says hello for the given name"
}
input {
String name
}
command <<<
echo "Hello, ~{name}!"
>>>
}KnownRules
- Kind: Validation
- Tags: None
Ensures only known rules are used in except directives.
When writing WDL, except directives are used to suppress certain rules. If a rule is unknown, nothing will be suppressed. This rule flags unknown rules as they are often mistakes.
Examples
Problem
#@ except: LintThatDoesNotExist
version 1.2
workflow example {
}Revision
version 1.2
workflow example {
}MatchingOutputMeta
- Kind: Lint
- Tags: Completeness, Documentation, SprocketCompatibility
- Related rules:
MetaDescription,ParameterMetaMatched,OutputSection,RequirementsSection,RuntimeSection,DescriptionLength
Ensures that each output field is documented in the meta section under meta.outputs.
The meta section should have an outputs key that is an object and contains keys with descriptions for each output of the task/workflow. These must match exactly. i.e. for each named output of a task or workflow, there should be an entry under meta.outputs with that same name. Additionally, these entries should be in the same order (that order is up to the developer to decide). No extraneous meta.outputs entries are allowed.
Examples
Problem
version 1.2
task generate_greeting {
meta {
outputs: {
# Missing `greeting`
}
}
input {
String name
}
output {
String greeting = "Hello, ~{name}!"
}
}Revision
version 1.2
task generate_greeting {
meta {
outputs: {
greeting: "The generated greeting for the provided name",
}
}
input {
String name
}
output {
String greeting = "Hello, ~{name}!"
}
}MeaninglessLintDirective
- Kind: Validation
- Tags: None
Warns if an #@ except: comment doesn't actually suppress a lint.
Unused #@ except: comments are likely leftovers of refactoring or debugging, and can reduce the clarity of the code. It is best to remove them.
Examples
Problem
version 1.3
task do_work {
command <<<
echo "Lots of hard work!"
>>>
output {
String result = read_string(stdout())
}
}
# We except `UnusedCall` unnecessarily.
workflow calculate {
#@ except: UnusedCall
call do_work
output {
# We're using the result here!
String result = do_work.result
}
}Consider removing the unused exception
version 1.3
task do_work {
command <<<
echo "Lots of hard work!"
>>>
output {
String result = read_string(stdout())
}
}
workflow calculate {
call do_work
output {
String result = do_work.result
}
}MetaDescription
- Kind: Lint
- Tags: Completeness, Documentation, SprocketCompatibility
- Related rules:
ParameterMetaMatched,ParameterDescription,OutputSection,RequirementsSection,RuntimeSection,MatchingOutputMeta,DescriptionLength
Ensures the meta section contains a description key.
Each task, workflow, and struct should have a description in the meta section. The description should be short, written in active voice, and be in complete sentences. More detailed information can be included in the help key.
Examples
Problem
version 1.2
task say_hello {
meta {
}
input {
String name
}
command <<<
echo "Hello, ~{name}!"
>>>
}Revision
version 1.2
task say_hello {
meta {
description: "Says hello for the given name"
}
input {
String name
}
command <<<
echo "Hello, ~{name}!"
>>>
}MetaSections
- Kind: Lint
- Tags: Completeness, Clarity, Documentation
- Related rules:
MetaDescription,ParameterMetaMatched,OutputSection,RequirementsSection,RuntimeSection,MatchingOutputMeta,DescriptionLength
Ensures that tasks and workflows have the required meta and parameter_meta sections, or supplementary doc comments.
It is important that WDL code is well-documented. Every task and workflow should be documented with both a meta and parameter_meta section, or doc comments. Tasks without an input section are permitted to skip the parameter_meta section.
Examples
Problem
version 1.2
task say_hello {
input {
String name
}
command <<<
echo "Hello, ~{name}!"
>>>
}Revision
version 1.2
task say_hello {
meta {
description: "Says hello for the given name"
}
parameter_meta {
name: "The name of the person to greet"
}
input {
String name
}
command <<<
echo "Hello, ~{name}!"
>>>
}MisleadingDeclarationOrder
- Kind: Validation
- Tags: None
Warns when a variable declaration is placed after a command block.
WDL tasks are evaluated based on their dependency graph, not top-to-bottom. Variable declarations that appear after command sections are visually misleading, as they will still be evaluated before the command is executed.
Examples
Problem
version 1.2
task greet {
String greeting = "Hello"
command <<<
echo "~{greeting}, ~{name}!"
>>>
String name = "World"
}Revision
version 1.2
task greet {
String greeting = "Hello"
String name = "World"
command <<<
echo "~{greeting}, ~{name}!"
>>>
}OutputName
- Kind: Lint
- Tags: Naming, Style
- Related rules:
InputName,DeclarationName
Ensures output names are meaningful (e.g. not generic like 'output', 'out', or too short).
Any output name matching these regular expressions will be flagged: /^[oO]ut[A-Z_]/, /^output/i or /^..?$/.
It is redundant and needlessly verbose to use an output's name to specify that it is an output. Output names should be short yet descriptive. Prefixing a name with out or output adds length to the name without adding clarity or context. Additionally, names with only 2 characters can lead to confusion and obfuscates the content of an output. Output names should be at least 3 characters long.
Examples
Problem
version 1.2
task generate_greeting {
input {
String name
}
command <<<
>>>
output {
String output_greeting = "Hello, ~{name}!"
}
}Revision
version 1.2
task generate_greeting {
input {
String name
}
command <<<
>>>
output {
String greeting = "Hello, ~{name}!"
}
}ParameterDescription
- Kind: Lint
- Tags: Completeness, Documentation
- Related rules:
DescriptionLength,MatchingOutputMeta,MetaDescription
Ensures that parameters and outputs have proper descriptions.
Documentation is expected for each parameter (in parameter_meta) and each output (in meta.outputs). A valid description is either a simple String value or an object containing a description key with a String value.
Examples
Problem
version 1.2
task greet {
meta {
outputs: {
greeting: {},
}
}
parameter_meta {
name: {}
}
input {
String name
}
command <<<
echo "Hello, ~{name}"
>>>
output {
String greeting = stdout()
}
}Revision
version 1.2
task greet {
meta {
outputs: {
greeting: "The generated greeting message.",
}
}
parameter_meta {
name: "The name of the person to greet."
}
input {
String name
}
command <<<
echo "Hello, ~{name}"
>>>
output {
String greeting = stdout()
}
}ParameterMetaMatched
- Kind: Lint
- Tags: Completeness, Sorting, Documentation, SprocketCompatibility
- Related rules:
MetaDescription,OutputSection,RequirementsSection,RuntimeSection,MatchingOutputMeta,DescriptionLength
Ensures that inputs have a matching entry in a parameter_meta section.
Each input parameter within a task or workflow should have an associated parameter_meta entry with a detailed description of the input. Non-input keys are not permitted within the parameter_meta block.
Examples
Problem
version 1.2
task say_hello {
parameter_meta {
name: "The name of the person to greet"
does_not_exist: "This is not a real parameter"
}
input {
String name
}
command <<<
echo "Hello, ~{name}!"
>>>
}Revision
version 1.2
task say_hello {
parameter_meta {
name: "The name of the person to greet"
}
input {
String name
}
command <<<
echo "Hello, ~{name}!"
>>>
}PascalCase
- Kind: Lint
- Tags: Naming, Style, Clarity
- Related rules:
SnakeCase
Ensures that structs are defined with PascalCase names.
Struct names should be in PascalCase. Maintaining a consistent naming convention makes the code easier to read and understand.
Examples
Struct names should be in PascalCase
version 1.2
struct registered_user {
String name
}Revision
version 1.2
struct RegisteredUser {
String name
}RedundantNone
- Kind: Lint
- Tags: Style
Flags redundant assignment of None to optional inputs.
The specification states that an optional input declaration (e.g., String? foo) is implicitly initialized to None if no default is provided. Therefore explicitly writing String? foo = None is equivalent to String? foo but adds unnecessary verbosity.
Examples
Problem
version 1.2
workflow example {
input {
String? name = None
}
}Revision
version 1.2
workflow example {
input {
String? name
}
}RequirementsSection
- Kind: Lint
- Tags: Completeness, Portability
- Related rules:
DeprecatedRuntimeSection,ExpectedRuntimeKeys,MetaDescription,ParameterMetaMatched,MetaSections,OutputSection,MatchingOutputMeta
Ensures that tasks have a requirements section (for WDL v1.2 and beyond).
Tasks that don't declare requirements sections are unlikely to be portable.
Examples
Problem
version 1.2
task say_hello {
input {
String name
}
command <<<
echo "Hello, ~{name}!"
>>>
}Revision
version 1.2
task say_hello {
input {
String name
}
command <<<
echo "Hello, ~{name}!"
>>>
requirements {
container: "ubuntu:latest"
}
}RuntimeSection
- Kind: Lint
- Tags: Completeness, Portability
- Related rules:
MetaDescription,ParameterMetaMatched,MetaSections,OutputSection,MatchingOutputMeta
Ensures that tasks have a runtime section (for WDL v1.1 and prior).
Tasks that don't declare runtime sections are unlikely to be portable.
Examples
Problem
version 1.1
task say_hello {
input {
String name
}
command <<<
echo "Hello, ~{name}!"
>>>
}Revision
version 1.1
task say_hello {
input {
String name
}
command <<<
echo "Hello, ~{name}!"
>>>
runtime {
container: "ubuntu:latest"
}
}ShellCheck
- Kind: Lint
- Tags: Correctness
Ensures that command blocks are free of ShellCheck violations.
ShellCheck is a static analysis tool and linter for sh / bash. The lints provided by ShellCheck help prevent common errors and pitfalls in your scripts. Following its recommendations will increase the robustness of your command sections.
Examples
Problem
version 1.2
task say_hello {
# Triggers SC2154
command <<<
echo "Hello $name"
>>>
}Revision
version 1.2
task say_hello {
command <<<
name=World
echo "Hello $name"
>>>
}SnakeCase
- Kind: Lint
- Tags: Naming, Style, Clarity
- Related rules:
PascalCase
Ensures that tasks, workflows, and variables are defined with snake_case names.
Workflow, task, and variable names should be in snake case. Maintaining a consistent naming convention makes the code easier to read and understand.
Configuration
allowed_names
- Default:
[]
List of names to ignore in the SnakeCase and DeclarationName lints.
Example
allowed_names = ["Foo", "counter_int"]Examples
Problem
version 1.2
task SayHello {
command <<<
echo "Hello, World!"
>>>
}Revision
version 1.2
task say_hello {
command <<<
echo "Hello, World!"
>>>
}TodoComment
- Kind: Lint
- Tags: Style
Flags TODO statements in comments to ensure they are not forgotten.
When writing WDL, future tasks are often marked as TODO. This indicates that the implementor intended to go back to the code and handle the todo item. TODO items should not be long-term fixtures within code and, as such, they are flagged to ensure none are forgotten.
Examples
The following comment will be flagged
version 1.2
# TODO: Implement this workflow
workflow example {
meta {
}
output {
}
}UnnecessaryFunctionCall
- Kind: Validation
- Tags: None
Ensures that function calls are necessary.
Unnecessary function calls may impact evaluation performance.
Examples
Problem
version 1.2
workflow example {
# Calls to `defined` on values that are statically
# known to be non-None are unnecessary.
Boolean exists = defined("hello")
}UnusedCall
- Kind: Validation
- Tags: None
Ensures that outputs of a call statement are used in the declaring workflow.
Unused calls may cause unnecessary consumption of compute resources.
Examples
Problem
version 1.2
workflow example {
# The output of `do_work` is never used
call do_work
}
task do_work {
command <<<
>>>
output {
Int x = 0
}
}Consider removing the call entirely
version 1.2
workflow example {
}
task do_work {
command <<<
>>>
output {
Int x = 0
}
}UnusedDeclaration
- Kind: Validation
- Tags: None
Ensures that private declarations in tasks or workspaces are used within the declaring task or workspace.
Unused private declarations degrade evaluation performance and reduce the clarity of the code.
Examples
Problem
version 1.2
workflow example {
String unused = "this will produce a warning"
}Consider removing the declaration entirely
version 1.2
workflow example {
}UnusedDocComments
- Kind: Lint
- Tags: Documentation
Reports doc comments that are attached to WDL items that don't support them.
Some Workflow Definition Language items do not support doc comments (##). This lint reports if a doc comment is attached to an item that isn't supported.
Doc comments are supported on:
- Workflow Definitions
- Task Definitions
- Struct Definitions
- Fields in Struct Definitions
- Fields in Input Sections
- Fields in Output Sections
- Enum Definitions
- Enum Choices
Examples
Problem
version 1.2
workflow example {
# This isn't documenting anything!
## The inputs for the workflow
input {
String name
}
# Neither is this!
## The outputs for the workflow
output {
String greeting = "Hello, ~{name}!"
}
}Consider removing the comments or moving them to applicable items
version 1.2
workflow example {
input {
## The name to greet
String name
}
output {
## The generated greeting
String greeting = "Hello, ~{name}!"
}
}UnusedImport
- Kind: Validation
- Tags: None
Ensures that import namespaces are used in the importing document.
Imported WDL documents should be used in the document that imports them. Unused imports impact parsing and evaluation performance.
Examples
Problem
version 1.3
import "bar.wdl"
import "foo.wdl" as used
workflow example {
call used.test
}Consider removing the import entirely
version 1.3
import "foo.wdl" as used
workflow example {
call used.test
}UnusedInput
- Kind: Validation
- Tags: None
Ensures that task or workspace inputs are used within the declaring task or workspace.
Unused inputs degrade evaluation performance and reduce the clarity of the code. Unused file inputs in tasks can also cause unnecessary file localizations.
Examples
Problem
version 1.2
workflow example {
input {
String unused
}
}Consider removing the input entirely
version 1.2
workflow example {
input {
}
}UsingFallbackVersion
- Kind: Validation
- Tags: None
Warns if interpretation of a document with an unsupported version falls back to a default.
A document with an unsupported version may have unpredictable behavior if interpreted as a different version.
Examples
Problem
# Not a valid version. If a fallback version is configured,
# the document will be interpreted as that version.
version development
workflow example {
}