Add items (like Mailpit or tailing logs) to the CommandBox Tray

I’ve always been a fan of the tray that’s available when CommandBox launches. On the Mac, it appears as a menu bar icon in the top menu (first icon below).

CommandBox menu bar icon
CommandBox menu bar icon

It has some very convenient built-in options:

Servername
├── Stop Server
├── Restart Server
├── Open...
│   ├── Webroot
│   ├── Server Home
│   ├── Site Home
│   └── Server Admin
├── Info
│   ├── Engine: adobe 2023.0.17+330864
│   ├── Webroot: /Path/to/website/
│   ├── URL: https://127.0.0.1:8443
│   ├── PID: 99999
│   └── Heap: Not set

I frequently need to access Mailpit and various log files as I work through the development process on a current project. This got me wondering if I could add some of my own frequently accessed items to the tray. It turns out you can add quite a few different things.

Enable the tray

First, enable the tray in the server.json file for the site.

"trayEnable": true,

Next, let’s start simple and add a single item to open Mailpit in a browser. The trayOptions setting accepts an array of objects. Each object should contain a label, an action, and one of [url, path, command]. The label is the text that appears in the tray. The action tells CommandBox what to perform (openbrowseropenfilesystemrunAsync, etc.). The [url, path, command] values provide the URL of the site, the filesystem path, or the command to execute, respectively.

Setting the tray options

"trayOptions": [
    {
        "label": "Mailpit Web Interface",
        "action": "openbrowser",
        "url": "http://localhost:8025"
    }
]

Let’s dive a little deeper with some other options.

Add a “divider” of blank space between CommandBox’s built-in options and the ones we are adding.

{
    "label": " ",
    "disabled": true
}

Open a folder on the filesystem.

{
    "label": "Open Log Folder",
    "action": "openfilesystem",
    "path": "${serverinfo.serverHomeDirectory}/WEB-INF/cfusion/logs"
}

Tail the last 100 lines of mailsent.log (macOS only as written, but adaptable for Linux or Windows).

{
    "label": "Tail mailsent.log",
    "action": "runAsync",
    "command": "tmpfile=$(mktemp -t tail-mailsent).command; printf '#!/bin/zsh\nprintf \"\\e]0;Tail mailsent.log\\a\"\nclear\ntail -n 100 -f \"%s\"\n' '${serverinfo.serverHomeDirectory}/WEB-INF/cfusion/logs/mailsent.log' > \"$tmpfile\"; chmod +x \"$tmpfile\"; open -a Terminal \"$tmpfile\""
}

Explanation of the tail command

Add some icons using emojis.

🛠 Development Tools
📬 Mailpit Web Interface
📂 Logs

Putting it all together

CommandBox tray open
CommandBox tray open
"trayEnable": true,
"trayOptions": [
    {
        "label": " ",
        "disabled": true
    },
    {
        "label": "🛠 Development Tools",
        "items": [
            {
                "label": "📬 Mailpit Web Interface",
                "action": "openbrowser",
                "url": "http://localhost:8025"
            },
            {
                "label": "📂 Logs",
                "items": [
                    {
                        "label": "Open Log Folder",
                        "action": "openfilesystem",
                        "path": "${serverinfo.serverHomeDirectory}/WEB-INF/cfusion/logs"
                    },
                    {
                        "label": "Tail application.log",
                        "action": "runAsync",
                        "command": "tmpfile=$(mktemp -t tail-application).command; printf '#!/bin/zsh\nclear\ntail -n 100 -f \"%s\"\n' '${serverinfo.serverHomeDirectory}/WEB-INF/cfusion/logs/application.log' > \"$tmpfile\"; chmod +x \"$tmpfile\"; open -a Terminal \"$tmpfile\""
                    },
                    {
                        "label": "Tail exception.log",
                        "action": "runAsync",
                        "command": "tmpfile=$(mktemp -t tail-exception).command; printf '#!/bin/zsh\nclear\ntail -n 100 -f \"%s\"\n' '${serverinfo.serverHomeDirectory}/WEB-INF/cfusion/logs/exception.log' > \"$tmpfile\"; chmod +x \"$tmpfile\"; open -a Terminal \"$tmpfile\""
                    },
                    {
                        "label": "Tail mailsent.log",
                        "action": "runAsync",
                        "command": "tmpfile=$(mktemp -t tail-mailsent).command; printf '#!/bin/zsh\nprintf \"\\e]0;Tail mailsent.log\\a\"\nclear\ntail -n 100 -f \"%s\"\n' '${serverinfo.serverHomeDirectory}/WEB-INF/cfusion/logs/mailsent.log' > \"$tmpfile\"; chmod +x \"$tmpfile\"; open -a Terminal \"$tmpfile\""
                    }
                ]
            }
        ]
    }
]

Explanation of the tail command

This command creates a temporary shell script, makes it executable, and opens it in Terminal. Reminder: this version is macOS only as written because it uses zshmktemp, and open -a Terminal.

"command": "tmpfile=$(mktemp -t tail-mailsent).command; printf '#!/bin/zsh\nprintf \"\\e]0;Tail mailsent.log\\a\"\nclear\ntail -n 100 -f \"%s\"\n' '${serverinfo.serverHomeDirectory}/WEB-INF/cfusion/logs/mailsent.log' > \"$tmpfile\"; chmod +x \"$tmpfile\"; open -a Terminal \"$tmpfile\""

What it does

  • mktemp -t tail-mailsent
    • Creates a uniquely named temporary file.
  • printf '#!/bin/zsh ...'
    • Writes a small shell script into the temporary file.
  • printf "\e]0;Tail mailsent.log\a"
    • Sets the Terminal window title.
  • clear
    • Clears the Terminal window before output begins.
  • tail -n 100 -f
    • Displays the last 100 lines of the log file and continues following new entries in real time.
  • chmod +x
    • Makes the temporary script executable.
  • open -a Terminal
    • Opens the script in the macOS Terminal application.

Next Steps

There are all kinds of other tasks that could be added to the tray such as:

  • Ping a server
  • Flushing DNS
  • Open the hosts file to edit
  • Open the site’s Git repo

Good luck!

Mailpit + ColdFusion Local Email Testing

Overview

This article documents how to configure Adobe ColdFusion to use Mailpit for local email testing.

Mailpit captures outbound email locally so messages can be reviewed in a browser without sending real email.


Export Existing Mail Settings (Optional)

Before making changes, export the current ColdFusion mail configuration so it can be restored later if needed.

cfconfig export from=server.local to=mailSettings.json includeList=mailservers**

Example exported configuration:

{
    "mailServers": [
        {
            "tls": true,
            "password": "SUPERSECRET",
            "port": 587,
            "username": "USERNAME",
            "ssl": false,
            "smtp": "smtp.mailgun.org"
        }
    ]
}

Install Mailpit

Install Mailpit using Homebrew.

brew install mailpit

Start Mailpit

Start the Mailpit service.

brew services start mailpit

Configure ColdFusion to Use Mailpit

Method 1: Configure with CommandBox

Create a file named mailSettingsMailpit.json.

{
    "mailServers": [
        {
            "tls": false,
            "password": "",
            "port": 1025,
            "username": "",
            "ssl": false,
            "smtp": "127.0.0.1"
        }
    ]
}

Import the Mailpit configuration into ColdFusion.

cfconfig import from=mailSettingsMailpit.json to=server.local

Method 2: Configure in ColdFusion Administrator

Navigate to:

ColdFusion Administrator → Server Settings → Mail

Configure the following values:

Mail Server: 127.0.0.1
Server Port: 1025
Username: blank
Password: blank
Use TLS: unchecked
Use SSL: unchecked

Test Email Delivery

Create a .cfm test page.

<cfscript>
    recipientCount = 5;

    for (i = 1; i <= recipientCount; i++) {

        recipient = "foo#i#@bar.com";

        cfmail(
            to = recipient,
            from = "[email protected]",
            subject = "Mailpit Test",
            type = "html"
        ) {

            writeOutput("
                <h1>Hello from ColdFusion</h1>
                <p>
                    It is #dateFormat(now(), 'mm/dd/yyyy')# at
                    #dateTimeFormat(now(), 'hh:mm:ss tt')#.
                    This should show up in Mailpit.
                </p>
            ");
        }

        sleep(3000);

        writeOutput("<p>Mail sent.</p>");
    }

    writeOutput('<p><a href="#cgi.script_name#">Run again</a></p>');
</cfscript>

Verify Email Delivery

Open the Mailpit web interface:

http://localhost:8025

The test emails should appear in the Mailpit inbox.

Mailpit inbox
Mailpit inbox

Restore Original Mail Settings (Optional)

Restore the original ColdFusion mail configuration.

cfconfig import from=mailSettings.json to=server.local

Restart ColdFusion after restoring the configuration if required.


Stop Mailpit

Stop the Mailpit service.

brew services stop mailpit

Set CommandBox to the default terminal profile in VSCode terminal

Here is a helpful workspace configuration option(s) that allows you to set the default terminal profile (terminal.integrated.defaultProfile.YOUR_OS) to CommandBox in VSCode terminal.  This allows you to launch CommandBox by default when you open a terminal in the workspace.  Further, you can specify the current working directory (terminal.integrated.cwd) so any new terminal you open in the workspace will start in that directory.

On OSX

"settings": {
  "terminal.integrated.profiles.osx": {
    "CommandBox": {
      "path": "/Users/csimmons/.CommandBox/bin/box"
    }
  },
  "terminal.integrated.defaultProfile.osx": "CommandBox",
  "terminal.integrated.cwd": "/Users/csimmons/websites/demos"
}

On Windows

"settings": {
  "terminal.integrated.profiles.windows": {
    "CommandBox": {
      "path": "C:\\Users\\csimmons\\.CommandBox\\box.exe"
    }
  },
  "terminal.integrated.defaultProfile.windows": "CommandBox",
  "terminal.integrated.cwd": "C:\\Users\\csimmons\\websites\\demos"
}

Note(s)

  • These settings are stored in YOUR_WORKSPACE.code-workspace so you can set them on a per workspace basis.
  • YOUR_OS = the OS you are running
  • YOUR_WORKSPACE = whatever the .workspace file is for your current workspace.

This post is a follow up to a topic I originally posted titled Run CommandBox directly inside VSCode Terminal which explained setting up a custom terminal profile (terminal.integrated.profiles.YOUR_OS) for CommandBox.

Revisiting CFML Formatter (VSCode extension) with cfformat-ignore

I’d like to revisit the CFML Formatter (VSCode extension) that I posted on not too long ago and mention the cfformat-ignore functionality.

I had to work on some 15+ year old code today. I won’t say who wrote that code. Cough Me. Cough Embarrassing. Every time I saved a file the CFML Formatter was working overtime to try to figure out how to get the code formatted. Long story short there were a few blocks involving concatenation that ended up throwing errors after they were reformatted. Due to time constraints I needed to put a pin in figuring how to rewrite the offending blocks and just needed cfformat to ignore those blocks.

In a nutshell there are 3 ways to do it depending on the block of code.

Tag Based

Script Based

Comment Block

    /* cfformat-ignore-start */
    /* Crazy, unformatted code here. */
    /* cfformat-ignore-end */

Note: The special cfformat-ignore-start and cfformat-ignore-end comments must be at the same level of indentation within the file to work properly.

Cleaner code with CFML Formatter (VSCode extension) + cfformat (CommandBox module)

I don’t work on a lot of non-CFML development but I had a couple of PHP projects and a JavaScript project I was working on last month. In an effort to tidy up my code in those projects I started using Prettier. I even wrote a post on Prettier and how I would be including it in future projects. Well, this weekend I was working on a CFML project and came across Mark Drew’s incredible CFML formatter extension for Visual Studio Code. Per the documentation:

CFML formatter is a Visual Studio Code extension that provides formatting for CFML files using Lucee Server and CFFormat

CFML formatter and cfformat are two great tools you can use to:

  1. Set and implement coding standards for yourself and/or your team.
  2. Format code in real time as you work in Visual Studio Code.
  3. Scan, review, and even format code issues manually or in an automated manner by watching directories.

Installation

CFML formatter

Install the CFML formatter VSCode extension from the Extensions view in Visual Studio Code.

cfformat

Install the cfformat CommandBox module by launching CommandBox and running the following command.

box install commandbox-cfformat

A few things you can do with CFML formatter

Format code on save

This will format your code using CFML formatter every time you save a file in Visual Studio Code (you can define the rules using a .cfformat.json file which you can also share with your team!).

To configure Format code on save:

  • Open Settings by pressing Cmd+, for Mac (or CTRL+, for Windows/Linux).
  • Type format in the search box and enable the option Format On Save.
Format on save in VSCode
Format on save in VSCode

Format code using right click

This will format your code using CFML formatter when you right click in the Visual Studio Code editor and choose Format Document

More info on CFML formatter

You should also check out CFRules.

Read the full CFML formatter documentation at Visual Studio Marketplace.

A few things you can do with cfformat

Run a wizard

cfformat settings wizard

The wizard will:

  1. Walk you through all settings.
  2. Display the options AND what those options will do to an example of code.
  3. Indicate the default setting.
  4. Generate a .cfformat.json file for you based on your choices.

This wizard option is incredible!

View existing settings

cfformat settings show

cfformat settings show command in CommandBox
cfformat settings show command in CommandBox

These settings can be stored in a .cfformat.json file in your Visual Studio Code project. They will then govern Format code on save and the Format Document on right click action.

Read the full settings reference at: https://github.com/jcberquist/commandbox-cfformat/blob/master/reference.md.

Learn more about a setting

cfformat settings info tab_indent

cfformat settings show info command in CommandBox
cfformat settings show info command in CommandBox

Checking Tag structure

This option looks for:

tags that are unbalanced or incorrectly structured

Check a file:

cfformat tag-check about.cfm

cfformat tag-check file command in CommandBox
cfformat tag-check file command in CommandBox

Check a directory:

cfformat tag-check

cfformat tag-check command in CommandBox
cfformat tag-check command in CommandBox

Not only will cfformat tag-check locate cfml tag issues it will also locate html tag issues as shown above!

More info on cfformat

Check out some other really cool stuff like Watching Directories and Ignoring Code Sections.

Read the full cfformat documentation at FORGEBOX.

In Summary

My workflow is going to be:

  1. Generate a .cfformat.json file in my CFML project using the cfformat CommandBox module
  2. Let CFML formatter format my code on save in Visual Studio Code (governed by the .cfformat.json file)
  3. Periodically I will manually run cfformat tag-check on my project using the cfformat CommandBox module.

Using cfpm in CommandBox to List packages, Install a package, Export package list

Method 1

This example assumes you are running ColdFusion 2021 via CommandBox.

Launch CommandBox and run the following command(s):

> set CFPM_SERVER=YOUR_SERVER_NAME
> cfpm list
> cfpm install <cfpackagename>
> cfpm export path/to/packages.txt
> env clear CFPM_SERVER

Method 2

This example assumes you are NOT running ColdFusion via CommandBox. It assumes you are running a default installation of ColdFusion 2021 (Path is for Windows).

Launch CommandBox and run the following command(s):

> !C:\ColdFusion2021\cfusion\bin\cfpm.bat list
> !C:\ColdFusion2021\cfusion\bin\cfpm.bat install <cfpackagename>
> !C:\ColdFusion2021\cfusion\bin\cfpm.bat C:\path\to\packages.txt

Run CommandBox directly inside VSCode Terminal

Run CommandBox directly inside VSCode Terminal

Initially I looked at the CommandBox documentation for running it inside VSCode: https://commandbox.ortusbooks.com/ide-integrations/visual-studio-code. However, the Shell Launcher extension was deprecated in favor of Terminal Profiles in the Integrated Terminal (VSCode >= v1.55). See this article: https://code.visualstudio.com/updates/v1_55#_terminal-profiles.

Set up a Terminal Profile for CommandBox:

  1. Open VSCode Preferences > Settings
  2. Search for terminal.integrated.profiles.osx (Replace osx with windows or linux based on your os)
  3. Click “edit in settings.json”
  4. Add the following under terminal.integrated.profiles.osx
    "CommandBox": {
        "path": "path/to/box"
    }
  1. Close and save settings.json

Launch CommandBox from VSCode terminal

  1. Press Cmd + Shift + P or Ctrl + Shift + P to launch the Command Palette
  2. Begin typing Terminal
  3. When you see Create New Terminal (With Profile) press Enter
  4. Under Select the terminal profile to create you should see the CommandBox profile you created. Select it with the down arrow key and press Enter
  5. CommandBox should launch in the terminal window