optimizing your daily workflow

Scott Steinbeck

  • Software Developer
  • 15+ Year of experience 
  • Father
  • Hardware Tinkerer

Hobbies

 
  • Coding on my free time
  • Overcommitting myself
  • Automating everything
  • IOT Development
  • Teaching Coding/Electronics
  • Contributing to open source

What is CommandBox?

  • Is it a CLI ?
  • Does it scaffold apps like yeoman?
  • Is it a REPL (Read Evauluate Print Loop)
  • Can it start servers?
  • Is it configurable?
  • Is it only for lucee?
  • Is it secure?
  • Does it look cool?
  • Can I build custom plugins?
  • Can I add core features?
  • Is it only for coldfusion development?
  • Is it better than node?
  • Is it easy to use?

Installation

brew install commandbox
curl -fsSl https://downloads.ortussolutions.com/debs/gpg | sudo apt-key add -

echo "deb https://downloads.ortussolutions.com/debs/noarch /" | sudo tee -a /etc/apt/sources.list.d/commandbox.list

sudo apt-get update && sudo apt-get install apt-transport-https commandbox

https://www.ortussolutions.com/products/commandbox

Download the binary

CommandBox Commandline Tools

CommandBox REPL

CommandBox> set foo=bar
CommandBox> REPL

CFSCRIPT-REPL: echo( '${foo}' )
bar

CFSCRIPT-REPL: breakfast = ['bacon','eggs']
=> [
    "bacon",
    "eggs"
]
CFSCRIPT-REPL: breakfast.len()
=> 2
CFSCRIPT-REPL: breakfast.append( 'orange juice' )
=> [
    "bacon",
    "eggs",
    "orange juice"
]

JMES JSON filtering / jq Command

CommandBox> jq [1,2,-3,4,-5,6,-7,-8,9] [?contains('-8',@)]
=> [
    -8
]

CommandBox> jq box.json pluck(@,'name') # only show 'name' key

CommandBox> jq box.json omit(@,'name,version') # show all keys except 'name & version' keys

CommandBox> jq box.json keys(@) # list an array of all keys in a struct

CommandBox> jq box.json values(@) # list an array of all values in a struct

CommandBox> jq box.json to_entries(@)
=> [
  {
    "key":"author",
    "value":"Scott Steinbeck"
  },
  {
    "key":"bugs",
    "value":""
  },
  {
    "key":"changelog",
    "value":""
  }...
  
  

JMES JSON filtering / jq Command (2)

Commandbox> jq box.json key_contains(@,'on')
=> {
  "shortDescription":"",
  "instructions":"",
  "version":"0.0.0",
  "location":"ForgeboxStorage",
  "documentation":"",
  "contributors":[],
  "description":""
}

# filter the server list and then group by the value of the status key
Commandbox>  server list --json | jq "group_by([].{name: name, status: status},'status')" 

# Can also be written with pipes
# Step 1. filter server list array to just name and status keys
# Step 2. group by @ (each item) where the value of 'status' is the same
Commandbox> server list --json | jq "[].{name: name, status: status} | group_by(@,'status')" 

=> {
  "running":[
    {
      "status":"running",
      "name":"Server 1"
    }
  ],
  "stopped":[
    {
      "status":"stopped",
      "name":"Server 2"
    },...

CommandBox Settings


# commandbox CLI settings
config set tabCompleteInline=true #show autocomplete as dropdown
config set preferredBrowser=chrome #set preferred browser 

# server defaults (get used if not set in a sites box.json)
config set server.defaults.web.rewrites.enable=true
config set server.defaults.openbrowser=false
config set server.defaults.jvm.heapsize=1024

# commandbox module setting overrides 
config set modules.TestModule.mySetting=overridden
config set modules.TestModule.somethingEnabled=false

Server Management

server list

site1 (starting)
  https://127.0.0.1:8081
  C:\site1

site1 (running)
  https://127.0.0.1:8082
  C:\site2

site3 (stopped)
  https://127.0.0.1:8083
  C:\site3
  
# Running servers
server list --running

# Stop Servers
server stop site1
server stop --all

# Clean up old severs
server prune days=30

Env Var Overrides


# CommandBox CLI config settings
# config set tabCompleteInline=true #show autocomplete as dropdown
env set box_config_tabCompleteInline=true


# CFConfig Settings
# configure admin password for all servers
env set cfconfig_adminPassword myPassword
env set cfconfig_scopeCascading "standard"
env set cfconfig_whitespaceManagement= "smart"

# Server.json settings
env set box_server_profile=production
env set box_server_web_http_port=8080
env set box_server_web_ssl={ "enabled" : true, "port": 443 }

Env Variables override any local settings

perfect for setting docker container settings

Trick for "cd"ing up directories

// current directory
cd .   -> ./
// back 1 directory
cd ..  -> ../
// back 2 directories
cd ... -> cd ../../
// back 3 directories
cd .... -> cd ../../../ 

Command Shortcuts

box> !myApp.exe
box> !git pull
box> !dir

Shortcut for Native OS Binaries

box> #now
> {ts '2016-01-19 16:14:23'}
box> #hash mypass
> A029D0DF84EB5549C641E04A9EF389E5
box> #reverse abc
> cba

Shortcut for CFML Functions via REPL

box> #listGetAt www.foo.com 2 . | #ucase | #reverse
> OOF
box> package show name | #lcase
> my package
box> !java -ver | #ucase
> JAVA VERSION "1.8.0_92"

 Piping commands

printTable

sql Command

  • select

  • where 

  • orderby

  • limit
  • headerNames
#extensionlist  | sql select=id,name >> extentions.json

recipe's

# Recomended CommandBox system modules
install commandbox-bullet-train,commandbox-cfconfig,commandbox-cfformat,commandbox-dotenv,commandbox-update-check,commandbox-fusionreactor,cfdocs,commandbox-githooks,commandbox-migrations,quick-commands,commandbox-cflint,commandbox-bookmarks

# Recommended CommandBox shortcuts
config set command.aliases.git="!git"
config set command.aliases.gulp="!gulp"
config set command.aliases.npm="!npm"

essentials.boxr

David Belanger

box> recipe essentials.boxr

Great for setting up new dev's with all of the tools

Tasks

Automate common jobs

/**
* Description of task
*/
component {

	/**
	* 
	*/
	function run() {
		print.greenLine( 'Complete!' );
	}

}
CommandBox> task run task.cfc
Complete!

Simple Job

Task will execute anything in the run method and finish

component{
    function greet( string name, boolean verbose ){
        print.line( 'Well, hello there #name#!' );
        if( verbose ) {
            print.line( "You're looking quite well today." );
        }
    }
}
task run taskFile=fun target=greet :name=Brad :verbose=false

Paramaters

&

Alternate Method Names

Tasks

Available base class methods

// Returns the AsyncManager class
async()

// Convenience method for getting stuff from WireBox
getInstance( name, dsl, initArguments={}, targetObject='' )

// Retuns current exit code
getExitCode()

// Sets exit code to be returned when task completes
setExitCode( required numeric exitCode )

// Returns the current working directory of the shell
getCWD()

// ask the user a question and wait for response
ask( message, string mask='', string defaultResponse='', keepHistory=false, highlight=true, complete=false )

// Wait until the user's next keystroke amd return the char code
waitForKey( message='' )

// Ask the user a question looking for a yes/no response and return a boolean
confirm( required message )

// Intiator for multiselect DSL. (Check "task interactiviy" page in docs)
multiSelect()

// Run another command by name.
runCommand( required command, returnOutput=false )
// Intiator for Command DSL. (Check "running other commands" page in docs)
command( required name )

// Intiator for directory watcher DSL.  (Check "Watchers" page in docs)
watch()

// This resolves an absolute or relative path using the rules of the operating system and CLI.
resolvePath( required string path, basePath=shell.pwd() )

// Intiator for PropertyFile DSL (check "property files" page in docs)
propertyFile( propertyFilePath='' )

// Report error in your task. Raises an exception that will not print the stack trace
error( required message, detail='', clearPrintBuffer=false, exitCode=1 )

// Open a file or folder externally in the default editor for the user.
openPath( path )

// Open a URL in the user's browser
openURL( theURL, browser='' )

// Get and Set Environment Variables
setSystemSetting( required string key, string value )
getSystemSetting( required string key, defaultValue )
getSystemProperty( required string key, defaultValue )
getEnv( required string key, defaultValue )

// Call this method periodically in a long-running task to check and see
// if the user has hit Ctrl-C.
checkInterrupted( thisThread=variables.thisThread )

// Loads up Java classes into the class loader that loaded the CLI for immediate use.
classLoad( paths )

Additional Task Features

  • Printing Tables
  • Threading/Async
  • Interactive Shell
  • Downloading Files
  • Sending Emails
  • File/folder watchers
  • Sending emails
  • Running other tasks
  • And Many More....

https://commandbox.ortusbooks.com/task-runners

job.start( 'Starting server' );
  job.addLog( 'This is the server name' );
  job.addWarnLog( 'Hey, don''t touch that dial' );

    job.start( 'Installing CF Engine first' );
      job.addLog( 'This was the version used' );
      job.addLog( 'Yeah, we''re done' );
    job.complete();

  job.addLog( 'Aaand, we''re back!.' );
  job.addErrorLog( 'I think we''re going to crash' );

job.error( 'Didn''t see that coming' );

Task - Task Manager

component {

    this.config = [
        {'name': 'Update models', 'file':'weather_models.cfc','timespan': createtimespan(0,0,60,0)},
        {'name': 'Setup stations','file':'setup_station.cfc', 'timespan': createtimespan(0,1,0,0)}    ];

    function run(){
        if(!this.config.len())print.greenLine("We have no cron jobs..... goodbye").toConsole();  return;
        
        //setup config vars
        for(var cronItem in this.config){ cronItem.lastRun = 0; }

        while( true ) {
            for(var cronItem in this.config){
                var timeElapsedMs = GetTickCount() - cronItem.lastRun;
                if(timeElapsedMs > cronItem.millis) {

                    //run our task
                    try{
                        task(cronItem.file).run(); //run the task
                    } catch (any e) {
                        print.line(e.Message).toConsole();
                        print.line(e.Detail).toConsole()
                        savecontent variable="mailBody" {  writeDump(e); };

                        // Send
                        mailService.send();
                    }
                    cronItem.lastRun = GetTickCount(); //record last run in milliseconds

                }
                sleep( 10000 ); //just a safety 
            }

        }
    }


}

Scaffolding

  • Coldbox
  • Task

Via Extention

  • Quick
  • Intergrated
  • Preside
  • ColdBox module
  • Commandbox module

Would you like more features?

CommandBox Modules

Popular Modules (67 Total)

A command for accessing CFML documentation powered - Pete Freitag

CFFormat

A CommandBox module for formatting CFML component files. -  John Berquist

Convention approach to adding environment variables to JVM args for CommandBox. - Eric Peterson

A CLI library for importing, exporting, transferring, and otherwise managing CF engine configuration. - Ortus

cfScript.me

A CommandBox command for CFML Tag to Script Conversion. - Pete Freitag

A static code analysis tool for CFML.  

Adds support to start your CommandBox servers with FusionReactor - Ortus

Generating API docs from the command line via DocBox

For Development

Popular Modules (67 Total)

commandbox-banner-hack

A CommandBox module for customizing all the text in the CLI startup banner. - Matthew Clemente

A CommandBox module that allows for directory bookmarking. - Adam Euans

 

A conversion tool for taking table like data and converting it to excel, csv, json, or pdf - Scott Steinbeck

Send messages to Slack from the Commandbox command line.

For Commandbox workflow

Automatic reload on file changes via nodejs browsersync - John Wilson

Create a PDF eBook from any Gitbook data export - 

Make a local commandbox site published live for anyone to see - Eric Peterson

This module can check for new versions of the CLI and system modules

 Bullet Train (Toot Toot!!)

Git Repo Info

Current Working Directory

CommandBox Version & Time

Custom Bullet Train Car

Creating modules/commands

box install commandbox-command-scaffold

Creating modules/commands

{
  "author":"Scott Steinbeck",
  "location":"ForgeboxStorage",
  "name":"bookmarks Command",
  "private":false,
  "slug":"commandbox-bookmarks-command",
  "type":"commandbox-modules",
  "version":"0.0.0"
}

box.json

Scaffolding

/**
* This is a description of what 
* this command does!
*
* {code:bash}
* bookmarks param1
* {code} 
**/
component {
  
  /**
  * @param1.hint Description of 
  * the first parameter
  */
  function run( required String param1 ){
      return  'Hello ' & param1 & '!'; 
  }
}

CommandBox Module Hooks

(interception Points)

component {
  function configure(){
    settings = {}; // settings specific to the module
    interceptors = []; //interception points within the module 
  }

  function onCLIStart( interceptData ) {} // when commandbox starts
  function onCLIExit() {} // when commandbox exits
  function onBulletTrain( interceptData ) {   // when bullet train starts    
    var print = wirebox.getInstance( 'print' );
    interceptData.cars.myBulletTrainCar.text = print.whiteOnRed(' This sure is easy! ');
    interceptData.cars.myBulletTrainCar.background = 'red';   
  } 
  function preCommandParamProcess( interceptData ) {} // prior to the time the system settings are expanded
  function preCommand( interceptData ) {} // before the execution of a command
  function postCommand( interceptData ) {} // after command execution is complete
  function prePrompt( interceptData ) {} // before drawing the prompt in the interactive shell
  function preProcessLine( interceptData ) {} // fire before and after each command
  function postProcessLine( interceptData ) {} // after the entire line has been executed.
}

CommandBox

Local Server Development

Mulit Engine Support

# Start the default engine
CommandBox> start

# Start the latest stable Railo engine
CommandBox> start cfengine=railo

# Start a specific engine and version
CommandBox> start cfengine=adobe@10.0.12

# Start the most recent Adobe server that starts with version "11"
CommandBox> start cfengine=adobe@11

# Start the most recent adobe engine that matches the range
CommandBox> start cfengine="adobe@>9.0 <=11"

# Start the most recent adobe engine that matches the range
CommandBox> start cfengine=lucee@5.3 javaVersion=11

CFConfig

Export your coldfusion config settings to a JSON file 

CommandBox> install commandbox-cfconfig

cfconfig export myConfig.json
cfconfig import myConfig.json

#automatically export after when the server is stopped
config set modules.commandbox-cfconfig.exportOnStop=true

Manage CF Mappings

Manage Datasources

Manage Mail Servers

Manage Lucee Caches

cfconfig datasource save name=myDSN dbdriver=mysql host=localhost port=3306 database=myDB username=brad password=foobar
cfconfig datasource save name=myDS ... to=serverName
cfconfig datasource save name=myDS ... to=/path/to/server/home

Manage Custom Tag Paths

Manage Event Gateway Configurations

Manage Lucee Loggers

Manage Scheduled Tasks

Dot Env (Enviroment Variables)

Simple way to store variables that can be used in tons of places throughout commandbox & coldbox

# ColdBox Name and Environment
APPNAME=crazy horse
ENVIRONMENT=development
MYSUPERSECRETPASSWORD=bacon

located in .env file in the root of the project (or in ~./box.env globally)

  • often times contains secrets

Can be used to configure

  • commandbox config
  • box.json
  • server.json
  • cfconfig
  • coldbox config

Debugging

Start a server, follow the logs

server log --follow

# You can also look at your server's access log (if enabled) and rewrite log (if enabled).  
server log --follow -access
server log --follow --rewrite

Start server in console mode

# SMALL 
start --console

# MEDIUM DEBUGGING
start --console --debug

# X-LARGE DEBUGGING
# You may still really be having issues getting your server to start up correctly due to a setting not getting picked up, rewrites not working, or maybe a jar not loading.
start --console --trace

CommandBox

Production Server

HTTPS Redirect/HSTS

{
  "web": {
    "ssl" : {
      "enable" : true,
      "port" : 443,
      "HSTS" : {
        "enable" : true,
        "maxAge" : 31536000,
        "includeSubDomains" : true
      }
     }
  }
}
server.json

HTTP Strict Transport Security (HSTS)

This instructs the browser to automatically use HTTPS

Server Security Profiles

  • Production - Locked down for production hosting

  • Development - Lax security for local development

  • None - For backwards compat and custom setups. Doesn't apply any web server rules​​

Default Profile

  • If there is an env var called environment, it is used to set the default profile
  • If the site is bound on localhost, default the profile to "development". Localhost is defined as any IP address starting with 127.(be careful for reverse proxy)

  • If neither of the above are true, the default profile is "production". This makes CommandBox servers secure by default.

 

Server Security Profiles

Profile Production Development
directoryListing false true
blockCFAdmin external false
blockSensitivePaths true true
blockFlashRemoting true true

Server.json overrides

{
  "profile": "production",
  "web": {
    "blockCFAdmin": false
  }
}

Server Rules

{
    "web" : {
        "rules" : [
            "path-suffix(/box.json) -> set-error(404)",
            "path-suffix(hidden.js) -> set-error(404)",
            "path-prefix(/admin/) -> ip-access-control(192.168.0.* allow)",
            "path(/sitemap.xml) -> rewrite(/sitemap.cfm)",
       		 "disallowed-methods(trace)"
        ],
        "rulesFile" : "..." //or rules file
    }
}
  • Security - Block paths, IPs, or users

  • URL rewrites - Rewrite incoming URLs to something different

  • Modifying HTTP requests on the fly - Set headers, cookies, or response codes

The magic behind Server Profiles

Server Service

[Unit]
Description=mySite Service

[Service]
ExecStart=/usr/bin/box server start /var/www/mySiteAPI/server.json
Type=forking

[Install]
WantedBy=multi-user.target

Starting your site on boot

mySite.service

# place in startup directory
Filepath: /usr/lib/systemd/system/mySite.service

#start the service
systemctl start mySite.service

#stop the service
systemctl stop mySite.service

#check service status
systemctl status mySite.service

CommandBox optimizing your daily workflow

By uniquetrio2000

CommandBox optimizing your daily workflow

  • 463