{"_id":"multibot","_rev":"90-5cf645d18cc4e236221bc6df4286eef6","name":"multibot","dist-tags":{"latest":"0.5.1"},"versions":{"0.0.1":{"name":"multibot","version":"0.0.1","author":{"name":"Ryan Roemer","email":"ryan.roemer@formidable.com"},"license":"MIT","_id":"multibot@0.0.1","maintainers":[{"name":"ryan.roemer","email":"ryan@loose-bits.com"}],"homepage":"https://github.com/FormidableLabs/multibot","bugs":{"url":"https://github.com/FormidableLabs/multibot/issues"},"bin":{"builder":"bin/multibot.js"},"dist":{"shasum":"8c65f3afdcfd165cd893cfe57e881fba1168998c","tarball":"https://registry.npmjs.org/multibot/-/multibot-0.0.1.tgz","integrity":"sha512-EA6YZWcoQ25tweVHRXE1angSDXOLzL/W/fALzwQi75mzidaqOxsEpZREEGcU2me+wRYL17yZF88grN4B1mY3Zw==","signatures":[{"sig":"MEUCIBqUCAaw5DZ11K18c+O28xcmjs/q8En+Of5t10qTDEUHAiEAygBeMU8XDR8VZkymGPPNnbQoM8Az1WCkGi3KVJapeJQ=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}]},"main":"lib/index.js","_from":".","_shasum":"8c65f3afdcfd165cd893cfe57e881fba1168998c","gitHead":"7cf5502d1efdc83351823626eecc3f82fafbea47","scripts":{"lint":"npm run lint-server","check":"npm run lint","lint-server":"eslint --color -c .eslintrc-server lib bin"},"_npmUser":{"name":"ryan.roemer","email":"ryan@loose-bits.com"},"repository":{"url":"git+https://github.com/FormidableLabs/multibot.git","type":"git"},"_npmVersion":"3.5.3","description":"A friendly multi-repository robot.","directories":{},"_nodeVersion":"4.2.4","dependencies":{"diff":"^2.2.2","async":"^1.5.2","chalk":"^1.1.1","yargs":"^4.3.1","octonode":"^0.7.4"},"devDependencies":{"eslint":"^2.4.0","eslint-config-defaults":"^10.0.0-alpha.1","eslint-plugin-filenames":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/multibot-0.0.1.tgz_1458166818488_0.15625814744271338","host":"packages-13-west.internal.npmjs.com"}},"0.1.0":{"name":"multibot","version":"0.1.0","author":{"name":"Ryan Roemer","email":"ryan.roemer@formidable.com"},"license":"MIT","_id":"multibot@0.1.0","maintainers":[{"name":"ryan.roemer","email":"ryan@loose-bits.com"}],"homepage":"https://github.com/FormidableLabs/multibot","bugs":{"url":"https://github.com/FormidableLabs/multibot/issues"},"bin":{"builder":"bin/multibot.js"},"dist":{"shasum":"6da713fab331ae4a01eefd5b0dbe1e8d9242bb1c","tarball":"https://registry.npmjs.org/multibot/-/multibot-0.1.0.tgz","integrity":"sha512-KqDldiuGz6zx3bbaUQjQFmw3jGcjbwk0p1NubBn1tjyG+EHD7TBI1/MMgbJdKBIoEk++oGYadpEhZGcyP884hg==","signatures":[{"sig":"MEUCIQCkZOkbUc7Kp3fDF7lLygI4PdsBk1JiDPeYUFf3SQm/FgIgMLGs77z+zl3iE9Aa7NuC0JwS7jqLowpQaokbK8gYXzk=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}]},"main":"lib/index.js","_from":".","_shasum":"6da713fab331ae4a01eefd5b0dbe1e8d9242bb1c","gitHead":"4f6536cf7742a46a1cbd6c08f3e313877d4d242d","scripts":{"lint":"npm run lint-server","check":"npm run lint","lint-server":"eslint --color -c .eslintrc-server lib bin"},"_npmUser":{"name":"ryan.roemer","email":"ryan@loose-bits.com"},"repository":{"url":"git+https://github.com/FormidableLabs/multibot.git","type":"git"},"_npmVersion":"3.5.3","description":"A friendly multi-repository robot.","directories":{},"_nodeVersion":"4.2.2","dependencies":{"diff":"^2.2.2","async":"^1.5.2","chalk":"^1.1.1","yargs":"^4.3.1","octonode":"^0.7.4"},"devDependencies":{"eslint":"^2.4.0","eslint-config-defaults":"^10.0.0-alpha.1","eslint-plugin-filenames":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/multibot-0.1.0.tgz_1460481978152_0.9843306026887149","host":"packages-12-west.internal.npmjs.com"}},"0.2.0":{"name":"multibot","version":"0.2.0","author":{"name":"Ryan Roemer","email":"ryan.roemer@formidable.com"},"license":"MIT","_id":"multibot@0.2.0","maintainers":[{"name":"ryan.roemer","email":"ryan@loose-bits.com"}],"homepage":"https://github.com/FormidableLabs/multibot","bugs":{"url":"https://github.com/FormidableLabs/multibot/issues"},"bin":{"builder":"bin/multibot.js"},"dist":{"shasum":"93f0deeac609a56242bca25681a821f5b49e1639","tarball":"https://registry.npmjs.org/multibot/-/multibot-0.2.0.tgz","integrity":"sha512-x/N0oR9DcGDlbnTBZbbETGEspVDqTAcfCiLqEnu28zz2EvFiXXmm00GoZx025wWihdLC5W0fkruBoIp9uCJ5tw==","signatures":[{"sig":"MEUCIDGQmhD2Ds27wLQRgOeI8MmYQP+LhMiU2ID0USzgv4QvAiEA9LMxWBV60kQFS+NWyjVnb8OgGTM2wgEsCTwdoCLreBk=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}]},"main":"lib/index.js","_from":".","_shasum":"93f0deeac609a56242bca25681a821f5b49e1639","gitHead":"2bffe199a20c996ccfc2400db09daa026bd11628","scripts":{"lint":"npm run lint-server","check":"npm run lint","lint-server":"eslint --color -c .eslintrc-server lib bin"},"_npmUser":{"name":"ryan.roemer","email":"ryan@loose-bits.com"},"repository":{"url":"git+https://github.com/FormidableLabs/multibot.git","type":"git"},"_npmVersion":"3.5.3","description":"A friendly multi-repository robot.","directories":{},"_nodeVersion":"4.2.2","dependencies":{"diff":"^2.2.2","async":"^1.5.2","chalk":"^1.1.1","yargs":"^4.3.1","github":"^0.2.4"},"devDependencies":{"eslint":"^2.4.0","eslint-config-defaults":"^10.0.0-alpha.1","eslint-plugin-filenames":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/multibot-0.2.0.tgz_1461948402551_0.26596969086676836","host":"packages-16-east.internal.npmjs.com"}},"0.3.0":{"name":"multibot","version":"0.3.0","author":{"name":"Ryan Roemer","email":"ryan.roemer@formidable.com"},"license":"MIT","_id":"multibot@0.3.0","maintainers":[{"name":"ryan.roemer","email":"ryan@loose-bits.com"}],"homepage":"https://github.com/FormidableLabs/multibot","bugs":{"url":"https://github.com/FormidableLabs/multibot/issues"},"bin":{"builder":"bin/multibot.js"},"dist":{"shasum":"642d2b078493b069ac6871ad0dc5d382bb66cf00","tarball":"https://registry.npmjs.org/multibot/-/multibot-0.3.0.tgz","integrity":"sha512-7dK8iK0Gt8W+38jIYx22pTD8BSJtnXLCwC3xXqH+WQNdWObwzFPhLOPLUXV1q4NJFbVIJjlHNhb1aagY/XyGQw==","signatures":[{"sig":"MEQCIDk4IO+NehDesJUyBQc7VtCBPgGNb1OJniJP0cRyji7dAiAUBt/UiebQk50M1kRKHxEDE2xfsjijdk3zMjoStsmr8w==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}]},"main":"lib/index.js","_from":".","_shasum":"642d2b078493b069ac6871ad0dc5d382bb66cf00","gitHead":"3c3456bb27c7a5ebf070cf89b09572c656409a04","scripts":{"lint":"npm run lint-server","check":"npm run lint","lint-server":"eslint --color -c .eslintrc-server lib bin"},"_npmUser":{"name":"ryan.roemer","email":"ryan@loose-bits.com"},"repository":{"url":"git+https://github.com/FormidableLabs/multibot.git","type":"git"},"_npmVersion":"3.5.3","description":"A friendly multi-repository robot.","directories":{},"_nodeVersion":"4.2.2","dependencies":{"diff":"^2.2.2","async":"^1.5.2","chalk":"^1.1.1","yargs":"^4.3.1","github":"^0.2.4"},"devDependencies":{"eslint":"^2.4.0","eslint-config-defaults":"^10.0.0-alpha.1","eslint-plugin-filenames":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/multibot-0.3.0.tgz_1462318622925_0.5996154707390815","host":"packages-12-west.internal.npmjs.com"}},"0.3.1":{"name":"multibot","version":"0.3.1","author":{"name":"Ryan Roemer","email":"ryan.roemer@formidable.com"},"license":"MIT","_id":"multibot@0.3.1","maintainers":[{"name":"ryan.roemer","email":"ryan@loose-bits.com"}],"homepage":"https://github.com/FormidableLabs/multibot","bugs":{"url":"https://github.com/FormidableLabs/multibot/issues"},"bin":{"multibot":"bin/multibot.js"},"dist":{"shasum":"39937b3d2c2adb2e909c554d040b676561e44952","tarball":"https://registry.npmjs.org/multibot/-/multibot-0.3.1.tgz","integrity":"sha512-p0/1EB6ZvkldJZGOjFSxylkHWn+gC07rcK1PtXXy9OV+mZAbwGy3aHzKwg3/oUGjn1+ZfRXHnAIpmGJtUeUbJQ==","signatures":[{"sig":"MEQCIGrmOwEBYgK2FngHORWMz8tWKtZee1YoBh4L7WsgdojiAiB/Iz+dcsLVmrcMUq4vqT5DOvQowXheGZJJ6/Gz53fCZQ==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}]},"main":"lib/index.js","_from":".","_shasum":"39937b3d2c2adb2e909c554d040b676561e44952","gitHead":"0d7a8eebd535ed996bbdea6438593e843b1e3329","scripts":{"lint":"npm run lint-server","check":"npm run lint","lint-server":"eslint --color -c .eslintrc-server lib bin"},"_npmUser":{"name":"ryan.roemer","email":"ryan@loose-bits.com"},"repository":{"url":"git+https://github.com/FormidableLabs/multibot.git","type":"git"},"_npmVersion":"3.5.3","description":"A friendly multi-repository robot.","directories":{},"_nodeVersion":"4.2.2","dependencies":{"diff":"^2.2.2","async":"^1.5.2","chalk":"^1.1.1","yargs":"^4.3.1","github":"^0.2.4"},"devDependencies":{"eslint":"^2.4.0","eslint-config-defaults":"^10.0.0-alpha.1","eslint-plugin-filenames":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/multibot-0.3.1.tgz_1462915461346_0.5854404142592102","host":"packages-16-east.internal.npmjs.com"}},"0.4.0":{"name":"multibot","version":"0.4.0","author":{"name":"Ryan Roemer","email":"ryan.roemer@formidable.com"},"license":"MIT","_id":"multibot@0.4.0","maintainers":[{"name":"ryan.roemer","email":"ryan@loose-bits.com"}],"homepage":"https://github.com/FormidableLabs/multibot","bugs":{"url":"https://github.com/FormidableLabs/multibot/issues"},"bin":{"multibot":"bin/multibot.js"},"dist":{"shasum":"60b7be4b8ec29e31ba5577c56c5920785a977418","tarball":"https://registry.npmjs.org/multibot/-/multibot-0.4.0.tgz","integrity":"sha512-Yuh94xkOz2QRbqFc2wp+3JbfJlOsdUWd6inLaUpqctqR395okdJkxLShG4+zkHVXetedqXODlTUe00qImZB0Pw==","signatures":[{"sig":"MEYCIQD9UB88r9F4z3hSZLchd7oM68YO+gdsDCniB8Qb7YCvCAIhAKty9371N5U0N+pfuxkFSQ3dMcQZyFuY259sP9FQrzjc","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}]},"main":"lib/index.js","_from":".","_shasum":"60b7be4b8ec29e31ba5577c56c5920785a977418","gitHead":"7a0817a9d78c8df0d725569fdbf37c5f2b848cc3","scripts":{"lint":"npm run lint-server","check":"npm run lint","lint-server":"eslint --color -c .eslintrc-server lib bin"},"_npmUser":{"name":"ryan.roemer","email":"ryan@loose-bits.com"},"repository":{"url":"git+https://github.com/FormidableLabs/multibot.git","type":"git"},"_npmVersion":"3.5.3","description":"A friendly multi-repository robot.","directories":{},"_nodeVersion":"4.2.2","dependencies":{"diff":"^2.2.2","async":"^1.5.2","chalk":"^1.1.1","yargs":"^4.3.1","github":"^0.2.4"},"devDependencies":{"eslint":"^2.4.0","eslint-config-defaults":"^10.0.0-alpha.1","eslint-plugin-filenames":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/multibot-0.4.0.tgz_1462973930695_0.6555498875677586","host":"packages-12-west.internal.npmjs.com"}},"0.5.0":{"name":"multibot","version":"0.5.0","author":{"name":"Ryan Roemer","email":"ryan.roemer@formidable.com"},"license":"MIT","_id":"multibot@0.5.0","maintainers":[{"name":"ryan.roemer","email":"ryan@loose-bits.com"}],"homepage":"https://github.com/FormidableLabs/multibot","bugs":{"url":"https://github.com/FormidableLabs/multibot/issues"},"bin":{"multibot":"bin/multibot.js"},"dist":{"shasum":"1a56dec3355ad04131dd26acf3ae4dcdf3a251bd","tarball":"https://registry.npmjs.org/multibot/-/multibot-0.5.0.tgz","integrity":"sha512-a6xC9RZGCoIhscCmkEZOTBbud8+muITRIbfV1Mq/B35CQIH2rj/Ksgir8tearETMQPc0gTwm+dPD2/AqmSMsBg==","signatures":[{"sig":"MEUCIQDuG4VV/OvALCWvWT9FZWDiNXGFufUqcMNUZ2dxfRoeagIgN5N5CluSHF6zEdXFSkJXeUO8k3kqrn3Fi5oiGdCzhko=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}]},"main":"lib/index.js","_from":".","_shasum":"1a56dec3355ad04131dd26acf3ae4dcdf3a251bd","gitHead":"54f337311eb8f6042cf525bf5a864d669704af20","scripts":{"lint":"eslint lib bin test","check":"npm run lint"},"_npmUser":{"name":"ryan.roemer","email":"ryan@loose-bits.com"},"repository":{"url":"git+https://github.com/FormidableLabs/multibot.git","type":"git"},"_npmVersion":"3.9.6","description":"A friendly multi-repository robot.","directories":{},"_nodeVersion":"4.2.2","dependencies":{"diff":"^3.0.0","async":"^2.0.1","chalk":"^1.1.3","yargs":"^6.0.0","github":"^3.1.1"},"devDependencies":{"eslint":"^2.13.1","eslint-plugin-import":"^1.16.0","eslint-plugin-filenames":"^1.1.0","eslint-config-formidable":"^2.0.1"},"_npmOperationalInternal":{"tmp":"tmp/multibot-0.5.0.tgz_1475588672020_0.6560747923795134","host":"packages-12-west.internal.npmjs.com"}},"0.5.1":{"name":"multibot","version":"0.5.1","author":{"name":"Ryan Roemer","email":"ryan.roemer@formidable.com"},"license":"MIT","_id":"multibot@0.5.1","maintainers":[{"name":"ryan.roemer","email":"ryan@loose-bits.com"}],"homepage":"https://github.com/FormidableLabs/multibot","bugs":{"url":"https://github.com/FormidableLabs/multibot/issues"},"bin":{"multibot":"bin/multibot.js"},"dist":{"shasum":"5254c56d8e6e91b7ae2e1d66d58ad547d3a183fa","tarball":"https://registry.npmjs.org/multibot/-/multibot-0.5.1.tgz","integrity":"sha512-zyiRWZkFdCgbq58XAXzc8zohHUsXJ7FQng/g8HGNtp2LWSuS5eTzyB3lyx2T5sPe16sdzJ6wJM/KrlqDj+3drA==","signatures":[{"sig":"MEQCIGTXEE1eeNjnXQjB13LImX9HtwC/Pd27ESPopgODtzzqAiB2ececeBaL3Dp55PZaQ62fTsUsQYIP1wfA1smEduejNg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}]},"main":"lib/index.js","_from":".","_shasum":"5254c56d8e6e91b7ae2e1d66d58ad547d3a183fa","gitHead":"780b6e9409e64a1b86cc34f609d4a5cc3a7dd403","scripts":{"lint":"eslint lib bin test","check":"npm run lint"},"_npmUser":{"name":"ryan.roemer","email":"ryan@loose-bits.com"},"repository":{"url":"git+https://github.com/FormidableLabs/multibot.git","type":"git"},"_npmVersion":"3.9.6","description":"A friendly multi-repository robot.","directories":{},"_nodeVersion":"4.2.2","dependencies":{"diff":"^3.0.0","async":"^2.0.1","chalk":"^1.1.3","yargs":"^6.0.0","github":"^3.1.1"},"devDependencies":{"eslint":"^2.13.1","eslint-plugin-import":"^1.16.0","eslint-plugin-filenames":"^1.1.0","eslint-config-formidable":"^2.0.1"},"_npmOperationalInternal":{"tmp":"tmp/multibot-0.5.1.tgz_1475609051327_0.41781592974439263","host":"packages-12-west.internal.npmjs.com"}}},"time":{"created":"2016-03-16T22:20:18.974Z","modified":"2026-07-20T21:09:19.617Z","0.0.1":"2016-03-16T22:20:18.974Z","0.1.0":"2016-04-12T17:26:18.597Z","0.2.0":"2016-04-29T16:46:44.948Z","0.3.0":"2016-05-03T23:37:03.359Z","0.3.1":"2016-05-10T21:24:23.909Z","0.4.0":"2016-05-11T13:38:51.123Z","0.5.0":"2016-10-04T13:44:32.286Z","0.5.1":"2016-10-04T19:24:11.662Z"},"bugs":{"url":"https://github.com/FormidableLabs/multibot/issues"},"author":{"name":"Ryan Roemer","email":"ryan.roemer@formidable.com"},"license":"MIT","homepage":"https://github.com/FormidableLabs/multibot","repository":{"url":"git+https://github.com/FormidableLabs/multibot.git","type":"git"},"description":"A friendly multi-repository robot.","maintainers":[{"email":"sarahmichelleegan@gmail.com","name":"sarmeyer"},{"email":"mariano.martinez@formidable.com","name":"mariano-formidable"},{"email":"ryan@loose-bits.com","name":"ryan.roemer"},{"email":"admin@formidablelabs.com","name":"formidable-owner"},{"email":"npm@formidablelabs.com","name":"formidablelabs"},{"email":"sarah.wright@formidable.com","name":"sarahformidable"},{"email":"senesealessandro@gmail.com","name":"ceceppa"}],"readme":"[![Travis Status][trav_img]][trav_site]\n<!--[![Coverage Status][cov_img]][cov_site]-->\n\nmultibot\n========\n\nA programmatic multi-repository helper.\n\nMultibot allows you to invoke actions (`read`, etc.) on 1+ files in 1+\nrepositories concurrently. It is meant to be a helper tool for multi-project\nupdates, branches, pull requests, etc.\n\n## Install\n\n```sh\n$ npm install multibot\n```\n\n## Usage\n\n```\nA friendly multi-repository robot.\n\nUsage: multibot --action=<string> [options]\n\nOptions:\n  --action          Actions to take\n    [string] [choices: \"read\", \"branch\", \"commit\", \"pull-request\", \"branch-to-pr\"] [default: \"read\"]\n  --branch-src      Source branch to start from / target for pull request\n                                                                        [string] [default: \"master\"]\n  --branch-dest     Destination branch to create / commit / open in a pull request          [string]\n  --allow-existing  Allow existing destination branches / PRs for `--action=branch|pull-request`?\n                                                                          [boolean] [default: false]\n  --files           List of files (space delimited) to read / transform                      [array]\n  --org             GitHub organization for repos (can be instead specified on repos)       [string]\n  --repos           GitHub repositories (space delimited) of form `repo` or `org/repo`\n                                                                                  [array] [required]\n  --gh-user         GitHub user name (needs user + pass)                                    [string]\n  --gh-pass         GitHub password (needs user + pass)                                     [string]\n  --gh-token        GitHub token                                                            [string]\n  --auth            Use authentication (limited functionality without)     [boolean] [default: true]\n  --gh-host         GitHub host URL (for enterprise)                                        [string]\n  --gh-path-prefix  GitHub path prefix (for enterprise). E.g., '/api/v3'                    [string]\n  --transform       Path to transform JS file                                               [string]\n  --format          Display output format\n                                        [string] [choices: \"json\", \"text\", \"diff\"] [default: \"diff\"]\n  --msg             Commit message / pull request description                               [string]\n  --title           Title for pull request (fallback to first line of `--msg`)              [string]\n  --dry-run         Skip / simulate all mutating actions                  [boolean] [default: false]\n  -h, --help        Show help                                                              [boolean]\n  -v, --version     Show version number                                                    [boolean]\n\nExamples:\n  multibot --action=branch-to-pr --gh-token=TOKEN     Create branch, commits, and PR for new\n  --org FormidableLabs --repos repo1 repo2 --files    `feature-foo` branch\n  README.md --transform=/PATH/TO/transform.js\n  --branch-dest=feature-foo --title='PR from Bot'\n  --msg='BotBot'\n```\n\n## Transforms\n\nThe bread and butter of Multibot is actually _changing_ files across many\nprojects in a sensible and predictable way. A \"transform\" is really just a JS\nfile exporting a method with the following signature:\n\n```js\n/**\n * Transform contents of file to new format.\n *\n * @param {Object}    obj           File object.\n * @param {String}    obj.repo      Repository name\n * @param {String}    obj.file      File path\n * @param {String}    obj.contents  UTF8 content of file (or `null` if doesn't exist)\n * @param {Function}  callback      Send transformed contents to `(err, newContents)`\n * @returns {void}\n */\nmodule.exports = function (obj, callback) {\n  callback(null, obj.contents.replace(\"multibot\", \"MULTIBOT ROCKS!!!\"));\n};\n```\n\nA transform is hooked into a multibot action with the option:\n`--transform=PATH/TO/file.js` (absolute or relative paths work).\n\nThe `repo` and `file` fields are passed in the case that you wish to have\nconditional transform logic, while still operating over a lot of files.\n\nThe `contents` field will be `null` if the source file does not presently exist\nin the repository.\n\nThere are four things a transform can really do:\n\n* Update an existing file. `contents` is non-null, and called back with a\n  string that is different from the original.\n* Do nothing with an existing file, if the contents do not change.\n* Create a new file. `contents` is `null`, called back with a string.\n* Delete an existing file. `contents` is non-null, called back with `null`.\n\nThese are obviously very powerful features, and should be thoroughly tested\nwith `--action=read` which provides essentially a dry-run of a real commit\naction.\n\nNote that a file cannot be both created (source is `null`) and deleted\n(transform produces `null`) at the same time.\n\nAlso note that all files passed into a string and transformed **must be\nstrings**. Do not allow a transform to process anything that is not UTF8 string\ndata. (We could refactor `multibot` in the future to accomodate non-string\nformats.)\n\n### Note - Repeated Transforms\n\nBe very careful to inspect and watch your transforms for repeated runs if a\n`--action=commit` fails for some, but not all, repositories and you're\nretrying. The reason is that for repositories that _succeeeded_ the first time,\nthe transform will get applied _again_ with potentially negative results unless\nyou plan for this.\n\nFor example, if you have a situation like:\n\n```js\n// --transform=rocks.js\nmodule.exports = function (obj, callback) {\n  callback(null, obj.contents.replace(\"multibot\", \"multibot ROCKS!!!\"));\n};\n```\n\n```\n// --files README.md\nWoah, multibot\n```\n\nA sucessful transform would produce:\n\n```\n// --files README.md\nWoah, multibot ROCKS!!!\n```\n\nHowever, if that transform was run again (say this repo succeed first time\nbut others failed, so you're running the same commit command again):\n\n```\n// --files README.md\nWoah, multibot ROCKS!!! ROCKS!!!\n```\n\nwhich is probably not what we want. The remedy for this specific situation is\nto either, adjust the repositories passed to `--repos` on the command line to\nremove the success, or to refactor the transform to be able to run repeatedly.\nHere, that may just be detecting that `ROCKS!!!` doesn't already occur, or\nallowing it to, then squashing it.\n\n```js\n// --transform=rocks.js\nmodule.exports = function (obj, callback) {\n  callback(null, obj.contents\n    .replace(\"multibot\", \"multibot ROCKS!!!\") // Add the rocks\n    .replace(\"ROCKS!!! ROCKS!!!\", \"ROCKS!!!\") // Squash the rocks if 2\n  );\n};\n```\n\n## Actions\n\nMultibot can initiate various read-only and repository-mutating actions. A\nbasic workflow for initiating a change across multiple repositories via a PR\nwould look like:\n\nFirst, check the transform looks good without changing anything:\n\n```sh\n$ multibot \\\n  --org FormidableLabs --repos repo1 repo2 repo3 \\\n  --transform=foo.js --files README.md \\\n  --action=read \\\n  --format=diff\n```\n\nIf that looks good, then create a branch, commit the transform, and open a PR:\n\n```sh\n$ multibot \\\n  --org FormidableLabs --repos repo1 repo2 repo3 \\\n  --branch-src=master \\\n  --branch-dest=feature-foo \\\n  --action=branch \\\n  --format=text\n\n$ multibot \\\n  --org FormidableLabs --repos repo1 repo2 repo3 \\\n  --transform=foo.js --files README.md \\\n  --branch-dest=feature-foo \\\n  --action=commit \\\n  --format=diff\n\n$ multibot \\\n  --org FormidableLabs --repos repo1 repo2 repo3 \\\n  --branch-dest=feature-foo \\\n  --msg=$'A big change\\nfrom a bot.' \\ # Note use of bash ANSI C parsing of newline\n  --action=pull-request \\\n  --format=text\n```\n\nor all as a single command:\n\n```sh\n$ multibot \\\n  --org FormidableLabs --repos repo1 repo2 repo3 \\\n  --transform=foo.js --files README.md \\\n  --branch-src=master \\\n  --branch-dest=feature-foo \\\n  --msg=$'A big change\\nfrom a bot.' \\\n  --action=branch-to-pr\n```\n\n### `read`\n\nRead files from repositories, optionally applying a `--transform`. Does not\nmutate the underlying repositories. Great for doing a test run of the future\nmutating actions with `multibot`.\n\nExample:\n\n```sh\n$ multibot \\\n  --org FormidableLabs --repos repo1 repo2 repo3 \\\n  --transform=foo.js --files README.md \\\n  --action=read \\\n  --format=diff\n```\n\nFlags:\n\n* `--action=read`\n* `--branch-src`: (Optional, default: `master`) Source branch to read from.\n* `--org`: (Optional) GitHub organization for repos\n* `--repos`: GitHub repositories (space delimited) of form `repo` or `org/repo`\n* `--files`: List of files (space delimited) to read / transform\n* `--transform`: (Optional) Path to transform JS file\n* `--format`: (Optional) Output report as `json`, `text`, or `diff`\n\nNote that because we do one shot HTTP requests for existing contents to a\nrepository, `multibot` can't distinguish a single file not being found (normal,\nmeans you'd create one) with a non-existent repo which will error in other\ncommands.\n\n### `branch`\n\nCreate a branch in repositories.\n\nExample:\n\n```sh\n$ multibot \\\n  --org FormidableLabs --repos repo1 repo2 repo3 \\\n  --branch-src=master \\\n  --branch-dest=branch-o-doom \\\n  --action=branch \\\n  --format=text\n```\n\nFlags:\n\n* `--action=branch`\n* `--branch-src`: (Optional, default: `master`) Source to branch from.\n* `--branch-dest`: Non-`master` new branch to create.\n* `--org`: (Optional) GitHub organization for repos\n* `--repos`: GitHub repositories (space delimited) of form `repo` or `org/repo`\n* `--format`: (Optional) Output report as `json`, `text`, or `diff`\n* `--allow-existing`: (Optional, default: `false`) Allow existing destination branches?\n* `--dry-run`: (Optional) Simulate mutating actions.\n\n### `commit`\n\nCommit changes from a transform to a non-`master` branch in a repository.\nTypically, you will create a new branch first with `--action=branch` and then\nfill it with 1+ commits using `--action=commit` here.\n\nExample:\n\n```sh\n$ multibot \\\n  --org FormidableLabs --repos repo1 repo2 repo3 \\\n  --branch-dest=branch-o-doom \\\n  --files README.md LICENSE docs/DANGER.md \\\n  --action=commit \\\n  --transform=\"PATH/TO/transformify.js\" \\\n  --msg=\"Add some DANGER to the repo files.\" \\\n  --format=diff\n```\n\nWith a transform file: `PATH/TO/transformify.js`\n\n```js\nmodule.exports = function (obj, callback) {\n  // CREATE a new file if none exists. Here `obj.file === \"docs/DANGER.md\"`\n  if (obj.contents === null) {\n    callback(null, \"DANGER! DANGER!\\n\");\n    return;\n  }\n\n  // DELETE a specific file by file path.\n  if (obj.file === \"LICENSE\") {\n    callback(null, null);\n    return;\n  }\n\n  // UPDATE everything else to emphasize danger.\n  callback(null, obj.contents.replace(/danger/g, \"DANGER\"));\n};\n```\n\nWill create a new tree with the updates, deletes, and creates. If the operation\non a specific repository is a noop, no actual mutation actions are performed.\n\nFlags:\n\n* `--action=commit`\n* `--branch-dest`: Non-`master` target branch to update with commit. (Also the\n  source branch to read current files from.)\n* `--org`: (Optional) GitHub organization for repos\n* `--repos`: GitHub repositories (space delimited) of form `repo` or `org/repo`\n* `--files`: List of files (space delimited) to read / transform\n* `--msg`: Commit message\n* `--transform`: (Optional) Path to transform JS file\n* `--format`: (Optional) Output report as `json`, `text`, or `diff`\n* `--dry-run`: (Optional) Simulate mutating actions.\n\nNote that if a specific repository has no actual changes, no commit will be\ncreated. (E.g., `multibot` won't create an empty commit.)\n\n#### Commit API Notes\n\nCreating commits with the GitHub API (and well, git) is a tad complex. We\nactually have two separate scenarios for a commit based on whether or not\nthere is a file deletion (from a `null` transform contents value).\n\n**If there are no deletes**, then `multibot` gets posts new blobs for all the\nexisting / new files, then _updates_ the current branch tree reference off\nof the [base tree](https://developer.github.com/v3/git/trees/#create-a-tree).\n\n**If there are deletes**, then we have to retrieve the entire existing tree\nfirst. This runs the risk of\n[truncated results](https://developer.github.com/v3/git/trees/#get-a-tree-recursively)\nif a tree is too large for the GitHub API is returned. If we have truncated\nresults in any tree in a commit action, then `multibot` simply fails without\nhaving performed any mutations. If we get the entire tree without truncation,\nthen `multibot` continues.\n\nOnce we have the full tree, then we can actually perform deletes by _removing_\nexisting blob references while splicing in our updates / creates and creating\na new tree _without_ a base tree reference, which completely replaces the\nentire former tree.\n\n### `pull-request`\n\nCreate a pull request from a branch in repositories.\n\nExample:\n\n```sh\n$ multibot \\\n  --org FormidableLabs --repos repo1 repo2 repo3 \\\n  --branch-src=master \\\n  --branch-dest=branch-o-doom \\\n  --action=pull-request \\\n  --title=\"The Bots have arrived\" \\\n  --msg=\"...and are making mischief\" \\\n  --format=text\n```\n\nFlags:\n\n* `--action=pull-request`\n* `--branch-src`: Base branch for pull request against\n* `--branch-dest`: Non-`master` target branch to create pull request for\n* `--org`: (Optional) GitHub organization for repos\n* `--repos`: GitHub repositories (space delimited) of form `repo` or `org/repo`\n* `--title`: Title for pull request (fallback to first line of `--msg`)\n* `--msg`: Pull request description\n* `--format`: (Optional) Output report as `json`, `text`, or `diff`\n* `--allow-existing`: (Optional, default: `false`) Allow existing pull requests?\n* `--dry-run`: (Optional) Simulate mutating actions.\n\n\n### `branch-to-pr`\n\nCreate a branch, add commits, open a PR. An \"all-in-one\" aggregator for a common\nuse case for multibot.\n\n```sh\n$ multibot \\\n  --org FormidableLabs --repos repo1 repo2 repo3 \\\n  --branch-src=master \\\n  --branch-dest=branch-o-doom \\\n  --files README.md LICENSE docs/DANGER.md \\\n  --transform=\"PATH/TO/transformify.js\" \\\n  --action=branch-to-pr \\\n  --title=\"The Bots have arrived\" \\\n  --msg=\"...and are making mischief\" \\\n  --format=diff\n```\nFlags:\n\n* `--action=pull-request`\n* `--branch-src`: Base branch for pull request against\n* `--branch-dest`: Non-`master` target branch to create pull request for\n* `--org`: (Optional) GitHub organization for repos\n* `--repos`: GitHub repositories (space delimited) of form `repo` or `org/repo`\n* `--files`: List of files (space delimited) to read / transform\n* `--transform`: Path to transform JS file\n* `--title`: Title for pull request (fallback to first line of `--msg`)\n* `--msg`: Pull request description\n* `--format`: (Optional) Output report as `json`, `text`, or `diff`\n* `--dry-run`: (Optional) Simulate mutating actions.\n\nNote that we _disallow_ the following flags here:\n\n* `--allow-existing`\n\n\n## GitHub API\n\n`multibot` has the convenient feature that it never touches disk to perform any\nrepository / branch operations. This is done by relying entirely on the\n[GitHub API](https://developer.github.com/v3/) for operations.\n\nThis also means that `multibot` must stay within the\n[API rate limits](https://developer.github.com/v3/rate_limit/). If you go\nbeyond the limit, you will most likely encounter 403 HTTP error codes. If this\nhappens, check your rate limit with:\n\n```sh\n$ curl -H \"Authorization: token OAUTH-TOKEN\" https://api.github.com/rate_limit\n```\n\nLook at the `remaining` field to see how many requests you have left for the\nhour. GitHub currently allows authenticated users to make up to 5,000 requests\nper hour.\n\n### Enterprise GitHub\n\n`multibot` supports GitHub enterprise installations that use the `v3` API. The\nrelevant options that you may need to use include:\n\n* `--gh-host`: The GitHub API host to use. By default, public GitHub is used\n  (`api.github.com`). Switch to your enterprise host like:\n  `my-github.my-company.com`.\n* `--gh-path-prefix`: Some GitHub enterprise instances need an extra prefix to\n  the API URLs, which is usually `\"/api/v3\"` if omitting this option doesn't\n  work. Public GitHub does not need this option.\n\nPut together, a full-fledged command for GitHub enterprise might look like:\n\n```sh\n$ multibot \\\n  --gh-token=<SNIPPED> \\\n  --gh-host=my-github.my-company.com \\\n  --gh-path-prefix=\"/api/v3\" \\\n  --org EnterpriseGHOrg --repos repo1 repo2 repo3 \\\n  --transform=foo.js --files README.md \\\n  --action=read \\\n  --format=diff\n```\n\n[trav_img]: https://api.travis-ci.org/FormidableLabs/multibot.svg\n[trav_site]: https://travis-ci.org/FormidableLabs/multibot\n[cov]: https://coveralls.io\n[cov_img]: https://img.shields.io/coveralls/FormidableLabs/multibot.svg\n[cov_site]: https://coveralls.io/r/FormidableLabs/multibot\n","readmeFilename":"README.md"}