{"id":13449133,"url":"https://github.com/pstadler/flightplan","last_synced_at":"2025-10-08T10:13:43.402Z","repository":{"id":13922076,"uuid":"16621454","full_name":"pstadler/flightplan","owner":"pstadler","description":"Run sequences of shell commands against local and remote hosts.","archived":false,"fork":false,"pushed_at":"2022-01-18T01:10:28.000Z","size":364,"stargazers_count":1817,"open_issues_count":21,"forks_count":114,"subscribers_count":31,"default_branch":"master","last_synced_at":"2025-09-10T11:23:22.234Z","etag":null,"topics":["deploy","devops","javascript","nodejs","ssh","tasks"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/flightplan","language":"JavaScript","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/pstadler.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":"2014-02-07T17:09:02.000Z","updated_at":"2025-09-06T14:17:12.000Z","dependencies_parsed_at":"2022-09-19T10:41:10.975Z","dependency_job_id":null,"html_url":"https://github.com/pstadler/flightplan","commit_stats":null,"previous_names":[],"tags_count":53,"template":false,"template_full_name":null,"purl":"pkg:github/pstadler/flightplan","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pstadler%2Fflightplan","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pstadler%2Fflightplan/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pstadler%2Fflightplan/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pstadler%2Fflightplan/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pstadler","download_url":"https://codeload.github.com/pstadler/flightplan/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pstadler%2Fflightplan/sbom","scorecard":{"id":748377,"data":{"date":"2025-08-11","repo":{"name":"github.com/pstadler/flightplan","commit":"9e698c1ff7e1e112732acf946091acbb3a3ebda5"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.3,"checks":[{"name":"Code-Review","score":2,"reason":"Found 6/23 approved changesets -- score normalized to 2","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 16 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"63 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-9vvw-cc9w-f27h","Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c","Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq","Warn: Project is vulnerable to: GHSA-h6ch-v84p-w6p9","Warn: Project is vulnerable to: GHSA-3w5v-p54c-f74x","Warn: Project is vulnerable to: GHSA-6x77-rpqf-j6mw","Warn: Project is vulnerable to: GHSA-hwcf-pp87-7x6p","Warn: Project is vulnerable to: GHSA-phwq-j96m-2c2q","Warn: Project is vulnerable to: GHSA-ghr5-ch3p-vcr6","Warn: Project is vulnerable to: GHSA-4gmj-3p3h-gm8h","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-qh2h-chj9-jffq","Warn: Project is vulnerable to: GHSA-w457-6q6x-cgp9","Warn: Project is vulnerable to: GHSA-62gr-4qp9-h98f","Warn: Project is vulnerable to: GHSA-f52g-6jhx-586p","Warn: Project is vulnerable to: GHSA-2cf5-4w76-r9qv","Warn: Project is vulnerable to: GHSA-3cqr-58rm-57f8","Warn: Project is vulnerable to: GHSA-g9r4-xpmj-mj65","Warn: Project is vulnerable to: GHSA-q2c6-c6pm-g3gh","Warn: Project is vulnerable to: GHSA-765h-qjxv-5f44","Warn: Project is vulnerable to: GHSA-f2jv-r9rf-7988","Warn: Project is vulnerable to: GHSA-44pw-h2cw-w3vq","Warn: Project is vulnerable to: GHSA-jp4x-w63m-7wgm","Warn: Project is vulnerable to: GHSA-c429-5p7v-vgjp","Warn: Project is vulnerable to: GHSA-x55w-vjjp-222r","Warn: Project is vulnerable to: GHSA-qqgx-2p2h-9c37","Warn: Project is vulnerable to: GHSA-2pr6-76vf-7546","Warn: Project is vulnerable to: GHSA-8j8c-7jfh-h6hx","Warn: Project is vulnerable to: GHSA-896r-f27r-55mw","Warn: Project is vulnerable to: GHSA-282f-qqgm-c34q","Warn: Project is vulnerable to: GHSA-6c8f-qphg-qjgp","Warn: Project is vulnerable to: GHSA-jf85-cpcp-j695","Warn: Project is vulnerable to: GHSA-fvqr-27wr-82fm","Warn: Project is vulnerable to: GHSA-4xc9-xhrj-v574","Warn: Project is vulnerable to: GHSA-x5rq-j2xg-h7qm","Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-2m96-9w4j-wgv7","Warn: Project is vulnerable to: GHSA-h726-x36v-rx45","Warn: Project is vulnerable to: GHSA-6vfc-qv3f-vr6c","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-hxm2-r34f-qmc5","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-vh95-rmgr-6w4m","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-fhjf-83wg-r2j9","Warn: Project is vulnerable to: GHSA-w9mr-4mfr-499f","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-4g88-fppr-53pp","Warn: Project is vulnerable to: GHSA-4jqc-8m5r-9rpr","Warn: Project is vulnerable to: GHSA-4rq4-32rv-6wp6","Warn: Project is vulnerable to: GHSA-64g7-mvw6-v9qj","Warn: Project is vulnerable to: GHSA-652h-xwhf-q4h6","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-xc7v-wxcw-j472","Warn: Project is vulnerable to: GHSA-cf4h-3jhx-xvhq"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-22T19:32:59.864Z","repository_id":13922076,"created_at":"2025-08-22T19:32:59.864Z","updated_at":"2025-08-22T19:32:59.864Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278925614,"owners_count":26069729,"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","status":"online","status_checked_at":"2025-10-08T02:00:06.501Z","response_time":56,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["deploy","devops","javascript","nodejs","ssh","tasks"],"created_at":"2024-07-31T06:00:31.961Z","updated_at":"2025-10-08T10:13:43.385Z","avatar_url":"https://github.com/pstadler.png","language":"JavaScript","readme":"# Flightplan\n\n[![NPM version][npm-version-image]][npm-url] [![Build Status][build-status-image]][build-status-url]\n[![Build Status][coverage-image]][coverage-url] [![NPM downloads][npm-downloads-image]][npm-url] [![Dependency Status][dependencies-image]][dependencies-url]\n\nRun sequences of shell commands against local and remote hosts.\n\nFlightplan is a [node.js](http://nodejs.org) library for streamlining application deployment or systems administration tasks.\n\nA complete list of changes can be found in the [Changelog](https://github.com/pstadler/flightplan/releases).\n\n**Looking for help / maintainers:** See [#162](https://github.com/pstadler/flightplan/issues/162).\n\n## Installation \u0026 Usage\n\n```bash\n# install the cli tool\n$ npm install -g flightplan\n\n# use it in your project\n$ npm install flightplan --save-dev\n\n# run a flightplan (`fly --help` for more information)\n$ fly [task:]\u003ctarget\u003e [--flightplan flightplan.(js|coffee)]\n```\n\nBy default, the `fly` command will try to load `flightplan.js` or `flightplan.coffee`.\n\n## Sample flightplan.js\n\n```javascript\n// flightplan.js\nvar plan = require('flightplan');\n\n// configuration\nplan.target('staging', {\n  host: 'staging.example.com',\n  username: 'pstadler',\n  agent: process.env.SSH_AUTH_SOCK\n});\n\nplan.target('production', [\n  {\n    host: 'www1.example.com',\n    username: 'pstadler',\n    agent: process.env.SSH_AUTH_SOCK\n  },\n  {\n    host: 'www2.example.com',\n    username: 'pstadler',\n    agent: process.env.SSH_AUTH_SOCK\n  }\n]);\n\nvar tmpDir = 'example-com-' + new Date().getTime();\n\n// run commands on localhost\nplan.local(function(local) {\n  local.log('Run build');\n  local.exec('gulp build');\n\n  local.log('Copy files to remote hosts');\n  var filesToCopy = local.exec('git ls-files', {silent: true});\n  // rsync files to all the target's remote hosts\n  local.transfer(filesToCopy, '/tmp/' + tmpDir);\n});\n\n// run commands on the target's remote hosts\nplan.remote(function(remote) {\n  remote.log('Move folder to web root');\n  remote.sudo('cp -R /tmp/' + tmpDir + ' ~', {user: 'www'});\n  remote.rm('-rf /tmp/' + tmpDir);\n\n  remote.log('Install dependencies');\n  remote.sudo('npm --production --prefix ~/' + tmpDir\n                            + ' install ~/' + tmpDir, {user: 'www'});\n\n  remote.log('Reload application');\n  remote.sudo('ln -snf ~/' + tmpDir + ' ~/example-com', {user: 'www'});\n  remote.sudo('pm2 reload example-com', {user: 'www'});\n});\n\n// run more commands on localhost afterwards\nplan.local(function(local) { /* ... */ });\n// ...or on remote hosts\nplan.remote(function(remote) { /* ... */ });\n```\n\n# Documentation\n\n\u003c!-- DOCS --\u003e\n\n\u003c!-- Start ../lib/index.js --\u003e\n\n## Flightplan\n\nA flightplan is a set of subsequent flights to be executed on one or more\nhosts. Configuration is handled with the `target()` method.\n\n```javascript\nvar plan = require('flightplan');\n```\n\n### Flights\nA flight is a set of commands to be executed on one or more hosts. There are\ntwo types of flights:\n\n#### Local flights\n\nCommands in local flights are executed on the **localhost**.\n\n```javascript\nplan.local(function(transport) {\n  transport.hostname(); // prints the hostname of localhost\n});\n```\n\n#### Remote flights\n\nCommands in remote flights are executed in **parallel** against remote hosts.\n\n```javascript\nplan.remote(function(transport) {\n  transport.hostname(); // prints the hostname(s) of the remote host(s)\n});\n```\n\nYou can define multiple flights of each type. They will be executed in the\norder of their definition. If a previous flight failed, all subsequent\nflights won't get executed. For more information about what it means for\na flight to fail, see the section about `Transport`.\n\n```javascript\n// executed first\nplan.local(function(transport) {});\n\n// executed if first flight succeeded\nplan.remote(function(transport) {});\n\n// executed if second flight succeeded\nplan.local(function(transport) {});\n\n// ...\n```\n\n### Tasks\nFlightplan supports optional tasks to run a subset of flights.\n\n```javascript\n// fly deploy:\u003ctarget\u003e\nplan.local('deploy', function(transport) {});\n\n// fly build:\u003ctarget\u003e\nplan.local('build', function(transport) {});\n\n// fly deploy:\u003ctarget\u003e or...\n// fly build:\u003ctarget\u003e\nplan.local(['deploy', 'build'], function(transport) {});\nplan.remote(['deploy', 'build'], function(transport) {});\n```\n\nIf no task is specified it's implicitly set to \"default\". Therefore,\n`fly \u003ctarget\u003e` is the same as `fly default:\u003ctarget\u003e`.\n\n```javascript\n// fly \u003ctarget\u003e\nplan.local(function(transport) {});\n// is the same as...\nplan.local('default', function(transport) {});\n// \"default\" + other tasks:\nplan.remote(['default', 'deploy', 'build'], function(transport) {});\n```\n\n### flightplan.target(name, hosts[, options]) → this\n\nConfigure the flightplan's targets with `target()`. Without a\nproper setup you can't do remote flights which require at\nleast one remote host. Each target consists of one or more hosts.\n\nValues in the hosts section are passed directly to the `connect()`\nmethod of [mscdex/ssh2](https://github.com/mscdex/ssh2#connection-methods)\nwith one exception: `privateKey` needs to be passed as a string\ncontaining the path to the keyfile instead of the key itself.\n\n```javascript\n// run with `fly staging`\nplan.target('staging', {\n  // see: https://github.com/mscdex/ssh2#connection-methods\n  host: 'staging.example.com',\n  username: 'pstadler',\n  agent: process.env.SSH_AUTH_SOCK\n});\n\n// run with `fly production`\nplan.target('production', [\n  {\n    host: 'www1.example.com',\n    username: 'pstadler',\n    agent: process.env.SSH_AUTH_SOCK\n  },\n  {\n    host: 'www2.example.com',\n    username: 'pstadler',\n    agent: process.env.SSH_AUTH_SOCK\n  }\n]);\n\n// run with `fly dynamic-hosts`\nplan.target('dynamic-hosts', function(done, runtime) {\n  var AWS = require('aws-sdk');\n  AWS.config.update({accessKeyId: '...', secretAccessKey: '...'});\n  var ec2 = new AWS.EC2();\n  var params = {Filters: [{Name: 'instance-state-name', Values: ['running']}]};\n  ec2.describeInstances(params, function(err, response) {\n    if(err) {\n      return done(err);\n    }\n    var hosts = [];\n    response.data.Reservations.forEach(function(reservation) {\n      reservation.Instances.forEach(function(instance) {\n        hosts.push({\n          host: instance.PublicIpAddress,\n          username: 'pstadler',\n          agent: process.env.SSH_AUTH_SOCK\n        });\n      });\n    });\n    done(hosts);\n  });\n});\n```\n\nUsually flightplan will abort when a host is not reachable or authentication\nfails. This can be prevented by setting a property `failsafe` to `true` on\nany of the host configurations:\n\n```javascript\nplan.target('production', [\n  {\n    host: 'www1.example.com',\n    username: 'pstadler',\n    agent: process.env.SSH_AUTH_SOCK\n  },\n  {\n    host: 'www2.example.com',\n    username: 'pstadler',\n    agent: process.env.SSH_AUTH_SOCK,\n    failsafe: true // continue flightplan even if connection to www2 fails\n  }\n]);\n```\n\nYou can override the `username` value of hosts by calling `fly` with\nthe `-u|--username` option:\n\n```bash\nfly production --username=admin\n```\n\n#### Configuring remote hosts during runtime (e.g. using AWS/EC2)\n\nInstead of having a static hosts configuration for a target you can configure\nit on the fly by passing a function `fn(done)` as the second argument to\n`target()`.\n\nThis function is executed at the very beginning. Whatever is passed to\n`done()` will be used for connecting to remote hosts. This can either be an\nobject or an array of objects depending on if you want to connect to one or\nmultiple hosts. Passing an `Error` object will immediately abort the current\nflightplan.\n\n```javascript\nplan.target('dynamic-hosts', function(done, runtime) {\n  var AWS = require('aws-sdk');\n  AWS.config.update({accessKeyId: '...', secretAccessKey: '...'});\n  var ec2 = new AWS.EC2();\n  var params = {Filters: [{Name: 'instance-state-name', Values: ['running']}]};\n  ec2.describeInstances(params, function(err, response) {\n    if(err) {\n      return done(err);\n    }\n    var hosts = [];\n    response.data.Reservations.forEach(function(reservation) {\n      reservation.Instances.forEach(function(instance) {\n        hosts.push({\n          host: instance.PublicIpAddress,\n          username: 'pstadler',\n          agent: process.env.SSH_AUTH_SOCK\n        });\n      });\n    });\n    done(hosts);\n  });\n});\n```\n\n#### Defining and using properties depending on the target\n\n`target()` takes an optional third argument to define properties used by\nthis target. Values defined in this way can be accessed during runtime.\n\n```javascript\nplan.target('staging', {...}, {\n  webRoot: '/usr/local/www',\n  sudoUser: 'www'\n});\n\nplan.target('production', {...}, {\n  webRoot: '/home/node',\n  sudoUser: 'node'\n});\n\nplan.remote(function(remote) {\n  var webRoot = plan.runtime.options.webRoot;   // fly staging -\u003e '/usr/local/www'\n  var sudoUser = plan.runtime.options.sudoUser; // fly staging -\u003e 'www'\n  remote.sudo('ls -al ' + webRoot, {user: sudoUser});\n});\n```\n\nProperties can be set and overwritten by passing them as named options to the\n `fly` command.\n\n```bash\n$ fly staging --sudoUser=foo\n# plan.runtime.options.sudoUser -\u003e 'foo'\n```\n\n### flightplan.local([tasks, ]fn) → this\n\nCalling this method registers a local flight. Local flights are\nexecuted on your localhost. When `fn` gets called a `Transport` object\nis passed with the first argument.\n\n```javascript\nplan.local(function(local) {\n  local.echo('hello from your localhost.');\n});\n```\n\nAn optional first parameter of type Array or String can be passed for\ndefining the flight's task(s).\n\n### flightplan.remote([tasks, ]fn) → this\n\nRegister a remote flight. Remote flights are executed on the current\ntarget's remote hosts defined with `target()`. When `fn` gets called\na `Transport` object is passed with the first argument.\n\n```javascript\nplan.remote(function(remote) {\n  remote.echo('hello from the remote host.');\n});\n```\n\nAn optional first parameter of type Array or String can be passed for\ndefining the flight's task(s).\n\n### flightplan.abort([message])\n\nManually abort the current flightplan and prevent any further commands and\nflights from being executed. An optional message can be passed which\nis displayed after the flight has been aborted.\n\n```javascript\nplan.abort('Severe turbulences over the atlantic ocean!');\n```\n\n\u003c!-- End ../lib/index.js --\u003e\n\n\u003c!-- Start ../lib/transport/index.js --\u003e\n\n## Transport\n\nA transport is the interface you use during flights. Basically they\noffer you a set of methods to execute a chain of commands. Depending on the\ntype of flight, this is either a `Shell` object for local\nflights, or an `SSH` for remote flights. Both transports\nexpose the same set of methods as described in this section.\n\n```javascript\nplan.local(function(local) {\n  local.echo('Shell.echo() called');\n});\n\nplan.remote(function(remote) {\n  remote.echo('SSH.echo() called');\n});\n```\n\nWe call the Transport object `transport` in the following section to avoid\nconfusion. However, do yourself a favor and use `local` for local, and\n`remote` for remote flights.\n\n#### Accessing runtime information\n\nFlightplan provides information during flights with the `runtime` properties:\n\n```javascript\nplan.remote(function(transport) { // applies to local flights as well\n  // Flightplan specific information\n  console.log(plan.runtime.task);    // 'default'\n  console.log(plan.runtime.target);  // 'production'\n  console.log(plan.runtime.hosts);   // [{ host: 'www1.example.com', port: 22 }, ...]\n  console.log(plan.runtime.options); // { debug: true, ... }\n\n  // Flight specific information\n  console.log(transport.runtime); // { host: 'www1.example.com', port: 22 }\n});\n```\n\n### transport.exec(command[, options]) → code: int, stdout: String, stderr: String\n\nTo execute a command you have the choice between using `exec()` or one\nof the handy wrappers for often used commands:\n`transport.exec('ls -al')` is the same as `transport.ls('-al')`. If a\ncommand returns a non-zero exit code, the flightplan will be aborted and\nall subsequent commands and flights won't get executed.\n\n#### Options\nOptions can be passed as a second argument. If `failsafe: true` is\npassed, the command is allowed to fail (i.e. exiting with a non-zero\nexit code), whereas `silent: true` will simply suppress its output.\n\n```javascript\n// output of `ls -al` is suppressed\ntransport.ls('-al', {silent: true});\n\n// flightplan continues even if command fails with exit code `1`\ntransport.ls('-al foo', {failsafe: true}); // ls: foo: No such file or directory\n\n// both options together\ntransport.ls('-al foo', {silent: true, failsafe: true});\n```\n\nTo apply these options to multiple commands check out the docs of\n`transport.silent()` and `transport.failsafe()`.\n\n#### Return value\nEach command returns an object containing `code`, `stdout` and`stderr`:\n\n```javascript\nvar result = transport.echo('Hello world');\nconsole.log(result); // { code: 0, stdout: 'Hello world\\n', stderr: null }\n```\n\n#### Advanced options\nFlightplan uses `child_process#exec()` for executing local commands and\n`mscdex/ssh2#exec()` for remote commands. Options passed with `exec` will\nbe forwarded to either of these functions.\n\n```javascript\n// increase maxBuffer for child_process#exec()\nlocal.ls('-al', {exec: {maxBuffer: 2000*1024}});\n\n// enable pty for mscdex/ssh2#exec()\nremote.ls('-al', {exec: {pty: true}});\n```\n\n### transport.sudo(command[, options]) → code: int, stdout: String, stderr: String\n\nExecute a command as another user with `sudo()`. It has the same\nsignature as `exec()`. Per default, the user under which the command\nwill be executed is \"root\". This can be changed by passing\n`user: \"name\"` with the second argument:\n\n```javascript\n// will run: echo 'echo Hello world' | sudo -u root -i bash\ntransport.sudo('echo Hello world');\n\n// will run echo 'echo Hello world' | sudo -u www -i bash\ntransport.sudo('echo Hello world', {user: 'www'});\n\n// further options passed (see `exec()`)\ntransport.sudo('echo Hello world', {user: 'www', silent: true, failsafe: true});\n```\n\nFlightplan's `sudo()` requires a certain setup on your host. In order to\nmake things work on a typical Ubuntu installation, follow these rules:\n\n```bash\n# Scenario:\n# 'pstadler' is the user for connecting to the host and 'www' is the user\n# under which you want to execute commands with sudo.\n\n# 1. 'pstadler' has to be in the sudo group:\n$ groups pstadler\npstadler : pstadler sudo\n\n# 2. 'pstadler' needs to be able to run sudo -u 'www' without a password.\n# In order to do this, add the following line to /etc/sudoers:\npstadler ALL=(www) NOPASSWD: ALL\n\n# 3. user 'www' needs to have a login shell (e.g. bash, sh, zsh, ...)\n$ cat /etc/passwd | grep www\nwww:x:1002:1002::/home/www:/bin/bash   # GOOD\nwww:x:1002:1002::/home/www:/bin/false  # BAD\n```\n\n### transport.transfer(files, remoteDir[, options]) → [results]\n\nCopy a list of files to the current target's remote host(s) using\n`rsync` with the SSH protocol. File transfers are executed in parallel.\n After finishing all transfers, an array containing results from\n`transport.exec()` is returned. This method is only available on local\nflights.\n\n```javascript\nvar files = ['path/to/file1', 'path/to/file2'];\nlocal.transfer(files, '/tmp/foo');\n```\n\n#### Files argument\nTo make things more comfortable, the `files` argument doesn't have to be\npassed as an array. Results from previous commands and zero-terminated\nstrings are handled as well:\n\n```javascript\n// use result from a previous command\nvar files = local.git('ls-files', {silent: true}); // get list of files under version control\nlocal.transfer(files, '/tmp/foo');\n\n// use zero-terminated result from a previous command\nvar files = local.exec('(git ls-files -z;find node_modules -type f -print0)', {silent: true});\nlocal.transfer(files, '/tmp/foo');\n\n// use results from multiple commands\nvar result1 = local.git('ls-files', {silent: true}).stdout.split('\\n');\nvar result2 = local.find('node_modules -type f', {silent: true}).stdout.split('\\n');\nvar files = result1.concat(result2);\nfiles.push('path/to/another/file');\nlocal.transfer(files, '/tmp/foo');\n```\n\n`transfer()` will use the current host's username defined with\n`target()` unless `fly` is called with the `-u|--username` option.\nIn this case the latter will be used. If debugging is enabled\n(either with `target()` or with `fly --debug`), `rsync` is executed\nin verbose mode (`-vv`).\n\n### transport.prompt(message[, options]) → input\n\nPrompt for user input.\n\n```javascript\nvar input = transport.prompt('Are you sure you want to continue? [yes]');\nif(input.indexOf('yes') === -1) {\n  plan.abort('User canceled flight');\n}\n\n// prompt for password (with UNIX-style hidden input)\nvar password = transport.prompt('Enter your password:', { hidden: true });\n\n// prompt when deploying to a specific target\nif(plan.runtime.target === 'production') {\n  var input = transport.prompt('Ready for deploying to production? [yes]');\n  if(input.indexOf('yes') === -1) {\n    plan.abort('User canceled flight');\n  }\n}\n```\n\n### transport.waitFor(fn(done)) → {} mixed\n\nExecute a function and return after the callback `done` is called.\nThis is used for running asynchronous functions in a synchronous way.\n\nThe callback takes an optional argument which is then returned by\n`waitFor()`.\n\n```javascript\nvar result = transport.waitFor(function(done) {\n  require('node-notifier').notify({\n      message: 'Hello World'\n    }, function(err, response) {\n      done(err || 'sent!');\n    });\n});\nconsole.log(result); // 'sent!'\n```\n\n### transport.with(command|options[, options], fn)\n\nExecute commands with a certain context.\n\n```javascript\ntransport.with('cd /tmp', function() {\n  transport.ls('-al'); // 'cd /tmp \u0026\u0026 ls -al'\n});\n\ntransport.with({silent: true, failsafe: true}, function() {\n  transport.ls('-al'); // output suppressed, fail safely\n});\n\ntransport.with('cd /tmp', {silent: true}, function() {\n  transport.ls('-al'); // 'cd /tmp \u0026\u0026 ls -al', output suppressed\n});\n```\n\n### transport.silent()\n\nWhen calling `silent()` all subsequent commands are executed without\nprinting their output to stdout until `verbose()` is called.\n\n```javascript\ntransport.ls(); // output will be printed to stdout\ntransport.silent();\ntransport.ls(); // output won't be printed to stdout\n```\n\n### transport.verbose()\n\nCalling `verbose()` reverts the behavior introduced with `silent()`.\nOutput of commands will be printed to stdout.\n\n```javascript\ntransport.silent();\ntransport.ls(); // output won't be printed to stdout\ntransport.verbose();\ntransport.ls(); // output will be printed to stdout\n```\n\n### transport.failsafe()\n\nWhen calling `failsafe()`, all subsequent commands are allowed to fail\nuntil `unsafe()` is called. In other words, the flight will continue\neven if the return code of the command is not `0`. This is helpful if\neither you expect a command to fail or their nature is to return a\nnon-zero exit code.\n\n```javascript\ntransport.failsafe();\ntransport.ls('foo'); // ls: foo: No such file or directory\ntransport.log('Previous command failed, but flight was not aborted');\n```\n\n### transport.unsafe()\n\nCalling `unsafe()` reverts the behavior introduced with `failsafe()`.\nThe flight will be aborted if a subsequent command fails (i.e. returns\na non-zero exit code). This is the default behavior.\n\n```javascript\ntransport.failsafe();\ntransport.ls('foo'); // ls: foo: No such file or directory\ntransport.log('Previous command failed, but flight was not aborted');\ntransport.unsafe();\ntransport.ls('foo'); // ls: foo: No such file or directory\n// flight aborted\n```\n\n### transport.log(message)\n\nPrint a message to stdout. Flightplan takes care that the message\nis formatted correctly within the current context.\n\n```javascript\ntransport.log('Copying files to remote hosts');\n```\n\n### transport.debug(message)\n\nPrint a debug message to stdout if debug mode is enabled. Flightplan\ntakes care that the message is formatted correctly within the current\ncontext.\n\n```javascript\ntransport.debug('Copying files to remote hosts');\n```\n\n\u003c!-- End ../lib/transport/index.js --\u003e\n\n\u003c!-- ENDDOCS --\u003e\n\n[npm-url]: https://npmjs.com/package/flightplan\n[npm-version-image]: https://img.shields.io/npm/v/flightplan.svg?style=flat-square\n[npm-downloads-image]: https://img.shields.io/npm/dm/flightplan.svg?style=flat-square\n\n[dependencies-url]: https://david-dm.org/pstadler/flightplan\n[dependencies-image]: https://david-dm.org/pstadler/flightplan.svg?style=flat-square\n\n[build-status-url]: https://travis-ci.org/pstadler/flightplan\n[build-status-image]: https://img.shields.io/travis/pstadler/flightplan/master.svg?style=flat-square\n\n[coverage-url]: https://coveralls.io/github/pstadler/flightplan?branch=master\n[coverage-image]: https://img.shields.io/coveralls/pstadler/flightplan/master.svg?style=flat-square\n","funding_links":[],"categories":["Install from Source","JavaScript","Node SSH"],"sub_categories":["SSH"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpstadler%2Fflightplan","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpstadler%2Fflightplan","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpstadler%2Fflightplan/lists"}