
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
deploy-toolkit
Advanced tools
A toolkit make it easy(with plain config) to manipulate(upload/download/exec command) server via ssh, can be used to deploy stuffs or CI/CD
All actions are run in sequence, and you can set allow failure for specific action(wont stop the sequence even it failed).
import { deploy, runShellCmd, findFileRecursive, addGitTag} from 'deploy-toolkit'
import fs from 'fs'
import path from 'path'
const config = {
// ssh connection config
ssh: {
host: 'my.server.com',
username: 'fancy',
// // use password if you prefer password
// password: '123456'
// or private ssh key file path(or key text content)
// use ~ as user homedir
privateKey: '~/.ssh/my-private-key'
// set passphrase if private key is encrypted
passphrase: '3344',
},
// whether to show command execution logs
log: true,
// commands sequence, will execute by its order
cmds: [
{
// exec command
type: 'cmd',
// command arguments list
args: ['mkdir', '-p', 'saiya/test'],
// command work directory on remote server
cwd: '/home/user'
},
{
type: 'cmd',
args: ['pm2', 'stop', 'my-fancy-app'],
// if pm2 stop failed, still continue to run the following cmds
allowFailure: true
},
{
type: 'cmd',
args: ['ls', 'saiya', '-l'],
cwd: '/home/user'
},
{
// upload files
type: 'upload',
// files' glob pattern, could also be a file/dir path
src: path.join(__dirname, '../*/*.json'),
// if upload multi files with glob pattern, srcPrefix is needed to determine to saved path on server
// no need if `src` is certain a file/dir path
srcPrefix: path.join(__dirname, '..'),
// server directory path to save the files
dest: '/home/kk/saiya'
},
{
// download file, only support download a single file at a time
type: 'download',
// source file path on server
src: '/home/kk/start.sh',
// saved path in local
dest: path.join(__dirname, 'gg.sh')
}
]
}
// run commands
deploy(config).then(() => {
console.log('all done!')
}).catch((err) => {
console.warn(err)
})
// run local shell commands
runShellCmd('ls', ['-l']).then((res) => {
console.log('ls response', res)
})
// find closest package.json file full path, return '' if not found
console.log(findFileRecursive('package.json'))
// found closest .git dir full path from current work dir, return '' if not found
console.log(findFileRecursive('.git', process.cwd(), true))
// add git tag & puth to remote, use `v${package.version}` in package.json as tag name by default
addGitTag().then((tagName) => {
console.log('done, has added tag', tagName)
})
// sepecify the tag name
addGitTag('v10.0.0-beta').then(() => {
console.log('done')
})
yarn add deploy-toolkit
or
npm i deploy-toolkit -D
deployDeploy stuffs to remote server with simple json config, you can upload/download/execute-command on remote server.
// import the main function like this
import { deploy } from 'deploy-toolkit'
// // you can import the following types if you are using typescript
// import { IDeployConfig } from 'deploy-toolkit'
function deploy(deployCmd: IDeployConfig): Promise<void>
/** deploy confgi */
interface IDeployConfig {
/** ssh connection config */
ssh: ISshConfig
/** whether to show log when executing cmds */
log?: boolean
/** command sequence */
cmds: ICmds
}
/** SSH Connection config */
interface ISshConfig {
/** Hostname or IP address of the server. */
host: string
/** Port number of the server. */
port?: number
/** Username for authentication. */
username?: string
/** Password for password-based user authentication. */
password?: string
/** file path of the private key, or the private key text content */
privateKey?: string
/** For an encrypted private key, this is the passphrase used to decrypt it. */
passphrase?: string
/** any other options from ssh2 ConnectConfig */
[k: string]: any
}
/** commands sequence */
type ICmds = ICmd[]
/** command */
type ICmd = IUploadConfig | IDownloadConfig | IRunConfig | IScriptConfig
/** upload config */
interface IUploadConfig {
type: 'upload'
/** source file(in local), could be a specified file/directory path or a glob pattern */
src: string
/** if src is a glob pattern, then srcPrefix is need, to determine the path save on server. omit it if src is a spicifed file/directory path */
srcPrefix?: string
/** destination path(on server), should be a file path if src is a specified file, or a directory for other situations */
dest: string
/** allow failure, so the command sequence will continue to run even this failed */
allowFailure?: boolean
}
/** download config */
interface IDownloadConfig {
type: 'download'
/** source file path(on server) */
src: string
/** dest save path(in local) */
dest: string
/** allow failure, so the command sequence will continue to run even this failed */
allowFailure?: boolean
}
/** custom command */
interface IRunConfig {
type: 'cmd'
/** cmd arguments */
args: string[]
/** cmd work directory */
cwd?: string
/** options */
options?: {
/** another way to set work directory, will be rewrite if set outside */
cwd?: string
/** extra options for ssh2.exec */
options?: Object
/** input for the command */
stdin?: string
/** output */
stream?: 'stdout' | 'stderr' | 'both'
/** stdout event */
onStdout?: ((chunk: Buffer) => void)
/** stderror event */
onStderr?: ((chunk: Buffer) => void)
}
/** allow failure, so the command sequence will continue to run even this failed */
allowFailure?: boolean
}
/**
* custom script
*/
interface IScriptConfig {
type: 'script'
/* custom shebang, default #!/usr/bin/env bash */
shebang?: string
/* shell name, default bash. if shebang specified, then shell will be used */
shell?: string
/* script content, you can use DOWNLOAD/UPLOAD keywords to download or upload file */
script: string
/* initial work dir */
cwd?: string
/** allow failure, so the command sequence will continue to run even this failed */
allowFailure?: boolean
}
import { deploy, IDeployConfig, runShellCmd, findFileRecursive } from '../src/'
import path from 'path'
const config: IDeployConfig = {
ssh: {
host: '10.213.85.1',
username: 'deploy',
password: 'passw0rp!'
},
log: true,
cmds: [
{
type: 'cmd',
args: ['mkdir', '-p', 'saiya/test'],
cwd: '~/Documents'
},
{
type: 'download',
src: '/home/deploy/start.sh',
dest: path.join(__dirname, 'start.sh')
},
{
type: 'upload',
src: '/home/user1/Documents/Hobby/project1/dist',
dest: '/home/deploy/Documents/project1'
},
{
type: 'script',
script: `
cd ~
rm -rf Document/project1
UPLOAD /home/user1/Documents/Hobby/project1/dist > /home/deploy/Documents/project1
cd Document/project1
npm start
DOWNLOAD /home/deploy/Documents/project1/logs/latest.log > /home/user1/Documents/Hobby/logs/latest.log
echo "done"
`
}
]
}
deploy(config).then(() => {
console.log('all done')
})
runShellCmdrun shell command on local machine, a promise wrapper of node child_process.spawn, by default run the command in cwd process.cwd()
import { runShellCmd } from 'deploy-toolkit'
// return promise with execution result
function runShellCmd(cmd: string, options?: SpawnOptions): Promise<string>
function runShellCmd(
cmd: string,
args?: string[],
options?: SpawnOptions
): Promise<string>
// check nodejs doc http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options for detail explains
interface SpawnOptions {
cwd?: string // default is process.cwd()
env?: any
stdio?: any
detached?: boolean
uid?: number
gid?: number
shell?: boolean | string
windowsVerbatimArguments?: boolean
windowsHide?: boolean
}
find a file/dir recursively from specified dir to the root until found, return '' if not found.
import { findFileRecursive } from 'deploy-toolkit'
/**
* find a file(dir) recursive( aka try to find package.json, node_modules, etc.)
* @param fileName file name(s)(or dir name(s) if isDir is true), if an array, return the first matched one
* @param dir the initial dir path to find, use `process.cwd()` by default
* @param isDir whether to find a dir, default false
*/
function findFileRecursive(
fileName: string | string[],
dir?: string,
isDir?: boolean
): string
// e.g. find babel config file path
const babelRcPath = findFileRecursive([
'.babelrc',
'.babelrc.js',
'babel.config.js'
])
add git tag and push it to remote, you can use it on postbuild or postpublish.
use v${package.version} in package.json as tag name by default, return the tagName after tag push
function addGitTag(tagName?: string): Promise<string>
FAQs
A toolkit make it easy(with plain config) to manipulate(upload/download/exec command) server via ssh, can be used to deploy stuffs or CI/CD
The npm package deploy-toolkit receives a total of 21 weekly downloads. As such, deploy-toolkit popularity was classified as not popular.
We found that deploy-toolkit 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
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.