├── .eslintignore ├── .eslintrc ├── .gitignore ├── CHANGES.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── bin ├── sl-deploy.js └── sl-deploy.txt ├── index.js ├── lib ├── deploy-endpoint.js ├── git.js ├── package.js ├── post-json.js ├── put-file.js └── service.js ├── package.json └── test ├── helpers.js ├── test-deploy-default-branch.js ├── test-deploy-from-workingdir.js ├── test-deploy-specific-branch.js ├── test-git-deploy-auth-failure.js ├── test-git-deploy-auth.js ├── test-git-http-ssh.js ├── test-put-deploy-auth-failure.js ├── test-put-deploy-auth.js ├── test-put-file-deploy.js ├── test-put-file-http-ssh.js └── test-usage.js /.eslintignore: -------------------------------------------------------------------------------- 1 | test 2 | node_modules 3 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "strongloop", 3 | "rules": { 4 | "comma-dangle": 0 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .strong-pm 2 | *.log 3 | *.tgz 4 | node_modules 5 | test/.test_artifacts 6 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | 2016-05-06, Version 3.1.4 2 | ========================= 3 | 4 | * update copyright notices and license (Ryan Graham) 5 | 6 | 7 | 2016-04-11, Version 3.1.3 8 | ========================= 9 | 10 | * package: update to eslint@2 (Sam Roberts) 11 | 12 | * Refer to licenses with a link (Sam Roberts) 13 | 14 | 15 | 2015-10-01, Version 3.1.2 16 | ========================= 17 | 18 | 19 | 20 | 2015-10-01, Version 3.1.1 21 | ========================= 22 | 23 | * Use strongloop conventions for licensing (Sam Roberts) 24 | 25 | 26 | 2015-09-09, Version 3.1.0 27 | ========================= 28 | 29 | * docs: add SSH_PORT to usage message (Ryan Graham) 30 | 31 | * set ssh port when use http+ssh deploy via env (SemonCat) 32 | 33 | 34 | 2015-07-27, Version 3.0.0 35 | ========================= 36 | 37 | * sl-deploy API accept cluster size parameter (Setogit) 38 | 39 | 40 | 2015-06-03, Version 2.2.2 41 | ========================= 42 | 43 | * test: expose mesh model apiVersion, not version (Sam Roberts) 44 | 45 | * Bump mesh-models dependency version (Krishna Raman) 46 | 47 | 48 | 2015-05-15, Version 2.2.1 49 | ========================= 50 | 51 | * eslint: new doesn't need parentheses (Sam Roberts) 52 | 53 | * package: remove usage from README (Sam Roberts) 54 | 55 | * package: run linters in pretest (Sam Roberts) 56 | 57 | 58 | 2015-05-08, Version 2.2.0 59 | ========================= 60 | 61 | * use provided service name in error as fallback (Ryan Graham) 62 | 63 | * print real url in error messages (Ryan Graham) 64 | 65 | * Log name of service that was deployed to (Sam Roberts) 66 | 67 | * deploy: check remote API version compatibility (Sam Roberts) 68 | 69 | 70 | 2015-04-30, Version 2.0.0 71 | ========================= 72 | 73 | * Support multiapp pm in deploy APIs (Sam Roberts) 74 | 75 | * put-file: report error message from server (Setogit) 76 | 77 | * Add multi-service support (Krishna Raman) 78 | 79 | 80 | 2015-04-17, Version 1.2.0 81 | ========================= 82 | 83 | * usage: add default npm package to documentation (Setogit) 84 | 85 | * slc deploy, should in non-git, deploy the archive, if it exists (Setogit) 86 | 87 | * Update README for strong-pm.io (Sam Roberts) 88 | 89 | 90 | 2015-03-18, Version 1.1.6 91 | ========================= 92 | 93 | * fix error in error handler (Ryan Graham) 94 | 95 | * deps: update dev dependencies (Ryan Graham) 96 | 97 | * deps: fix bad version of strong-tunnel (Ryan Graham) 98 | 99 | * package: update lint for CI (Sam Roberts) 100 | 101 | * Add support for deploying using http over ssh (Ryan Graham) 102 | 103 | 104 | 2015-03-16, Version 1.1.5 105 | ========================= 106 | 107 | * cli: update usage text (Ryan Graham) 108 | 109 | * git: show git error message on failure (Ryan Graham) 110 | 111 | * local deploy: tune messaging on failure (Sam Roberts) 112 | 113 | * deploy: URL defaulting to http://localhost:8701 (Sam Roberts) 114 | 115 | * lint: support eslint and jscs (Sam Roberts) 116 | 117 | * Read responses, so request can complete (Sam Roberts) 118 | 119 | 120 | 2015-02-09, Version 1.1.4 121 | ========================= 122 | 123 | * deps: make async a regular dependency (Ryan Graham) 124 | 125 | * Give useful error message when auth is required (Ryan Graham) 126 | 127 | * docs: document implicit support for HTTP auth (Ryan Graham) 128 | 129 | * test: use strong-fork-cicada for tests (Ryan Graham) 130 | 131 | 132 | 2015-01-20, Version 1.1.3 133 | ========================= 134 | 135 | * tests: use os.tmpdir() instead of /tmp (Ryan Graham) 136 | 137 | 138 | 2015-01-09, Version 1.1.2 139 | ========================= 140 | 141 | * Fix wrong output for --version, --help, etc. (Sam Roberts) 142 | 143 | * Fix bad CLA URL in CONTRIBUTING.md (Ryan Graham) 144 | 145 | 146 | 2014-12-12, Version 1.1.1 147 | ========================= 148 | 149 | * Fix local deploy logic error (Sam Roberts) 150 | 151 | * package: use debug v2.x in all strongloop deps (Sam Roberts) 152 | 153 | 154 | 2014-12-01, Version 1.1.0 155 | ========================= 156 | 157 | * Support local in-place deployment (Sam Roberts) 158 | 159 | * Fix incorrect error reporting for put based deploy (Krishna Raman) 160 | 161 | * Fix test and noisy console output for git deploys (Krishna Raman) 162 | 163 | * Enable strong-deploy to be used as a module (Krishna Raman) 164 | 165 | 166 | 2014-10-02, Version 1.0.0 167 | ========================= 168 | 169 | * Update contribution guidelines (Ryan Graham) 170 | 171 | 172 | 2014-09-02, Version 0.1.4 173 | ========================= 174 | 175 | * package: mention slc in usage, and git package (Sam Roberts) 176 | 177 | * package: add keywords for npm search (Sam Roberts) 178 | 179 | 180 | 2014-08-26, Version 0.1.3 181 | ========================= 182 | 183 | * usage: clarify pack and branch meaning (Sam Roberts) 184 | 185 | * Remove --redeploy and make git always force push (Krishna Raman) 186 | 187 | * Add ability to deploy npm package (tarball) (Krishna Raman) 188 | 189 | * Add usage enhancements (Krishna Raman) 190 | 191 | 192 | 2014-08-05, Version 0.1.2 193 | ========================= 194 | 195 | * Update package license to match LICENSE.md (Sam Roberts) 196 | 197 | * package: add .gitignore file (Sam Roberts) 198 | 199 | 200 | 2014-07-21, Version 0.1.1 201 | ========================= 202 | 203 | * Fix silent exit when run from non git repo (Krishna Raman) 204 | 205 | 206 | 2014-07-21, Version 0.1.0 207 | ========================= 208 | 209 | * First release! 210 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ### Contributing ### 2 | 3 | Thank you for your interest in `strong-deploy`, an open source project 4 | administered by StrongLoop. 5 | 6 | Contributing to `strong-deploy` is easy. In a few simple steps: 7 | 8 | * Ensure that your effort is aligned with the project's roadmap by 9 | talking to the maintainers, especially if you are going to spend a 10 | lot of time on it. 11 | 12 | * Make something better or fix a bug. 13 | 14 | * Adhere to code style outlined in the [Google C++ Style Guide][] and 15 | [Google Javascript Style Guide][]. 16 | 17 | * Sign the [Contributor License Agreement](https://cla.strongloop.com/agreements/strongloop/strong-deploy) 18 | 19 | * Submit a pull request through Github. 20 | 21 | 22 | ### Contributor License Agreement ### 23 | 24 | ``` 25 | Individual Contributor License Agreement 26 | 27 | By signing this Individual Contributor License Agreement 28 | ("Agreement"), and making a Contribution (as defined below) to 29 | StrongLoop, Inc. ("StrongLoop"), You (as defined below) accept and 30 | agree to the following terms and conditions for Your present and 31 | future Contributions submitted to StrongLoop. Except for the license 32 | granted in this Agreement to StrongLoop and recipients of software 33 | distributed by StrongLoop, You reserve all right, title, and interest 34 | in and to Your Contributions. 35 | 36 | 1. Definitions 37 | 38 | "You" or "Your" shall mean the copyright owner or the individual 39 | authorized by the copyright owner that is entering into this 40 | Agreement with StrongLoop. 41 | 42 | "Contribution" shall mean any original work of authorship, 43 | including any modifications or additions to an existing work, that 44 | is intentionally submitted by You to StrongLoop for inclusion in, 45 | or documentation of, any of the products owned or managed by 46 | StrongLoop ("Work"). For purposes of this definition, "submitted" 47 | means any form of electronic, verbal, or written communication 48 | sent to StrongLoop or its representatives, including but not 49 | limited to communication or electronic mailing lists, source code 50 | control systems, and issue tracking systems that are managed by, 51 | or on behalf of, StrongLoop for the purpose of discussing and 52 | improving the Work, but excluding communication that is 53 | conspicuously marked or otherwise designated in writing by You as 54 | "Not a Contribution." 55 | 56 | 2. You Grant a Copyright License to StrongLoop 57 | 58 | Subject to the terms and conditions of this Agreement, You hereby 59 | grant to StrongLoop and recipients of software distributed by 60 | StrongLoop, a perpetual, worldwide, non-exclusive, no-charge, 61 | royalty-free, irrevocable copyright license to reproduce, prepare 62 | derivative works of, publicly display, publicly perform, 63 | sublicense, and distribute Your Contributions and such derivative 64 | works under any license and without any restrictions. 65 | 66 | 3. You Grant a Patent License to StrongLoop 67 | 68 | Subject to the terms and conditions of this Agreement, You hereby 69 | grant to StrongLoop and to recipients of software distributed by 70 | StrongLoop a perpetual, worldwide, non-exclusive, no-charge, 71 | royalty-free, irrevocable (except as stated in this Section) 72 | patent license to make, have made, use, offer to sell, sell, 73 | import, and otherwise transfer the Work under any license and 74 | without any restrictions. The patent license You grant to 75 | StrongLoop under this Section applies only to those patent claims 76 | licensable by You that are necessarily infringed by Your 77 | Contributions(s) alone or by combination of Your Contributions(s) 78 | with the Work to which such Contribution(s) was submitted. If any 79 | entity institutes a patent litigation against You or any other 80 | entity (including a cross-claim or counterclaim in a lawsuit) 81 | alleging that Your Contribution, or the Work to which You have 82 | contributed, constitutes direct or contributory patent 83 | infringement, any patent licenses granted to that entity under 84 | this Agreement for that Contribution or Work shall terminate as 85 | of the date such litigation is filed. 86 | 87 | 4. You Have the Right to Grant Licenses to StrongLoop 88 | 89 | You represent that You are legally entitled to grant the licenses 90 | in this Agreement. 91 | 92 | If Your employer(s) has rights to intellectual property that You 93 | create, You represent that You have received permission to make 94 | the Contributions on behalf of that employer, that Your employer 95 | has waived such rights for Your Contributions, or that Your 96 | employer has executed a separate Corporate Contributor License 97 | Agreement with StrongLoop. 98 | 99 | 5. The Contributions Are Your Original Work 100 | 101 | You represent that each of Your Contributions are Your original 102 | works of authorship (see Section 8 (Submissions on Behalf of 103 | Others) for submission on behalf of others). You represent that to 104 | Your knowledge, no other person claims, or has the right to claim, 105 | any right in any intellectual property right related to Your 106 | Contributions. 107 | 108 | You also represent that You are not legally obligated, whether by 109 | entering into an agreement or otherwise, in any way that conflicts 110 | with the terms of this Agreement. 111 | 112 | You represent that Your Contribution submissions include complete 113 | details of any third-party license or other restriction (including, 114 | but not limited to, related patents and trademarks) of which You 115 | are personally aware and which are associated with any part of 116 | Your Contributions. 117 | 118 | 6. You Don't Have an Obligation to Provide Support for Your Contributions 119 | 120 | You are not expected to provide support for Your Contributions, 121 | except to the extent You desire to provide support. You may provide 122 | support for free, for a fee, or not at all. 123 | 124 | 6. No Warranties or Conditions 125 | 126 | StrongLoop acknowledges that unless required by applicable law or 127 | agreed to in writing, You provide Your Contributions on an "AS IS" 128 | BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER 129 | EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES 130 | OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR 131 | FITNESS FOR A PARTICULAR PURPOSE. 132 | 133 | 7. Submission on Behalf of Others 134 | 135 | If You wish to submit work that is not Your original creation, You 136 | may submit it to StrongLoop separately from any Contribution, 137 | identifying the complete details of its source and of any license 138 | or other restriction (including, but not limited to, related 139 | patents, trademarks, and license agreements) of which You are 140 | personally aware, and conspicuously marking the work as 141 | "Submitted on Behalf of a Third-Party: [named here]". 142 | 143 | 8. Agree to Notify of Change of Circumstances 144 | 145 | You agree to notify StrongLoop of any facts or circumstances of 146 | which You become aware that would make these representations 147 | inaccurate in any respect. Email us at callback@strongloop.com. 148 | ``` 149 | 150 | [Google C++ Style Guide]: https://google.github.io/styleguide/cppguide.html 151 | [Google Javascript Style Guide]: https://google.github.io/styleguide/javascriptguide.xml 152 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) IBM Corp. 2014,2016. All Rights Reserved. 2 | Node module: strong-deploy 3 | This project is licensed under the Artistic License 2.0, full text below. 4 | 5 | -------- 6 | 7 | The Artistic License 2.0 8 | 9 | Copyright (c) 2000-2006, The Perl Foundation. 10 | 11 | Everyone is permitted to copy and distribute verbatim copies 12 | of this license document, but changing it is not allowed. 13 | 14 | Preamble 15 | 16 | This license establishes the terms under which a given free software 17 | Package may be copied, modified, distributed, and/or redistributed. 18 | The intent is that the Copyright Holder maintains some artistic 19 | control over the development of that Package while still keeping the 20 | Package available as open source and free software. 21 | 22 | You are always permitted to make arrangements wholly outside of this 23 | license directly with the Copyright Holder of a given Package. If the 24 | terms of this license do not permit the full use that you propose to 25 | make of the Package, you should contact the Copyright Holder and seek 26 | a different licensing arrangement. 27 | 28 | Definitions 29 | 30 | "Copyright Holder" means the individual(s) or organization(s) 31 | named in the copyright notice for the entire Package. 32 | 33 | "Contributor" means any party that has contributed code or other 34 | material to the Package, in accordance with the Copyright Holder's 35 | procedures. 36 | 37 | "You" and "your" means any person who would like to copy, 38 | distribute, or modify the Package. 39 | 40 | "Package" means the collection of files distributed by the 41 | Copyright Holder, and derivatives of that collection and/or of 42 | those files. A given Package may consist of either the Standard 43 | Version, or a Modified Version. 44 | 45 | "Distribute" means providing a copy of the Package or making it 46 | accessible to anyone else, or in the case of a company or 47 | organization, to others outside of your company or organization. 48 | 49 | "Distributor Fee" means any fee that you charge for Distributing 50 | this Package or providing support for this Package to another 51 | party. It does not mean licensing fees. 52 | 53 | "Standard Version" refers to the Package if it has not been 54 | modified, or has been modified only in ways explicitly requested 55 | by the Copyright Holder. 56 | 57 | "Modified Version" means the Package, if it has been changed, and 58 | such changes were not explicitly requested by the Copyright 59 | Holder. 60 | 61 | "Original License" means this Artistic License as Distributed with 62 | the Standard Version of the Package, in its current version or as 63 | it may be modified by The Perl Foundation in the future. 64 | 65 | "Source" form means the source code, documentation source, and 66 | configuration files for the Package. 67 | 68 | "Compiled" form means the compiled bytecode, object code, binary, 69 | or any other form resulting from mechanical transformation or 70 | translation of the Source form. 71 | 72 | 73 | Permission for Use and Modification Without Distribution 74 | 75 | (1) You are permitted to use the Standard Version and create and use 76 | Modified Versions for any purpose without restriction, provided that 77 | you do not Distribute the Modified Version. 78 | 79 | 80 | Permissions for Redistribution of the Standard Version 81 | 82 | (2) You may Distribute verbatim copies of the Source form of the 83 | Standard Version of this Package in any medium without restriction, 84 | either gratis or for a Distributor Fee, provided that you duplicate 85 | all of the original copyright notices and associated disclaimers. At 86 | your discretion, such verbatim copies may or may not include a 87 | Compiled form of the Package. 88 | 89 | (3) You may apply any bug fixes, portability changes, and other 90 | modifications made available from the Copyright Holder. The resulting 91 | Package will still be considered the Standard Version, and as such 92 | will be subject to the Original License. 93 | 94 | 95 | Distribution of Modified Versions of the Package as Source 96 | 97 | (4) You may Distribute your Modified Version as Source (either gratis 98 | or for a Distributor Fee, and with or without a Compiled form of the 99 | Modified Version) provided that you clearly document how it differs 100 | from the Standard Version, including, but not limited to, documenting 101 | any non-standard features, executables, or modules, and provided that 102 | you do at least ONE of the following: 103 | 104 | (a) make the Modified Version available to the Copyright Holder 105 | of the Standard Version, under the Original License, so that the 106 | Copyright Holder may include your modifications in the Standard 107 | Version. 108 | 109 | (b) ensure that installation of your Modified Version does not 110 | prevent the user installing or running the Standard Version. In 111 | addition, the Modified Version must bear a name that is different 112 | from the name of the Standard Version. 113 | 114 | (c) allow anyone who receives a copy of the Modified Version to 115 | make the Source form of the Modified Version available to others 116 | under 117 | 118 | (i) the Original License or 119 | 120 | (ii) a license that permits the licensee to freely copy, 121 | modify and redistribute the Modified Version using the same 122 | licensing terms that apply to the copy that the licensee 123 | received, and requires that the Source form of the Modified 124 | Version, and of any works derived from it, be made freely 125 | available in that license fees are prohibited but Distributor 126 | Fees are allowed. 127 | 128 | 129 | Distribution of Compiled Forms of the Standard Version 130 | or Modified Versions without the Source 131 | 132 | (5) You may Distribute Compiled forms of the Standard Version without 133 | the Source, provided that you include complete instructions on how to 134 | get the Source of the Standard Version. Such instructions must be 135 | valid at the time of your distribution. If these instructions, at any 136 | time while you are carrying out such distribution, become invalid, you 137 | must provide new instructions on demand or cease further distribution. 138 | If you provide valid instructions or cease distribution within thirty 139 | days after you become aware that the instructions are invalid, then 140 | you do not forfeit any of your rights under this license. 141 | 142 | (6) You may Distribute a Modified Version in Compiled form without 143 | the Source, provided that you comply with Section 4 with respect to 144 | the Source of the Modified Version. 145 | 146 | 147 | Aggregating or Linking the Package 148 | 149 | (7) You may aggregate the Package (either the Standard Version or 150 | Modified Version) with other packages and Distribute the resulting 151 | aggregation provided that you do not charge a licensing fee for the 152 | Package. Distributor Fees are permitted, and licensing fees for other 153 | components in the aggregation are permitted. The terms of this license 154 | apply to the use and Distribution of the Standard or Modified Versions 155 | as included in the aggregation. 156 | 157 | (8) You are permitted to link Modified and Standard Versions with 158 | other works, to embed the Package in a larger work of your own, or to 159 | build stand-alone binary or bytecode versions of applications that 160 | include the Package, and Distribute the result without restriction, 161 | provided the result does not expose a direct interface to the Package. 162 | 163 | 164 | Items That are Not Considered Part of a Modified Version 165 | 166 | (9) Works (including, but not limited to, modules and scripts) that 167 | merely extend or make use of the Package, do not, by themselves, cause 168 | the Package to be a Modified Version. In addition, such works are not 169 | considered parts of the Package itself, and are not subject to the 170 | terms of this license. 171 | 172 | 173 | General Provisions 174 | 175 | (10) Any use, modification, and distribution of the Standard or 176 | Modified Versions is governed by this Artistic License. By using, 177 | modifying or distributing the Package, you accept this license. Do not 178 | use, modify, or distribute the Package, if you do not accept this 179 | license. 180 | 181 | (11) If your Modified Version has been derived from a Modified 182 | Version made by someone other than you, you are nevertheless required 183 | to ensure that your Modified Version complies with the requirements of 184 | this license. 185 | 186 | (12) This license does not grant you the right to use any trademark, 187 | service mark, tradename, or logo of the Copyright Holder. 188 | 189 | (13) This license includes the non-exclusive, worldwide, 190 | free-of-charge patent license to make, have made, use, offer to sell, 191 | sell, import and otherwise transfer the Package with respect to any 192 | patent claims licensable by the Copyright Holder that are necessarily 193 | infringed by the Package. If you institute patent litigation 194 | (including a cross-claim or counterclaim) against any party alleging 195 | that the Package constitutes direct or contributory patent 196 | infringement, then this Artistic License to you shall terminate on the 197 | date that such litigation is filed. 198 | 199 | (14) Disclaimer of Warranty: 200 | THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS 201 | IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED 202 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR 203 | NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL 204 | LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL 205 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 206 | DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF 207 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 208 | 209 | 210 | -------- 211 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # strong-deploy 2 | 3 | Deploy a node application package to the StrongLoop process manager, strong-pm. 4 | 5 | Both git branches and npm packages can be deployed. They will typically be 6 | prepared using [strong-build](http://github.com/strongloop/strong-build). 7 | 8 | For more details, see http://strong-pm.io. 9 | 10 | 11 | ## Installation 12 | 13 | `sl-deploy` is made available through the 14 | [strongloop](https://github.com/strongloop/strongloop) tool as `slc deploy`, and 15 | works well with the StrongLoop Process Manager, 16 | [strong-pm](https://github.com/strongloop/strong-pm). 17 | 18 | `sl-deploy` can be installed standalone with: 19 | 20 | npm install -g strong-deploy 21 | 22 | 23 | ## License 24 | 25 | strong-deploy uses a dual license model. 26 | 27 | You may use this library under the terms of the [Artistic 2.0 license][], 28 | or under the terms of the [StrongLoop Subscription Agreement][]. 29 | 30 | [Artistic 2.0 license]: http://opensource.org/licenses/Artistic-2.0 31 | [StrongLoop Subscription Agreement]: http://strongloop.com/license 32 | -------------------------------------------------------------------------------- /bin/sl-deploy.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 3 | // Node module: strong-deploy 4 | // This file is licensed under the Artistic License 2.0. 5 | // License text available at https://opensource.org/licenses/Artistic-2.0 6 | 7 | 'use strict'; 8 | 9 | var Parser = require('posix-getopt').BasicParser; 10 | var debug = require('debug')('strong-deploy'); 11 | var defaults = require('strong-url-defaults'); 12 | var getPackageInfo = require('../lib/package').getPackageInfo; 13 | var defaultPackagePath = require('../lib/package').getPackagePath; 14 | var deploy = require('../'); 15 | var fs = require('fs'); 16 | var maybeTunnel = require('strong-tunnel'); 17 | var path = require('path'); 18 | 19 | function printHelp($0, prn) { 20 | var USAGE = fs.readFileSync(require.resolve('./sl-deploy.txt'), 'utf-8') 21 | .replace(/%MAIN%/g, $0) 22 | .trim(); 23 | 24 | prn(USAGE); 25 | } 26 | 27 | var argv = process.argv; 28 | var $0 = process.env.CMD ? process.env.CMD : path.basename(argv[1]); 29 | var parser = new Parser([ 30 | ':v(version)', 31 | 'h(help)', 32 | 's:(service)', 33 | 'z:(size)', 34 | 'L(local)' // Undocumented for now, just for local testing 35 | ].join(''), argv); 36 | var option; 37 | var branchOrPack; 38 | var local; 39 | var serviceName; 40 | var clusterSize; 41 | 42 | while ((option = parser.getopt()) !== undefined) { 43 | switch (option.option) { 44 | case 'v': 45 | console.log(require('../package.json').version); 46 | process.exit(0); 47 | break; 48 | case 'h': 49 | printHelp($0, console.log); 50 | process.exit(0); 51 | break; 52 | case 's': 53 | serviceName = option.optarg; 54 | break; 55 | case 'z': 56 | clusterSize = option.optarg; 57 | break; 58 | case 'L': 59 | local = true; 60 | break; 61 | default: 62 | console.error('Invalid usage (near option \'%s\'), try `%s --help`.', 63 | option.optopt, 64 | $0); 65 | process.exit(1); 66 | } 67 | } 68 | 69 | var numArgs = argv.length - parser.optind(); 70 | if (numArgs > 2) { 71 | console.error('Invalid usage, try `%s --help`.', $0); 72 | process.exit(1); 73 | } 74 | 75 | var workingDir = process.cwd(); 76 | 77 | // XXX(sam) our CWD isn't necessarily the package we are deploying, we should 78 | // get the package name from the git branch, or the tarball being deployed, 79 | // or the path being deployed. 80 | var packageInfo = getPackageInfo(workingDir); 81 | serviceName = serviceName || (packageInfo ? packageInfo.name : null); 82 | 83 | if (!serviceName) { 84 | console.error( 85 | 'Unable to detect service name, package.json has no "name" property.\n' + 86 | 'Please update your package.json or specify a service name.\n' + 87 | 'See `%s --help` for more details.', $0 88 | ); 89 | process.exit(1); 90 | } 91 | 92 | var baseURL = argv[parser.optind()]; 93 | branchOrPack = argv[parser.optind() + 1]; 94 | 95 | baseURL = baseURL || 'http://'; 96 | branchOrPack = branchOrPack || defaultPackagePath(workingDir) || 'deploy'; 97 | 98 | // Truncate any paths from the baseURL, because the final path of the URL is 99 | // dependent on the ID of the service being updated by this deployment. 100 | // Older versions of deploy use to allow paths on the git push. 101 | baseURL = defaults(baseURL, {host: '127.0.0.1', port: 8701}, {path: '/'}); 102 | 103 | debug('deploy %j to %j', branchOrPack, baseURL); 104 | 105 | var sshOpts = {}; 106 | 107 | if (process.env.SSH_USER) { 108 | sshOpts.username = process.env.SSH_USER; 109 | } 110 | 111 | if (process.env.SSH_KEY) { 112 | sshOpts.privateKey = fs.readFileSync(process.env.SSH_KEY); 113 | } 114 | 115 | if (process.env.SSH_PORT) { 116 | sshOpts.port = process.env.SSH_PORT; 117 | } 118 | 119 | exit.url = baseURL; 120 | if (!local) { 121 | maybeTunnel(baseURL, sshOpts, function(err, url) { 122 | exit.url = url; 123 | if (err) { 124 | console.error('Error setting up tunnel:', err); 125 | return exit(err); 126 | } 127 | debug('Connecting to %s via %s', baseURL, url); 128 | var options = { 129 | workingDir: workingDir, 130 | baseURL: url, 131 | serviceName: serviceName, 132 | branchOrPack: branchOrPack, 133 | clusterSize: clusterSize, 134 | }; 135 | deploy(options, exit); 136 | }); 137 | } else { 138 | var options = { 139 | baseURL: baseURL, 140 | serviceName: serviceName, 141 | branchOrPack: branchOrPack, 142 | clusterSize: clusterSize, 143 | }; 144 | deploy.local(options, exit); 145 | } 146 | 147 | function exit(err, service) { 148 | var svc = service ? (service.name || service.id) : serviceName; 149 | if (err) { 150 | console.error('Failed to deploy `%s` as `%s` to `%s` via `%s`', 151 | branchOrPack, svc, baseURL, exit.url); 152 | process.exit(1); 153 | } else { 154 | console.log('Deployed `%s` as `%s` to `%s`', branchOrPack, svc, baseURL); 155 | process.exit(0); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /bin/sl-deploy.txt: -------------------------------------------------------------------------------- 1 | usage: %MAIN% [options] [URL [PACK|BRANCH]] 2 | 3 | Deploy a node application to a StrongLoop process manager 4 | 5 | Options: 6 | -h,--help Print this message and exit. 7 | -v,--version Print version and exit. 8 | -s,--service SVC Deploy to service SVC. 9 | -z,--size SIZE Set size of cluster to SIZE before deploy. 10 | 11 | Arguments: 12 | SIZE: A number, n, or the string 'cpus', meaning run a worker per CPU. 13 | 14 | SVC: The name or ID of the service on the process manager. 15 | 16 | Defaults to the name of the application specified in the package.json file. 17 | 18 | If a service with that name is not available on the process manager, 19 | a new service will be created and the application will be deployed. 20 | 21 | URL: The URL of the StrongLoop process manager. 22 | 23 | Defaults to `http://localhost:8701`. If a URL is provided, both the host 24 | and port is optional, they default to `localhost` and 8701, respectively. 25 | If the server requires authentication, the credentials must be part of the 26 | URL, see examples. 27 | 28 | URL may also use the `http+ssh://` protocol which will connect using ssh 29 | and then tunnel the http requests over that connection. The ssh username 30 | will default to your current user and authentication defaults to using your 31 | current ssh-agent. The username can be overridden by setting an `SSH_USER` 32 | environment variable. The authentication can be overridden to use an 33 | existing private key instead of an agent by setting the `SSH_KEY` 34 | environment variable to the path of the private key to be used. The port 35 | used for ssh can be overridden by setting an `SSH_PORT` environment 36 | variable. 37 | 38 | PACK: Deploy an NPM package/tarball. 39 | 40 | BRANCH: Deploy a git branch. 41 | 42 | Default behaviour is to deploy to `http://localhost:8701`. An npm package from 43 | the parent directory is deployed, if one is found for the current application 44 | version, otherwise the git branch `deploy` is deployed. 45 | 46 | Note that if PACK or BRANCH is specified, URL *must* be specified as well. 47 | 48 | Examples: 49 | 50 | Deploy the default npm package or git branch to localhost: 51 | 52 | %MAIN% 53 | 54 | Deploy the default npm package or git branch to a remote host: 55 | 56 | %MAIN% http://prod1.example.com 57 | 58 | Deploy to a remote host, on a non-standard port, using authentication: 59 | 60 | %MAIN% http://user:pass@prod1.example.com:8765 61 | 62 | Deploy 'production' branch to localhost: 63 | 64 | %MAIN% http://localhost production 65 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2015. All Rights Reserved. 2 | // Node module: strong-deploy 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var path = require('path'); 9 | var shell = require('shelljs'); 10 | 11 | var gitDeployment = require('./lib/git').performGitDeployment; 12 | var httpPutDeployment = require('./lib/put-file').performHttpPutDeployment; 13 | var localDeployment = require('./lib/post-json').performLocalDeployment; 14 | 15 | module.exports = deploy; 16 | module.exports.local = localDeployment; 17 | 18 | /** 19 | * Deploy a NPM pack or GIT branch to a strong-pm service 20 | * 21 | * @param {string} options.workingDir The working directory 22 | * @param {string} options.baseURL The URL of the service. 23 | * @param {integer} options.serviceName The name of the service. 24 | * @param {integer} options.clusterSize The size of the cluster. 25 | * @param {string} branchOrPack Name of the GIT branch or path to the NPM pack. 26 | * @param {function} cb Callback 27 | */ 28 | function deploy(options, cb) { 29 | if (shell.test('-f', path.resolve(options.branchOrPack))) { 30 | return httpPutDeployment(options, cb); 31 | } else { 32 | return gitDeployment(options, cb); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/deploy-endpoint.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2015. All Rights Reserved. 2 | // Node module: strong-deploy 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var MeshClient = require('strong-mesh-models').Client; 9 | var debug = require('debug')('strong-deploy'); 10 | 11 | exports.get = getDeployEndpoint; 12 | 13 | function getDeployEndpoint(url, serviceName, clusterSize, callback) { 14 | var client = new MeshClient(url); 15 | client.checkRemoteApiSemver(function(err) { 16 | if (err) { 17 | console.error('Failed to connect to %s: %s', 18 | url, err.message || err); 19 | return callback(err); 20 | } 21 | serviceFindOrCreate(client, serviceName, clusterSize, callback); 22 | }); 23 | } 24 | 25 | function serviceFindOrCreate(client, serviceName, clusterSize, callback) { 26 | client.serviceFindOrCreate(serviceName, 1, function(err, service) { 27 | if (err) { 28 | debug('client.serviceFindOrCreate(%s, 1): %s', serviceName, err); 29 | if (err.statusCode === 401) { 30 | console.error( 31 | 'Cannot access remote. If authentication is required,' + 32 | ' credentials should be given in the URL.'); 33 | } else { 34 | console.error('Error connecting to server:', err); 35 | } 36 | return callback(err); 37 | } 38 | var endpoint = service.getDeployEndpoint(); 39 | debug('client.serviceFindOrCreate(%s, 1): endpoint %s svc %j', 40 | serviceName, endpoint, service); 41 | if (clusterSize != null) service.setClusterSize(clusterSize, true, 42 | function(err) { 43 | debug('service.setClusterSize(%s): serviceName %s endpoint %s svc %j', 44 | clusterSize, serviceName, endpoint, service); 45 | if (err) callback(err); 46 | return callback(null, endpoint, service); 47 | }); 48 | else callback(null, endpoint, service); 49 | }); 50 | } 51 | 52 | -------------------------------------------------------------------------------- /lib/git.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-deploy 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var async = require('async'); 9 | var childProcess = require('child_process'); 10 | var debug = require('debug')('strong-deploy:git'); 11 | var getDeployEndpoint = require('./deploy-endpoint').get; 12 | var shell = require('shelljs'); 13 | var util = require('util'); 14 | var urlParse = require('url').parse; 15 | var urlFormat = require('url').format; 16 | 17 | function getCurrentBranch(workingDir) { 18 | if (!shell.pushd(workingDir)) { 19 | return Error(util.format('Directory %s does not exist', workingDir)); 20 | } 21 | var output = shell.exec('git symbolic-ref --short HEAD', {silent: true}); 22 | shell.popd(); 23 | if (output.code !== 0) { 24 | return Error('This directory does not contain a valid git repository'); 25 | } 26 | return output.output.trim(); 27 | } 28 | 29 | function isValidBranch(workingDir, branchName, callback) { 30 | var cmd = util.format('git rev-parse --abbrev-ref %s', branchName); 31 | debug(cmd); 32 | var options = {cwd: workingDir}; 33 | childProcess.exec(cmd, options, function(err, stdout, stderr) { 34 | debug('stdout: ' + stdout); 35 | debug('stderr: ' + stderr); 36 | if (err) { 37 | debug(err); 38 | console.error('Branch `%s` is not available in this repository', 39 | branchName); 40 | err = Error('invalid branch'); 41 | } 42 | callback(err); 43 | }); 44 | } 45 | 46 | function isValidGitURL(workingDir, url, callback) { 47 | var cmd = util.format('git ls-remote %s', url); 48 | debug(cmd); 49 | var options = {cwd: workingDir}; 50 | childProcess.exec(cmd, options, function(err, stdout, stderr) { 51 | debug('stdout: ' + stdout); 52 | debug('stderr: ' + stderr); 53 | if (err) { 54 | debug(err); 55 | if (urlFormat(urlParse(url)) !== url) { 56 | console.error('URL `%s` is not valid', url); 57 | } 58 | err = Error('invalid url'); 59 | } 60 | callback(err); 61 | }); 62 | } 63 | 64 | function doGitPush(workingDir, gitURL, branch, callback) { 65 | var cmd = 'git'; 66 | var args = ['push', '-f', gitURL, branch + ':' + branch]; 67 | 68 | debug(cmd, args); 69 | var options = {cwd: workingDir, stdio: [0, 1, 2]}; 70 | var child = childProcess.spawn(cmd, args, options); 71 | 72 | // Based on node docs, `exit` event may or may not fire afetr `error`. 73 | child.once('error', function(err) { 74 | if (!callback) return; 75 | 76 | callback(err); 77 | callback = null; 78 | }); 79 | 80 | child.once('exit', function(code) { 81 | if (!callback) return; 82 | 83 | if (code !== 0) return callback(Error('git push failed')); 84 | callback(); 85 | callback = null; 86 | }); 87 | } 88 | 89 | function performGitDeployment(options, callback) { 90 | getDeployEndpoint(options.baseURL, options.serviceName, options.clusterSize, 91 | function(err, deployUrl, service) { 92 | if (err) return callback(err); 93 | _performGitDeployment(service, options.workingDir, deployUrl, 94 | options.branchOrPack, callback); 95 | }); 96 | } 97 | 98 | function _performGitDeployment(service, workingDir, baseUrl, branch, callback) { 99 | var deployURL = baseUrl + '/default'; 100 | async.series([ 101 | isValidBranch.bind(null, workingDir, branch), 102 | isValidGitURL.bind(null, workingDir, deployURL), 103 | doGitPush.bind(null, workingDir, deployURL, branch) 104 | ], function(err) { 105 | if (err) return callback(err); 106 | return callback(null, service); 107 | }); 108 | } 109 | 110 | exports.performGitDeployment = performGitDeployment; 111 | // for unit tests 112 | exports._getCurrentBranch = getCurrentBranch; 113 | -------------------------------------------------------------------------------- /lib/package.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2015. All Rights Reserved. 2 | // Node module: strong-deploy 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var debug = require('debug')('strong-deploy:package'); 9 | var fs = require('fs'); 10 | var path = require('path'); 11 | 12 | function getPackageInfo(workingDir) { 13 | var packageJsonPath = path.join(workingDir, 'package.json'); 14 | try { 15 | var json = require(packageJsonPath); 16 | debug('package.json found: %s', packageJsonPath); 17 | return {name: json.name, version: json.version}; 18 | } catch (e) { 19 | debug('package.json require failed: %s', packageJsonPath); 20 | return null; 21 | } 22 | } 23 | exports.getPackageInfo = getPackageInfo; 24 | 25 | function getPackagePath(workingDir) { 26 | var info = getPackageInfo(workingDir); 27 | if (!info) return null; 28 | 29 | var tgzPath = path.join('..', info.name + '-' + info.version + '.tgz'); 30 | if (!fs.existsSync(tgzPath)) { 31 | debug('Package tgz not found: %s', tgzPath); 32 | return null; 33 | } 34 | return tgzPath; 35 | } 36 | exports.getPackagePath = getPackagePath; 37 | -------------------------------------------------------------------------------- /lib/post-json.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-deploy 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var concat = require('concat-stream'); 9 | var debug = require('debug')('strong-deploy'); 10 | var getDeployEndpoint = require('./deploy-endpoint').get; 11 | var http = require('http'); 12 | var path = require('path'); 13 | var shell = require('shelljs'); 14 | var url = require('url'); 15 | 16 | exports.performLocalDeployment = performLocalDeployment; 17 | 18 | function performLocalDeployment(options, callback) { 19 | getDeployEndpoint(options.baseURL, options.serviceName, options.clusterSize, 20 | function(err, deployUrl, service) { 21 | if (err) return callback(err); 22 | _performLocalDeployment(service, deployUrl, 23 | options.branchOrPack, callback); 24 | }); 25 | } 26 | 27 | function _performLocalDeployment(service, baseURL, what, callback) { 28 | what = path.resolve(what); 29 | 30 | var postURL = url.parse(baseURL); 31 | if (postURL.protocol !== 'http:') { 32 | console.error('Invalid URL `%s`. Only http:// URLs are supported.', 33 | baseURL 34 | ); 35 | return callback(Error('Invalid URL')); 36 | } 37 | 38 | if (postURL.hostname) { 39 | if (postURL.hostname !== 'localhost' && postURL.hostname !== '127.0.0.1') { 40 | console.error('Invalid URL `%s`. Only localhost URLs are supported.', 41 | baseURL); 42 | return callback(Error('Invalid URL')); 43 | } 44 | } 45 | 46 | if (!shell.test('-d', what)) { 47 | console.error('Cannot deploy `%s`: not an npm package directory', what); 48 | return callback(Error('Invalid path, not a directory')); 49 | } 50 | 51 | var postOptions = { 52 | auth: postURL.auth, 53 | hostname: 'localhost', 54 | port: postURL.port || 80, 55 | path: postURL.path + '/default', 56 | method: 'POST', 57 | headers: { 58 | 'Content-Type': 'application/x-pm-deploy', 59 | }, 60 | }; 61 | var postBody = { 62 | 'local-directory': what, 63 | }; 64 | 65 | debug('post %j options %j', postBody, postOptions); 66 | 67 | var req = http.request(postOptions, function(res) { 68 | res.pipe(concat(res, function(msg) { 69 | debug('response: code=%d %j', res.statusCode, String(msg)); 70 | if (res.statusCode === 200) { 71 | return callback(null, service); 72 | } 73 | console.error('%s', msg); 74 | callback(Error('HTTP error ' + res.statusCode)); 75 | })); 76 | }); 77 | 78 | req.end(JSON.stringify(postBody)); 79 | 80 | req.on('error', function(err) { 81 | console.error('Deploy `%s` to `%s` failed: %s', 82 | what, 83 | baseURL, 84 | err.message 85 | ); 86 | callback(err); 87 | }); 88 | } 89 | -------------------------------------------------------------------------------- /lib/put-file.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-deploy 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var concat = require('concat-stream'); 9 | var debug = require('debug')('strong-deploy'); 10 | var fs = require('fs'); 11 | var getDeployEndpoint = require('./deploy-endpoint').get; 12 | var http = require('http'); 13 | var path = require('path'); 14 | var url = require('url'); 15 | 16 | function performHttpPutDeployment(options, callback) { 17 | getDeployEndpoint(options.baseURL, options.serviceName, options.clusterSize, 18 | function(err, deployUrl, service) { 19 | if (err) return callback(err); 20 | _performHttpPutDeployment(service, deployUrl, 21 | options.branchOrPack, callback); 22 | }); 23 | } 24 | 25 | function _performHttpPutDeployment(service, baseURL, npmPkg, callback) { 26 | npmPkg = path.resolve(npmPkg); 27 | 28 | var fileReader = fs.createReadStream(npmPkg); 29 | fileReader.on('error', function(err) { 30 | console.error('Error while reading file: %s', err); 31 | callback(err); 32 | }); 33 | 34 | var postURL = url.parse(baseURL); 35 | if (postURL.protocol !== 'http:') { 36 | console.error('Invalid URL `%s`. Only http:// URLs are supported.', 37 | baseURL 38 | ); 39 | return callback(Error('Invalid URL')); 40 | } 41 | 42 | var postOptions = { 43 | auth: postURL.auth, 44 | hostname: postURL.hostname, 45 | port: postURL.port || 80, 46 | path: postURL.path + '/default', 47 | method: 'PUT' 48 | }; 49 | 50 | var req = http.request(postOptions, function(res) { 51 | res.pipe(concat(res, function(msg) { 52 | debug('response: code=%d %j', res.statusCode, String(msg)); 53 | if (res.statusCode === 200) { 54 | return callback(null, service); 55 | } 56 | 57 | if (res.statusCode === 401 || res.statusCode === 403) { 58 | console.error('Valid credentials must be given in the URL'); 59 | } else { 60 | console.error('%s', msg); 61 | } 62 | callback(Error('HTTP error' + res.statusCode)); 63 | })); 64 | }); 65 | 66 | req.on('error', function(err) { 67 | console.error('Deploy `%s` to `%s` failed: %s', 68 | npmPkg, 69 | baseURL, 70 | err.message 71 | ); 72 | callback(err); 73 | }); 74 | 75 | fileReader.pipe(req); 76 | } 77 | 78 | exports.performHttpPutDeployment = performHttpPutDeployment; 79 | -------------------------------------------------------------------------------- /lib/service.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2015. All Rights Reserved. 2 | // Node module: strong-deploy 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "strong-deploy", 3 | "description": "Deploy a node application to a StrongLoop process manager", 4 | "version": "3.1.4", 5 | "keywords": [ 6 | "StrongLoop", 7 | "build", 8 | "bundle", 9 | "dependencies", 10 | "deploy", 11 | "git", 12 | "heroku", 13 | "npm", 14 | "openshift", 15 | "pm", 16 | "slc" 17 | ], 18 | "repository": { 19 | "type": "git", 20 | "url": "git://github.com/strongloop/strong-deploy.git" 21 | }, 22 | "author": "Krishna Raman ", 23 | "license": "Artistic-2.0", 24 | "readmeFilename": "README.md", 25 | "main": "index.js", 26 | "scripts": { 27 | "pretest": "eslint --ignore-path .gitignore .", 28 | "test": "tap --timeout=300 ./test/test-*.js" 29 | }, 30 | "bin": { 31 | "sl-deploy": "./bin/sl-deploy.js" 32 | }, 33 | "dependencies": { 34 | "async": "^0.9.0", 35 | "concat-stream": "^1.4.7", 36 | "debug": "^2.0.0", 37 | "posix-getopt": "^1.0.0", 38 | "shelljs": "^0.3.0", 39 | "strong-mesh-models": "^7.0.0", 40 | "strong-tunnel": "^1.1.1", 41 | "strong-url-defaults": "^1.0.0" 42 | }, 43 | "devDependencies": { 44 | "eslint": "^2.7.0", 45 | "eslint-config-strongloop": "^2.0.1", 46 | "http-auth": "^2.2.5", 47 | "strong-fork-cicada": "^1.1.2", 48 | "tap": "^0.7.1" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /test/helpers.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2015. All Rights Reserved. 2 | // Node module: strong-deploy 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var assert = require('assert'); 9 | var auth = require('http-auth'); 10 | var cicada = require('strong-fork-cicada'); 11 | var debug = require('debug')('strong-deploy:test'); 12 | var http = require('http'); 13 | var path = require('path'); 14 | var shell = require('shelljs'); 15 | var url = require('url'); 16 | 17 | var artifactDir = path.join(__dirname, '.test_artifacts'); 18 | 19 | shell.rm('-rf', artifactDir); 20 | 21 | var ci = cicada(artifactDir); 22 | var allowAll = auth.basic({ realm: 'git' }, alwaysSay(true)); 23 | var denyAll = auth.basic({ realm: 'git' }, alwaysSay(false)); 24 | 25 | debug('cicada artifacts %j', artifactDir); 26 | debug('cwd %j', process.cwd()); 27 | 28 | module.exports = exports = { 29 | gitServerAllow: httpServer.bind(null, [allowAll, findServiceAndHandleCi]), 30 | gitServerDeny: httpServer.bind(null, [denyAll, findServiceAndHandleCi]), 31 | gitServer: httpServer.bind(null, [findServiceAndHandleCi]), 32 | httpServerAllow: httpServer.bind(null, [allowAll]), 33 | httpServerDeny: httpServer.bind(null, [denyAll]), 34 | httpServer: httpServer.bind(null, []), 35 | assertMatch: assertMatch, 36 | ok: false, 37 | findServiceAndRunHandler: findServiceAndRunHandler, 38 | }; 39 | 40 | // Check for node silently exiting with code 0 when tests have not passed. 41 | process.on('exit', function(code) { 42 | if (code === 0) { 43 | assert(exports.ok); 44 | } 45 | }); 46 | 47 | function findServiceAndRunHandler(handler, req, res) { 48 | var reqUrl = url.parse(req.url); 49 | var path = reqUrl.pathname.toLowerCase(); 50 | debug('findServiceAndRunHandler: %j path %j', req.url, path); 51 | switch (reqUrl.pathname.toLowerCase()) { 52 | case '/api/api': 53 | getApiInfo(req, res); 54 | break; 55 | case '/api/services/findone': 56 | findService(req, res); 57 | break; 58 | case '/api/services': 59 | createService(req, res); 60 | break; 61 | default: 62 | req.url = req.url.replace('/api/services/1/deploy', ''); 63 | handler(req, res); 64 | } 65 | } 66 | 67 | function getApiInfo(req, res) { 68 | res.writeHead(200, { 69 | 'Content-Type': 'application/json', 70 | charset: 'utf-8', 71 | }); 72 | res.end(JSON.stringify({ 73 | apiVersion: require('strong-mesh-models/package.json').apiVersion, 74 | })); 75 | } 76 | 77 | function findServiceAndHandleCi(req, res) { 78 | findServiceAndRunHandler(ci.handle, req, res); 79 | } 80 | 81 | function findService(req, res) { 82 | res.writeHead(404, { 83 | 'Content-Type': 'application/json', 84 | charset: 'utf-8', 85 | }); 86 | res.end(JSON.stringify({ 87 | error: { 88 | name: 'Error', 89 | status: 404, 90 | message: 'Unknown \"ServerService\" id \"undefined\".', 91 | statusCode: 404, 92 | code: 'MODEL_NOT_FOUND' 93 | } 94 | })); 95 | } 96 | 97 | function createService(req, res) { 98 | res.end(JSON.stringify({ 99 | id: 1, 100 | _groups: [{id: 1, name: 'g1', scale: 1}] 101 | })); 102 | res.writeHead(201, { 103 | 'Content-Type': 'application/json', 104 | charset: 'utf-8', 105 | }); 106 | } 107 | 108 | function httpServer(handlers, customHandler, callback) { 109 | if (customHandler) { 110 | if (callback) { 111 | handlers.push(findServiceAndRunHandler.bind(null, customHandler)); 112 | } else { 113 | callback = customHandler; 114 | } 115 | } 116 | var server = http.createServer.apply(http, handlers); 117 | 118 | return server.listen(0, returnServer); 119 | 120 | function returnServer() { 121 | debug('Server started at: %j', server.address()); 122 | callback(server, ci); 123 | } 124 | } 125 | 126 | function alwaysSay(result) { 127 | return function(user, pass, callback) { 128 | debug('HTTP Auth: %s:%s => %s', user, pass, result ? 'ALLOW' : 'DENY'); 129 | callback(result); 130 | }; 131 | } 132 | 133 | function assertMatch(actual, expected, message) { 134 | actual = actual.toString('utf8'); 135 | expected = new RegExp(expected); 136 | if (!expected.test(actual)) { 137 | assert.fail(actual, expected.toString(), message, 'match'); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /test/test-deploy-default-branch.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2015. All Rights Reserved. 2 | // Node module: strong-deploy 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var assert = require('assert'); 9 | var childProcess = require('child_process'); 10 | var concat = require('concat-stream'); 11 | var helpers = require('./helpers'); 12 | var shell = require('shelljs'); 13 | 14 | shell.exec('git branch deploy'); 15 | helpers.gitServer(test); 16 | 17 | function test(server, ci) { 18 | ci.once('commit', assertCommit); 19 | 20 | var child = childProcess.fork( 21 | require.resolve('../bin/sl-deploy'), 22 | [ 23 | '--service', 's1', 24 | 'http://any:thin@127.0.0.1:' + server.address().port 25 | ], 26 | {silent: true} 27 | ); 28 | 29 | child.stderr.pipe(process.stderr); 30 | child.stdout.pipe(concat(function(msg) { 31 | console.log('Check deploy message: <%s>', String(msg).trim()); 32 | assert(/Deployed.*as `1` to `http.*/.test(msg), msg); 33 | })); 34 | 35 | function assertCommit(commit) { 36 | var branch = 'deploy'; 37 | assert(commit.branch === branch); 38 | helpers.ok = true; 39 | server.close(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/test-deploy-from-workingdir.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2015. All Rights Reserved. 2 | // Node module: strong-deploy 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var assert = require('assert'); 9 | var debug = require('debug')('test'); 10 | var helpers = require('./helpers'); 11 | var os = require('os'); 12 | var shell = require('shelljs'); 13 | 14 | var performGitDeployment = require('../lib/git').performGitDeployment; 15 | 16 | shell.exec('git branch deploy'); 17 | 18 | helpers.gitServer(test); 19 | 20 | function test(server, ci) { 21 | ci.once('commit', assertCommit); 22 | var workingDir = __dirname; 23 | var baseUrl = 'http://127.0.0.1:' + server.address().port; 24 | process.chdir(os.tmpdir()); 25 | 26 | debug('workingDir: %s', workingDir); 27 | performGitDeployment( 28 | { 29 | workingDir: workingDir, 30 | baseURL: baseUrl, 31 | serviceName: 'svc', 32 | branchOrPack: 'deploy', 33 | }, 34 | function(err) { 35 | assert.ifError(err); 36 | }); 37 | 38 | function assertCommit(commit) { 39 | assert(commit.repo === 'default'); 40 | var branch = 'deploy'; 41 | assert(!(branch instanceof Error)); 42 | assert(commit.branch === branch); 43 | helpers.ok = true; 44 | server.close(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/test-deploy-specific-branch.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2015. All Rights Reserved. 2 | // Node module: strong-deploy 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var assert = require('assert'); 9 | var childProcess = require('child_process'); 10 | var shell = require('shelljs'); 11 | var helpers = require('./helpers'); 12 | 13 | var getCurrentBranch = require('../lib/git.js')._getCurrentBranch; 14 | var currentBranch = getCurrentBranch(process.cwd()); 15 | 16 | // Ensure that master and production exist as local branches 17 | shell.exec('git branch master origin/master'); 18 | shell.exec('git branch production origin/production'); 19 | 20 | var pushBranch = 'production'; 21 | // Jenkins runs tests in detached state which would cause currentBranch() 22 | // to return an Error object. Just use the master branch in that case. 23 | if (currentBranch instanceof Error || currentBranch === 'production') { 24 | pushBranch = 'master'; 25 | } 26 | 27 | helpers.gitServer(function(server, ci) { 28 | ci.once('commit', assertCommit); 29 | childProcess.fork( 30 | require.resolve('../bin/sl-deploy'), 31 | [ 32 | '--service', 's1', 33 | 'http://127.0.0.1:' + server.address().port, pushBranch 34 | ] 35 | ); 36 | 37 | function assertCommit(commit) { 38 | assert(commit.branch === pushBranch); 39 | helpers.ok = true; 40 | server.close(); 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /test/test-git-deploy-auth-failure.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2015. All Rights Reserved. 2 | // Node module: strong-deploy 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var assert = require('assert'); 9 | var childProcess = require('child_process'); 10 | var debug = require('debug')('test'); 11 | var helpers = require('./helpers'); 12 | 13 | helpers.gitServerDeny(test); 14 | 15 | function test(server, ci) { 16 | ci.once('commit', assertNoCommit); 17 | var deploy = childProcess.fork( 18 | require.resolve('../bin/sl-deploy'), 19 | [ 20 | '--service', 's1', 21 | 'http://any:thing@127.0.0.1:' + server.address().port 22 | ], 23 | { env: { GIT_ASKPASS: 'echo' }, silent: true } 24 | ); 25 | deploy.on('exit', function(code, signal) { 26 | helpers.ok = true; 27 | server.close(); 28 | var stderr = deploy.stderr.read(); 29 | var stdout = deploy.stdout.read(); 30 | debug('sl-deploy stdout: %s', stdout); 31 | debug('sl-deploy stderr: %s', stderr); 32 | assert.notEqual(code, 0, 'sl-deploy should exit with failure'); 33 | helpers.assertMatch(stderr, /If authentication is required/i); 34 | helpers.assertMatch(stderr, /credentials should be given in the URL/i); 35 | }); 36 | } 37 | 38 | function assertNoCommit(commit) { 39 | assert.fail('commit', 'none', 'cicada should never see this'); 40 | } 41 | -------------------------------------------------------------------------------- /test/test-git-deploy-auth.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2015. All Rights Reserved. 2 | // Node module: strong-deploy 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var assert = require('assert'); 9 | var childProcess = require('child_process'); 10 | var helpers = require('./helpers'); 11 | var shell = require('shelljs'); 12 | 13 | shell.exec('git branch deploy'); 14 | helpers.gitServerAllow(test); 15 | 16 | function test(server, ci) { 17 | ci.once('commit', assertCommit); 18 | 19 | childProcess.fork( 20 | require.resolve('../bin/sl-deploy'), 21 | [ 22 | '--service', 's1', 23 | 'http://any:thin@127.0.0.1:' + server.address().port 24 | ] 25 | ); 26 | 27 | function assertCommit(commit) { 28 | assert(commit.repo === 'default'); 29 | var branch = 'deploy'; 30 | assert(commit.branch === branch); 31 | helpers.ok = true; 32 | server.close(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/test-git-http-ssh.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2015. All Rights Reserved. 2 | // Node module: strong-deploy 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var assert = require('assert'); 9 | var childProcess = require('child_process'); 10 | var helpers = require('./helpers'); 11 | var shell = require('shelljs'); 12 | 13 | if (/win32/.test(process.platform)) { 14 | helpers.ok = true; 15 | console.log('TAP version 13\n1..0 # skip ssh tests on Windows'); 16 | return; 17 | } 18 | 19 | shell.exec('git branch deploy'); 20 | helpers.gitServer(test); 21 | 22 | function test(server, ci) { 23 | ci.once('commit', assertCommit); 24 | 25 | childProcess.fork( 26 | require.resolve('../bin/sl-deploy'), 27 | [ 28 | '--service', 's1', 29 | 'http+ssh://127.0.0.1:' + server.address().port 30 | ] 31 | ); 32 | 33 | function assertCommit(commit) { 34 | assert(commit.repo === 'default'); 35 | var branch = 'deploy'; 36 | assert(commit.branch === branch); 37 | helpers.ok = true; 38 | server.close(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /test/test-put-deploy-auth-failure.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2015. All Rights Reserved. 2 | // Node module: strong-deploy 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var assert = require('assert'); 9 | var childProcess = require('child_process'); 10 | var debug = require('debug')('test'); 11 | var helpers = require('./helpers'); 12 | 13 | helpers.httpServerDeny(assertNoPut, doPut); 14 | 15 | function assertNoPut(req, res) { 16 | assert.fail('request', 'none', 'Handler should never see request'); 17 | } 18 | 19 | function doPut(server, _ci) { 20 | var port = server.address().port; 21 | var put = childProcess.fork( 22 | require.resolve('../bin/sl-deploy'), 23 | [ 24 | '--service', 's1', 25 | 'http://always:allow@127.0.0.1:' + port, __filename 26 | ], 27 | { silent: true } 28 | ); 29 | put.on('exit', function(code, signal) { 30 | helpers.ok = true; 31 | server.close(); 32 | var stderr = put.stderr.read(); 33 | var stdout = put.stdout.read(); 34 | debug('sl-deploy stdout: %s', stdout); 35 | debug('sl-deploy stderr: %s', stderr); 36 | assert.notEqual(code, 0, 'sl-deploy should exit with failure'); 37 | helpers.assertMatch(stderr, /If authentication is required/i); 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /test/test-put-deploy-auth.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2015. All Rights Reserved. 2 | // Node module: strong-deploy 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var assert = require('assert'); 9 | var childProcess = require('child_process'); 10 | var helpers = require('./helpers'); 11 | 12 | var handler = helpers.findServiceAndRunHandler.bind(null, assertPut); 13 | var server = helpers.httpServerAllow(handler, doPut); 14 | 15 | function assertPut(req, res) { 16 | assert(req.method === 'PUT'); 17 | helpers.ok = true; 18 | 19 | res.end(); 20 | server.close(); 21 | } 22 | 23 | function doPut(server, _ci) { 24 | var port = server.address().port; 25 | childProcess.fork( 26 | require.resolve('../bin/sl-deploy'), 27 | [ 28 | '--service', 's1', 29 | 'http://always:allow@127.0.0.1:' + port, 30 | __filename 31 | ] 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /test/test-put-file-deploy.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2015. All Rights Reserved. 2 | // Node module: strong-deploy 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var assert = require('assert'); 9 | var childProcess = require('child_process'); 10 | var helpers = require('./helpers'); 11 | 12 | var handler = helpers.findServiceAndRunHandler.bind(null, assertPut); 13 | var server = helpers.httpServer(handler, doPut); 14 | 15 | function assertPut(req, res) { 16 | assert.equal(req.method, 'PUT'); 17 | helpers.ok = true; 18 | 19 | res.end(); 20 | server.close(); 21 | } 22 | 23 | function doPut(server, _ci) { 24 | var port = server.address().port; 25 | childProcess.fork( 26 | require.resolve('../bin/sl-deploy'), 27 | [ 28 | '--service', 's1', 29 | 'http://127.0.0.1:' + port, __filename 30 | ] 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /test/test-put-file-http-ssh.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2015. All Rights Reserved. 2 | // Node module: strong-deploy 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var assert = require('assert'); 9 | var childProcess = require('child_process'); 10 | var helpers = require('./helpers'); 11 | 12 | if (/win32/.test(process.platform)) { 13 | helpers.ok = true; 14 | console.log('TAP version 13\n1..0 # skip ssh tests on Windows'); 15 | return; 16 | } 17 | 18 | var handler = helpers.findServiceAndRunHandler.bind(null, assertPut); 19 | var server = helpers.httpServer(handler, doPut); 20 | 21 | function assertPut(req, res) { 22 | assert(req.method === 'PUT'); 23 | helpers.ok = true; 24 | 25 | res.end(); 26 | server.close(); 27 | } 28 | 29 | function doPut(server, _ci) { 30 | var port = server.address().port; 31 | childProcess.fork( 32 | require.resolve('../bin/sl-deploy'), 33 | [ 34 | '--service', 's1', 35 | 'http+ssh://127.0.0.1:' + port, __filename 36 | ] 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /test/test-usage.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2015. All Rights Reserved. 2 | // Node module: strong-deploy 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var assert = require('assert'); 9 | var async = require('async'); 10 | var debug = require('debug')('strong-deploy:test'); 11 | var exec = require('shelljs').exec; 12 | var path = require('path'); 13 | var util = require('util'); 14 | var helpers = require('./helpers'); 15 | 16 | function deployCli(args, cb) { 17 | var cmd = util.format('%s %s', 18 | path.resolve(__dirname, '../bin/sl-deploy.js'), 19 | args 20 | ); 21 | console.log(cmd); 22 | var res = exec(cmd); 23 | if (res.code !== 0) { 24 | return cb(Error(res.output)); 25 | } 26 | return cb(); 27 | } 28 | 29 | function expectError(er) { 30 | if (er) { 31 | return null; 32 | } 33 | return Error('expected error'); 34 | } 35 | 36 | // argv [0] and [1] are ignored (they are node and script name, not options) 37 | async.parallel([ 38 | deployCli.bind(null, '-h'), 39 | deployCli.bind(null, '--help'), 40 | deployCli.bind(null, '-hv'), 41 | deployCli.bind(null, '-v'), 42 | deployCli.bind(null, '--version'), 43 | deployCli.bind(null, '-vh'), 44 | function(callback) { 45 | deployCli('--no-such-option', function(er) { 46 | return callback(expectError(er)); 47 | }); 48 | }, 49 | function(callback) { 50 | deployCli('http://some-invalid-repo no-such-branch', 51 | function(er) { 52 | return callback(expectError(er)); 53 | } 54 | ); 55 | }, 56 | function(callback) { 57 | deployCli('http://some-invalid-repo', function(er) { 58 | return callback(expectError(er)); 59 | }); 60 | } 61 | ], function(er, results) { 62 | debug('test-help: error=%s:', er, results); 63 | assert.ifError(er); 64 | helpers.ok = true; 65 | }); 66 | --------------------------------------------------------------------------------