deploy-toolkit
v0.1.1
Published
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
Downloads
37
Readme
All actions are run in sequence, and you can set allow failure for specific action(wont stop the sequence even it failed).
Exmaple
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')
})
Install
yarn add deploy-toolkit
or
npm i deploy-toolkit -D
Usage
deploy
Deploy 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
}
Example
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')
})
runShellCmd
run 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
}
findFileRecursive
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'
])
addGitTag
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>