The shell
Statement
The shell
statement is the main way to run commands in Cicada. The shell
statement allows you
to run commands and as well as execute inline shell code in your workflow.
Arguments passed to
shell
are escaped before being passed to/bin/sh
, though if used incorrectly, you still might be vulnerable to command injections. Please read the Security Considerations section for tips on how to better secure your workflows.
Examples
Here are a few examples of how the shell
statement can be used in workflows:
Run a Command
shell npm install
This will run npm install
in the current directory.
Pass Expressions as Arguments
let name = "Bob"
shell echo You name is (name)
Cicada will pass name
as the argument for (name)
. The ()
notation is used to
differentiate from the typical $
notation used in shell.
Note you can use any valid Cicada expression as an argument, not just variable names:
shell echo 1 + 2 = (1 + 2)
The above workflow will print out 1 + 2 = 3
.
Capture stdout
In Cicada you can capture and manipulate the stdout of a command by assigning it to a variable and accessing it's properties:
let cmd =
shell echo Hello world!
print(cmd.stdout)
Running this workflow will print Hello world!
*. Using stdout like this helps you
utilize more of what Cicada has to offer, without having to rely on shell scripts.
* An extra newline will be printed because the stdout from
echo
includes a newline, and.strip()
to strip the whitespace before printing.
Note that when capturing command output, stdout and stderr will be merged into one.
Using Shell Features
You can also use shell
to gain access to shell features like env vars, piping, and conditional
execution:
# Print the current directory
shell echo Current dir: $PWD
# Get the top 10 biggest files/folders in the "src" folder
shell du src | sort -nr | head -n 10
# Print message and exit if backup fails
shell ./backup.sh || { echo "Backup failed" && exit 1; }
While shell code can be very useful in writing your workflows, we encourage you to use the Cicada DSL instead of shell scripts wherever possible.
Run Shell Scripts
In addition to running single line shell commands, the shell
statement can be used to
run larger, multi-line shell scripts:
shell "
echo running tests
./run-tests.sh
if [ $? = 1 ]; then
echo tests failed
else
echo tests passeed
fi
"
This shell script runs ./run-tests.sh
and print whether the tests passed or failed
based on the resulting exit code.
Shell Aliases
Shell aliases are special identifiers that can be used directly without the need to prefix it with shell
.
For example, the following are equivalent in Cicada:
shell echo hi
echo hi
These are the current commands that are allowed to be used as aliases, though this list may grow in the future:
cd
cp
echo
git
ls
make
mkdir
rm
Notes on Environment Variables
By default, Cicada will inject environment variables into each command before running it.
However, environment variables that are set while running a shell
command will not
be saved.
For example:
shell echo ----
shell env
env.HELLO = "world"
shell echo ----
shell env
shell echo ----
shell export TESTING="123"
shell env
This will emit something similar to the following:
PWD=<CURRENT DIRECTORY>
SHLVL=0
_=/usr/bin/env
----
HELLO=world
PWD=<CURRENT DIRECTORY>
SHLVL=0
_=/usr/bin/env
----
HELLO=world
PWD=<CURRENT DIRECTORY>
SHLVL=0
_=/usr/bin/env
Notice that the HELLO
env var is passed to the next commands, but TESTING
is not.
Using Secrets
For security purposes Cicada does not export secrets as environment variables. This means that you have to export secrets that you want to expose as environment variables:
# prints nothing
echo $API_KEY
env.API_KEY = secret.API_KEY
# prints API_KEY
echo $API_KEY
Security Considerations
Since the shell
statement allows you to run arbitrary commands, it is
paramount that you ensure it is safeguarded from malicious users.
The shell
statement will escape all interpolated arguments you pass to it,
though this alone does not stop all command injections.
For example, this workflow is safe as name
is properly escaped via ()
:
let name = "hacker; echo Command injection"
shell echo Your name is: (name)
Running the above workflow results in the following:
Your name is: hacker; echo Command injection
As you can see, name
was escaped and the command injection was not successful.
However, if we were to change the shell
command to this:
shell eval (name)
We would get the following result:
Your name is: hacker
Command injection
While (name)
does escapes the parameter, eval
will execute the escaped
shell shell code, rendering the escaping useless.
In short, make sure that you do not directly execute untrusted code! Call commands directly like in the first example, and if you do need to call shell scripts, ensure you are only passing trusted input that you created, and ensure these scripts are not interpreting any inputs as shell code.