| name | cwl-create-commandlinetool |
| description | Create CWL CommandLineTool packages from scratch including proper structure, inputs, outputs, and
requirements. Learn best practices for wrapping command-line tools and creating reusable process
definitions. Use when creating new CWL packages for Weaver deployment.
|
| license | Apache-2.0 |
| compatibility | Requires understanding of command-line tools and CWL basics. Supports CWL v1.0, v1.1, v1.2. |
| metadata | {"author":"fmigneault"} |
Create CWL CommandLineTool
Learn to create well-structured CWL CommandLineTool packages from scratch.
When to Use
- Wrapping a command-line tool for Weaver
- Creating a new process definition
- Converting existing scripts to CWL
- Building reusable process components
- Standardizing tool execution
Basic Structure
Minimal CommandLineTool
cwlVersion: v1.2
class: CommandLineTool
baseCommand: [echo]
inputs:
message:
type: string
inputBinding:
position: 1
outputs:
output:
type: stdout
Complete Template
cwlVersion: v1.2
class: CommandLineTool
label: Process Name
doc: |
Detailed description of what this process does.
Include usage examples and expected behavior.
baseCommand: [command, subcommand]
requirements:
DockerRequirement:
dockerPull: appropriate-image:version
inputs:
required_input:
type: File
label: Required input file
doc: Description of this input
inputBinding:
position: 1
prefix: --input
optional_param:
type: string?
default: "default_value"
inputBinding:
position: 2
prefix: --param
outputs:
output_file:
type: File
label: Output result
doc: Description of output
outputBinding:
glob: "output.txt"
stdout: output.log
stderr: error.log
Building Inputs
Simple Literal Input
inputs:
threshold:
type: float
doc: "Threshold value (0.0-1.0)"
inputBinding:
position: 1
prefix: -t
File Input
inputs:
input_file:
type: File
label: "Input data file"
doc: "NetCDF or GeoTIFF file"
format:
- edam:format_3650
- edam:format_3591
inputBinding:
position: 1
prefix: --input
Array Input
inputs:
input_files:
type: File[]
doc: "Multiple input files"
inputBinding:
position: 1
prefix: --files
itemSeparator: ","
Optional Input
inputs:
optional_flag:
type: boolean?
default: false
inputBinding:
prefix: --verbose
Input with Default
inputs:
output_format:
type: string
default: "netcdf"
inputBinding:
position: 2
prefix: --format
InputBinding Configuration
Position
inputs:
input1:
type: File
inputBinding:
position: 1
input2:
type: File
inputBinding:
position: 2
output_name:
type: string
inputBinding:
position: 3
Prefix
inputs:
input_file:
type: File
inputBinding:
prefix: --input
format:
type: string
inputBinding:
prefix: --format
Separate vs Together
inputs:
input_with_space:
type: File
inputBinding:
prefix: --input
separate: true
inputs:
input_no_space:
type: File
inputBinding:
prefix: --input
separate: false
Value From Expression
inputs:
input_file:
type: File
inputBinding:
position: 1
valueFrom: $(self.basename)
Building Outputs
File Output
outputs:
output_file:
type: File
outputBinding:
glob: "result.txt"
Multiple Files
outputs:
output_files:
type: File[]
outputBinding:
glob: "*.txt"
Directory Output
outputs:
output_dir:
type: Directory
outputBinding:
glob: "results/"
Standard Streams
outputs:
stdout_output:
type: stdout
stderr_output:
type: stderr
stdout: output.log
stderr: error.log
Conditional Output
outputs:
optional_output:
type: File?
outputBinding:
glob: "optional.txt"
Requirements
Docker
requirements:
DockerRequirement:
dockerPull: python:3.12-slim
Initial Work Directory
requirements:
InitialWorkDirRequirement:
listing:
- entryname: script.py
entry: |
#!/usr/bin/env python3
print("Hello from Python")
- entryname: config.json
entry: |
{"setting": "value"}
- $(inputs.input_file)
Resource Requirements
requirements:
ResourceRequirement:
coresMin: 2
coresMax: 4
ramMin: 4096
ramMax: 8192
tmpdirMin: 10240
outdirMin: 10240
Environment Variables
requirements:
EnvVarRequirement:
envDef:
PATH: "/usr/local/bin:$(PATH)"
PYTHONUNBUFFERED: "1"
Inline JavaScript
requirements:
InlineJavascriptRequirement: {}
inputs:
value:
type: int
inputBinding:
valueFrom: $(self * 2)
Advanced Patterns
Conditional Arguments
inputs:
verbose:
type: boolean?
default: false
arguments:
- valueFrom: |
${
if (inputs.verbose) {
return "--verbose";
} else {
return null;
}
}
Dynamic Output Names
inputs:
input_file:
type: File
outputs:
output_file:
type: File
outputBinding:
glob: |
${
return inputs.input_file.nameroot + "_processed.txt";
}
Capture Success/Exit Codes
successCodes: [0]
temporaryFailCodes: [1, 2]
permanentFailCodes: [3, 4]
outputs:
exit_code:
type: int
outputBinding:
glob: .
outputEval: $(runtime.exitCode)
Complete Examples
Simple Python Script
cwlVersion: v1.2
class: CommandLineTool
baseCommand: [python]
requirements:
DockerRequirement:
dockerPull: python:3.12-slim
InitialWorkDirRequirement:
listing:
- entryname: script.py
entry: |
import sys
with open(sys.argv[1]) as f:
data = f.read()
with open('output.txt', 'w') as f:
f.write(data.upper())
arguments:
- script.py
- $(inputs.input_file.path)
inputs:
input_file:
type: File
outputs:
output_file:
type: File
outputBinding:
glob: output.txt
Command with Multiple Options
cwlVersion: v1.2
class: CommandLineTool
label: Image Processor
doc: Process images with various filters
baseCommand: [convert]
requirements:
DockerRequirement:
dockerPull: dpokidov/imagemagick:7.1.0-57
inputs:
input_image:
type: File
doc: "Input image file"
inputBinding:
position: 1
resize:
type: string?
doc: "Resize dimensions (e.g., 800x600)"
inputBinding:
prefix: -resize
quality:
type: int?
default: 90
doc: "JPEG quality (1-100)"
inputBinding:
prefix: -quality
output_format:
type: string
default: "jpg"
doc: "Output format"
arguments:
- valueFrom: "output.$(inputs.output_format)"
position: 100
outputs:
output_image:
type: File
outputBinding:
glob: "output.*"
Testing Your CWL
Local Validation
cwltool --validate my-tool.cwl
Local Execution
cat > test-inputs.json << EOF
{
"input_file": {
"class": "File",
"path": "test.txt"
},
"threshold": 0.5
}
EOF
cwltool my-tool.cwl test-inputs.json
Deploy to Weaver
weaver deploy -u $WEAVER_URL -p my-tool -b my-tool.cwl
Best Practices
- Use descriptive names: Clear input/output names
- Add documentation: Use
doc and label fields
- Specify types clearly: Be explicit with types
- Use proper positions: Order arguments logically
- Pin Docker versions: Use specific image tags
- Test locally first: Use cwltool before deploying
- Handle optional inputs: Use
? for optional
- Capture all outputs: Don't lose important files
- Use runtime variables:
$(runtime.outdir), etc.
- Version your CWL: Track changes
Related Skills
Documentation
Templates
See the examples in this skill as starting templates for your own CWL packages!