Automating an HTML email workflow with bash and NPM

I recently encountered an HTML email workflow that I thought could be streamlined through automation. There are some great resources covering general tools for an HTML email workflow, so I won’t cover those here. In my case, I was receiving an HTML email from another team member, and I just needed to do some post-processing steps to prepare it to be sent via an in-house email system.

Understanding the workflow

The original workflow involved a series of manual steps, including making a few updates to the html email file in a text editor, running it through an online CSS inliner tool and then a separate online html to text conversion tool, using FTP to send the processed files up to the dev host server, and then accessing the server via SSH to run a command that would sync the dev host server with the live host server.

In order to streamline this process, I wrote a bash script and made use of some NPM packages to replace the online tools. Here is an overview of what the script does:

  • Uses the sed command in bash to clean up a few things in the original HTML email file
  • Inlines the CSS using the inline-css NPM package
  • Generates a plaintext version of the email using the html-to-text NPM package
  • Sends the processed files up to the dev host server using scp
  • Syncs the dev host server with the live server using a custom command over ssh

How I built it

Let’s walk through the setup in detail. First, I created two folders:

  • html-email-script
  • html-email-folder

The html-email-script folder is for the bash script, NPM packages, and JavaScript files, while the html-email-folder is for the initial html email and then will contain the folder with the processed files when the script is complete.

Next, from within the html-email-script folder, I ran the following command:

npm init -y

This created a starter package.json file. (Note that the -y option prevents it from asking you to fill in the defaults.)

Then I ran:

npm install --save inline-css html-to-text

This created the node_modules folder and package-lock.json. (The –save option adds inline-css and html-to-text as dependencies in package.json.)

In order to make use of the NPM packages, I set up two JavaScript files. The first was inline.js, and it reads in a file and then executes inline-css. Note that I selected the option to avoid removing the style tags so that media queries would remain in place.

var fs = require('fs');

var inlineCss = require('inline-css');
var html = fs.readFileSync(process.argv[2], 'utf8');

var options = {url: ' ', removeStyleTags: false}
 
inlineCss(html, options)	
    .then(function(html) { console.log(html); });

The second JavaScript file was htmltext.js, which uses the html-to-text NPM package to generate a plaintext version of the email. I had to update some of the settings, including ignoring all image links and adding some formatting around headers to make it more readable.

var fs = require('fs');

var htmlToText = require('html-to-text');
var html = fs.readFileSync(process.argv[2], 'utf8');

var text = htmlToText.fromString(html, {
  wordwrap: 100,
  ignoreImage: true,
  noLinkBrackets: true,
  format: {
    heading: function (elem, fn, options) {
      var h = fn(elem.children, options);
      return '\n\n====\n' + h.toUpperCase() + '\n====\n\n';
    }
  }
});
console.log(text);

Finally, I put it all together in a bash script. Note that I’ve generalized the script a bit for clarity.

#!/bin/bash

FOLDER='/Users/username/Desktop/html-email-folder/'

COPY='/tmp/processedhtml'
COPY2='/tmp/processedhtml2'
COPY3='/tmp/processedhtml3'

# Copy original file
cp ${FOLDER}input.html $COPY

# Replace string1 with string2
sed -i='' 's/string1/string2/' $COPY

# Get subject from user and replace SUBJECT placeholder
echo Please provide a subject line for the email
read subjectline
sed -i='' "s/SUBJECT/$subjectline/" $COPY

# Make new folder based on input
echo Please provide the folder name: e.g., 201710-2
read foldername
mkdir ${FOLDER}$foldername

# Run node script with inline-css and copy into output folder
node inline.js $COPY > $COPY2
mv $COPY2 ${FOLDER}$foldername/index.html

# Run node script with html-to-text and copy into output folder
node htmltext.js $COPY > $COPY3
mv $COPY3 ${FOLDER}$foldername/text.txt

# Check if we should move the new folder to the host server
echo Do you want to move the output to the host server?: Y/N
read movetoserver

if [ "$movetoserver" = "Y" ] || [ "$movetoserver" = "y" ]; then

    echo Moving the output to the host server...
    
    # Move new folder to host server    
    scp -r ${FOLDER}$foldername username@hostname:/path/to/folder

    # Sync dev to prod
    ssh username@hostname "command to sync dev to prod server"

else
    echo You chose not to move the output to the host server.
fi

Here is what the html-email-script directory looked like after the setup. Note that I truncated the contents of the node_modules folder.

.
├── email-script
├── htmltext.js
├── inline.js
├── node_modules
│   ├──lots-of-stuff
├── package-lock.json
└── package.json

In order to be able to run the script, it was necessary to update the permissions for the bash file as follows:

chmod +x email-script

Then, after placing the starting html email file as input.html in html-email-folder, the script could be run from within html-email-script as follows:

./email-script

The script reads in input.html and generates a new folder inside of html-email-folder containing the processed html email (as index.html) and the plaintext version (as text.txt). If the user opts to move the output to the host server, it copies that new folder up to the remote server and syncs the dev server with the prod server.

What I learned

I hadn’t done much with bash scripts to this point, so it was fun to find an excuse to write one. While the manual process that the script replaces wasn’t too time consuming, it was a bit cumbersome and therefore prone to human error.

As a WordPress/PHP developer, there were some gotchas that I encountered with bash. For example, the sed command can be a bit tricky. I found that, on a Mac, if you want to use the -i argument with sed (which allows you to edit the file in place), you have to follow it with an extension, which can just be an empty string. You also have to remember to use the backslash to escape special characters ($.*[\^). And, if you’re using variables within the sed command, you have to use double quotes.

Overall, I was quite pleased to be able to automate a slightly annoying html email workflow process. I’m still amazed when I see something that used to take multiple manual steps happen in a matter of milliseconds.

Leave a Reply

Your email address will not be published. Required fields are marked *