Security News
Input Validation Vulnerabilities Dominate MITRE's 2024 CWE Top 25 List
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
A tool to make invoking a series of shell commands safer & better-looking.
import shellac from 'shellac'
test('morty', async () => await shellac`
$ echo "End-to-end CLI testing made nice"
$ node -p "5 * 9"
stdout >> ${ answer => expect(Number(answer)).toBeGreaterThan(40) }
`)
await shellac`
// To execute a command, use $
$ my command here
// If you want the output piped through to process.stdout/err, use $$
$$ echo "This command will print to terminal"
// Use stdout/err and >> to check the output of the last command
stdout >> ${ last_cmd_stdout => {
expect(last_cmd_stdout).toBe("This command will print to terminal")
}}
`
Shellac returns the stdout/err of the last command in a block as { stdout, stderr }
const { stdout, stderr } = await shellac`
$ echo "This command will run but its output will be lost"
$ echo "The last command executed returns its stdout/err"
`
expect(stdout).toBe("The last command executed returns its stdout/err")
You can also return named captures from a series of commands:
const { current_sha, current_branch } = await shellac`
$ git rev-parse --short HEAD
stdout >> current_sha
$ git rev-parse --abbrev-ref HEAD
stdout >> current_branch
`
You can use if ${ ... } { ... } else { ... }
to run conditionally based on the value of an interpolation:
await shellac`
if ${ process.env.CLEAN_RUN } {
$ yarn create react-app
} else {
$ git reset --hard
$ git clean -df
}
$$ npx fab init -y
// ...
`
You can either use an in
directive:
await shellac`
// Change directory for the duration of the block:
in ${ __dirname } {
$ pwd
stdout >> ${ cwd => expect(cwd).toBe(__dirname) }
}
// By default we run in process.cwd()
$ pwd
stdout >> ${ cwd => expect(cwd).toBe(process.cwd()) }
`
If the whole script needs to run in one place, use shellac.in(dir)
:
import tmp from 'tmp-promise'
const dir = await tmp.dir()
await shellac.in(dir.path)`
$ pwd
stdout >> ${ cwd => expect(cwd).toBe(dir.path) }
`
Use the await
declaration to invoke & wait for some JS inline with your script. It works great when Bash doesn't quite do what you need.
import fs from 'fs-extra'
await shellac.in(cwd)`
await ${ async () => {
await fs.writeFile(
path.join(cwd, 'bigfile.dat'),
huge_data
)
}}
$ ls -l
stdout >> ${(files) => expect(files).toMatch('bigfile.dat')}
`
Inside a $
command you can use string interpolation like normal:
await shellac.in(cwd)`
$ echo "${JSON.stringify({ current_sha, current_branch })}" > git_info.json
`
These can even be promises or async functions:
const getAllPackageNames = async () => { /* ... */ }
await shellac.in(cwd)`
// You can pass a promise and it will be awaited
$ yarn link ${ getAllPackageNames() }
// ...
// Or pass an async function and shellac will call and await it
$ yarn unlink ${ async () => getAllPackageNames() }
`
A shellac
call invokes a single instance of bash
for the duration, so changes you make are reflected later in the script:
await shellac`
$ echo $LOL
stdout >> ${lol => expect(lol).toBe('') }
$ LOL=boats
$ echo $LOL
stdout >> ${lol => expect(lol).toBe('boats') }
`
Note: the current working directory is only configured by shellac.in()
or the in ${} { ... }
directive:
const cwd = __dirname
const parent_dir = path.resolve(cwd, '..')
await shellac.in(cwd)`
// Normal behaviour
$ pwd
stdout >> ${pwd => expect(pwd).toBe(cwd) }
// Has no effect on the remaining commands
$ cd ..
$ pwd
stdout >> ${pwd => expect(pwd).toBe(cwd) }
// If you want to change dir use in {}
in ${ parent_dir } {
$ pwd
stdout >> ${pwd => expect(pwd).toBe(parent_dir) }
}
// Or do it on a single line
$ cd .. && pwd
stdout >> ${pwd => expect(pwd).toBe(parent_dir) }
// Joining commands with ; also works
$ cd ..; pwd
stdout >> ${(pwd) => expect(pwd).toBe(parent_dir)}
`
All these examples are valid, since // single-line-comments
are ignored as expected.
Works great with ts-jest:
// ts-jest-example.test.js
import shellac from 'shellac'
describe('my CLI tool', () => {
it('should do everything I need', async () => {
await shellac`
$ echo "Hello, world!"
stdout >> ${(echo) => {
expect(echo).toBe('Hello, world!')
}}
$ rm -rf working-dir
$ mkdir -p working-dir/example
$ cp -R fixtures/run-1/* working-dir/example
await ${async () => {
// generate some more test data
}}
in ${'working-dir/example'} {
$ ls -l
stdout >> ${(files) => {
expect(files).toMatch('package.json')
}}
$ yarn
$$ run-app
}
`
})
})
Using CommonJS, import it like:
const test = require('ava')
const shellac = require('shellac').default
test('plugin should be installable', async (t) => {
await shellac.default`
$ echo "Hello, world!"
stdout >> ${(echo) => {
t.is(echo, 'Hello, world!')
}}
`
})
Use double-$ $$
for logging while the test runs:
shellac.in(cwd)`
$$ ls -al
`
is the same as:
shellac.in(cwd)`
$ ls -al
stdout >> ${console.log}
`
Confirm a file is present:
shellac`
$ ls -l
stdout >> ${files => expect(files).toMatch('fab.zip')}
`
@kitten
for reghex which is genuinely incredible and the only reason this library is possible at all.
@superhighfives
for coming up with the name!
FAQs
Protect and beautify your shell scripting
The npm package kush-cli receives a total of 1 weekly downloads. As such, kush-cli popularity was classified as not popular.
We found that kush-cli demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.
Research
Security News
A threat actor's playbook for exploiting the npm ecosystem was exposed on the dark web, detailing how to build a blockchain-powered botnet.