| name | commandbox-task-runners |
| description | Use this skill for CommandBox task runners: creating task CFCs, targets, passing parameters, lifecycle events (preTask/postTask/onError), interactive jobs with job DSL, progress bars, async/threading with AsyncManager, watching files, running commands from tasks, shell integration, property files, downloading files, sending email from tasks, and task target dependencies. |
CommandBox Task Runners
Overview
Task Runners are CFML/BoxLang CFCs that automate build processes, migrations, and workflows. They are analogous to Ant/Gradle tasks but written in CFML. Tasks live in your project folder and are not globally registered — they are context-aware based on your current working directory.
task create
task create --open
task run
task run path/to/myTask
task run path/to/myTask myTarget
Task Anatomy
component {
property name="artifactService" inject="artifactService";
function run() {
print.greenLine( "Build complete!" );
}
function build() {
print.line( "Building..." );
command( "package version patch" ).run();
print.greenLine( "Built!" );
}
function deploy( required string environment, boolean verbose=false ) {
print.line( "Deploying to #environment#" );
if ( verbose ) {
print.yellowLine( "Verbose mode on" );
}
}
}
Default conventions:
- Default task file:
task.cfc in current directory
- Default target method:
run()
task run
task run build
task run workbench/build
task run workbench/build createZips
Passing Parameters
Named parameters (recommended)
task run fun greet :name=Brad :verbose=true
Positional parameters
task run fun greet Brad true
Boolean flags
task run --:verbose
task run --no:verbose
task run --!:verbose
Dynamic values
task run :message=`cat message.txt`
task run taskFile=deploy :env=${DEPLOY_ENV:staging}
Lifecycle Events
Special methods that fire automatically around target execution:
component {
function preTask( string target, struct taskArgs ) {
print.line( "Starting task: #target#" );
}
function postTask( string target, struct taskArgs ) {
print.line( "Finished task: #target#" );
}
function aroundTask( string target, struct taskArgs, any invokeUDF ) {
var startTime = getTickCount();
local.result = invokeUDF();
print.line( "Elapsed: #(getTickCount()-startTime)#ms" );
return local.result;
}
function preRun() {
print.line( "Before run" );
}
function postRun() {
print.line( "After run" );
}
function onComplete( string target, struct taskArgs ) {
print.line( "Always fires" );
}
function onSuccess( string target, struct taskArgs ) {
print.greenLine( "Task succeeded!" );
}
function onFail( string target, struct taskArgs ) {
print.redLine( "Task failed!" );
}
function onError( string target, struct taskArgs, any exception ) {
print.redLine( "Exception: #exception.message#" );
}
function onCancel( string target, struct taskArgs ) {
print.yellowLine( "Task cancelled by user" );
}
function run() {
print.line( "Hello!" );
}
}
Limit lifecycle events to specific targets:
this.preTask_only = "run,build";
this.postTask_except = "cleanUp";
Task Target Dependencies
component {
this.depends_build = "clean,compile";
function clean() {
directoryDelete( "dist", true );
}
function compile() {
}
function build() {
zip( action="zip", file="dist/app.zip", source="src" );
}
}
Interactive Jobs (Progress Display)
function run() {
job.start( "Deploying application" );
job.addLog( "Connecting to server..." );
job.addSuccessLog( "Connected!" );
job.addWarnLog( "Using staging credentials" );
job.addErrorLog( "Warning: old config detected" );
job.addLog( "Uploading files..." );
if ( success ) {
job.complete();
} else {
job.error( "Deploy failed: connection refused" );
}
job.start( "Running migrations", lineSize=10 );
job.addLog( "Migration 001..." );
job.addLog( "Migration 002..." );
job.complete();
}
Progress Bar
function run() {
var total = 100;
progressBar.update( percent=0, currentCount=0, totalCount=total );
for ( var i = 1; i <= total; i++ ) {
progressBar.update( percent=int((i/total)*100), currentCount=i, totalCount=total );
}
print.line();
}
Print Helper (ANSI Output)
function run() {
print.line( "Normal text" );
print.greenLine( "Success message" );
print.redLine( "Error message" );
print.yellowLine( "Warning" );
print.cyanLine( "Info" );
print.boldLine( "Bold text" );
print.boldGreenLine( "Bold green" );
print.green( "Processing..." );
print.line();
print.table(
headers = [ "Name", "Version", "Status" ],
data = [
[ "coldbox", "7.0.0", "active" ],
[ "testbox", "5.0.0", "active" ]
]
);
}
Running Commands from Tasks
function run() {
command( "install coldbox" ).run();
var result = command( "package show version" ).run( returnOutput=true );
print.line( "Version: #result#" );
command( "server stop" )
.params( name="myApp" )
.run();
command( "!git pull origin main" ).run();
var exitCode = command( "testbox run" ).run( returnExitCode=true );
if ( exitCode != 0 ) {
error( "Tests failed!" );
}
}
Shell Integration
function run() {
var result = shell( "ls -la" );
var result = shell( "dir" );
shell( command="npm install", dir="/my/app" );
}
Async / Threading
function run() {
var threadName = createGUID();
cfthread( action="run" name=threadName ) {
}
cfthread( action="join" name=threadName );
var results = async().all(
() => command( "install module1" ).run( returnOutput=true ),
() => command( "install module2" ).run( returnOutput=true )
).get();
}
Watching Files
function run() {
watch()
.paths( "**.cfc,**.cfm" )
.inDirectory( getCWD() )
.withDelay( 500 )
.onChange( function() {
command( "testbox run" ).run();
} )
.start();
}
Property Files
function run() {
var props = propertyFile( getCWD() & "/config.properties" );
var dbHost = props[ "db.host" ];
props[ "build.version" ] = "2.0.0";
props.store();
}
Downloading Files
function run() {
var filePath = getCWD() & "/downloads/package.zip";
progressable
.download( "https://example.com/package.zip", filePath )
.withProgressBar()
.start();
}
Sending Email from Tasks
function run() {
bx:mail
from="build@example.com"
to="team@example.com"
subject="Build #packageVersion# complete"
server="#mailServer#"
port=587
{
writeOutput( "The build finished successfully." );
}
}
Cancelling Long Tasks
function run() {
for ( var i = 1; i <= 1000; i++ ) {
if ( isCancelled() ) {
print.yellowLine( "Cancelled at step #i#" );
return;
}
}
}
function onCancel() {
print.redLine( "Cleaning up temporary files..." );
directoryDelete( "/tmp/build", true );
}
Hitting a Database
component {
property name="datasource" inject="coldbox:datasource:myDS";
function run() {
var q = queryExecute(
"SELECT * FROM users WHERE active = :active",
{ active: { value: 1, cfsqltype: "integer" } },
{ datasource: "myDS" }
);
q.each( function( row ) {
print.line( "#row.name# - #row.email#" );
} );
}
}
Loading Ad-Hoc JARs
component {
function run() {
classLoad( getCWD() & "/lib/mylib.jar" );
var obj = createObject( "java", "com.example.MyClass" );
}
}
Loading Ad-Hoc Modules
component {
function run() {
modulesConfig = { "myTempModule": { "path": getCWD() & "/myModule" } };
moduleService.registerAndActivateModule( "myTempModule", getCWD() & "/myModule" );
var svc = getInstance( "myService@myTempModule" );
svc.doWork();
}
}