Skip to content

Creating new widgets

Workflows in Bwb are comprised of widgets that execute modular tasks in a step-by-step manner. As seen in previous sections, there are multiple parts and settings inside a widget that needs to be set up in order to work as expected. You can use these widgets as examples to see what widgets would look like, as well as the default widgets located on the tool dock. However, if you want to make your own widget for a tool or module not yet implemented in Bwb, there are a couple of ways to do so.

In this episode, you will learn how to create a new widget from three different methods. This new widget will run a simple Bash command that takes in a message the user wants to output, and then it stores this message into a user-defined text file.

After completing this episode, you will:

  • Build a simple bash script to make a text file from a user-generated message.
  • Set up inputs, outputs, and parameters for the widget to run this script.
  • Write a command the widget will execute when running the widget.
  • Write a Dockerfile and build a Docker image for the widget.
  • Customize the widget's name, description, Docker settings (image and tag), and icon for the widget.
  • Test the widget created to see if it works.

There is coding involved in the video that needs to be typed if you want to follow along. Here are the code snippets seen in the video.

The shell script to output the phrase into a text file:

echo-to-file.sh
#!/bin/bash
echo $1 > $OUTPUTFILE

The new widget's Command tab command:

bash _bwb{inputFile} “_bwb{phrase}”

If you want to follow another example of adding and customizing a widget, there is a tutorial for adding a Python2 widget, text and video included, located here: https://github.com/BioDepot/BioDepot-workflow-builder?tab=readme-ov-file#tutorial---adding-a-python-script-to-a-bwb-workflow

Dealing with multi-line commands

The widget's Command tab executes the code in the box as a single-line Bash command. The way the widget treats multi-line code is by then adding "&&" after each new line. When making a widget that contains multi-line code, this is not too much of an issue if each line is dedicated to at least one command. However, if a command extends multiple lines, then there will be issues in the execution when converting the multi-line code to single-line.

For example, if the code contains a multi-line conditional statement, then the widget will fail when executing the command.

THIS WILL NOT WORK.

if [ -n "$SHOWSTATEMENT" ];
then
    printf "Statement will be shown";
fi

This multi-line command, when executed, becomes if [ -n "$SHOWSTATEMENT" ] && then && printf “Statement will be shown”; && fi. Instead, make this conditional statement a single-line of code:

This command should now work.

if [ -n "$SHOWSTATEMENT" ]; then printf "Statement will be shown"; fi

Note: If the command already has && typed at the end of the lines, then it would not work. For example:

This will not work

echo hello &&
echo Bwb

This would be treated as echo hello && && echo Bwb, which the double && would cause problems. Make sure to remove any existing && at the end of the lines, as the widget will add those automatically when executed.

Note: Do not use comments in the Command tab code. Given how Bwb converts multi-line commands into a single line, using the # character to make a comment in Linux makes the executed command ignore everything that comes after the comment. This applies to comments that have their own lines and inline comments.

For example, the following command will does work as intended:

This will not work

echo hello # Greeting here
echo Bwb

Only "hello" is displayed.

In this case, the two-line command converts to echo hello # Greeting here && echo Bwb in Bwb as a single-line command. This means that Greeting here && echo Bwb is treated as a comment and cannot be executed.

Exercise: Adding a shell script directly to the widget's Docker image.

Exercise

Add a shell script directly to the widget's Docker image.

Solution

Adding long Bash code in the Command line might get tricky the more complex the executed command is, so a way to add complex code is by creating a separate shell script dedicated to running in the widget's command line. Then, paste your commands into the script without having to worry about how the widget handles multi-line code. Make sure to copy this shell script in the Dockerfile, and then build the image with this script included.

In the same example widget from the video, the shell script that stores the input phrase into a text file can be directly incorporated into the widget. While the script is relatively short, adding this script directly into the widget not just eliminates the need for a parameter and entry to read the shell script, but it can also declutter the widget's execution command in the Command tab.

To do so, in the widget's Command tab, change the command to the following:

echo-to-file.sh “_bwb{phrase}”

Exercise

Then, go to the Parameters tab and remove the "inputFile" parameter. There is no need to specify the shell script as an input in the entries, because this shell script will be built into the widget internally. You can also delete the parameter in the Inputs tab as well.

Exercise

After removing the parameter, launch the Docker Image Builder in the Docker tab, open the widget's Dockerfile, and add this line of code at the bottom of the Dockerfile:

COPY echo-to-file.sh /usr/local/bin/echo-to-file.sh
RUN chmod +x /usr/local/bin/echo-to-file.sh

Exercise

This copies the script into the Docker image, located at the /usr/local/bin/ directory to make the script executable. Then, use the chmod +x on the script to have permissions to execute it. Make sure to save the Dockerfile with this change. However, the script is not accessible to the widget yet, so you need to copy this shell script into the widget's dedicated "Dockerfiles" directory. The easiest way to access this is to delete the code on the Docker Image Builder (would be the Dockerfile code currently), copy and paste the shell script code into the code block, and save this file as "echo-to-file.sh".

Exercise

You should see the Dockerfile when saving this shell script, so make sure you are adding this shell script in the same directory as the widget's Dockerfile.

Exercise

After saving both the Dockerfile and script in the same folder, reopen the Dockerfile, and set an image name and tag (either to the same one as the tutorial video or a new one), and finally build the image. Once it is finished building, change the Docker image and tag (in the General tab) for the widget to the one you just built, and save the widget.

If you try out the widget with these changes, you no longer need to specify the shell script, and the widget should function the same way it did previously. Again, this example script is too short to showcase the potential of using a shell script to run your widget's execution command. But once you start building longer and more complex lines of code that cannot seamlessly work directly in the widget Command tab code block, it is more convenient to store the script internally in the widget and call it when you run the widget.

Note

Generally, passing widget arguments to the shell script is achievable by using the special parameter $@, which represents all the arguments passed to the script. This way, you can forward any number of optional flags and arguments from your script (specified in the widget's entries by the user) to the tool command.

If there is a complicated script that ends with running some tool, and you want to include flags and arguments, it could look something like this:

#!/bin/bash
…
# complicated lines of code here…
…
runTool $@

And the expected command that the widget executes is something like this:

complicatedScript.sh --noRepeats --inputFile test.txt outputFile.csv

What the script executes is this:

# complicated lines of code here…
…
runTool --noRepeats --inputFile test.txt outputFile.csv

This is because $@ takes positional parameters after calling the shell script in the command line and adds it into the shell script when executing the script.

How this would look if done in the example widget's script is by modifying the shell script such that the phrase is read in as positional parameters:

#!/bin/bash
echo $@ > $OUTPUTFILE

Then, the widget's Command tab execution would be the following:

echo-to-file.sh _bwb{phrase}

Notice how there are no quotes for the phrase parameter anymore, because #@ takes all words from the phrase (each as their own positional parameter $1 $2 $3 …) into the shell script.