{"id":17109946,"url":"https://github.com/oe/deploy-toolkit","last_synced_at":"2025-04-13T03:26:54.796Z","repository":{"id":47421760,"uuid":"158212023","full_name":"oe/deploy-toolkit","owner":"oe","description":"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","archived":false,"fork":false,"pushed_at":"2023-01-03T19:22:00.000Z","size":461,"stargazers_count":5,"open_issues_count":8,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-12T11:14:46.766Z","etag":null,"topics":["ci-cd","deploy","nodejs","ssh","typescript"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/oe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-11-19T11:40:15.000Z","updated_at":"2023-01-20T09:23:01.000Z","dependencies_parsed_at":"2023-02-01T09:16:38.261Z","dependency_job_id":null,"html_url":"https://github.com/oe/deploy-toolkit","commit_stats":null,"previous_names":["evecalm/deploy-toolkit"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oe%2Fdeploy-toolkit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oe%2Fdeploy-toolkit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oe%2Fdeploy-toolkit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oe%2Fdeploy-toolkit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oe","download_url":"https://codeload.github.com/oe/deploy-toolkit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248658939,"owners_count":21141037,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ci-cd","deploy","nodejs","ssh","typescript"],"created_at":"2024-10-14T16:24:54.882Z","updated_at":"2025-04-13T03:26:54.766Z","avatar_url":"https://github.com/oe.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eDeploy toolkit\u003c/h1\u003e\n\n\u003ch5\u003eA 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. \u003c/h5\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003ca href=\"https://travis-ci.org/evecalm/deploy-toolkit\"\u003e\n    \u003cimg src=\"https://travis-ci.org/evecalm/deploy-toolkit.svg?branch=master\" alt=\"Travis CI\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"#readme\"\u003e\n    \u003cimg src=\"https://badges.frapsoft.com/typescript/code/typescript.svg?v=101\" alt=\"code with typescript\" height=\"20\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"#readme\"\u003e\n    \u003cimg src=\"https://badge.fury.io/js/deploy-toolkit.svg\" alt=\"npm version\" height=\"18\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/deploy-toolkit\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/dm/deploy-toolkit.svg\" alt=\"npm version\" height=\"18\"\u003e\n  \u003c/a\u003e\n\u003c/div\u003e\n\nAll actions are run in sequence, and you can set allow failure for specific action(wont stop the sequence even it failed).\n\n## Exmaple\n\n```js\nimport { deploy, runShellCmd,  findFileRecursive, addGitTag} from 'deploy-toolkit'\nimport fs from 'fs'\nimport path from 'path'\n\nconst config = {\n  // ssh connection config\n  ssh: {\n    host: 'my.server.com',\n    username: 'fancy',\n    // // use password if you prefer password\n    // password: '123456'\n    // or private ssh key file path(or key text content)\n    //   use ~ as user homedir\n    privateKey: '~/.ssh/my-private-key'\n    // set passphrase if private key is encrypted\n    passphrase: '3344',\n  },\n  // whether to show command execution logs\n  log: true,\n  // commands sequence, will execute by its order\n  cmds: [\n    {\n      // exec command\n      type: 'cmd',\n      // command arguments list\n      args: ['mkdir', '-p', 'saiya/test'],\n      // command work directory on remote server\n      cwd: '/home/user'\n    },\n    {\n      type: 'cmd',\n      args: ['pm2', 'stop', 'my-fancy-app'],\n      // if pm2 stop failed, still continue to run the following cmds\n      allowFailure: true\n    },\n    {\n      type: 'cmd',\n      args: ['ls', 'saiya', '-l'],\n      cwd: '/home/user'\n    },\n    {\n      // upload files\n      type: 'upload',\n      // files' glob pattern, could also be a file/dir path\n      src: path.join(__dirname, '../*/*.json'),\n      // if upload multi files with glob pattern, srcPrefix is needed to determine to saved path on server\n      //  no need if `src` is certain a file/dir path\n      srcPrefix: path.join(__dirname, '..'),\n      // server directory path to save the files\n      dest: '/home/kk/saiya'\n    },\n    {\n      // download file, only support download a single file at a time\n      type: 'download',\n      // source file path on server\n      src: '/home/kk/start.sh',\n      // saved path in local\n      dest: path.join(__dirname, 'gg.sh')\n    }\n  ]\n}\n\n// run commands\ndeploy(config).then(() =\u003e {\n  console.log('all done!')\n}).catch((err) =\u003e {\n  console.warn(err)\n})\n\n// run local shell commands\nrunShellCmd('ls', ['-l']).then((res) =\u003e {\n  console.log('ls response', res)\n})\n\n// find closest package.json file full path, return '' if not found\nconsole.log(findFileRecursive('package.json'))\n// found closest .git dir full path from current work dir, return '' if not found\nconsole.log(findFileRecursive('.git', process.cwd(), true))\n\n// add git tag \u0026 puth to remote, use `v${package.version}` in package.json as tag name by default\naddGitTag().then((tagName) =\u003e {\n  console.log('done, has added tag', tagName)\n})\n\n// sepecify the tag name\naddGitTag('v10.0.0-beta').then(() =\u003e {\n  console.log('done')\n})\n\n```\n\n## Install\n\n```sh\nyarn add deploy-toolkit\n```\n\nor\n\n```\nnpm i deploy-toolkit -D\n```\n\n## Usage\n\n### `deploy`\n\nDeploy stuffs to remote server with simple json config, you can upload/download/execute-command on remote server.\n\n```typescript\n// import the main function like this\nimport { deploy } from 'deploy-toolkit'\n// // you can import the following types if you are using typescript\n// import { IDeployConfig } from 'deploy-toolkit'\n\nfunction deploy(deployCmd: IDeployConfig): Promise\u003cvoid\u003e\n\n/** deploy confgi */\ninterface IDeployConfig {\n    /** ssh connection config */\n    ssh: ISshConfig\n    /** whether to show log when executing cmds */\n    log?: boolean\n    /** command sequence */\n    cmds: ICmds\n}\n\n/** SSH Connection config */\ninterface ISshConfig {\n    /** Hostname or IP address of the server. */\n    host: string\n    /** Port number of the server. */\n    port?: number\n    /** Username for authentication. */\n    username?: string\n    /** Password for password-based user authentication. */\n    password?: string\n    /** file path of the private key, or the private key text content */\n    privateKey?: string\n    /** For an encrypted private key, this is the passphrase used to decrypt it. */\n    passphrase?: string\n    /** any other options from ssh2 ConnectConfig */\n    [k: string]: any\n}\n/** commands sequence */\ntype ICmds = ICmd[]\n\n/** command */\ntype ICmd = IUploadConfig | IDownloadConfig | IRunConfig | IScriptConfig\n\n/** upload config */\ninterface IUploadConfig {\n    type: 'upload'\n    /** source file(in local), could be a specified file/directory path or a glob pattern */\n    src: string\n    /** 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 */\n    srcPrefix?: string\n    /** destination path(on server), should be a file path if src is a specified file, or a directory for other situations */\n    dest: string\n    /** allow failure, so the command sequence will continue to run even this failed */\n    allowFailure?: boolean\n}\n\n/** download config */\ninterface IDownloadConfig {\n    type: 'download'\n    /** source file path(on server) */\n    src: string\n    /** dest save path(in local) */\n    dest: string\n    /** allow failure, so the command sequence will continue to run even this failed */\n    allowFailure?: boolean\n}\n\n/** custom command */\ninterface IRunConfig {\n    type: 'cmd'\n    /** cmd arguments */\n    args: string[]\n    /** cmd work directory */\n    cwd?: string\n    /** options */\n    options?: {\n        /** another way to set work directory, will be rewrite if set outside */\n        cwd?: string\n        /** extra options for ssh2.exec */\n        options?: Object\n        /** input for the command */\n        stdin?: string\n        /** output */\n        stream?: 'stdout' | 'stderr' | 'both'\n        /** stdout event */\n        onStdout?: ((chunk: Buffer) =\u003e void)\n        /** stderror event */\n        onStderr?: ((chunk: Buffer) =\u003e void)\n    }\n    /** allow failure, so the command sequence will continue to run even this failed */\n    allowFailure?: boolean\n}\n\n\n/**\n * custom script\n */ \ninterface IScriptConfig {\n  type: 'script'\n  /* custom shebang, default #!/usr/bin/env bash */\n  shebang?: string\n  /* shell name, default bash. if shebang specified, then shell will be used */\n  shell?: string\n  /* script content, you can use DOWNLOAD/UPLOAD keywords to download or upload file */\n  script: string\n  /* initial work dir */\n  cwd?: string\n  /** allow failure, so the command sequence will continue to run even this failed */\n  allowFailure?: boolean\n}\n```\n\n#### Example\n```js\nimport { deploy, IDeployConfig, runShellCmd, findFileRecursive } from '../src/'\nimport path from 'path'\n\nconst config: IDeployConfig = {\n  ssh: {\n    host: '10.213.85.1',\n    username: 'deploy',\n    password: 'passw0rp!'\n  },\n  log: true,\n  cmds: [\n    {\n      type: 'cmd',\n      args: ['mkdir', '-p', 'saiya/test'],\n      cwd: '~/Documents'\n    },\n    {\n      type: 'download',\n      src: '/home/deploy/start.sh',\n      dest: path.join(__dirname, 'start.sh')\n    },\n    {\n      type: 'upload',\n      src: '/home/user1/Documents/Hobby/project1/dist',\n      dest: '/home/deploy/Documents/project1'\n    },\n    {\n      type: 'script',\n      script: `\n      cd ~\n      rm -rf Document/project1\n      UPLOAD /home/user1/Documents/Hobby/project1/dist \u003e /home/deploy/Documents/project1\n      cd Document/project1\n      npm start\n      DOWNLOAD  /home/deploy/Documents/project1/logs/latest.log \u003e /home/user1/Documents/Hobby/logs/latest.log\n      echo \"done\"\n      `\n    }\n  ]\n}\n\n\ndeploy(config).then(() =\u003e {\n  console.log('all done')\n})\n\n```\n\n### `runShellCmd`\n\nrun shell command on local machine, a promise wrapper of node `child_process.spawn`, by default run the command in cwd `process.cwd()`\n\n```typescript\nimport { runShellCmd } from 'deploy-toolkit'\n// return promise with execution result\nfunction runShellCmd(cmd: string, options?: SpawnOptions): Promise\u003cstring\u003e\nfunction runShellCmd(\n    cmd: string,\n    args?: string[],\n    options?: SpawnOptions\n): Promise\u003cstring\u003e\n\n// check nodejs doc http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options for detail explains\ninterface SpawnOptions {\n    cwd?: string // default is process.cwd()\n    env?: any\n    stdio?: any\n    detached?: boolean\n    uid?: number\n    gid?: number\n    shell?: boolean | string\n    windowsVerbatimArguments?: boolean\n    windowsHide?: boolean\n}\n```\n\n#### findFileRecursive\n\nfind a file/dir recursively from specified dir to the root until found, return `''` if not found.\n\n```typescript\nimport { findFileRecursive } from 'deploy-toolkit'\n/**\n * find a file(dir) recursive( aka try to find package.json, node_modules, etc.)\n * @param fileName file name(s)(or dir name(s) if isDir is true), if an array, return the first matched one\n * @param dir the initial dir path to find, use `process.cwd()` by default\n * @param isDir whether to find a dir, default false\n */\nfunction findFileRecursive(\n    fileName: string | string[],\n    dir?: string,\n    isDir?: boolean\n): string\n\n// e.g. find babel config file path\n\nconst babelRcPath = findFileRecursive([\n    '.babelrc',\n    '.babelrc.js',\n    'babel.config.js'\n])\n```\n\n#### addGitTag\n\nadd git tag and push it to remote, you can use it on `postbuild` or `postpublish`.  \nuse `v${package.version}` in package.json as tag name by default, return the `tagName` after tag push\n\n```typescript\nfunction addGitTag(tagName?: string): Promise\u003cstring\u003e\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foe%2Fdeploy-toolkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foe%2Fdeploy-toolkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foe%2Fdeploy-toolkit/lists"}