├── .eslintignore ├── .eslintrc.yml ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .nycrc ├── .prettierignore ├── .prettierrc ├── .travis.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── bin └── tools ├── builders ├── base │ ├── Dockerfile │ ├── README.md │ └── cloudbuild.yaml ├── nodejs │ ├── Dockerfile │ ├── README.md │ └── cloudbuild.yaml └── ruby │ ├── Dockerfile │ ├── README.md │ └── cloudbuild.yaml ├── key.json.enc ├── package.json ├── scripts ├── clean └── release ├── src ├── build_packs │ ├── build_pack.js │ ├── index.js │ ├── nodejs.js │ ├── python.js │ └── ruby.js ├── cli │ ├── commands │ │ ├── bump.js │ │ ├── exec.js │ │ ├── generate.js │ │ ├── list.js │ │ ├── test.js │ │ ├── test_commands │ │ │ ├── app.js │ │ │ ├── build.js │ │ │ ├── deploy.js │ │ │ ├── install.js │ │ │ └── run.js │ │ └── unify.js │ ├── index.js │ └── options.js ├── index.js └── utils │ ├── index.js │ └── products.js ├── templates ├── Dockerfile.tpl ├── cloudbuild.yaml.tpl ├── coc.tpl ├── contributing.tpl ├── issue_template.tpl ├── lib_readme.tpl ├── lib_samples_readme.tpl ├── license.tpl ├── nodejs │ ├── contributing.tpl │ ├── eslintignore.tpl │ ├── eslintrc.tpl │ ├── eslintrc_samples.tpl │ ├── eslintrc_samples_test.tpl │ ├── eslintrc_systemtest.tpl │ ├── eslintrc_test.tpl │ ├── gitignore.tpl │ ├── issue_template.tpl │ ├── jsdoc.tpl │ ├── nycrc.tpl │ ├── pkgjson.tpl │ ├── prettierignore.tpl │ └── prettierrc.tpl ├── pr_template.tpl ├── python │ ├── gitignore.tpl │ ├── lib_readme.tpl │ └── lib_samples_readme.tpl └── samples_readme.tpl └── test ├── cli ├── nodejs │ └── bump.test.js └── test_commands │ └── nodejs │ ├── app.test.js │ └── snippet.test.js ├── index.test.js ├── samples └── nodejs │ ├── app │ ├── app.js │ └── package.json │ ├── bump │ ├── app.js │ ├── package.json.default │ └── samples │ │ └── package.json.default │ └── snippet │ ├── index.js │ ├── lint_error.js │ ├── package.json │ └── test │ └── index.test.js └── utils └── index.test.js /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | samples/node_modules/* 3 | src/**/doc/* 4 | test/samples/nodejs/app/node_modules/* 5 | test/samples/nodejs/app/app.js 6 | test/samples/nodejs/snippet/node_modules/* 7 | test/samples/nodejs/snippet/index.js 8 | test/samples/nodejs/snippet/lint_error.js 9 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | extends: 3 | - 'eslint:recommended' 4 | - 'plugin:node/recommended' 5 | - prettier 6 | plugins: 7 | - node 8 | - prettier 9 | rules: 10 | prettier/prettier: error 11 | block-scoped-var: error 12 | eqeqeq: error 13 | no-warning-comments: warn 14 | node/no-unsupported-features: off 15 | node/no-missing-require: off 16 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to become a contributor and submit your own code 2 | 3 | **Table of contents** 4 | 5 | * [Contributor License Agreements](#contributor-license-agreements) 6 | * [Contributing a patch](#contributing-a-patch) 7 | * [Running the tests](#running-the-tests) 8 | * [Releasing the library](#releasing-the-library) 9 | 10 | ## Contributor License Agreements 11 | 12 | We'd love to accept your sample apps and patches! Before we can take them, we 13 | have to jump a couple of legal hurdles. 14 | 15 | Please fill out either the individual or corporate Contributor License Agreement 16 | (CLA). 17 | 18 | * If you are an individual writing original source code and you're sure you 19 | own the intellectual property, then you'll need to sign an [individual CLA] 20 | (https://developers.google.com/open-source/cla/individual). 21 | * If you work for a company that wants to allow you to contribute your work, 22 | then you'll need to sign a [corporate CLA] 23 | (https://developers.google.com/open-source/cla/corporate). 24 | 25 | Follow either of the two links above to access the appropriate CLA and 26 | instructions for how to sign and return it. Once we receive it, we'll be able to 27 | accept your pull requests. 28 | 29 | ## Contributing A Patch 30 | 31 | 1. Submit an issue describing your proposed change to the repo in question. 32 | 1. The repo owner will respond to your issue promptly. 33 | 1. If your proposed change is accepted, and you haven't already done so, sign a 34 | Contributor License Agreement (see details above). 35 | 1. Fork the desired repo, develop and test your code changes. 36 | 1. Ensure that your code adheres to the existing style in the sample to which 37 | you are contributing. Refer to the 38 | [Google Cloud Platform Samples Style Guide] 39 | (https://github.com/GoogleCloudPlatform/Template/wiki/style.html) for the 40 | recommended coding standards for this organization. 41 | 1. Ensure that your code has an appropriate set of unit tests which all pass. 42 | 1. Submit a pull request. 43 | 44 | ## Running the tests 45 | 46 | 1. [Prepare your environment for Node.js setup][setup]. 47 | 48 | 1. Install dependencies: 49 | 50 | npm install 51 | 52 | 1. Run the tests: 53 | 54 | export GCLOUD_PROJECT=your-project-id 55 | export GOOGLE_APPLICATION_CREDENTIALS=/path/to/keyfile.json 56 | npm test 57 | 58 | [setup]: https://cloud.google.com/nodejs/docs/setup 59 | 60 | ## Releasing the library 61 | 62 | 1. Run the tests, see above. 63 | 1. Bump the version in `package.json`. 64 | 1. Commit and push changes. Commit message should be the new version number. 65 | 1. Publish to `npm`: 66 | 67 | npm publish . 68 | 69 | 1. Install [pkg](https://www.npmjs.com/package/pkg). 70 | 71 | npm install -g pkg 72 | 73 | 1. Run the release script: 74 | 75 | npm run release 76 | 77 | You will need access to the `cloud-docs-samples` project to run this script. 78 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thanks for stopping by to let us know something could be better! 2 | 3 | Please run down the following list and make sure you've tried the usual "quick 4 | fixes": 5 | 6 | - Search the issues already opened: https://github.com/GoogleCloudPlatform/nodejs-repo-tools/issues 7 | - Search StackOverflow: http://stackoverflow.com/questions/tagged/google-cloud-platform+node.js 8 | - Check our Troubleshooting guide: https://googlecloudplatform.github.io/google-cloud-node/#/docs/guides/troubleshooting 9 | - Check our FAQ: https://googlecloudplatform.github.io/google-cloud-node/#/docs/guides/faq 10 | 11 | If you are still having issues, please be sure to include as much information as 12 | possible: 13 | 14 | #### Environment details 15 | 16 | - OS: 17 | - Node.js version: 18 | - npm version: 19 | - @google-cloud/nodejs-repo-tools version: 20 | 21 | #### Steps to reproduce 22 | 23 | 1. ??? 24 | 2. ??? 25 | 26 | Following these steps will guarantee the quickest resolution possible. 27 | 28 | Thanks! 29 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Fixes # (it's a good idea to open an issue first for discussion) 2 | 3 | - [ ] Tests and linter pass 4 | - [ ] Code coverage does not decrease (if any source code was changed) 5 | - [ ] Appropriate docs were updated (if necessary) 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | settings.json 4 | @google-cloud 5 | cjs 6 | .nyc_output 7 | package-lock.json 8 | yarn.lock 9 | binaries/ 10 | test/samples/nodejs/bump/package.json 11 | test/samples/nodejs/bump/samples/package.json 12 | -------------------------------------------------------------------------------- /.nycrc: -------------------------------------------------------------------------------- 1 | { 2 | "report-dir": "./.coverage", 3 | "exclude": [ 4 | "src/*{/*,/**/*}.js", 5 | "src/*/v*/*.js", 6 | "test/**/*.js" 7 | ], 8 | "watermarks": { 9 | "branches": [ 10 | 95, 11 | 100 12 | ], 13 | "functions": [ 14 | 95, 15 | 100 16 | ], 17 | "lines": [ 18 | 95, 19 | 100 20 | ], 21 | "statements": [ 22 | 95, 23 | 100 24 | ] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | samples/node_modules/* 3 | src/**/doc/* 4 | cjs/* 5 | test/samples/nodejs/app/node_modules/* 6 | test/samples/nodejs/app/app.js 7 | test/samples/nodejs/snippet/node_modules/* 8 | test/samples/nodejs/snippet/index.js 9 | test/samples/nodejs/snippet/lint_error.js -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | --- 2 | bracketSpacing: false 3 | printWidth: 80 4 | semi: true 5 | singleQuote: true 6 | tabWidth: 2 7 | trailingComma: es5 8 | useTabs: false 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | sudo: false 16 | language: node_js 17 | node_js: 18 | - "8" 19 | - "10" 20 | 21 | before_install: 22 | - openssl aes-256-cbc -K $AES_256_KEY -iv $AES_256_IV -in key.json.enc -out key.json -d 23 | install: 24 | - npm install 25 | 26 | after_script: 27 | - nyc report --reporter=lcov > coverage.lcov && codecov 28 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ##### 3.3.0 - 13 May 2019 2 | 3 | - Update dependencies 4 | 5 | ##### 3.2.0 - 14 February 2019 6 | 7 | - Update dependencies 8 | 9 | ##### 3.1.0 - 03 December 2018 10 | 11 | - fix: fix: update badges on default readme (#175) 12 | - fix: update eslint and jsdoc templates (#174) 13 | - fix: remove contributors code (#173) 14 | 15 | ##### 3.0.0 - 09 November 2018 16 | 17 | ###### Breaking changes 18 | - Removed the `lint` command and dependency on `semistandard` 19 | 20 | ##### 2.3.6 - 07 November 2018 21 | 22 | ###### Backwards compatible changes 23 | - Updated dependencies 24 | - Use new `gcloud builds` command 25 | - Switch `semistandard` to being a non-dev dependency 26 | - Respect URLs specified in build packs 27 | 28 | ##### 2.3.5 - 27 September 2018 29 | 30 | ###### Backwards compatible changes 31 | - Updated dependencies 32 | 33 | ##### 2.3.3 - 27 July 2018 34 | 35 | ###### Backwards compatible changes 36 | - Updated Readme template 37 | 38 | ##### 2.3.2 - 23 July 2018 39 | 40 | ###### Backwards compatible changes 41 | - Added Cloud IoT Core product 42 | - Added Cloud AutoML product 43 | 44 | ##### 2.3.1 - 11 July 2018 45 | 46 | ###### Backwards compatible changes 47 | - Added Cloud Tasks product 48 | 49 | ##### 2.3.0 - 06 April 2018 50 | 51 | ###### Backwards compatible changes 52 | - Added a Node.js-only "bump" command (thanks @alexander-fenster) 53 | 54 | ##### 2.2.6 - 28 March 2018 55 | 56 | - Add Text-to-Speech product info 57 | 58 | ###### Bug fixes 59 | - Fix link to open in cloud shell button image 60 | 61 | ##### 2.2.5 - 22 March 2018 62 | 63 | - Added comments in Node.js README templates that the files are autogenerated. 64 | 65 | ##### 2.2.4 - 16 March 2018 66 | 67 | ###### Bug fixes 68 | - Add missing lines to `.gitignore` Node.js template 69 | - Add missing configuration for two products 70 | 71 | ##### 2.2.3 - 14 March 2018 72 | 73 | ###### Bug fixes 74 | - Fix link to "Open in Cloud Shell" button image 75 | 76 | ##### 2.2.2 - 13 March 2018 77 | 78 | ###### Bug fixes 79 | - Un-ignore package-lock.json files in Node.js .gitignore template 80 | 81 | ##### 2.2.1 - 21 February 2018 82 | 83 | ###### Bug fixes 84 | - Removed broken links from Node.js issue template 85 | 86 | ##### 2.2.0 - 26 January 2018 87 | 88 | ###### Backwards compatible changes 89 | - Improved Python support 90 | - Upgraded dependencies 91 | 92 | ##### 2.1.4 - 17 January 2018 93 | 94 | - Updated a product name 95 | 96 | ##### 2.1.3 - 17 November 2017 97 | 98 | ###### Backwards compatible changes 99 | - #81 - Add ability to specify test url in config file 100 | 101 | ##### 2.1.2 - 08 November 2017 102 | 103 | ###### Backwards compatible changes 104 | - Added `yaml` flag to the `test deploy` command. 105 | 106 | ##### 2.1.1 - 01 November 2017 107 | 108 | ###### Bug fixes 109 | - Fix issue with `getUrl` 110 | 111 | ##### 2.1.0 - 27 October 2017 112 | 113 | ###### Bug fixes 114 | - #59 - Link to license and contributing files in lib_readme are wrong. 115 | - #60 - Add link to the library's repo in its readme 116 | - #61 - Compatible when sample directory does not exist 117 | - #67 - Samples link broken in the docs 118 | 119 | ###### Backwards compatible changes 120 | - #56 - Successfully ignore the new package-lock.json-12345 files. 121 | - #63 - Add --except option to generate command to be used in combination with the "all" target 122 | - #65 - Support deprecated release level. 123 | - Add Open in Cloud Shell button to readmes. 124 | - Dogfood repo-tools on repo-tools. Switched to eslint + prettier. 125 | 126 | ###### Other 127 | - Updated dependencies 128 | 129 | ##### 2.0.11 - 22 October 2017 130 | 131 | ###### Bug fixes 132 | - Fix to `spawnAsyncWithIO` 133 | 134 | ##### 2.0.10 - 17 October 2017 135 | 136 | ###### Backwards compatible changes 137 | - Fixed wrong samples link in lib_readme.tpl 138 | - #52 - Make alpha release level badges orange. 139 | - #54 - Remove the autogen beta disclaimer. 140 | 141 | ###### Other 142 | - Upgraded dependencies 143 | 144 | ##### 2.0.9 - 12 October 2017 145 | 146 | ###### Backwards compatible changes 147 | - Updated eslintrc_systemtest.tpl 148 | 149 | ##### 2.0.8 - 12 October 2017 150 | 151 | ###### Backwards compatible changes 152 | - Added support for generating and updating a package.json file. 153 | - Added support for generating a CONTRIBUTORS file. 154 | 155 | ##### 2.0.7 - 03 October 2017 156 | 157 | ###### Bug fixes 158 | - Fix Firestore docs link 159 | 160 | ##### 2.0.6 - 03 October 2017 161 | 162 | ###### Backwards compatible changes 163 | - #43 - Fix "quickstart" capitalization 164 | - #45 - Add `.prettierignore` 165 | - Use official Apache 2.0 license template. 166 | - Provide a mechanism to suppress the billing instruction. 167 | - Add Cloud Firestore product information. 168 | - #46 - Codecov badges should use master branch. 169 | 170 | ##### 2.0.5 - 02 October 2017 171 | 172 | ###### Other 173 | - Tweaked library readme 174 | - Fix for JSDoc config 175 | 176 | ##### 2.0.4 - 22 September 2017 177 | 178 | ###### Backwards compatible changes 179 | - #39 - Add ability to hide "Enable API" if api_id doesn't exist 180 | - #40 - Add all API IDs to src/utils/products.js 181 | 182 | ##### 2.0.3 - 22 September 2017 183 | 184 | ###### Bug fixes 185 | - Fixed --help and --version flags 186 | 187 | ##### 2.0.2 - 22 September 2017 188 | 189 | ###### Backwards compatible changes 190 | - Made command logs more consistent 191 | 192 | ##### 2.0.1 - 22 September 2017 193 | 194 | ###### Backwards compatible changes 195 | - #36 - Automatically write a JavaScript appropriate .gitignore 196 | 197 | ###### Bug fixes 198 | - Fixed release script 199 | 200 | ###### Other 201 | - Updated contributing guide 202 | 203 | ##### 2.0.0 - 22 September 2017 204 | 205 | Stable 2.0.0 release 206 | 207 | ###### Bug fixes 208 | - #35 - Fix logger stutter 209 | 210 | ##### 2.0.0-beta.12 - 20 September 2017 211 | 212 | ###### Breaking changes 213 | - Changed signature of `lint` command to match that of the `test` commands, i.e. using `--cmd` and `--` for args. 214 | 215 | ###### Other 216 | - Improved repository URL handling 217 | - Upgraded dependencies 218 | 219 | ##### 2.0.0-beta.11 - 19 September 2017 220 | 221 | - Small tweak to the `lib_readme` template. 222 | 223 | ##### 2.0.0-beta.10 - 18 September 2017 224 | 225 | - Fixed typo in `lib_readme` template. 226 | 227 | ##### 2.0.0-beta.9 - 18 September 2017 228 | 229 | - Make `lib_readme` quickstart sample region tag configurable. 230 | - Improve the look of the samples list in `lib_readme`. 231 | 232 | ##### 2.0.0-beta.8 - 18 September 2017 233 | 234 | - Updates to `templates/nodejs/jsdoc.tpl` 235 | 236 | ##### 2.0.0-beta.7 - 07 September 2017 237 | 238 | - Added 6 new targets to the `generate` command. 239 | - Added `shell: true` to improve stability of CLI commands. 240 | 241 | ##### 2.0.0-beta.6 - 29 August 2017 242 | 243 | - Added `templates/` to distribution (it was missing) 244 | 245 | ##### 2.0.0-beta.5 - 29 August 2017 246 | 247 | - Fix Release Quality badge in lib_readme 248 | - #23 - Add syntax highlighting to quickstart sample in lib_readme 249 | - Fix relative Github links in lib_readme 250 | - #22 - Add support for year ranges in the license file. 251 | 252 | ##### 2.0.0-beta.4 - 23 August 2017 253 | 254 | - Fix: `utils.fatal` => `utils.logger.fatal` 255 | 256 | ##### 2.0.0-beta.3 - 21 August 2017 257 | 258 | - Added `yargs-parser` to list of dependencies. 259 | 260 | ##### 2.0.0-beta.2 - 21 August 2017 261 | 262 | - Add use of babel-preset-env to prepublish step to product a Node.js v4 263 | version of Repo Tools. 264 | - Added `tools exec -- [args..]` command. 265 | - Now testing on more Node.js version (though the testing on Node.js v4 is just 266 | a smoke test). 267 | 268 | ##### 2.0.0-beta.1 - 21 August 2017 269 | 270 | - Add support for generating client lib files. 271 | - Upgraded dependencies. 272 | - Refactored build pack objects to classes 273 | - Improved README and added contributing guide -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, 4 | and in the interest of fostering an open and welcoming community, 5 | we pledge to respect all people who contribute through reporting issues, 6 | posting feature requests, updating documentation, 7 | submitting pull requests or patches, and other activities. 8 | 9 | We are committed to making participation in this project 10 | a harassment-free experience for everyone, 11 | regardless of level of experience, gender, gender identity and expression, 12 | sexual orientation, disability, personal appearance, 13 | body size, race, ethnicity, age, religion, or nationality. 14 | 15 | Examples of unacceptable behavior by participants include: 16 | 17 | * The use of sexualized language or imagery 18 | * Personal attacks 19 | * Trolling or insulting/derogatory comments 20 | * Public or private harassment 21 | * Publishing other's private information, 22 | such as physical or electronic 23 | addresses, without explicit permission 24 | * Other unethical or unprofessional conduct. 25 | 26 | Project maintainers have the right and responsibility to remove, edit, or reject 27 | comments, commits, code, wiki edits, issues, and other contributions 28 | that are not aligned to this Code of Conduct. 29 | By adopting this Code of Conduct, 30 | project maintainers commit themselves to fairly and consistently 31 | applying these principles to every aspect of managing this project. 32 | Project maintainers who do not follow or enforce the Code of Conduct 33 | may be permanently removed from the project team. 34 | 35 | This code of conduct applies both within project spaces and in public spaces 36 | when an individual is representing the project or its community. 37 | 38 | Instances of abusive, harassing, or otherwise unacceptable behavior 39 | may be reported by opening an issue 40 | or contacting one or more of the project maintainers. 41 | 42 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, 43 | available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/) 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![status: inactive](https://img.shields.io/badge/status-inactive-red.svg) 2 | 3 | This project is no longer actively developed or maintained. 4 | 5 | # nodejs-repo-tools [![build status][travis_badge]][travis_link] [![coverage][coverage_badge]][coverage_link] [![Greenkeeper badge][greenkeeper_badge]][greenkeeper_link] 6 | 7 | A tool used to maintain and test repositories in the GoogleCloudPlatform 8 | organization. 9 | 10 | **Table of contents** 11 | 12 | * [Installation](#installation) 13 | * [CLI usage](#cli-usage) 14 | * [Programmatic usage](#programmatic-usage) 15 | * [Language support](#language-support) 16 | * [Available build packs](#available-build-packs) 17 | * [Adding a build pack](#adding-a-build-pack) 18 | 19 | ## Installation 20 | 21 | Via `npm`: 22 | 23 | 1. `npm install -g @google-cloud/nodejs-repo-tools` 24 | 1. `repo-tools --help` 25 | 26 | Via download (Linux): 27 | 28 | 1. `curl -O https://storage.googleapis.com/cloud-docs-samples/releases/latest/nodejs-repo-tools-linux` 29 | 1. `mv ./nodejs-repo-tools-linux $HOME/bin/repo-tools` 30 | 1. `chmod +x $HOME/bin/repo-tools` 31 | 32 | Via download (Mac): 33 | 34 | 1. `curl -O https://storage.googleapis.com/cloud-docs-samples/releases/latest/nodejs-repo-tools-macos` 35 | 1. `mv ./nodejs-repo-tools-macos $HOME/bin/repo-tools` 36 | 1. `chmod +x $HOME/bin/repo-tools` 37 | 38 | Via download (Windows): 39 | 40 | [Download link](https://storage.googleapis.com/cloud-docs-samples/releases/latest/nodejs-repo-tools-win.exe) 41 | 42 | ## CLI usage 43 | 44 | Usage: `repo-tools --help` or `tools --help` 45 | 46 | ``` 47 | Commands: 48 | exec Run a given command in /Users/jdobry/projects/nodejs-repo-tools. 49 | generate Generate the given target(s) in /Users/jdobry/projects/nodejs-repo-tools. 50 | test Run a test sub-command. 51 | unify (Node.js only) Recursively add sub-directory dependencies to the top-level package.json file. 52 | 53 | Options: 54 | --build-pack, -b Choices: nodejs, python, ruby. Detected: nodejs. The build pack to use. The tool will attempt to 55 | detect which build to use. [string] 56 | --local-path, -l Current: /Users/jdobry/projects/nodejs-repo-tools. Use this option to set the current working 57 | directory of the command. [string] 58 | --dry-run Default: false. Print the actions that would be taken, but don't actually do anything. [boolean] 59 | --silent Default: false. If true, hide the output of shell commands. [boolean] 60 | --help Show help [boolean] 61 | --version Show version number [boolean] 62 | 63 | For more information, see https://github.com/GoogleCloudPlatform/nodejs-repo-tools 64 | ``` 65 | 66 | ## Programmatic usage 67 | 68 | 1. Install the tool: 69 | 70 | npm install --save @google-cloud/nodejs-repo-tools 71 | 72 | 1. Then in your code: 73 | 74 | const tools = require('@google-cloud/nodejs-repo-tools');` 75 | 76 | ## Language support 77 | 78 | Support for various programming languages is added via [build packs][]. A build 79 | pack specifies language-specific config and commands that should be used when 80 | performing the various Repo Tools tasks. Repo Tools does its best to infer the 81 | build pack it should use, but when running a command to can specify a specific 82 | build pack with `--build-pack [BUILD_PACK]` or `-b [BUILD_PACK]`. 83 | 84 | ### Available build packs 85 | 86 | * [global][] - The default, global configuration for all build packs. 87 | * [nodejs][] - Build pack for the Node.js programming language. 88 | * [python][] - Build pack for the Python programming language. 89 | * [ruby][] - Build pack for the Ruby programming language. 90 | 91 | [global]: https://github.com/GoogleCloudPlatform/nodejs-repo-tools/blob/master/src/build_packs/build_pack.js 92 | [nodejs]: https://github.com/GoogleCloudPlatform/nodejs-repo-tools/blob/master/src/build_packs/nodejs.js 93 | [python]: https://github.com/GoogleCloudPlatform/nodejs-repo-tools/blob/master/src/build_packs/python.js 94 | [ruby]: https://github.com/GoogleCloudPlatform/nodejs-repo-tools/blob/master/src/build_packs/ruby.js 95 | 96 | ### Adding a build pack 97 | 98 | A build pack can be added by adding a `.js` file to the [src/build_packs][build_packs] 99 | directory. This file should export a JavaScript object. You can see the 100 | available options by perusing the existing build packs. 101 | 102 | [build_packs]: https://github.com/GoogleCloudPlatform/nodejs-repo-tools/tree/master/src/build_packs 103 | 104 | ## Contributing 105 | 106 | See [CONTRIBUTING.md](https://github.com/GoogleCloudPlatform/nodejs-repo-tools/blob/master/.github/CONTRIBUTING.md). 107 | 108 | ## License 109 | 110 | Apache Version 2.0 111 | 112 | See [LICENSE](https://github.com/GoogleCloudPlatform/nodejs-repo-tools/blob/master/LICENSE). 113 | 114 | [travis_badge]: https://img.shields.io/travis/GoogleCloudPlatform/nodejs-repo-tools.svg 115 | [travis_link]: https://travis-ci.org/GoogleCloudPlatform/nodejs-repo-tools 116 | [coverage_badge]: https://img.shields.io/codecov/c/github/GoogleCloudPlatform/nodejs-repo-tools/master.svg 117 | [coverage_link]: https://codecov.io/gh/GoogleCloudPlatform/nodejs-repo-tools 118 | [greenkeeper_badge]: https://badges.greenkeeper.io/GoogleCloudPlatform/nodejs-repo-tools.svg 119 | [greenkeeper_link]: https://greenkeeper.io/ 120 | -------------------------------------------------------------------------------- /bin/tools: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Copyright 2017, Google, Inc. 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | // Infer the build pack to use 19 | require('../cjs/build_packs').getBuildPack(process.argv); 20 | 21 | // Run the main CLI 22 | require('../cjs/cli').parse(process.argv.slice(2)); 23 | -------------------------------------------------------------------------------- /builders/base/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the base gcloud image, based on ubuntu:trusty 2 | FROM gcr.io/cloud-docs-samples/gcloud 3 | 4 | # Install updates and dependencies 5 | RUN apt-get update -y && \ 6 | apt-get install --no-install-recommends -y -q curl python build-essential git ca-certificates libkrb5-dev imagemagick && \ 7 | apt-get clean && \ 8 | rm /var/lib/apt/lists/*_* 9 | 10 | # Install the latest LTS release of nodejs 11 | RUN mkdir /nodejs && curl https://nodejs.org/dist/v8.16.0/node-v8.16.0-linux-x64.tar.gz | tar xvzf - -C /nodejs --strip-components=1 12 | ENV PATH $PATH:/nodejs/bin 13 | 14 | # Install the Repo Tools binary 15 | RUN curl -O https://storage.googleapis.com/cloud-docs-samples/releases/latest/nodejs-repo-tools-linux \ 16 | && mv ./nodejs-repo-tools-linux /usr/local/bin/repo-tools \ 17 | && chmod +x /usr/local/bin/repo-tools \ 18 | && ln -s /usr/local/bin/repo-tools /usr/local/bin/samples \ 19 | && ln -s /usr/local/bin/repo-tools /usr/local/bin/tools 20 | ENV PATH $PATH:/usr/local/bin 21 | 22 | # Install the Cloud SQL Proxy 23 | RUN curl -o /usr/local/bin/cloud_sql_proxy https://dl.google.com/cloudsql/cloud_sql_proxy.linux.amd64 \ 24 | && chmod +x /usr/local/bin/cloud_sql_proxy 25 | 26 | ENTRYPOINT ["gcloud"] 27 | -------------------------------------------------------------------------------- /builders/base/README.md: -------------------------------------------------------------------------------- 1 | # Tool builder: `gcr.io/cloud-builders/base` 2 | 3 | This Container Builder build step runs the `gcloud` tool. 4 | 5 | It also install Node.js and @google-cloud/nodejs-repo-tools. 6 | 7 | ## Building this builder 8 | 9 | To build this builder, run the following command in this directory. 10 | 11 | $ gcloud container builds submit . --config=cloudbuild.yaml 12 | -------------------------------------------------------------------------------- /builders/base/cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | # In this directory, run the following command to build this builder. 2 | # $ gcloud container builds submit . --config=cloudbuild.yaml 3 | 4 | steps: 5 | - name: 'gcr.io/cloud-builders/docker' 6 | args: ['build', '--tag=gcr.io/$PROJECT_ID/base', '.'] 7 | 8 | # Print Node.js version. 9 | - name: 'gcr.io/$PROJECT_ID/base' 10 | entrypoint: 'node' 11 | args: ['--version'] 12 | 13 | # Print npm version. 14 | - name: 'gcr.io/$PROJECT_ID/base' 15 | entrypoint: 'npm' 16 | args: ['--version'] 17 | 18 | # Print repo-tools version. 19 | - name: 'gcr.io/$PROJECT_ID/base' 20 | entrypoint: 'repo-tools' 21 | args: ['--version'] 22 | 23 | images: ['gcr.io/$PROJECT_ID/base'] 24 | -------------------------------------------------------------------------------- /builders/nodejs/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the base gcloud image, based on ubuntu:trusty 2 | FROM gcr.io/cloud-docs-samples/base 3 | 4 | # Install Yarn 5 | ENV YARN_VERSION 0.27.5 6 | 7 | RUN set -ex \ 8 | && for key in \ 9 | 6A010C5166006599AA17F08146C2130DFD2497F5 \ 10 | ; do \ 11 | gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key" || \ 12 | gpg --keyserver pgp.mit.edu --recv-keys "$key" || \ 13 | gpg --keyserver keyserver.pgp.com --recv-keys "$key" ; \ 14 | done \ 15 | && curl -fSL -o yarn.js "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-legacy-$YARN_VERSION.js" \ 16 | && curl -fSL -o yarn.js.asc "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-legacy-$YARN_VERSION.js.asc" \ 17 | && gpg --batch --verify yarn.js.asc yarn.js \ 18 | && rm yarn.js.asc \ 19 | && mv yarn.js /usr/local/bin/yarn \ 20 | && chmod +x /usr/local/bin/yarn 21 | 22 | RUN yarn global add nyc codecov node-gyp 23 | 24 | ENTRYPOINT ["node"] 25 | -------------------------------------------------------------------------------- /builders/nodejs/README.md: -------------------------------------------------------------------------------- 1 | # Tool builder: `gcr.io/cloud-builders/nodejs` 2 | 3 | This Container Builder build step runs the `node` tool. 4 | 5 | It also installs Yarn, nyc, and codecov. 6 | 7 | ## Building this builder 8 | 9 | To build this builder, run the following command in this directory. 10 | 11 | $ gcloud builds submit . --config=cloudbuild.yaml 12 | -------------------------------------------------------------------------------- /builders/nodejs/cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | # In this directory, run the following command to build this builder. 2 | # $ gcloud container builds submit . --config=cloudbuild.yaml 3 | 4 | steps: 5 | - name: 'gcr.io/cloud-builders/docker' 6 | args: ['build', '--tag=gcr.io/$PROJECT_ID/nodejs', '.'] 7 | 8 | # Print Node.js version. 9 | - name: 'gcr.io/$PROJECT_ID/nodejs' 10 | entrypoint: 'node' 11 | args: ['--version'] 12 | 13 | # Print npm version. 14 | - name: 'gcr.io/$PROJECT_ID/nodejs' 15 | entrypoint: 'npm' 16 | args: ['--version'] 17 | 18 | # Print Yarn version. 19 | - name: 'gcr.io/$PROJECT_ID/nodejs' 20 | entrypoint: 'yarn' 21 | args: ['--version'] 22 | 23 | # Print repo-tools version. 24 | - name: 'gcr.io/$PROJECT_ID/nodejs' 25 | entrypoint: 'repo-tools' 26 | args: ['--version'] 27 | 28 | images: ['gcr.io/$PROJECT_ID/nodejs'] 29 | -------------------------------------------------------------------------------- /builders/ruby/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the base gcloud image, based on ubuntu:trusty 2 | FROM gcr.io/cloud-docs-samples/base 3 | 4 | # Install Ruby 5 | RUN apt-get update -y && \ 6 | apt-get install -y -q --no-install-recommends \ 7 | apt-utils \ 8 | autoconf \ 9 | build-essential \ 10 | ca-certificates \ 11 | cmake \ 12 | curl \ 13 | git \ 14 | file \ 15 | imagemagick \ 16 | libcurl3 \ 17 | libcurl3-gnutls \ 18 | libcurl4-openssl-dev \ 19 | libffi-dev \ 20 | libgdbm-dev \ 21 | libgit2-dev \ 22 | libgmp-dev \ 23 | libicu-dev \ 24 | libmagickwand-dev \ 25 | libmysqlclient-dev \ 26 | libncurses5-dev \ 27 | libpq-dev \ 28 | libqdbm-dev \ 29 | libreadline6-dev \ 30 | libsqlite3-dev \ 31 | libssl-dev \ 32 | libxml2-dev \ 33 | libxslt-dev \ 34 | libyaml-dev \ 35 | libz-dev \ 36 | systemtap 37 | 38 | # Install rbenv 39 | ENV RBENV_ROOT /rbenv 40 | RUN git clone https://github.com/sstephenson/rbenv.git $RBENV_ROOT && \ 41 | git clone https://github.com/sstephenson/ruby-build.git $RBENV_ROOT/plugins/ruby-build 42 | ENV PATH $RBENV_ROOT/shims:$RBENV_ROOT/bin:$PATH 43 | 44 | # Preinstalled default ruby version. 45 | ENV DEFAULT_RUBY_VERSION 2.4.0 46 | ENV BUNDLER_VERSION 1.14.6 47 | 48 | # Set ruby runtime distribution 49 | ARG RUNTIME_DISTRIBUTION="ruby-runtime-jessie" 50 | 51 | # Install the default ruby binary and bundler. 52 | RUN (echo "deb http://packages.cloud.google.com/apt ${RUNTIME_DISTRIBUTION} main" \ 53 | | tee /etc/apt/sources.list.d/ruby-runtime-jessie.list) && \ 54 | (curl https://packages.cloud.google.com/apt/doc/apt-key.gpg \ 55 | | apt-key add -) && \ 56 | apt-get update -y && \ 57 | apt-get install -y -q gcp-ruby-$DEFAULT_RUBY_VERSION && \ 58 | rbenv rehash && \ 59 | rbenv global $DEFAULT_RUBY_VERSION && \ 60 | gem install -q --no-rdoc --no-ri bundler --version $BUNDLER_VERSION && \ 61 | rbenv rehash 62 | 63 | # Tell nokogiri >=1.6 to install using system libraries, for faster builds 64 | RUN bundle config build.nokogiri --use-system-libraries 65 | ENV NOKOGIRI_USE_SYSTEM_LIBRARIES 1 66 | 67 | # Clean up apt 68 | RUN apt-get clean && rm -f /var/lib/apt/lists/*_* 69 | 70 | ENTRYPOINT ["ruby"] 71 | -------------------------------------------------------------------------------- /builders/ruby/README.md: -------------------------------------------------------------------------------- 1 | # Tool builder: `gcr.io/cloud-builders/nodejs` 2 | 3 | This Container Builder build step runs the `node` tool. 4 | 5 | It also install Yarn, nyc, and codecov. 6 | 7 | ## Building this builder 8 | 9 | To build this builder, run the following command in this directory. 10 | 11 | $ gcloud container builds submit . --config=cloudbuild.yaml 12 | -------------------------------------------------------------------------------- /builders/ruby/cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | # In this directory, run the following command to build this builder. 2 | # $ gcloud container builds submit . --config=cloudbuild.yaml 3 | 4 | steps: 5 | - name: 'gcr.io/cloud-builders/docker' 6 | args: ['build', '--tag=gcr.io/$PROJECT_ID/ruby', '.'] 7 | 8 | # Print Ruby version. 9 | - name: 'gcr.io/$PROJECT_ID/ruby' 10 | entrypoint: 'ruby' 11 | args: ['--version'] 12 | 13 | # Print repo-tools version. 14 | - name: 'gcr.io/$PROJECT_ID/nodejs' 15 | entrypoint: 'repo-tools' 16 | args: ['--version'] 17 | 18 | images: ['gcr.io/$PROJECT_ID/ruby'] 19 | -------------------------------------------------------------------------------- /key.json.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/nodejs-repo-tools/0fb652693bd0aad3b20d5d1b49b94fec47c4cdc4/key.json.enc -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@google-cloud/nodejs-repo-tools", 3 | "description": "Tools used to maintain and test Node.js repositories in the GoogleCloudPlaftorm organization.", 4 | "version": "3.3.0", 5 | "license": "Apache-2.0", 6 | "author": "Google Inc.", 7 | "engines": { 8 | "node": ">=8.0.0" 9 | }, 10 | "repository": "GoogleCloudPlatform/nodejs-repo-tools", 11 | "main": "cjs/index.js", 12 | "bin": { 13 | "tools": "./bin/tools", 14 | "repo-tools": "./bin/tools" 15 | }, 16 | "module": "src/index.js", 17 | "files": [ 18 | "bin/", 19 | "cjs/", 20 | "src/", 21 | "templates/" 22 | ], 23 | "ava": { 24 | "files": [ 25 | "test/**/*.test.js", 26 | "!test/samples/**/*.test.js" 27 | ] 28 | }, 29 | "pkg": { 30 | "scripts": [ 31 | "./cjs/src/build_packs/*.js", 32 | "./cjs/cli/commands/**/*.js" 33 | ] 34 | }, 35 | "nyc": { 36 | "include": [ 37 | "cjs/**/*.js" 38 | ] 39 | }, 40 | "babel": { 41 | "presets": [ 42 | [ 43 | "env", 44 | { 45 | "targets": { 46 | "node": "10.15.1" 47 | } 48 | } 49 | ] 50 | ] 51 | }, 52 | "scripts": { 53 | "clean": "node ./scripts/clean", 54 | "build": "npm run clean && babel src --out-dir cjs", 55 | "generate-scaffolding": "node ./bin/tools generate coc license pkgjson", 56 | "lint": "npm run build && eslint src/ test/", 57 | "fix": "npm run build && eslint --fix '**/*.js'", 58 | "prepublishOnly": "npm run lint", 59 | "release": "npm run prepublishOnly && node ./scripts/release", 60 | "pretest": "npm run prepublishOnly", 61 | "cover": "nyc --cache ava -v -T 1m -s && nyc report", 62 | "test": "node ./bin/tools test run --cmd npm -- run cover" 63 | }, 64 | "dependencies": { 65 | "@sindresorhus/slugify": "^0.9.0", 66 | "ava": "^1.2.1", 67 | "colors": "^1.3.3", 68 | "fs-extra": "^8.0.0", 69 | "got": "^9.6.0", 70 | "handlebars": "^4.1.0", 71 | "lodash": "^4.17.11", 72 | "nyc": "^14.0.0", 73 | "proxyquire": "^2.1.0", 74 | "semver": "^6.0.0", 75 | "sinon": "^7.2.3", 76 | "supertest": "^4.0.0", 77 | "yargs": "^13.1.0", 78 | "yargs-parser": "^13.0.0" 79 | }, 80 | "devDependencies": { 81 | "babel-cli": "6.26.0", 82 | "babel-preset-env": "1.7.0", 83 | "codecov": "^3.2.0", 84 | "eslint": "^5.13.0", 85 | "eslint-config-prettier": "^4.0.0", 86 | "eslint-plugin-node": "9.0.0", 87 | "eslint-plugin-prettier": "^3.0.1", 88 | "prettier": "^1.16.4" 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /scripts/clean: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Copyright 2017, Google, Inc. 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | var fs = require('fs-extra'); 19 | var path = require('path'); 20 | 21 | var pathname = path.join(__dirname, '../binaries'); 22 | 23 | console.log('Removing ' + pathname + '...'); 24 | fs.remove(pathname); 25 | console.log('Removed ' + pathname); 26 | 27 | pathname = path.join(__dirname, '../cjs'); 28 | 29 | console.log('Removing ' + pathname + '...'); 30 | fs.remove(pathname); 31 | console.log('Removed ' + pathname); 32 | -------------------------------------------------------------------------------- /scripts/release: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Copyright 2017, Google, Inc. 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | const spawn = require('child_process').spawn; 19 | const path = require('path'); 20 | 21 | const version = require('../package.json').version; 22 | 23 | const options = { stdio: 'inherit', shell: true }; 24 | 25 | function run (cmd, args) { 26 | return new Promise((resolve, reject) => { 27 | spawn(cmd, args, options).on('exit', (code) => { 28 | if (code === 0) { 29 | resolve(); 30 | } else { 31 | reject(); 32 | } 33 | }); 34 | }); 35 | } 36 | 37 | run('pkg', ['.', '--out-path', 'binaries']) 38 | .then(() => run('gsutil', ['cp', 'binaries/*', `gs://cloud-docs-samples/releases/${version}/`])) 39 | .then(() => run('gsutil', ['acl', 'ch', '-u', 'AllUsers:R', `gs://cloud-docs-samples/releases/${version}/*`])) 40 | .then(() => run('gsutil', ['cp', `gs://cloud-docs-samples/releases/${version}/*`, 'gs://cloud-docs-samples/releases/latest/'])) 41 | .then(() => run('gsutil', ['acl', 'ch', '-u', 'AllUsers:R', 'gs://cloud-docs-samples/releases/latest/*'])) 42 | .catch(console.error); 43 | -------------------------------------------------------------------------------- /src/build_packs/build_pack.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, Google LLC. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | 'use strict'; 17 | 18 | const _ = require('lodash'); 19 | const path = require('path'); 20 | const utils = require('../utils'); 21 | 22 | const globalOpts = { 23 | global: { 24 | dryRun: false, 25 | localPath: process.cwd(), 26 | project: process.env.GCLOUD_PROJECT || process.env.GOOGLE_CLOUD_PROJECT, 27 | config: '.cloud-repo-tools.json', 28 | configKey: null, 29 | }, 30 | test: { 31 | app: {}, 32 | build: { 33 | builderProject: 'cloud-docs-samples', 34 | ci: process.env.CI, 35 | keyFile: process.env.GOOGLE_APPLICATION_CREDENTIALS, 36 | timeout: '20m', 37 | }, 38 | deploy: { 39 | cmd: 'gcloud', 40 | yaml: 'app.yaml', 41 | tries: 1, 42 | }, 43 | install: {}, 44 | run: {}, 45 | }, 46 | generate: { 47 | all: { 48 | description: 'Generate all available targets.', 49 | }, 50 | coc: { 51 | description: 'Generate a CODE_OF_CONDUCT.md file.', 52 | filename: 'CODE_OF_CONDUCT.md', 53 | }, 54 | contributing: { 55 | description: 'Generate a .github/CONTRIBUTING.md file.', 56 | filename: '.github/CONTRIBUTING.md', 57 | }, 58 | issue_template: { 59 | description: 'Generate a .github/ISSUE_TEMPLATE.md file.', 60 | filename: '.github/ISSUE_TEMPLATE.md', 61 | }, 62 | license: { 63 | description: 'Generate a LICENSE file.', 64 | filename: 'LICENSE', 65 | data: { 66 | year: new Date().getFullYear(), 67 | }, 68 | }, 69 | lib_readme: { 70 | description: 'Generate a README.md file for a client library.', 71 | filename: 'README.md', 72 | getLibPkgName() { 73 | utils.logger.fatal( 74 | 'generate', 75 | 'getLibPkgName() must be implemented by a subclass!' 76 | ); 77 | }, 78 | validate(data) { 79 | if (!data.lib_install_cmd) { 80 | utils.logger.fatal( 81 | 'generate', 82 | `In order to generate lib_readme, "lib_install_cmd" must be set!` 83 | ); 84 | } 85 | }, 86 | }, 87 | lib_samples_readme: { 88 | description: 89 | 'Generate a README.md file for the samples/ folder of a client library.', 90 | filename: 'README.md', 91 | validate(data) { 92 | if (!data.samples || !data.samples.length) { 93 | utils.logger.fatal( 94 | 'generate', 95 | `In order to generate lib_samples_readme, config must contain a non-empty "samples" array!` 96 | ); 97 | } 98 | }, 99 | }, 100 | pr_template: { 101 | description: 'Generate a .github/PULL_REQUEST_TEMPLATE.md file.', 102 | filename: '.github/PULL_REQUEST_TEMPLATE.md', 103 | }, 104 | samples_readme: { 105 | description: 'Generate a generate samples README.md file.', 106 | filename: 'README.md', 107 | validate(data) { 108 | if (!data.samples || !data.samples.length) { 109 | utils.logger.fatal( 110 | 'generate', 111 | `In order to generate samples_readme, config must contain a non-empty "samples" array!` 112 | ); 113 | } 114 | }, 115 | }, 116 | }, 117 | }; 118 | 119 | /** 120 | * Base class for configuratin a build pack, which is a collection of default 121 | * config values to be used by the various Repo Tools commands. 122 | * 123 | * Specific programming languages can subclass {@link BuildPack} to customize 124 | * behavior for that language. 125 | * 126 | * @class BuildPack 127 | * @returns {BuildPack} A new {@link BuildPack} instance. 128 | */ 129 | module.exports = class BuildPack { 130 | constructor(config = {}, innerOpts = {}) { 131 | this.config = _.merge(globalOpts, config); 132 | delete innerOpts.config; 133 | _.merge(this, innerOpts); 134 | } 135 | 136 | detect() { 137 | throw new Error('detect() must be implemented by a subclass!'); 138 | } 139 | 140 | expandConfig(opts) { 141 | opts.localPath = path.resolve(opts.localPath); 142 | opts.cwd = opts.localPath; 143 | const base = path.parse(opts.localPath).base; 144 | 145 | let config = {}; 146 | let name, repository; 147 | const configFilename = opts.config || this.config.global.config; 148 | const configKey = opts.configKey || this.config.global.configKey; 149 | 150 | if (configFilename) { 151 | try { 152 | config = this.load(path.join(opts.localPath, configFilename), opts); 153 | name = config.name; 154 | repository = config.repository; 155 | if (configKey && configKey !== '_') { 156 | config = config[configKey] || {}; 157 | } 158 | 159 | // Values in the config file take precedence 160 | _.merge(this.config, config); 161 | } catch (err) { 162 | // Ignore error 163 | } 164 | } 165 | 166 | opts.name = opts.name || config.name || name || base; 167 | opts.project = opts.project || this.config.global.project; 168 | opts.builderProject = 169 | opts.builderProject || this.config.test.build.builderProject; 170 | opts.repository = config.repository || repository; 171 | 172 | const args = process.argv.slice(2); 173 | 174 | args.forEach((arg, i) => { 175 | if (arg === '--') { 176 | opts.args = args.slice(i + 1); 177 | return false; 178 | } 179 | }); 180 | } 181 | 182 | getLibInstallCmd() { 183 | throw new Error('getLibInstallCmd() must be implemented by a subclass!'); 184 | } 185 | 186 | /** 187 | * By "requiring" the file, this method supports loading .json and .js files. 188 | * A .js file needs to export a function that returns the config object. 189 | * 190 | * TODO(jdobry): Augment this method to support loading .yaml and .xml files. 191 | * 192 | * @param {*} filename 193 | */ 194 | load(filename) { 195 | const file = require(filename); 196 | if (typeof file === 'function') { 197 | return file(this.config); 198 | } 199 | return file; 200 | } 201 | }; 202 | -------------------------------------------------------------------------------- /src/build_packs/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, Google LLC. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | 'use strict'; 17 | 18 | require('colors'); 19 | 20 | const fs = require('fs-extra'); 21 | const parser = require('yargs-parser'); 22 | const path = require('path'); 23 | 24 | const {logger} = require('../utils'); 25 | 26 | const packs = (exports.packs = fs 27 | .readdirSync(__dirname) 28 | .filter(name => name !== 'index.js' && name !== 'build_pack.js') 29 | .map(name => { 30 | return { 31 | name: name.replace('.js', ''), 32 | BuildPack: require(`./${name}`), 33 | }; 34 | })); 35 | 36 | exports.BuildPack = require('./build_pack'); 37 | exports.NodejsBuildPack = require('./nodejs'); 38 | exports.PythonBuildPack = require('./python'); 39 | exports.RubyBuildPack = require('./ruby'); 40 | 41 | let currentBuildPack; 42 | 43 | /** 44 | * This methods examines the arguments passed to the current process in order 45 | * to infer which build pack to use. If localPath wasn't specified then it 46 | * will add it to the process args. 47 | * 48 | * @param {*} args 49 | */ 50 | exports.getBuildPack = (args = process.argv) => { 51 | let isHelpOrVersion = false; 52 | for (let i = 0; i < args.length; i++) { 53 | const arg = args[i]; 54 | if ( 55 | arg === '--help' || 56 | arg === '-h' || 57 | arg === '--version' || 58 | arg === '-v' 59 | ) { 60 | isHelpOrVersion = true; 61 | break; 62 | } 63 | } 64 | 65 | if (currentBuildPack) { 66 | // Return the cached build pack 67 | return currentBuildPack; 68 | } 69 | 70 | logger.debug('Detecting buildPack...'); 71 | 72 | let buildPackName, localPath; 73 | const argv = parser(args); 74 | 75 | buildPackName = argv.buildPack || argv.b; 76 | localPath = argv.localPath || argv.l; 77 | 78 | if (localPath) { 79 | localPath = path.resolve(localPath); 80 | logger.debug(`Using provided localPath: ${localPath.yellow}`); 81 | } else { 82 | logger.debug('Inferring localPath...'); 83 | localPath = process.cwd(); 84 | const index = process.argv.indexOf('--'); 85 | if (index >= 0) { 86 | process.argv.splice(index, 0, `--local-path=${localPath}`); 87 | } else { 88 | process.argv.push(`--local-path=${localPath}`); 89 | } 90 | logger.debug(`Inferred localPath: ${localPath.yellow}`); 91 | } 92 | 93 | // Try to load the build pack selected by the user 94 | if (buildPackName) { 95 | // Otherwise try to infer the build pack 96 | const pack = packs.find(pack => pack.name === buildPackName); 97 | if (!pack) { 98 | logger.fatal('cli', `Invalid buildPack: ${buildPackName}`); 99 | } 100 | logger.debug(`Using specified buildPack: ${buildPackName.yellow}`); 101 | currentBuildPack = new pack.BuildPack( 102 | {}, 103 | { 104 | _name: buildPackName, 105 | _selected: true, 106 | _cwd: localPath, 107 | } 108 | ); 109 | return currentBuildPack; 110 | } 111 | logger.debug('Inferring buildPack...'); 112 | 113 | // Otherwise try to infer the build pack 114 | const pack = packs.find(pack => { 115 | try { 116 | return pack.BuildPack.detect(localPath); 117 | } catch (err) { 118 | // Ignore error 119 | } 120 | }); 121 | 122 | if (pack) { 123 | const index = process.argv.indexOf('--'); 124 | const buildPackArg = `--build-pack=${pack.name}`; 125 | if (index >= 0) { 126 | process.argv.splice(index, 0, buildPackArg); 127 | } else { 128 | process.argv.push(buildPackArg); 129 | } 130 | logger.debug(`Inferred buildPack: ${pack.name.yellow}`); 131 | currentBuildPack = new pack.BuildPack( 132 | {}, 133 | { 134 | _name: pack.name, 135 | _detected: true, 136 | _cwd: localPath, 137 | } 138 | ); 139 | return currentBuildPack; 140 | } 141 | 142 | if (!isHelpOrVersion) { 143 | logger.error( 144 | 'cli', 145 | `Could not infer build pack! Using the default build pack which does not support all commands.` 146 | .yellow 147 | ); 148 | } 149 | 150 | currentBuildPack = new exports.BuildPack(); 151 | currentBuildPack._name = 'default'; 152 | currentBuildPack._selected = true; 153 | currentBuildPack._cwd = localPath; 154 | return currentBuildPack; 155 | }; 156 | -------------------------------------------------------------------------------- /src/build_packs/nodejs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, Google LLC. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | 'use strict'; 17 | 18 | const _ = require('lodash'); 19 | const fs = require('fs-extra'); 20 | const path = require('path'); 21 | 22 | const BuildPack = require('./build_pack'); 23 | 24 | const SETUP = ` 25 | 1. Read [Prerequisites][prereq] and [How to run a sample][run] first. 26 | 1. Install dependencies: 27 | 28 | With **npm**: 29 | 30 | npm install 31 | 32 | With **yarn**: 33 | 34 | yarn install 35 | 36 | [prereq]: ../README.md#prerequisites 37 | [run]: ../README.md#how-to-run-a-sample`; 38 | 39 | const TESTS = ` 40 | 1. Set the **GCLOUD_PROJECT** and **GOOGLE_APPLICATION_CREDENTIALS** environment variables. 41 | 42 | 1. Run the tests: 43 | 44 | With **npm**: 45 | 46 | npm test 47 | 48 | With **yarn**: 49 | 50 | yarn test`; 51 | 52 | const nodejsConfig = { 53 | display: 'Node.js', 54 | test: { 55 | app: { 56 | cmd: 'node', 57 | args: ['app.js'], 58 | }, 59 | build: {}, 60 | deploy: {}, 61 | install: { 62 | cmd: 'npm', 63 | args: ['install'], 64 | }, 65 | run: { 66 | cmd: 'npm', 67 | args: ['test'], 68 | }, 69 | }, 70 | generate: { 71 | eslintignore: { 72 | description: 'Generate .eslintignore', 73 | filename: '.eslintignore', 74 | }, 75 | eslintrc: { 76 | description: 'Generate main ESLint configuration.', 77 | filename: '.eslintrc.yml', 78 | }, 79 | eslintrc_test: { 80 | description: 'Generate ESLint configuration for unit tests.', 81 | filename: 'test/.eslintrc.yml', 82 | }, 83 | eslintrc_samples: { 84 | description: 'Generate ESLint configuration for samples.', 85 | filename: 'samples/.eslintrc.yml', 86 | }, 87 | eslintrc_samples_test: { 88 | description: 'Generate ESLint configuration for samples tests.', 89 | filename: 'samples/system-test/.eslintrc.yml', 90 | }, 91 | eslintrc_systemtest: { 92 | description: 'Generate ESLint configuration for system tests.', 93 | filename: 'system-test/.eslintrc.yml', 94 | }, 95 | gitignore: { 96 | description: '.gitignore', 97 | filename: '.gitignore', 98 | }, 99 | jsdoc: { 100 | description: 'Generate JSDoc configuration.', 101 | filename: '.jsdoc.js', 102 | }, 103 | lib_readme: { 104 | lib_install_cmd: 'npm install --save {{name}}', 105 | quickstart_filename: 'samples/quickstart.js', 106 | getLibPkgName(buildPack) { 107 | return buildPack.config.pkgjson.name; 108 | }, 109 | }, 110 | nycrc: { 111 | description: 'Generate nyc configuration.', 112 | filename: '.nycrc', 113 | }, 114 | pkgjson: { 115 | description: 'Generate and/or update a package.json file.', 116 | filename: 'package.json', 117 | addData(data) { 118 | const json = {}; 119 | const origKeys = Object.keys(data.pkgjson); 120 | json.name = data.libPkgName || data.pkgjson.name || 'TODO'; 121 | _.pull(origKeys, 'name'); 122 | json.description = data.pkgjson.description || 'TODO'; 123 | _.pull(origKeys, 'description'); 124 | json.version = data.pkgjson.version || '0.0.0'; 125 | _.pull(origKeys, 'version'); 126 | json.license = data.pkgjson.license || 'Apache-2.0'; 127 | _.pull(origKeys, 'license'); 128 | json.author = data.pkgjson.author || 'Google Inc.'; 129 | _.pull(origKeys, 'author'); 130 | json.engines = data.pkgjson.engines || {}; 131 | json.engines.node = data.pkgjson.engines 132 | ? data.pkgjson.engines.node 133 | : '>=4.0.0'; 134 | _.pull(origKeys, 'engines'); 135 | json.repository = data.pkgjson.repository || data.repository; 136 | _.pull(origKeys, 'repository'); 137 | json.main = data.pkgjson.main || 'src/index.js'; 138 | _.pull(origKeys, 'main'); 139 | 140 | _.pull(origKeys, 'scripts'); 141 | const depRe = /dependencies/i; 142 | const depKeys = origKeys.filter(x => depRe.test(x)); 143 | _.pull(origKeys, ...depKeys); 144 | 145 | // Put extra keys that weren't used above here 146 | for (const key of origKeys) { 147 | json[key] = data.pkgjson[key]; 148 | } 149 | 150 | json.scripts = data.pkgjson.scripts || {}; 151 | depKeys.sort(); 152 | for (const key of depKeys) { 153 | json[key] = data.pkgjson[key]; 154 | } 155 | 156 | data.formattedPkgjson = JSON.stringify(json, null, 2); 157 | }, 158 | }, 159 | prettierignore: { 160 | description: 'Generate .prettierignore', 161 | filename: '.prettierignore', 162 | }, 163 | prettierrc: { 164 | description: 'Generate .prettierrc', 165 | filename: '.prettierrc', 166 | }, 167 | samples_readme: { 168 | setup: SETUP, 169 | tests: TESTS, 170 | }, 171 | }, 172 | }; 173 | 174 | /** 175 | * @class NodejsBuildPack 176 | * @returns {NodejsBuildPack} A new {@link NodejsBuildPack} instance. 177 | */ 178 | module.exports = class NodejsBuildPack extends BuildPack { 179 | constructor(config = {}, innerOpts = {}) { 180 | super(_.merge(nodejsConfig, _.cloneDeep(config)), innerOpts); 181 | this.config.pkgjson = this.config.pkgjson || {}; 182 | } 183 | 184 | static detect(cwd) { 185 | return fs.statSync(path.join(cwd, 'package.json')).isFile(); 186 | } 187 | 188 | expandConfig(opts) { 189 | super.expandConfig(opts); 190 | try { 191 | const pkg = require(path.join(opts.localPath, 'package.json')); 192 | this.config.pkgjson = pkg; 193 | opts.repository || (opts.repository = pkg.repository); 194 | } catch (err) { 195 | // Ignore error 196 | } 197 | } 198 | 199 | getLibInstallCmd(opts) { 200 | return `npm install --save ${opts.libPkgName}`; 201 | } 202 | }; 203 | -------------------------------------------------------------------------------- /src/build_packs/python.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, Google LLC. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | 'use strict'; 17 | 18 | const _ = require('lodash'); 19 | const fs = require('fs-extra'); 20 | const path = require('path'); 21 | 22 | const BuildPack = require('./build_pack'); 23 | const utils = require('../utils'); 24 | 25 | const SETUP = ` 26 | 1. Read [Prerequisites][prereq] and [How to run a sample][run] first. 27 | 1. Install dependencies: 28 | 29 | pip install -r requirements.txt 30 | 31 | [prereq]: ../README.md#prerequisites 32 | [run]: ../README.md#how-to-run-a-sample`; 33 | 34 | const TESTS = ` 35 | 1. Set the **GCLOUD_PROJECT** and **GOOGLE_APPLICATION_CREDENTIALS** environment variables. 36 | 37 | 1. Run the tests: 38 | 39 | nox`; 40 | 41 | const pythonConfig = { 42 | display: 'Python', 43 | test: { 44 | app: { 45 | cmd: 'python', 46 | args: ['main.py'], 47 | }, 48 | build: {}, 49 | deploy: {}, 50 | install: { 51 | cmd: 'pip', 52 | args: ['install', '-r', 'requirements.txt'], 53 | }, 54 | run: { 55 | cmd: 'nox', 56 | args: [], 57 | }, 58 | }, 59 | generate: { 60 | gitignore: { 61 | description: '.gitignore', 62 | filename: '.gitignore', 63 | }, 64 | lib_readme: { 65 | description: 'README.rst', 66 | filename: 'README.rst', 67 | lib_install_cmd: 'pip install {{name}}', 68 | quickstart_filename: 'samples/quickstart.py', 69 | getLibPkgName(buildPack) { 70 | return buildPack.config.client_name; 71 | }, 72 | }, 73 | lib_samples_readme: { 74 | description: 75 | 'Generate a README.rst file for the samples/ folder of a client library.', 76 | filename: 'README.rst', 77 | validate(data) { 78 | if (!data.samples || !data.samples.length) { 79 | utils.logger.fatal( 80 | 'generate', 81 | `In order to generate lib_samples_readme, config must contain a non-empty "samples" array!` 82 | ); 83 | } 84 | }, 85 | }, 86 | samples_readme: { 87 | setup: SETUP, 88 | tests: TESTS, 89 | }, 90 | }, 91 | }; 92 | 93 | /** 94 | * @class PythonBuildPack 95 | * @returns {PythonBuildPack} A new {@link PythonBuildPack} instance. 96 | */ 97 | module.exports = class PythonBuildPack extends BuildPack { 98 | constructor(config = {}, innerOpts = {}) { 99 | super(_.merge(pythonConfig, _.cloneDeep(config)), innerOpts); 100 | } 101 | 102 | static detect(cwd) { 103 | try { 104 | if (fs.statSync(path.join(cwd, 'requirements.txt')).isFile()) { 105 | return true; 106 | } 107 | } catch (err) { 108 | // Ignore error 109 | } 110 | try { 111 | if (fs.statSync(path.join(cwd, 'setup.py')).isFile()) { 112 | return true; 113 | } 114 | } catch (err) { 115 | // Ignore error 116 | } 117 | } 118 | }; 119 | -------------------------------------------------------------------------------- /src/build_packs/ruby.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, Google LLC. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | 'use strict'; 17 | 18 | const fs = require('fs-extra'); 19 | const path = require('path'); 20 | 21 | const SETUP = ` 22 | 1. Read [Prerequisites][prereq] and [How to run a sample][run] first. 23 | 1. Install dependencies: 24 | 25 | bundle install 26 | 27 | [prereq]: ../README.md#prerequisites 28 | [run]: ../README.md#how-to-run-a-sample`; 29 | 30 | const TESTS = ` 31 | 1. Set the **GCLOUD_PROJECT** and **GOOGLE_APPLICATION_CREDENTIALS** environment variables. 32 | 33 | 1. Run the tests: 34 | 35 | bundle exec rspec`; 36 | 37 | module.exports = { 38 | display: 'Ruby', 39 | detect: cwd => fs.statSync(path.join(cwd, 'Gemfile')).isFile(), 40 | load: filename => require(filename), 41 | test: { 42 | app: { 43 | cmd: 'bundle', 44 | args: ['exec', 'ruby', 'app.rb'], 45 | }, 46 | build: {}, 47 | deploy: {}, 48 | install: { 49 | cmd: 'bundle', 50 | args: ['install'], 51 | }, 52 | run: { 53 | cmd: 'bundle', 54 | args: ['exec', 'rspec', '--format', 'documentation'], 55 | }, 56 | }, 57 | readme: { 58 | setup: SETUP, 59 | tests: TESTS, 60 | }, 61 | }; 62 | -------------------------------------------------------------------------------- /src/cli/commands/bump.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, Google, Inc. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | 'use strict'; 17 | 18 | require('colors'); 19 | const fs = require('fs'); 20 | const path = require('path'); 21 | const semver = require('semver'); 22 | 23 | const buildPack = require('../../build_packs').getBuildPack(); 24 | const utils = require('../../utils'); 25 | 26 | const CLI_CMD = 'bump'; 27 | const COMMAND = `tools ${CLI_CMD} ${''.yellow}`; 28 | const DESCRIPTION = `Bumps the specified semver value in both the main package and dependent packages (e.g. samples). ${ 29 | 'rev'.yellow.bold 30 | } can be one of ${'major'.green.bold}, ${'minor'.green.bold}, or ${ 31 | 'patch'.green.bold 32 | }.`; 33 | const USAGE = `Usage: 34 | ${COMMAND.bold} 35 | Description: 36 | ${DESCRIPTION} 37 | Positional arguments: 38 | ${''.bold} 39 | The semver version to increase.`; 40 | 41 | exports.command = `${CLI_CMD} `; 42 | exports.description = DESCRIPTION; 43 | exports.builder = yargs => { 44 | yargs.usage(USAGE); 45 | }; 46 | exports.handler = opts => { 47 | try { 48 | if (opts.dryRun) { 49 | utils.logger.log(CLI_CMD, 'Beginning dry run.'.cyan); 50 | } 51 | 52 | if (!opts.rev) { 53 | throw new Error( 54 | `For argument "rev" expected one of "major", "minor", or "patch".` 55 | ); 56 | } else if (!opts.rev.match(/^major|minor|patch$/)) { 57 | throw new Error( 58 | `For argument "rev" expected one of "major", "minor", or "patch".` 59 | ); 60 | } 61 | 62 | buildPack.expandConfig(opts); 63 | 64 | let oldVersion = buildPack.config.pkgjson.version; 65 | let name = buildPack.config.pkgjson.name; 66 | let newVersion = semver.inc(oldVersion, opts.rev); 67 | 68 | utils.logger.log( 69 | CLI_CMD, 70 | `Version will be bumped from ${oldVersion.yellow} to ${ 71 | newVersion.yellow 72 | }.` 73 | ); 74 | 75 | let samplesPackageJson; 76 | let samplesPackageJsonPath = path.join( 77 | buildPack.config.global.localPath, 78 | 'samples', 79 | 'package.json' 80 | ); 81 | if (fs.existsSync(samplesPackageJsonPath)) { 82 | try { 83 | samplesPackageJson = JSON.parse( 84 | fs.readFileSync(samplesPackageJsonPath) 85 | ); 86 | } catch (err) { 87 | throw new Error( 88 | `Cannot parse samples package.json located at ${samplesPackageJsonPath}: ${err.toString()}` 89 | ); 90 | } 91 | } 92 | 93 | utils.logger.log( 94 | CLI_CMD, 95 | `Version in ${'package.json'.yellow} will be set to ${newVersion.yellow}.` 96 | ); 97 | if (samplesPackageJson !== undefined) { 98 | let dependency = `'${name}': '${newVersion}'`; 99 | utils.logger.log( 100 | CLI_CMD, 101 | `${'samples/package.json'.yellow} will depend on ${dependency.yellow}.` 102 | ); 103 | } 104 | 105 | if (opts.dryRun) { 106 | utils.logger.log(CLI_CMD, 'Dry run complete.'.cyan); 107 | return; 108 | } 109 | 110 | buildPack.config.pkgjson['version'] = newVersion; 111 | let packageJsonPath = path.join(opts.localPath, 'package.json'); 112 | 113 | try { 114 | fs.writeFileSync( 115 | packageJsonPath, 116 | JSON.stringify(buildPack.config.pkgjson, null, ' ') + '\n' 117 | ); 118 | } catch (err) { 119 | throw new Error(`Cannot write ${packageJsonPath}: ${err.toString()}`); 120 | } 121 | 122 | if (samplesPackageJson !== undefined) { 123 | if (samplesPackageJson['dependencies'] === undefined) { 124 | samplesPackageJson['dependencies'] = {}; 125 | } 126 | samplesPackageJson['dependencies'][name] = newVersion; 127 | try { 128 | fs.writeFileSync( 129 | samplesPackageJsonPath, 130 | JSON.stringify(samplesPackageJson, null, ' ') + '\n' 131 | ); 132 | } catch (err) { 133 | throw new Error( 134 | `Cannot write ${samplesPackageJsonPath}: ${err.toString()}` 135 | ); 136 | } 137 | } 138 | let message = `Files updated. Please regenerate package lock files and commit them: 139 | ${'rm -f package-lock.json && npm install && npm link'.yellow} 140 | ${'git add package.json package-lock.json'.yellow}`; 141 | if (samplesPackageJson !== undefined) { 142 | message += ` 143 | ${ 144 | 'cd samples && rm -f package-lock.json && npm link ../ && npm install && cd ..' 145 | .yellow 146 | } 147 | ${'git add samples/package.json samples/package-lock.json'.yellow}`; 148 | } 149 | message += ` 150 | ${'git commit -m "chore: bump version to'.yellow} ${newVersion.yellow}${ 151 | '"'.yellow 152 | }`; 153 | utils.logger.log('bump', message); 154 | } catch (err) { 155 | utils.logger.error(CLI_CMD, err.toString()); 156 | // eslint-disable-next-line no-process-exit 157 | process.exit(1); 158 | } 159 | }; 160 | -------------------------------------------------------------------------------- /src/cli/commands/exec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, Google LLC. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | 'use strict'; 17 | 18 | require('colors'); 19 | 20 | const childProcess = require('child_process'); 21 | 22 | const buildPack = require('../../build_packs').getBuildPack(); 23 | const utils = require('../../utils'); 24 | 25 | const CLI_CMD = 'exec'; 26 | const COMMAND = `tools exec ${'[options] [--] [args...]'.yellow}`; 27 | const DESCRIPTION = `Run a given command in ${buildPack._cwd.yellow}.`; 28 | const USAGE = `Usage: 29 | ${COMMAND.bold} 30 | Description: 31 | ${DESCRIPTION}`; 32 | 33 | exports.command = CLI_CMD; 34 | exports.description = DESCRIPTION; 35 | exports.builder = yargs => { 36 | yargs 37 | .usage(USAGE) 38 | .example('Run the given command in the specified directory:') 39 | .example(`- ${'tools exec -l=~/projects/some/dir -- echo "hi"'.cyan}`); 40 | }; 41 | 42 | exports.handler = opts => { 43 | if (opts.dryRun) { 44 | utils.logger.log(CLI_CMD, 'Beginning dry run.'.cyan); 45 | } 46 | 47 | buildPack.expandConfig(opts); 48 | 49 | opts.args || (opts.args = []); 50 | 51 | if (opts.args.length === 0) { 52 | utils.logger.fatal( 53 | CLI_CMD, 54 | 'You must provide at least 1 command argument!' 55 | ); 56 | } 57 | 58 | utils.logger.log(CLI_CMD, `Executing in: ${opts.localPath.yellow}`); 59 | utils.logger.log(CLI_CMD, 'Running:', opts.args.join(' ').yellow); 60 | 61 | if (opts.dryRun) { 62 | utils.logger.log(CLI_CMD, 'Dry run complete.'.cyan); 63 | return; 64 | } 65 | 66 | const options = { 67 | cwd: opts.localPath, 68 | stdio: opts.silent ? 'ignore' : 'inherit', 69 | shell: true, 70 | }; 71 | 72 | const start = Date.now(); 73 | 74 | childProcess 75 | .spawn(opts.args[0], opts.args.slice(1), options) 76 | .on('exit', (code, signal) => { 77 | const timeTakenStr = utils.getTimeTaken(start); 78 | if (code !== 0 || signal) { 79 | utils.logger.error( 80 | CLI_CMD, 81 | `Oh no! Execution failed after ${timeTakenStr}.` 82 | ); 83 | // eslint-disable-next-line no-process-exit 84 | process.exit(code || 1); 85 | } else { 86 | utils.logger.log( 87 | CLI_CMD, 88 | `Success! Execution finished in ${timeTakenStr}.`.green 89 | ); 90 | } 91 | }); 92 | }; 93 | -------------------------------------------------------------------------------- /src/cli/commands/generate.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, Google LLC. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | 'use strict'; 17 | 18 | require('colors'); 19 | 20 | const _ = require('lodash'); 21 | const execSync = require('child_process').execSync; 22 | const fs = require('fs-extra'); 23 | const handlebars = require('handlebars'); 24 | const path = require('path'); 25 | const slugify = require('@sindresorhus/slugify'); 26 | 27 | const buildPack = require('../../build_packs').getBuildPack(); 28 | const options = require('../options'); 29 | const products = require('../../utils/products'); 30 | const utils = require('../../utils'); 31 | 32 | const CLI_CMD = 'generate'; 33 | 34 | handlebars.registerHelper('slugify', str => slugify(str)); 35 | handlebars.registerHelper('trim', str => str.trim()); 36 | handlebars.registerHelper('release_quality', utils.createReleaseQualityBadge); 37 | handlebars.registerHelper('if_eq', (left, right, opts) => { 38 | if (left === right) { 39 | return opts.fn(this); 40 | } 41 | return opts.inverse(this); 42 | }); 43 | handlebars.registerHelper('syntax_highlighting_ext', opts => { 44 | const repoPath = path 45 | .parse(opts.data.root.repoPath) 46 | .name.replace('google', '') 47 | .replace('cloud', ''); 48 | if (repoPath.includes('csharp') || repoPath.includes('dotnet')) { 49 | return 'cs'; 50 | } else if (repoPath.includes('go')) { 51 | return 'go'; 52 | } else if (repoPath.includes('java')) { 53 | return 'java'; 54 | } else if (repoPath.includes('node')) { 55 | return 'javascript'; 56 | } else if (repoPath.includes('php')) { 57 | return 'php'; 58 | } else if (repoPath.includes('python')) { 59 | return 'python'; 60 | } else if (repoPath.includes('ruby')) { 61 | return 'ruby'; 62 | } 63 | return ''; 64 | }); 65 | 66 | function gatherHelpText(opts, buildPack) { 67 | (buildPack.config.samples || []).forEach(sample => { 68 | if (typeof sample.usage === 'string') { 69 | sample.usage = { 70 | cmd: sample.usage, 71 | text: sample.usage, 72 | }; 73 | } 74 | if (!sample.help && sample.usage && typeof sample.usage.cmd === 'string') { 75 | try { 76 | sample.help = execSync(sample.usage.cmd, { 77 | cwd: opts.localPath, 78 | }) 79 | .toString() 80 | .trim(); 81 | } catch (err) { 82 | utils.logger.error(CLI_CMD, err.message); 83 | // eslint-disable-next-line no-process-exit 84 | process.exit(err.status); 85 | } 86 | } 87 | }); 88 | } 89 | 90 | function expandOpts(opts, buildPack) { 91 | opts.samples || (opts.samples = []); 92 | gatherHelpText(opts, buildPack); 93 | } 94 | 95 | const RE_REGION_TAG_START = /\[START ([\w_-]+)\]/g; 96 | const RE_REGION_TAG_END = /\[END ([\w_-]+)\]/g; 97 | 98 | function getQuickstart(filename) { 99 | if (!path.isAbsolute(filename)) { 100 | filename = path.join(buildPack._cwd, filename); 101 | } 102 | let regionTag = 'quickstart'; 103 | try { 104 | regionTag = 105 | buildPack.config.generate.lib_readme.quickstart_region_tag || 106 | 'quickstart'; 107 | } catch (err) { 108 | // Ignore error 109 | } 110 | const content = fs.readFileSync(filename, 'utf-8'); 111 | const lines = content.split('\n'); 112 | let inRegion = false; 113 | let firstIdx = -1; 114 | let lastIdx = -1; 115 | 116 | lines.forEach((line, i) => { 117 | if (!inRegion) { 118 | const matches = line.match(RE_REGION_TAG_START); 119 | if (matches && matches[0] && matches[0].indexOf(regionTag) !== -1) { 120 | inRegion = true; 121 | if (firstIdx === -1) { 122 | firstIdx = i + 1; 123 | } 124 | } 125 | } else { 126 | const matches = line.match(RE_REGION_TAG_END); 127 | if (matches && matches[0] && matches[0].indexOf(regionTag) !== -1) { 128 | inRegion = false; 129 | if (lastIdx === -1) { 130 | lastIdx = i; 131 | } 132 | } 133 | } 134 | }); 135 | 136 | return lines.slice(firstIdx, lastIdx).join('\n'); 137 | } 138 | 139 | const TARGETS = buildPack.config.generate; 140 | let availableTargetsStr = ''; 141 | Object.keys(TARGETS).forEach(target => { 142 | availableTargetsStr += ` ${target.yellow}: ${ 143 | TARGETS[target].description 144 | }\n`; 145 | }); 146 | 147 | const COMMAND = `tools ${CLI_CMD} ${'[options]'.yellow}`; 148 | const DESCRIPTION = `Generate the given target(s) in ${buildPack._cwd.yellow}.`; 149 | 150 | const USAGE = `Usage: 151 | ${COMMAND.bold} 152 | Description: 153 | ${DESCRIPTION} 154 | 155 | ${'Available targets:'.bold} 156 | 157 | ${availableTargetsStr}`; 158 | 159 | exports.command = `${CLI_CMD} `; 160 | exports.description = DESCRIPTION; 161 | 162 | exports.builder = yargs => { 163 | yargs.usage(USAGE).options({ 164 | config: options.config, 165 | 'config-key': options.configKey, 166 | data: { 167 | description: `${'Default:'.bold} ${ 168 | `{}`.yellow 169 | }. JSON string, to be passed to the template.`, 170 | requiresArg: true, 171 | type: 'string', 172 | }, 173 | except: { 174 | description: `Exclude targets when using the ${'all'.bold} target.`, 175 | requiresArg: true, 176 | type: 'array', 177 | }, 178 | }); 179 | }; 180 | 181 | exports.handler = opts => { 182 | if (opts.dryRun) { 183 | utils.logger.log(CLI_CMD, 'Beginning dry run.'.cyan); 184 | } 185 | 186 | if (opts.targets.indexOf('all') !== -1) { 187 | // Generate all targets 188 | opts.targets = Object.keys(TARGETS); 189 | opts.targets.splice(opts.targets.indexOf('all'), 1); 190 | opts.targets.splice(opts.targets.indexOf('lib_samples_readme'), 1); 191 | opts.targets.splice(opts.targets.indexOf('samples_readme'), 1); 192 | if (Array.isArray(opts.except)) { 193 | for (const exception of opts.except) { 194 | if (opts.targets.indexOf(exception) !== -1) { 195 | opts.targets.splice(opts.targets.indexOf(exception), 1); 196 | } 197 | } 198 | } 199 | } 200 | 201 | buildPack.expandConfig(opts); 202 | utils.logger.log( 203 | CLI_CMD, 204 | `Generating ${opts.targets.join(', ')} in: ${opts.localPath.yellow}` 205 | ); 206 | 207 | // The badgeUri is used for test status badges 208 | opts.repoPath = utils.getRepoPath(opts.repository, opts.localPath) || null; 209 | buildPack.config.cloudBuildBadgeUri = path.join( 210 | 'cloud-docs-samples-badges', 211 | opts.repoPath, 212 | opts.name 213 | ); 214 | 215 | // Load associated product information, if any 216 | if (buildPack.config.product) { 217 | try { 218 | Object.keys(products[buildPack.config.product]).forEach(field => { 219 | buildPack.config[field] = products[buildPack.config.product][field]; 220 | }); 221 | } catch (err) { 222 | utils.logger.error( 223 | CLI_CMD, 224 | `Unrecognized product: ${buildPack.config.product}` 225 | ); 226 | return; 227 | } 228 | } 229 | 230 | const start = Date.now(); 231 | let errors = []; 232 | 233 | // Generate each specified target 234 | opts.targets.forEach(target => { 235 | const targetConfig = TARGETS[target]; 236 | const targetPath = path.join(opts.localPath, targetConfig.filename); 237 | utils.logger.log(CLI_CMD, 'Compiling:', targetPath.yellow); 238 | 239 | try { 240 | if (target === 'lib_samples_readme' || target === 'samples_readme') { 241 | // Prepare config for the samples, if any 242 | expandOpts(opts, buildPack); 243 | } 244 | 245 | // Prepare the data for the template 246 | const data = _.merge(opts, targetConfig.data || {}, buildPack.config); 247 | data.lib_pkg_name = buildPack.config.generate.lib_readme.getLibPkgName( 248 | buildPack 249 | ); 250 | // Other data prep 251 | if (target === 'lib_readme') { 252 | if (buildPack.config.generate.lib_readme.quickstart_filename) { 253 | data.quickstart = getQuickstart( 254 | path.join( 255 | opts.localPath, 256 | buildPack.config.generate.lib_readme.quickstart_filename 257 | ), 258 | buildPack 259 | ); 260 | } 261 | data.lib_install_cmd = buildPack.config.generate.lib_readme.lib_install_cmd.replace( 262 | '{{name}}', 263 | data.lib_pkg_name 264 | ); 265 | } 266 | 267 | // Load the target's template 268 | let tpl; 269 | try { 270 | tpl = fs.readFileSync( 271 | path.join( 272 | __dirname, 273 | `../../../templates/${opts.buildPack}/${target}.tpl` 274 | ), 275 | 'utf-8' 276 | ); 277 | } catch (err) { 278 | tpl = fs.readFileSync( 279 | path.join(__dirname, `../../../templates/${target}.tpl`), 280 | 'utf-8' 281 | ); 282 | } 283 | // Validate the data for the given target is sufficient 284 | if (targetConfig.validate) { 285 | targetConfig.validate(data); 286 | } 287 | if (targetConfig.addData) { 288 | targetConfig.addData(data, opts); 289 | } 290 | 291 | // Generate the content 292 | const generated = handlebars.compile(tpl)(data); 293 | 294 | if (opts.dryRun) { 295 | utils.logger.log( 296 | CLI_CMD, 297 | `Printing: ${targetPath.yellow}\n${generated}` 298 | ); 299 | return; 300 | } 301 | 302 | fs.ensureDir(path.parse(targetPath).dir, err => { 303 | if (err) { 304 | utils.logger.error(CLI_CMD, err.stack || err.message); 305 | // eslint-disable-next-line no-process-exit 306 | process.exit(1); 307 | } 308 | 309 | // Write the content to the target's filename 310 | fs.writeFile(targetPath, generated, err => { 311 | if (err) { 312 | utils.logger.error(CLI_CMD, err.stack || err.message); 313 | // eslint-disable-next-line no-process-exit 314 | process.exit(1); 315 | } 316 | 317 | utils.logger.log(CLI_CMD, `Generated: ${targetPath}`.green); 318 | }); 319 | }); 320 | } catch (err) { 321 | errors.push(err); 322 | utils.logger.error(CLI_CMD, `Failed to generate: ${targetPath}`.red); 323 | } 324 | }); 325 | 326 | const timeTakenStr = utils.getTimeTaken(start); 327 | 328 | if (errors.length) { 329 | utils.logger.error( 330 | CLI_CMD, 331 | `Oh no! Generating failed after ${timeTakenStr}.` 332 | ); 333 | errors.forEach(error => { 334 | utils.logger.error(CLI_CMD, error); 335 | }); 336 | } else { 337 | utils.logger.log( 338 | CLI_CMD, 339 | `Success! Generating finished in ${timeTakenStr}.`.green 340 | ); 341 | } 342 | }; 343 | -------------------------------------------------------------------------------- /src/cli/commands/list.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, Google LLC. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | 'use strict'; 17 | 18 | require('colors'); 19 | 20 | exports.command = 'list'; 21 | exports.description = 'List samples.'; 22 | 23 | exports.builder = () => { 24 | // Not yet implemented 25 | }; 26 | 27 | exports.handler = () => { 28 | // eslint-disable-next-line no-console 29 | console.log('Not yet implemented.'.red); 30 | }; 31 | -------------------------------------------------------------------------------- /src/cli/commands/test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, Google LLC. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | 'use strict'; 17 | 18 | require('colors'); 19 | 20 | const COMMAND = `tools test ${'[options]'.yellow}`; 21 | const DESCRIPTION = `Run a test sub-command.`; 22 | const USAGE = `Usage: 23 | ${COMMAND.bold} 24 | Description: 25 | ${DESCRIPTION}`; 26 | 27 | exports.command = 'test'; 28 | exports.description = DESCRIPTION; 29 | 30 | exports.builder = yargs => { 31 | yargs 32 | .demand(1) 33 | .usage(USAGE) 34 | .commandDir('test_commands'); 35 | }; 36 | 37 | exports.handler = () => {}; 38 | -------------------------------------------------------------------------------- /src/cli/commands/test_commands/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, Google LLC. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | 'use strict'; 17 | 18 | require('colors'); 19 | 20 | const _ = require('lodash'); 21 | const childProcess = require('child_process'); 22 | 23 | const buildPack = require('../../../build_packs').getBuildPack(); 24 | const options = require('../../options'); 25 | const utils = require('../../../utils'); 26 | 27 | const CLI_CMD = 'app'; 28 | const COMMAND = `tools test ${CLI_CMD} ${'[options]'.yellow}`; 29 | const DESCRIPTION = `Start an app and test it with a GET request.`; 30 | const USAGE = `Usage: 31 | ${COMMAND.bold} 32 | Description: 33 | ${DESCRIPTION} 34 | 35 | Override the args passed to the configured start command by appending ${ 36 | '-- "your" "args" "here"'.bold 37 | } when you run the ${'test app'.bold} command.`; 38 | 39 | exports.command = CLI_CMD; 40 | exports.description = DESCRIPTION; 41 | exports.builder = yargs => { 42 | yargs.usage(USAGE).options({ 43 | cmd: { 44 | description: `${'Default:'.bold} ${ 45 | buildPack.config.test.app.cmd.yellow 46 | }. The command used to start the app.`, 47 | type: 'string', 48 | }, 49 | port: { 50 | description: `Override the port the app should listen on. By default the command will find an open port.`, 51 | type: 'number', 52 | }, 53 | start: { 54 | default: true, 55 | description: `${'Default:'.bold} ${ 56 | 'true'.yellow 57 | }. Whether to start the app in addition to sending it a request.`, 58 | type: 'boolean', 59 | }, 60 | config: options.config, 61 | 'config-key': options['config-key'], 62 | url: { 63 | description: `Override the request url.`, 64 | type: 'string', 65 | }, 66 | msg: { 67 | description: 68 | 'Set a message the should be found in the response to the rest request.', 69 | requiresArg: true, 70 | type: 'string', 71 | }, 72 | code: { 73 | description: 'Override the expected status code of the response.', 74 | requiresArg: true, 75 | type: 'number', 76 | }, 77 | 'required-env-vars': { 78 | alias: 'r', 79 | description: 80 | 'Specify environment variables that must be set for the test to succeed.', 81 | requiresArg: true, 82 | type: 'string', 83 | }, 84 | }); 85 | }; 86 | 87 | exports.handler = opts => { 88 | if (opts.dryRun) { 89 | utils.logger.log(CLI_CMD, 'Beginning dry run.'.cyan); 90 | } 91 | 92 | buildPack.expandConfig(opts); 93 | 94 | opts.cmd || (opts.cmd = buildPack.config.test.app.cmd); 95 | opts.args || (opts.args = buildPack.config.test.app.args); 96 | opts.port || (opts.port = buildPack.config.test.app.port); 97 | opts.msg || (opts.msg = buildPack.config.test.app.msg); 98 | opts.code || (opts.code = buildPack.config.test.app.code); 99 | opts.url || (opts.code = buildPack.config.test.app.url); 100 | 101 | // Verify that required env vars are set, if any 102 | opts.requiredEnvVars = 103 | opts.requiredEnvVars || buildPack.config.test.app.requiredEnvVars || []; 104 | if (opts.requiredEnvVars && typeof opts.requiredEnvVars === 'string') { 105 | opts.requiredEnvVars = opts.requiredEnvVars.split(','); 106 | } 107 | opts.requiredEnvVars.forEach(envVar => { 108 | if (!process.env[envVar]) { 109 | utils.logger.fatal( 110 | CLI_CMD, 111 | `Test requires that the ${envVar} environment variable be set!` 112 | ); 113 | } 114 | }); 115 | 116 | if (!opts.start) { 117 | utils 118 | .testRequest( 119 | opts.url || 120 | `http://localhost:${opts.port || 121 | buildPack.config.test.app.port || 122 | 8080}`, 123 | opts 124 | ) 125 | .then( 126 | () => utils.logger.log(CLI_CMD, 'Test complete.'.green), 127 | err => utils.logger.error(CLI_CMD, 'Test failed.', err) 128 | ); 129 | return; 130 | } 131 | 132 | utils 133 | .getPort(opts) 134 | .then(port => { 135 | const options = { 136 | cwd: opts.localPath, 137 | stdio: opts.silent ? 'ignore' : 'inherit', 138 | env: _.merge( 139 | _.merge({}, process.env), 140 | buildPack.config.test.app.env || {} 141 | ), 142 | shell: true, 143 | }; 144 | 145 | options.env.PORT = port; 146 | 147 | utils.logger.log(CLI_CMD, `Starting app in: ${opts.localPath.yellow}`); 148 | utils.logger.log(CLI_CMD, `Using port: ${`${options.env.PORT}`.yellow}`); 149 | utils.logger.log( 150 | CLI_CMD, 151 | 'Running:', 152 | opts.cmd.yellow, 153 | opts.args.join(' ').yellow 154 | ); 155 | 156 | if (opts.dryRun) { 157 | utils.logger.log( 158 | CLI_CMD, 159 | `Verifying: ${ 160 | `${opts.url || `http://localhost:${options.env.PORT}`}`.yellow 161 | }.` 162 | ); 163 | utils.logger.log(CLI_CMD, 'Dry run complete.'.cyan); 164 | return; 165 | } 166 | 167 | let requestErr = null; 168 | 169 | const start = Date.now(); 170 | 171 | // Start the app 172 | const child = childProcess 173 | .spawn(opts.cmd, opts.args, options) 174 | .on('exit', (code, signal) => { 175 | const timeTakenStr = utils.getTimeTaken(start); 176 | if (code || signal !== 'SIGTERM' || requestErr) { 177 | utils.logger.error( 178 | CLI_CMD, 179 | `Oh no! Test failed after ${timeTakenStr}.`, 180 | requestErr 181 | ); 182 | } else { 183 | utils.logger.log( 184 | CLI_CMD, 185 | `Success! Test finished in ${timeTakenStr}.`.green 186 | ); 187 | } 188 | }); 189 | 190 | utils.logger.log(CLI_CMD, `Child process ID:`, `${child.pid}`.yellow); 191 | 192 | function cleanup() { 193 | // Try different ways of killing the child process 194 | try { 195 | process.kill(child.pid, 'SIGTERM'); 196 | } catch (err) { 197 | // Ignore error 198 | } 199 | try { 200 | child.kill('SIGTERM'); 201 | } catch (err) { 202 | // Ignore error 203 | } 204 | process.removeListener('exit', cleanup); 205 | } 206 | 207 | // Be prepared to cleanup the child process 208 | process.on('exit', cleanup); 209 | 210 | // Give the app time to start up 211 | setTimeout(() => { 212 | // Test that the app is working 213 | utils 214 | .testRequest(opts.url || `http://localhost:${options.env.PORT}`, opts) 215 | .then( 216 | () => cleanup(), 217 | err => { 218 | requestErr = err; 219 | cleanup(); 220 | } 221 | ); 222 | }, 3000); 223 | }) 224 | .catch(err => { 225 | utils.logger.fatal(CLI_CMD, err.stack || err.message); 226 | }); 227 | }; 228 | -------------------------------------------------------------------------------- /src/cli/commands/test_commands/deploy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, Google LLC. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | 'use strict'; 17 | 18 | require('colors'); 19 | 20 | const _ = require('lodash'); 21 | const childProcess = require('child_process'); 22 | const fs = require('fs-extra'); 23 | const path = require('path'); 24 | 25 | const buildPack = require('../../../build_packs').getBuildPack(); 26 | const options = require('../../options'); 27 | const utils = require('../../../utils'); 28 | 29 | const CLI_CMD = 'deploy'; 30 | const DEPLOY_CMD = buildPack.config.test.deploy.cmd; 31 | const COMMAND = `tools test ${CLI_CMD} ${'[options]'.yellow}`; 32 | const DESCRIPTION = `Deploy an app and test it with a GET request.`; 33 | const USAGE = `Usage: 34 | ${COMMAND.bold} 35 | Description: 36 | ${DESCRIPTION}`; 37 | 38 | exports.command = CLI_CMD; 39 | exports.description = DESCRIPTION; 40 | exports.builder = yargs => { 41 | yargs.usage(USAGE).options({ 42 | cmd: { 43 | description: `${'Default:'.bold} ${ 44 | DEPLOY_CMD.yellow 45 | }. Override the command used to deploy the app.`, 46 | type: 'string', 47 | }, 48 | project: { 49 | alias: 'p', 50 | description: `${'Default:'.bold} ${ 51 | `${buildPack.config.global.project}`.yellow 52 | }. The project ID to use ${'inside'.italic} the build.`, 53 | requiresArg: true, 54 | type: 'string', 55 | }, 56 | delete: { 57 | default: true, 58 | description: `${'Default:'.bold} ${ 59 | 'true'.yellow 60 | }. Whether to delete the deployed app after the test finishes.`, 61 | type: 'boolean', 62 | }, 63 | config: options.config, 64 | 'config-key': options['config-key'], 65 | msg: { 66 | description: 67 | 'Set a message the should be found in the response to the rest request.', 68 | requiresArg: true, 69 | type: 'string', 70 | }, 71 | 'required-env-vars': { 72 | alias: 'r', 73 | description: 74 | 'Specify environment variables that must be set for the test to succeed.', 75 | requiresArg: true, 76 | type: 'string', 77 | }, 78 | substitutions: { 79 | description: `Specify variable substitutions for the deployment's yaml file.`, 80 | requiresArg: true, 81 | type: 'string', 82 | }, 83 | tries: { 84 | description: `${'Default:'.bold} ${ 85 | '1'.yellow 86 | }. Number of times to attempt deployment. Deployment will only be attempted again if the previous deployment fails. Must be greater than zero.`, 87 | requiresArg: true, 88 | type: 'number', 89 | }, 90 | yaml: { 91 | description: `${'Default:'.bold} ${ 92 | 'app.yaml'.yellow 93 | }. Specify the base yaml file to use when deploying.`, 94 | requiresArg: true, 95 | type: 'string', 96 | }, 97 | }); 98 | }; 99 | 100 | exports.handler = opts => { 101 | if (opts.dryRun) { 102 | utils.logger.log(CLI_CMD, 'Beginning dry run.'.cyan); 103 | } 104 | 105 | buildPack.expandConfig(opts); 106 | 107 | opts.cmd || (opts.cmd = DEPLOY_CMD); 108 | opts.yaml || (opts.yaml = buildPack.config.test.deploy.yaml); 109 | opts.version || (opts.version = path.parse(opts.localPath).base); 110 | if (opts.tries === undefined) { 111 | opts.tries = buildPack.config.test.deploy.tries; 112 | } 113 | if (opts.tries < 1) { 114 | // Must try at least once 115 | opts.tries = 1; 116 | } 117 | 118 | // Verify that required env vars are set, if any 119 | opts.requiredEnvVars = 120 | opts.requiredEnvVars || 121 | _.get(buildPack, 'config.test.app.requiredEnvVars', []); 122 | if (opts.requiredEnvVars && typeof opts.requiredEnvVars === 'string') { 123 | opts.requiredEnvVars = opts.requiredEnvVars.split(','); 124 | } 125 | opts.requiredEnvVars.forEach(envVar => { 126 | if (!process.env[envVar]) { 127 | utils.logger.fatal( 128 | CLI_CMD, 129 | `Test requires that the ${envVar} environment variable be set!` 130 | ); 131 | } 132 | }); 133 | 134 | if (!opts.project) { 135 | utils.logger.fatal(CLI_CMD, 'You must provide a project ID!'); 136 | } 137 | 138 | return new Promise((resolve, reject) => { 139 | opts.now = Date.now(); 140 | 141 | utils.logger.log(CLI_CMD, `Deploying app in: ${opts.localPath.yellow}`); 142 | 143 | // Manually set # of instances to 1 144 | const tmpAppYaml = changeScaling(opts); 145 | 146 | if (process.env.CLOUD_BUILD) { 147 | try { 148 | childProcess.execSync( 149 | `gcloud auth activate-service-account --key-file key.json`, 150 | { 151 | cwd: opts.localPath, 152 | stdio: opts.silent ? 'ignore' : 'inherit', 153 | } 154 | ); 155 | } catch (err) { 156 | // Ignore error 157 | } 158 | } else { 159 | utils.logger.log(CLI_CMD, 'Using configured credentials.'); 160 | } 161 | 162 | opts.args = [ 163 | 'app', 164 | 'deploy', 165 | path.parse(tmpAppYaml).base, 166 | // Skip prompt 167 | '-q', 168 | `--project=${opts.project}`, 169 | // Deploy over existing version so we don't have to clean up 170 | `--version=${opts.version}`, 171 | '--no-promote', 172 | ]; 173 | 174 | utils.logger.log( 175 | CLI_CMD, 176 | 'Running:', 177 | opts.cmd.yellow, 178 | opts.args.join(' ').yellow 179 | ); 180 | 181 | if (opts.dryRun) { 182 | utils.logger.log(CLI_CMD, 'Dry run complete.'.cyan); 183 | return; 184 | } 185 | 186 | const options = { 187 | cwd: opts.localPath, 188 | // shell: true, 189 | stdio: opts.silent ? 'ignore' : 'inherit', 190 | timeout: 12 * 60 * 1000, // 12-minute timeout 191 | shell: true, 192 | }; 193 | 194 | const start = Date.now(); 195 | 196 | let triesRemaining = opts.tries; 197 | 198 | function attemptDeploy() { 199 | if (triesRemaining >= 1) { 200 | triesRemaining--; 201 | 202 | childProcess 203 | .spawn(opts.cmd, opts.args, options) 204 | .on('exit', (code, signal) => { 205 | let timeTakenStr = utils.getTimeTaken(start); 206 | 207 | if (code || signal) { 208 | utils.logger.error( 209 | CLI_CMD, 210 | `Oh no! Deployment failed after ${timeTakenStr}.` 211 | ); 212 | } else { 213 | utils.logger.log( 214 | CLI_CMD, 215 | `Success! Deployment finished in ${timeTakenStr}.`.green 216 | ); 217 | } 218 | 219 | // Give app time to start 220 | setTimeout(() => { 221 | // Test versioned url of "default" module 222 | let demoUrl = utils.getUrl(opts); 223 | 224 | // Test that app is running successfully 225 | utils.testRequest(demoUrl, opts).then( 226 | () => { 227 | timeTakenStr = utils.getTimeTaken(start); 228 | utils.logger.log( 229 | CLI_CMD, 230 | `Success! Test finished in ${timeTakenStr}.`.green 231 | ); 232 | resolve(); 233 | }, 234 | err => { 235 | utils.logger.error( 236 | CLI_CMD, 237 | `Oh no! Test failed after ${timeTakenStr}.`, 238 | err 239 | ); 240 | 241 | // Try the test again if any available tries remain 242 | attemptDeploy(); 243 | } 244 | ); 245 | }, 5000); 246 | }); 247 | } else { 248 | const timeTakenStr = utils.getTimeTaken(start); 249 | reject( 250 | new Error( 251 | `Exhausted available deployment attempts after ${timeTakenStr}.` 252 | ) 253 | ); 254 | } 255 | } 256 | 257 | attemptDeploy(); 258 | }) 259 | .then( 260 | () => { 261 | if (opts.delete && !opts.dryRun) { 262 | return utils.deleteVersion(opts).catch(() => {}); 263 | } 264 | }, 265 | err => { 266 | if (opts.delete && !opts.dryRun) { 267 | return utils 268 | .deleteVersion(opts) 269 | .catch(() => {}) 270 | .then(() => Promise.reject(err)); 271 | } 272 | return Promise.reject(err); 273 | } 274 | ) 275 | .catch(err => { 276 | utils.logger.fatal(CLI_CMD, err.message); 277 | }); 278 | }; 279 | 280 | function changeScaling(opts) { 281 | const oldYamlPath = path.join(opts.localPath, opts.yaml); 282 | const newYamlPath = path.join( 283 | opts.localPath, 284 | `${opts.version}-${opts.now}.yaml` 285 | ); 286 | 287 | utils.logger.log(CLI_CMD, 'Compiling:', newYamlPath.yellow); 288 | let yaml = fs.readFileSync(oldYamlPath, 'utf8'); 289 | yaml += `\n\nmanual_scaling:\n instances: 1\n`; 290 | 291 | if (opts.substitutions) { 292 | opts.substitutions 293 | .split(',') 294 | .map(sub => sub.split('=')) 295 | .forEach(([key, value]) => { 296 | yaml = yaml.replace( 297 | key, 298 | value.startsWith('$') ? process.env[value.substring(1)] : value 299 | ); 300 | }); 301 | } 302 | 303 | if (opts.dryRun) { 304 | utils.logger.log(CLI_CMD, 'Printing:', newYamlPath.yellow, `\n${yaml}`); 305 | } else { 306 | utils.logger.log(CLI_CMD, 'Writing:', newYamlPath.yellow); 307 | fs.writeFileSync(newYamlPath, yaml, 'utf8'); 308 | } 309 | 310 | return newYamlPath; 311 | } 312 | -------------------------------------------------------------------------------- /src/cli/commands/test_commands/install.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, Google LLC. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | 'use strict'; 17 | 18 | require('colors'); 19 | 20 | const childProcess = require('child_process'); 21 | 22 | const buildPack = require('../../../build_packs').getBuildPack(); 23 | const utils = require('../../../utils'); 24 | 25 | const CLI_CMD = 'install'; 26 | const INSTALL_CMD = buildPack.config.test.install.cmd; 27 | const INSTALL_ARGS = buildPack.config.test.install.args; 28 | const INSTALL_CMD_STR = `${INSTALL_CMD} ${INSTALL_ARGS.join(' ')}`.trim(); 29 | const COMMAND = `tools test ${CLI_CMD} ${'[options] [--] [args...]'.yellow}`; 30 | const DESCRIPTION = `Run ${INSTALL_CMD_STR.bold} in ${buildPack._cwd.yellow}.`; 31 | const USAGE = `Usage: 32 | ${COMMAND.bold} 33 | Description: 34 | ${DESCRIPTION} 35 | 36 | Override the args passed to the configured install command by appending ${ 37 | '-- "your" "args" "here"'.bold 38 | } when you run the ${'test install'.bold} command.`; 39 | 40 | exports.command = CLI_CMD; 41 | exports.description = DESCRIPTION; 42 | exports.builder = yargs => { 43 | yargs 44 | .usage(USAGE) 45 | .options({ 46 | cmd: { 47 | description: `${'Default:'.bold} ${ 48 | `${INSTALL_CMD}`.yellow 49 | }. The install command to use.`, 50 | type: 'string', 51 | }, 52 | }) 53 | .example('Run the install command in the specified directory:') 54 | .example(`- ${'tools test install -l=~/projects/some/dir'.cyan}`) 55 | .example( 56 | `Runs ${'npm install --no-optional'.bold} instead of the default command:` 57 | ) 58 | .example( 59 | `- ${'tools test install --cmd=npm -- install --no-optional'.cyan}` 60 | ); 61 | }; 62 | 63 | exports.handler = opts => { 64 | if (opts.dryRun) { 65 | utils.logger.log(CLI_CMD, 'Beginning dry run.'.cyan); 66 | } 67 | 68 | buildPack.expandConfig(opts); 69 | 70 | opts.cmd || (opts.cmd = buildPack.config.test.install.cmd); 71 | opts.args || (opts.args = buildPack.config.test.install.args); 72 | 73 | utils.logger.log( 74 | CLI_CMD, 75 | `Installing dependencies in: ${opts.localPath.yellow}` 76 | ); 77 | utils.logger.log( 78 | CLI_CMD, 79 | 'Running:', 80 | opts.cmd.yellow, 81 | opts.args.join(' ').yellow 82 | ); 83 | 84 | if (opts.dryRun) { 85 | utils.logger.log(CLI_CMD, 'Dry run complete.'.cyan); 86 | return; 87 | } 88 | 89 | const options = { 90 | cwd: opts.localPath, 91 | stdio: opts.silent ? 'ignore' : 'inherit', 92 | shell: true, 93 | }; 94 | 95 | const start = Date.now(); 96 | 97 | childProcess 98 | .spawn(opts.cmd, opts.args, options) 99 | .on('exit', (code, signal) => { 100 | const timeTakenStr = utils.getTimeTaken(start); 101 | if (code !== 0 || signal) { 102 | utils.logger.error( 103 | CLI_CMD, 104 | `Oh no! Install failed after ${timeTakenStr}.` 105 | ); 106 | // eslint-disable-next-line no-process-exit 107 | process.exit(code || 1); 108 | } else { 109 | utils.logger.log( 110 | CLI_CMD, 111 | `Success! Installation finished in ${timeTakenStr}.`.green 112 | ); 113 | } 114 | }); 115 | }; 116 | -------------------------------------------------------------------------------- /src/cli/commands/test_commands/run.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, Google LLC. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | 'use strict'; 17 | 18 | require('colors'); 19 | 20 | const childProcess = require('child_process'); 21 | 22 | const buildPack = require('../../../build_packs').getBuildPack(); 23 | const utils = require('../../../utils'); 24 | 25 | const CLI_CMD = 'run'; 26 | const TEST_CMD = buildPack.config.test.run.cmd; 27 | const TEST_ARGS = buildPack.config.test.run.args; 28 | const TEST_CMD_STR = `${TEST_CMD} ${TEST_ARGS.join(' ')}`.trim(); 29 | const COMMAND = `tools test ${CLI_CMD} ${'[options] [--] [args...]'.yellow}`; 30 | const DESCRIPTION = `Run ${TEST_CMD_STR.bold} in ${buildPack._cwd.yellow}.`; 31 | const USAGE = `Usage: 32 | ${COMMAND.bold} 33 | Description: 34 | ${DESCRIPTION} 35 | 36 | Override the args passed to the configured test command by appending ${ 37 | '-- "your" "args" "here"'.bold 38 | } when you run the ${'test run'.bold} command.`; 39 | 40 | exports.command = CLI_CMD; 41 | exports.description = DESCRIPTION; 42 | exports.builder = yargs => { 43 | yargs 44 | .usage(USAGE) 45 | .options({ 46 | cmd: { 47 | description: `${'Default:'.bold} ${ 48 | `${TEST_CMD}`.yellow 49 | }. The test command to use.`, 50 | type: 'string', 51 | }, 52 | }) 53 | .example('Run the test command in the specified directory:') 54 | .example(`- ${'tools test run -l=~/projects/some/dir'.cyan}`) 55 | .example( 56 | `Runs ${'npm run system-test'.bold} instead of the default command:` 57 | ) 58 | .example(`- ${'tools test install --cmd=npm -- run system-test'.cyan}`); 59 | }; 60 | 61 | exports.handler = opts => { 62 | if (opts.dryRun) { 63 | utils.logger.log(CLI_CMD, 'Beginning dry run.'.cyan); 64 | } 65 | 66 | buildPack.expandConfig(opts); 67 | 68 | opts.cmd || (opts.cmd = buildPack.config.test.run.cmd); 69 | opts.args || (opts.args = buildPack.config.test.run.args); 70 | 71 | utils.logger.log(CLI_CMD, `Executing tests in: ${opts.localPath.yellow}`); 72 | utils.logger.log( 73 | CLI_CMD, 74 | 'Running:', 75 | opts.cmd.yellow, 76 | opts.args.join(' ').yellow 77 | ); 78 | 79 | if (opts.dryRun) { 80 | utils.logger.log(CLI_CMD, 'Dry run complete.'.cyan); 81 | return; 82 | } 83 | 84 | const options = { 85 | cwd: opts.localPath, 86 | stdio: opts.silent ? 'ignore' : 'inherit', 87 | shell: true, 88 | }; 89 | 90 | const start = Date.now(); 91 | 92 | childProcess 93 | .spawn(opts.cmd, opts.args, options) 94 | .on('exit', (code, signal) => { 95 | const timeTakenStr = utils.getTimeTaken(start); 96 | if (code !== 0 || signal) { 97 | utils.logger.error( 98 | CLI_CMD, 99 | `Oh no! Test failed after ${timeTakenStr}.` 100 | ); 101 | // eslint-disable-next-line no-process-exit 102 | process.exit(code || 1); 103 | } else { 104 | utils.logger.log( 105 | CLI_CMD, 106 | `Success! Test finished in ${timeTakenStr}`.green 107 | ); 108 | } 109 | }); 110 | }; 111 | -------------------------------------------------------------------------------- /src/cli/commands/unify.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, Google LLC. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | 'use strict'; 17 | 18 | require('colors'); 19 | 20 | const fs = require('fs'); 21 | const path = require('path'); 22 | 23 | exports.command = 'unify'; 24 | exports.description = 25 | '(Node.js only) Recursively add sub-directory dependencies to the top-level package.json file.'; 26 | 27 | exports.builder = yargs => { 28 | yargs.options({ 29 | localPath: { 30 | alias: 'l', 31 | default: process.cwd(), 32 | requiresArg: true, 33 | type: 'string', 34 | }, 35 | }); 36 | }; 37 | 38 | exports.handler = opts => { 39 | // Dedupe package.json dependencies 40 | // WARNING: This will fail if two different versions of the same package are required. 41 | const pkgSet = {}; 42 | 43 | function getDeps(directory, depth) { 44 | // Limit recursion depth 45 | if (depth < 0) { 46 | return; 47 | } 48 | 49 | // Skip module directories 50 | if (directory.includes('/.') || directory.includes('node_modules')) { 51 | return; 52 | } 53 | 54 | // Get subdirectories 55 | const dirs = fs.readdirSync(directory); 56 | 57 | // Record subdirectories that contain a package.json file 58 | let pkgJson; 59 | const pkgJsonDirs = dirs.filter(dir => 60 | fs.existsSync(path.join(directory, dir, `package.json`)) 61 | ); 62 | pkgJsonDirs.forEach(dir => { 63 | pkgJson = JSON.parse( 64 | fs.readFileSync(path.join(directory, dir, `package.json`)) 65 | ); 66 | Object.assign(pkgSet, pkgJson.dependencies); 67 | }); 68 | 69 | // Recurse 70 | const recurseDirs = dirs.filter(dir => { 71 | return fs.statSync(path.join(directory, dir)).isDirectory(); 72 | }); 73 | recurseDirs.forEach(dir => { 74 | getDeps(path.join(directory, dir), depth - 1); 75 | }); 76 | } 77 | 78 | getDeps(opts.localPath, 3); 79 | 80 | // Update root-level package.json (by shelling to npm) 81 | const spawn = require('child_process').spawn; 82 | const args = [`add`, `-D`].concat( 83 | Object.keys(pkgSet).map(pkg => `${pkg}@${pkgSet[pkg]}`) 84 | ); 85 | spawn(`yarn`, args, { 86 | cwd: opts.localPath, 87 | shell: true, 88 | stdio: ['ignore', process.stdout, process.stderr], 89 | }); 90 | }; 91 | -------------------------------------------------------------------------------- /src/cli/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, Google LLC. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | 'use strict'; 17 | 18 | require('colors'); 19 | 20 | const buildPacks = require('../build_packs'); 21 | const NodejsBuildPack = require('../build_packs/nodejs'); 22 | const buildPack = buildPacks.getBuildPack(); 23 | 24 | module.exports = require('yargs') 25 | .demand(1) 26 | .commandDir('commands', { 27 | visit(command) { 28 | if ( 29 | command.command.startsWith('bump') && 30 | !(buildPack instanceof NodejsBuildPack) 31 | ) { 32 | return false; 33 | } 34 | return command; 35 | }, 36 | }) 37 | .options({ 38 | 'build-pack': { 39 | alias: 'b', 40 | description: `${'Choices:'.bold} ${buildPacks.packs 41 | .map(pack => pack.name.yellow) 42 | .concat('default'.yellow) 43 | .join(', ')}. ${ 44 | buildPack._selected ? 'Selected:'.bold : 'Detected:'.bold 45 | } ${ 46 | `${buildPack._name}`.green 47 | }. The build pack to use. The tool will attempt to detect which build to use.`, 48 | global: true, 49 | requiresArg: true, 50 | type: 'string', 51 | }, 52 | 'local-path': { 53 | alias: 'l', 54 | description: `${'Current:'.bold} ${ 55 | `${buildPack.config.global.localPath}`.yellow 56 | }. Use this option to set the current working directory of the command.`, 57 | global: true, 58 | requiresArg: true, 59 | type: 'string', 60 | }, 61 | 'dry-run': { 62 | description: `${'Default:'.bold} ${ 63 | `${buildPack.config.global.dryRun}`.yellow 64 | }. Print the actions that ${ 65 | 'would'.italic 66 | } be taken, but don't actually do anything.`, 67 | global: true, 68 | type: 'boolean', 69 | }, 70 | silent: { 71 | description: `${'Default:'.bold} ${ 72 | 'false'.yellow 73 | }. If true, hide the output of shell commands.`, 74 | global: true, 75 | type: 'boolean', 76 | }, 77 | }) 78 | .wrap(120) 79 | .recommendCommands() 80 | .epilogue( 81 | 'For more information, see https://github.com/GoogleCloudPlatform/nodejs-repo-tools' 82 | ) 83 | .help() 84 | .strict() 85 | .version(require('../../package.json').version); 86 | -------------------------------------------------------------------------------- /src/cli/options.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, Google LLC. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | 'use strict'; 17 | 18 | const buildPack = require('../build_packs').getBuildPack(); 19 | 20 | module.exports = { 21 | config: { 22 | description: `${'Default:'.bold} ${ 23 | `${buildPack.config.global.config}`.yellow 24 | }. Specify a .json or .js config file to load. Options set in the config file supercede options set at the command line. A .js file must export a function which returns a config object.`, 25 | requiresArg: true, 26 | type: 'string', 27 | }, 28 | 'config-key': { 29 | description: `${'Default:'.bold} ${ 30 | `${buildPack.config.global.configKey}`.yellow 31 | }. Specify the key under which options are nested in the config file.`, 32 | requiresArg: true, 33 | type: 'string', 34 | }, 35 | }; 36 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, Google LLC. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | const colors = require('colors'); 17 | 18 | const assert = require('assert'); 19 | const childProcess = require('child_process'); 20 | const path = require('path'); 21 | const proxyquire = require('proxyquire').noPreserveCache(); 22 | const sinon = require('sinon'); 23 | const supertest = require('supertest'); 24 | 25 | const utils = (exports.utils = require('./utils')); 26 | 27 | exports.buildPacks = require('./build_packs'); 28 | 29 | exports.getRequest = config => { 30 | if (process.env.TEST_URL || config.testUrl) { 31 | return supertest(process.env.TEST_URL || config.testUrl); 32 | } else if (process.env.E2E_TESTS) { 33 | return supertest(utils.getUrl(config)); 34 | } 35 | return supertest( 36 | proxyquire(path.join(config.cwd, config.cmd || 'app'), { 37 | process: { 38 | env: config.env || process.env, 39 | }, 40 | }) 41 | ); 42 | }; 43 | 44 | exports.run = (cmd, cwd) => { 45 | const output = childProcess 46 | .execSync(cmd, {cwd: cwd, shell: true}) 47 | .toString() 48 | .trim(); 49 | try { 50 | return colors.strip(output); 51 | } catch (err) { 52 | return output; 53 | } 54 | }; 55 | 56 | exports.runAsync = (cmd, cwd) => { 57 | return new Promise((resolve, reject) => { 58 | childProcess.exec(cmd, {cwd: cwd, shell: true}, (err, stdout) => { 59 | if (err) { 60 | reject(err); 61 | return; 62 | } 63 | if (stdout) { 64 | const output = stdout.toString().trim(); 65 | try { 66 | resolve(colors.strip(output)); 67 | } catch (err) { 68 | resolve(output); 69 | } 70 | } else { 71 | resolve(stdout); 72 | } 73 | }); 74 | }); 75 | }; 76 | 77 | exports.runAsyncWithIO = (cmd, cwd) => { 78 | return new Promise((resolve, reject) => { 79 | childProcess.exec(cmd, {cwd: cwd, shell: true}, (err, stdout, stderr) => { 80 | const result = { 81 | err: err, 82 | stdout: stdout ? stdout.toString().trim() : null, 83 | stderr: stderr ? stderr.toString().trim() : null, 84 | }; 85 | result.output = (result.stdout || '') + (result.stderr || ''); 86 | if (err) { 87 | reject(result); 88 | return; 89 | } 90 | try { 91 | if (result.stderr) { 92 | result.stderr = colors.strip(result.stderr); 93 | } 94 | if (result.stdout) { 95 | result.stdout = colors.strip(result.stdout); 96 | } 97 | if (result.output) { 98 | result.output = colors.strip(result.output); 99 | } 100 | resolve(result); 101 | } catch (err) { 102 | resolve(result); 103 | } 104 | }); 105 | }); 106 | }; 107 | 108 | exports.spawnAsyncWithIO = (cmd, args, cwd, debug) => { 109 | args || (args = []); 110 | let opts = debug; 111 | if (typeof opts === 'boolean') { 112 | opts = {debug: true}; 113 | } 114 | opts || (opts = {}); 115 | return new Promise((resolve, reject) => { 116 | let done = false; 117 | let stdout = ''; 118 | let stderr = ''; 119 | 120 | function finish(err) { 121 | if (!done) { 122 | done = true; 123 | const results = { 124 | stdout: stdout.trim(), 125 | stderr: stderr.trim(), 126 | output: stdout.trim() + stderr.trim(), 127 | err: err, 128 | }; 129 | try { 130 | if (results.stderr) { 131 | results.stderr = colors.strip(results.stderr); 132 | } 133 | if (results.stdout) { 134 | results.stdout = colors.strip(results.stdout); 135 | } 136 | if (results.output) { 137 | results.output = colors.strip(results.output); 138 | } 139 | } catch (err) { 140 | // Do nothing 141 | } 142 | if (err) { 143 | reject(results); 144 | } else { 145 | resolve(results); 146 | } 147 | } 148 | } 149 | 150 | if (debug || (debug !== false && process.env.DEBUG)) { 151 | utils.logger.log('CMD', cmd, ...args); 152 | } 153 | const child = childProcess.spawn(cmd, args, {cwd: cwd, shell: true}); 154 | child.stdout.on('data', chunk => { 155 | if (debug || (debug !== false && process.env.DEBUG)) { 156 | utils.logger.log('stdout', chunk.toString()); 157 | } 158 | stdout += chunk.toString(); 159 | }); 160 | child.stderr.on('data', chunk => { 161 | utils.logger.error('stderr', chunk.toString()); 162 | stderr += chunk.toString(); 163 | }); 164 | child 165 | .on('error', finish) 166 | .on('close', () => { 167 | finish(); 168 | }) 169 | .on('exit', () => { 170 | finish(); 171 | }); 172 | }); 173 | }; 174 | 175 | class Try { 176 | constructor(test) { 177 | this._maxTries = 10; 178 | this._maxDelay = 20000; 179 | this._timeout = 60000; 180 | this._iteration = 1; 181 | this._multiplier = 1.3; 182 | this._delay = 500; 183 | this._test = test; 184 | } 185 | 186 | execute() { 187 | if (this._iteration >= this._maxTries) { 188 | return this.reject( 189 | this._error || new Error('Reached maximum number of tries') 190 | ); 191 | } else if (Date.now() - this._start >= this._timeout) { 192 | return this.reject(this._error || new Error('Test timed out')); 193 | } 194 | 195 | return Promise.resolve() 196 | .then(() => this._test(assert)) 197 | .then(() => this.resolve()) 198 | .catch(err => { 199 | this._error = err; 200 | this._iteration++; 201 | this._delay = Math.min(this._delay * this._multiplier, this._maxDelay); 202 | return new Promise((resolve, reject) => { 203 | setTimeout(() => { 204 | Promise.resolve() 205 | .then(() => this.execute()) 206 | .then(resolve, reject); 207 | }, this._delay); 208 | }); 209 | }); 210 | } 211 | 212 | timeout(timeout) { 213 | this._timeout = timeout; 214 | } 215 | 216 | tries(maxTries) { 217 | this._maxTries = maxTries; 218 | } 219 | 220 | start() { 221 | this._start = Date.now(); 222 | this.promise = new Promise((resolve, reject) => { 223 | this.resolve = resolve; 224 | this.reject = reject; 225 | this.execute().then(resolve, reject); 226 | }); 227 | return this.promise; 228 | } 229 | } 230 | 231 | exports.tryTest = test => { 232 | return new Try(test); 233 | }; 234 | 235 | exports.stubConsole = () => { 236 | /* eslint-disable no-console */ 237 | if ( 238 | typeof console.log.restore !== `function` && 239 | typeof console.error.restore !== `function` 240 | ) { 241 | if (process.env.DEBUG) { 242 | sinon.spy(console, `error`); 243 | sinon.spy(console, `log`); 244 | } else { 245 | sinon.stub(console, `error`); 246 | sinon.stub(console, `log`).callsFake((a, b) => { 247 | if ( 248 | typeof a === `string` && 249 | a.indexOf(`\u001b`) !== -1 && 250 | typeof b === `string` 251 | ) { 252 | console.log.apply(console, arguments); 253 | } 254 | }); 255 | } 256 | } 257 | /* eslint-enable no-console */ 258 | }; 259 | 260 | exports.restoreConsole = () => { 261 | /* eslint-disable no-console */ 262 | if (typeof console.log.restore === `function`) { 263 | console.log.restore(); 264 | } 265 | if (typeof console.error.restore === `function`) { 266 | console.error.restore(); 267 | } 268 | /* eslint-enable no-console */ 269 | }; 270 | 271 | exports.checkCredentials = t => { 272 | if (t && typeof t.truthy === 'function') { 273 | t.truthy( 274 | process.env.GCLOUD_PROJECT, 275 | `Must set GCLOUD_PROJECT environment variable!` 276 | ); 277 | t.truthy( 278 | process.env.GOOGLE_APPLICATION_CREDENTIALS, 279 | `Must set GOOGLE_APPLICATION_CREDENTIALS environment variable!` 280 | ); 281 | } else { 282 | assert( 283 | process.env.GCLOUD_PROJECT, 284 | `Must set GCLOUD_PROJECT environment variable!` 285 | ); 286 | assert( 287 | process.env.GOOGLE_APPLICATION_CREDENTIALS, 288 | `Must set GOOGLE_APPLICATION_CREDENTIALS environment variable!` 289 | ); 290 | if (typeof t === 'function') { 291 | t(); 292 | } 293 | } 294 | }; 295 | -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, Google LLC. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | 'use strict'; 17 | 18 | require('colors'); 19 | 20 | const childProcess = require('child_process'); 21 | const got = require('got'); 22 | const net = require('net'); 23 | const url = require('url'); 24 | 25 | const {spawn} = require('child_process'); 26 | 27 | const MAX_TRIES = 8; 28 | 29 | exports.parseArgs = (args = '') => { 30 | if (Array.isArray(args)) { 31 | return args; 32 | } 33 | 34 | const parsed = []; 35 | let arg = ''; 36 | let inQuote = false; 37 | let quoteChar = null; 38 | 39 | for (let i = 0; i < args.length; i++) { 40 | if (args[i] === `'`) { 41 | if (inQuote) { 42 | if (args[i - 1] === `\\`) { 43 | arg += args[i]; 44 | } else { 45 | if (quoteChar === `'`) { 46 | arg += args[i]; 47 | inQuote = false; 48 | quoteChar = null; 49 | } else { 50 | arg += args[i]; 51 | } 52 | } 53 | } else { 54 | if (args[i - 1] === `\\`) { 55 | arg += args[i]; 56 | } else { 57 | arg += args[i]; 58 | quoteChar = `'`; 59 | inQuote = true; 60 | } 61 | } 62 | } else if (args[i] === `"`) { 63 | if (inQuote) { 64 | if (args[i - 1] === `\\`) { 65 | arg += args[i]; 66 | } else { 67 | if (quoteChar === `"`) { 68 | arg += args[i]; 69 | inQuote = false; 70 | quoteChar = null; 71 | } else { 72 | arg += args[i]; 73 | } 74 | } 75 | } else { 76 | if (args[i - 1] === `\\`) { 77 | arg += args[i]; 78 | } else { 79 | arg += args[i]; 80 | quoteChar = `"`; 81 | inQuote = true; 82 | } 83 | } 84 | } else if (/\s/.test(args[i])) { 85 | if (inQuote) { 86 | arg += args[i]; 87 | } else if (arg) { 88 | parsed.push(arg); 89 | arg = ``; 90 | } else { 91 | args = ``; 92 | } 93 | } else { 94 | arg += args[i]; 95 | } 96 | 97 | if (i === args.length - 1) { 98 | if (inQuote) { 99 | throw new Error(`Unclosed quote in: ${args}`); 100 | } else if (arg) { 101 | parsed.push(arg); 102 | } 103 | } 104 | } 105 | 106 | return parsed; 107 | }; 108 | 109 | exports.getUrl = opts => { 110 | return `https://${opts.version}-dot-${opts.project}.appspot.com`; 111 | }; 112 | 113 | exports.finalize = (err, resolve, reject) => { 114 | if (err) { 115 | reject(err); 116 | } else { 117 | resolve(); 118 | } 119 | }; 120 | 121 | const logger = { 122 | error(config, ...args) { 123 | // eslint-disable-next-line no-console 124 | console.error( 125 | `${(typeof config === 'string' ? config : config.test).bold.red}:`, 126 | ...args.map(arg => { 127 | if (arg.red) { 128 | return arg.red; 129 | } 130 | return arg; 131 | }) 132 | ); 133 | }, 134 | 135 | fatal(...args) { 136 | this.error(...args); 137 | // eslint-disable-next-line no-process-exit 138 | process.exit(1); 139 | }, 140 | 141 | log(config, ...args) { 142 | // eslint-disable-next-line no-console 143 | console.log( 144 | `${(typeof config === 'string' ? config : config.test).bold}:`, 145 | ...args 146 | ); 147 | }, 148 | 149 | debug(...args) { 150 | if (process.argv.indexOf('--debug') !== -1) { 151 | // eslint-disable-next-line no-console 152 | console.log('debug'.bold, ...args); 153 | } 154 | }, 155 | }; 156 | 157 | exports.logger = logger; 158 | 159 | exports.getRepoPath = (repository, cwd) => { 160 | repository || (repository = {}); 161 | if (typeof repository === 'string') { 162 | repository = { 163 | url: repository, 164 | }; 165 | } 166 | 167 | if (!repository.url) { 168 | let pushUrl = childProcess 169 | .execSync('git remote get-url --push origin', { 170 | cwd, 171 | stdout: 'silent', 172 | }) 173 | .toString() 174 | .trim(); 175 | const start = pushUrl.indexOf('github.com'); 176 | if (start >= 0) { 177 | pushUrl = pushUrl.substring(start + 11); 178 | if (pushUrl) { 179 | return `/${pushUrl.replace('.git', '')}`; 180 | } 181 | } 182 | } 183 | 184 | const result = url.parse(repository.url).path.replace('.git', ''); // eslint-disable-line node/no-deprecated-api 185 | if (!result.startsWith('/')) { 186 | return `/${result}`; 187 | } 188 | return result; 189 | }; 190 | 191 | exports.getTimeTaken = start => { 192 | let timeTaken = (Date.now() - start) / 1000; 193 | if (timeTaken <= 100) { 194 | timeTaken = timeTaken.toPrecision(3); 195 | } else if (timeTaken >= 100) { 196 | timeTaken = Math.floor(timeTaken); 197 | } 198 | return `${timeTaken}s`.cyan; 199 | }; 200 | 201 | /** 202 | * Generates a markdown badge for displaying a "Release Quality'. 203 | * 204 | * @param {string} releaseQuality One of: (ga, beta, alpha, eap, deprecated). 205 | * @returns {string} The markdown badge. 206 | */ 207 | exports.createReleaseQualityBadge = releaseQuality => { 208 | releaseQuality = releaseQuality.toUpperCase(); 209 | let badge = ''; 210 | if (releaseQuality === 'GA') { 211 | badge = 'general%20availability%20%28GA%29-brightgreen'; 212 | } else if (releaseQuality === 'BETA') { 213 | badge = 'beta-yellow'; 214 | } else if (releaseQuality === 'ALPHA') { 215 | badge = 'alpha-orange'; 216 | } else if (releaseQuality === 'EAP') { 217 | badge = 'EAP-yellow'; 218 | } else if (releaseQuality === 'DEPRECATED') { 219 | badge = 'deprecated-red'; 220 | } else { 221 | logger.error( 222 | 'generate', 223 | `Expected "release_quality" to be one of: (ga, beta, alpha, eap, deprecated)! Actual: "${releaseQuality}"` 224 | ); 225 | // eslint-disable-next-line no-process-exit 226 | process.exit(1); 227 | } 228 | return `[![release level](https://img.shields.io/badge/release%20level-${badge}.svg?style=flat)](https://cloud.google.com/terms/launch-stages)`; 229 | }; 230 | 231 | let portrange = 45032; 232 | const triedPorts = {}; 233 | 234 | exports.getPort = config => { 235 | let port = config.port || portrange; 236 | 237 | while (triedPorts[port]) { 238 | port += 1; 239 | } 240 | 241 | triedPorts[port] = true; 242 | 243 | return new Promise(resolve => { 244 | const server = net.createServer(); 245 | server.listen(port, () => { 246 | server.once('close', () => { 247 | resolve(port); 248 | }); 249 | server.close(); 250 | }); 251 | server.on('error', () => { 252 | resolve(exports.getPort(config)); 253 | }); 254 | }); 255 | }; 256 | 257 | exports.testRequest = (url, config, numTry) => { 258 | logger.log('app', `Verifying: ${url.yellow}`); 259 | if (!numTry) { 260 | numTry = 1; 261 | } 262 | 263 | let canTryAgain = false; 264 | 265 | return got(url, { 266 | timeout: 10000, 267 | }) 268 | .then( 269 | response => { 270 | const EXPECTED_STATUS_CODE = config.code || 200; 271 | 272 | const body = response.body || ''; 273 | const code = response.statusCode; 274 | 275 | if (code !== EXPECTED_STATUS_CODE) { 276 | canTryAgain = true; 277 | throw new Error( 278 | `failed verification!\nExpected status code: ${EXPECTED_STATUS_CODE}\nActual: ${code}` 279 | ); 280 | } else if (config.msg && !body.includes(config.msg)) { 281 | throw new Error( 282 | `failed verification!\nExpected body: ${ 283 | config.msg 284 | }\nActual: ${body}` 285 | ); 286 | } else if (config.testStr && !config.testStr.test(body)) { 287 | throw new Error( 288 | `failed verification!\nExpected body: ${ 289 | config.testStr 290 | }\nActual: ${body}` 291 | ); 292 | } 293 | }, 294 | err => { 295 | canTryAgain = true; 296 | if (err && err.response) { 297 | const EXPECTED_STATUS_CODE = config.code || 200; 298 | 299 | const body = err.response.body || ''; 300 | const code = err.response.statusCode; 301 | 302 | if (code !== EXPECTED_STATUS_CODE) { 303 | throw new Error( 304 | `failed verification!\nExpected status code: ${EXPECTED_STATUS_CODE}\nActual: ${code}` 305 | ); 306 | } else if (config.msg && !body.includes(config.msg)) { 307 | throw new Error( 308 | `failed verification!\nExpected body: ${ 309 | config.msg 310 | }\nActual: ${body}` 311 | ); 312 | } else if (config.testStr && !config.testStr.test(body)) { 313 | throw new Error( 314 | `failed verification!\nExpected body: ${ 315 | config.testStr 316 | }\nActual: ${body}` 317 | ); 318 | } 319 | } else { 320 | return Promise.reject(err); 321 | } 322 | } 323 | ) 324 | .catch(err => { 325 | if (numTry >= MAX_TRIES || !canTryAgain) { 326 | return Promise.reject(err); 327 | } 328 | 329 | return new Promise((resolve, reject) => { 330 | setTimeout(() => { 331 | exports.testRequest(url, config, numTry + 1).then(resolve, reject); 332 | }, 500 * Math.pow(numTry, 2)); 333 | }); 334 | }); 335 | }; 336 | 337 | // Delete an App Engine version 338 | exports.deleteVersion = config => { 339 | return new Promise((resolve, reject) => { 340 | const cmd = config.deleteCmd || 'gcloud'; 341 | 342 | logger.log(config, 'Deleting deployment...'); 343 | // Keep track off whether "done" has been called yet 344 | let calledDone = false; 345 | 346 | const args = [ 347 | `app`, 348 | `versions`, 349 | `delete`, 350 | config.test, 351 | `--project=${config.projectId}`, 352 | `-q`, 353 | ]; 354 | 355 | const child = spawn(cmd, args, { 356 | cwd: config.cwd, 357 | // Shouldn't take more than 4 minutes to delete a deployed version 358 | timeout: 4 * 60 * 1000, 359 | }); 360 | 361 | logger.log( 362 | config, 363 | `Delete command: ${(cmd + ' ' + args.join(' ')).yellow}` 364 | ); 365 | 366 | child.on('error', finish); 367 | 368 | child.stdout.on('data', data => { 369 | const str = data.toString(); 370 | if (str.includes('\n')) { 371 | process.stdout.write(`${config.test.bold}: ${str}`); 372 | } else { 373 | process.stdout.write(str); 374 | } 375 | }); 376 | child.stderr.on('data', data => { 377 | const str = data.toString(); 378 | if (str.includes('\n')) { 379 | process.stderr.write(`${config.test.bold}: ${str}`); 380 | } else { 381 | process.stderr.write(str); 382 | } 383 | }); 384 | 385 | child.on('exit', code => { 386 | if (code !== 0) { 387 | finish(new Error(`${config.test}: failed to delete deployment!`)); 388 | } else { 389 | finish(); 390 | } 391 | }); 392 | 393 | // Exit helper so we don't call "cb" more than once 394 | function finish(err) { 395 | if (!calledDone) { 396 | calledDone = true; 397 | exports.finalize(err, resolve, reject); 398 | } 399 | } 400 | }); 401 | }; 402 | -------------------------------------------------------------------------------- /templates/Dockerfile.tpl: -------------------------------------------------------------------------------- 1 | # Select the container builder 2 | FROM gcr.io/{{project}}/{{builder}} 3 | 4 | # Load the source code 5 | COPY . /{{src}}/ 6 | 7 | WORKDIR /{{src}} 8 | -------------------------------------------------------------------------------- /templates/cloudbuild.yaml.tpl: -------------------------------------------------------------------------------- 1 | steps: 2 | - name: 'gcr.io/$PROJECT_ID/{{buildPack}}' 3 | env: [ 4 | 'CLOUD_BUILD=true', 5 | 'SHA={{sha}}', 6 | 'REPO_PATH={{repoPath}}', 7 | 'CI={{ci}}', 8 | 'CONTEXT={{name}}', 9 | 'GOOGLE_APPLICATION_CREDENTIALS={{keyFileName}}', 10 | 'GCLOUD_PROJECT={{project}}', 11 | 'GOOGLE_CLOUD_PROJECT={{project}}', 12 | {{#each requiredEnvVars}} 13 | '{{key}}={{value}}', 14 | {{/each}} 15 | ] 16 | entrypoint: 'samples' 17 | args: ['test', 'install', '--cmd', '{{installCmd}}', '--', {{#each installArgs}}'{{this}}'{{#if @last}}{{else}}, {{/if}}{{/each}}] 18 | {{#if run}}- name: 'gcr.io/$PROJECT_ID/{{buildPack}}' 19 | env: [ 20 | 'CLOUD_BUILD=true', 21 | 'GOOGLE_APPLICATION_CREDENTIALS={{keyFileName}}', 22 | 'GCLOUD_PROJECT={{project}}', 23 | 'GOOGLE_CLOUD_PROJECT={{project}}', 24 | {{#each requiredEnvVars}} 25 | '{{key}}={{value}}', 26 | {{/each}} 27 | ] 28 | entrypoint: 'samples' 29 | args: ['test', 'run', '--cmd', '{{testCmd}}', '--', {{#each testArgs}}'{{this}}'{{#if @last}}{{else}}, {{/if}}{{/each}}]{{/if}} 30 | {{#if app}}- name: 'gcr.io/$PROJECT_ID/{{buildPack}}' 31 | env: [ 32 | 'CLOUD_BUILD=true', 33 | 'GOOGLE_APPLICATION_CREDENTIALS={{keyFileName}}', 34 | 'GCLOUD_PROJECT={{project}}', 35 | 'GOOGLE_CLOUD_PROJECT={{project}}', 36 | {{#each requiredEnvVars}} 37 | '{{key}}={{value}}', 38 | {{/each}} 39 | ] 40 | entrypoint: 'samples' 41 | args: ['test', 'app', '--cmd', '{{startCmd}}', '--', {{#each startArgs}}'{{this}}'{{#if @last}}{{else}}, {{/if}}{{/each}}]{{/if}} 42 | {{#if deploy}}- name: 'gcr.io/$PROJECT_ID/{{buildPack}}' 43 | env: [ 44 | 'CLOUD_BUILD=true', 45 | 'GOOGLE_APPLICATION_CREDENTIALS={{keyFileName}}', 46 | 'GCLOUD_PROJECT={{project}}', 47 | 'GOOGLE_CLOUD_PROJECT={{project}}', 48 | {{#each requiredEnvVars}} 49 | '{{key}}={{value}}', 50 | {{/each}} 51 | ] 52 | entrypoint: 'samples' 53 | args: ['test', 'deploy']{{/if}} 54 | -------------------------------------------------------------------------------- /templates/coc.tpl: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, 4 | and in the interest of fostering an open and welcoming community, 5 | we pledge to respect all people who contribute through reporting issues, 6 | posting feature requests, updating documentation, 7 | submitting pull requests or patches, and other activities. 8 | 9 | We are committed to making participation in this project 10 | a harassment-free experience for everyone, 11 | regardless of level of experience, gender, gender identity and expression, 12 | sexual orientation, disability, personal appearance, 13 | body size, race, ethnicity, age, religion, or nationality. 14 | 15 | Examples of unacceptable behavior by participants include: 16 | 17 | * The use of sexualized language or imagery 18 | * Personal attacks 19 | * Trolling or insulting/derogatory comments 20 | * Public or private harassment 21 | * Publishing other's private information, 22 | such as physical or electronic 23 | addresses, without explicit permission 24 | * Other unethical or unprofessional conduct. 25 | 26 | Project maintainers have the right and responsibility to remove, edit, or reject 27 | comments, commits, code, wiki edits, issues, and other contributions 28 | that are not aligned to this Code of Conduct. 29 | By adopting this Code of Conduct, 30 | project maintainers commit themselves to fairly and consistently 31 | applying these principles to every aspect of managing this project. 32 | Project maintainers who do not follow or enforce the Code of Conduct 33 | may be permanently removed from the project team. 34 | 35 | This code of conduct applies both within project spaces and in public spaces 36 | when an individual is representing the project or its community. 37 | 38 | Instances of abusive, harassing, or otherwise unacceptable behavior 39 | may be reported by opening an issue 40 | or contacting one or more of the project maintainers. 41 | 42 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, 43 | available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/) 44 | -------------------------------------------------------------------------------- /templates/contributing.tpl: -------------------------------------------------------------------------------- 1 | # How to become a contributor and submit your own code 2 | 3 | **Table of contents** 4 | 5 | * [Contributor License Agreements](#contributor-license-agreements) 6 | * [Contributing a patch](#contributing-a-patch) 7 | * [Running the tests](#running-the-tests) 8 | * [Releasing the library](#releasing-the-library) 9 | 10 | ## Contributor License Agreements 11 | 12 | We'd love to accept your sample apps and patches! Before we can take them, we 13 | have to jump a couple of legal hurdles. 14 | 15 | Please fill out either the individual or corporate Contributor License Agreement 16 | (CLA). 17 | 18 | * If you are an individual writing original source code and you're sure you 19 | own the intellectual property, then you'll need to sign an [individual CLA] 20 | (https://developers.google.com/open-source/cla/individual). 21 | * If you work for a company that wants to allow you to contribute your work, 22 | then you'll need to sign a [corporate CLA] 23 | (https://developers.google.com/open-source/cla/corporate). 24 | 25 | Follow either of the two links above to access the appropriate CLA and 26 | instructions for how to sign and return it. Once we receive it, we'll be able to 27 | accept your pull requests. 28 | 29 | ## Contributing A Patch 30 | 31 | 1. Submit an issue describing your proposed change to the repo in question. 32 | 1. The repo owner will respond to your issue promptly. 33 | 1. If your proposed change is accepted, and you haven't already done so, sign a 34 | Contributor License Agreement (see details above). 35 | 1. Fork the desired repo, develop and test your code changes. 36 | 1. Ensure that your code adheres to the existing style in the code to which 37 | you are contributing. 38 | 1. Ensure that your code has an appropriate set of tests which all pass. 39 | 1. Submit a pull request. 40 | -------------------------------------------------------------------------------- /templates/issue_template.tpl: -------------------------------------------------------------------------------- 1 | Thanks for stopping by to let us know something could be better! 2 | 3 | Please run down the following list and make sure you've tried the usual "quick 4 | fixes": 5 | 6 | - Search the issues already opened: https://github.com{{repoPath}}/issues 7 | - Search StackOverflow 8 | 9 | If you are still having issues, please be sure to include as much information as 10 | possible: 11 | 12 | #### Environment details 13 | 14 | - OS: ??? 15 | - ??? 16 | 17 | #### Steps to reproduce 18 | 19 | 1. ??? 20 | 2. ??? 21 | 22 | Following these steps will guarantee the quickest resolution possible. 23 | 24 | Thanks! 25 | -------------------------------------------------------------------------------- /templates/lib_readme.tpl: -------------------------------------------------------------------------------- 1 | [//]: # "This README.md file is auto-generated, all changes to this file will be lost." 2 | [//]: # "To regenerate it, use `npm run generate-scaffolding`." 3 | Google Cloud Platform logo 4 | 5 | # [{{name}}: {{display}} Client](https://github.com{{repoPath}}) 6 | 7 | {{release_quality release_quality}} 8 | [![npm version](https://img.shields.io/npm/v/{{lib_pkg_name}}.svg)](https://www.npmjs.org/package/{{lib_pkg_name}}) 9 | [![codecov](https://img.shields.io/codecov/c/github{{repoPath}}/master.svg?style=flat)](https://codecov.io/gh{{repoPath}}) 10 | 11 | {{description}} 12 | 13 | {{#if deprecated}} 14 | | :warning: Deprecated Module | 15 | | --- | 16 | | This library is **deprecated**. {{deprecated}} | 17 | {{/if}} 18 | 19 | * [Using the client library](#using-the-client-library) 20 | {{#if samples.length}} 21 | * [Samples](#samples) 22 | {{/if}} 23 | * [Versioning](#versioning) 24 | * [Contributing](#contributing) 25 | * [License](#license) 26 | 27 | ## Using the client library 28 | 29 | 1. [Select or create a Cloud Platform project][projects]. 30 | 31 | {{#unless suppress_billing}} 32 | 1. [Enable billing for your project][billing]. 33 | 34 | {{/unless}} 35 | {{#if api_id}} 36 | 1. [Enable the {{name}} API][enable_api]. 37 | 38 | {{/if}} 39 | 1. [Set up authentication with a service account][auth] so you can access the 40 | API from your local workstation. 41 | 42 | 1. Install the client library: 43 | 44 | {{lib_install_cmd}} 45 | 46 | {{#if quickstart}} 47 | 1. Try an example: 48 | 49 | ```{{syntax_highlighting_ext}} 50 | {{{quickstart}}} 51 | ``` 52 | {{/if}} 53 | 54 | {{#if samples.length}} 55 | ## Samples 56 | 57 | Samples are in the [`samples/`](https://github.com{{repoPath}}/tree/master/samples) directory. The samples' `README.md` 58 | has instructions for running the samples. 59 | 60 | | Sample | Source Code | Try it | 61 | | --------------------------- | --------------------------------- | ------ | 62 | {{#each samples}} 63 | | {{name}} | [source code](https://github.com{{../repoPath}}/blob/master/samples/{{file}}) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com{{../repoPath}}&page=editor&open_in_editor=samples/{{file}},samples/README.md) | 64 | {{/each}} 65 | {{/if}} 66 | 67 | The [{{short_name}} {{display}} Client API Reference][client-docs] documentation 68 | also contains samples. 69 | 70 | ## Versioning 71 | 72 | This library follows [Semantic Versioning](http://semver.org/). 73 | 74 | {{#if_eq release_quality 'ga'}} 75 | This library is considered to be **General Availability (GA)**. This means it 76 | is stable; the code surface will not change in backwards-incompatible ways 77 | unless absolutely necessary (e.g. because of critical security issues) or with 78 | an extensive deprecation period. Issues and requests against **GA** libraries 79 | are addressed with the highest priority. 80 | {{/if_eq}} 81 | {{#if_eq release_quality 'beta'}} 82 | This library is considered to be in **beta**. This means it is expected to be 83 | mostly stable while we work toward a general availability release; however, 84 | complete stability is not guaranteed. We will address issues and requests 85 | against beta libraries with a high priority. 86 | {{/if_eq}} 87 | {{#if_eq release_quality 'alpha'}} 88 | This library is considered to be in **alpha**. This means it is still a 89 | work-in-progress and under active development. Any release is subject to 90 | backwards-incompatible changes at any time. 91 | {{/if_eq}} 92 | {{#if_eq release_quality 'deprecated'}} 93 | This library is **deprecated**. This means that it is no longer being 94 | actively maintained and the only updates the library will receive will 95 | be for critical security issues. {{#if deprecated}}{{deprecated}}{{/if}} 96 | {{/if_eq}} 97 | 98 | More Information: [Google Cloud Platform Launch Stages][launch_stages] 99 | 100 | [launch_stages]: https://cloud.google.com/terms/launch-stages 101 | 102 | ## Contributing 103 | 104 | Contributions welcome! See the [Contributing Guide](https://github.com{{repoPath}}/blob/master/.github/CONTRIBUTING.md). 105 | 106 | ## License 107 | 108 | Apache Version 2.0 109 | 110 | See [LICENSE](https://github.com{{repoPath}}/blob/master/LICENSE) 111 | 112 | ## What's Next 113 | 114 | * [{{short_name}} Documentation][product-docs] 115 | * [{{short_name}} {{display}} Client API Reference][client-docs] 116 | * [github.com{{repoPath}}](https://github.com{{repoPath}}) 117 | 118 | Read more about the client libraries for Cloud APIs, including the older 119 | Google APIs Client Libraries, in [Client Libraries Explained][explained]. 120 | 121 | [explained]: https://cloud.google.com/apis/docs/client-libraries-explained 122 | 123 | [client-docs]: {{client_reference_url}} 124 | [product-docs]: {{docs_url}} 125 | [shell_img]: https://gstatic.com/cloudssh/images/open-btn.png 126 | [projects]: https://console.cloud.google.com/project 127 | [billing]: https://support.google.com/cloud/answer/6293499#enable-billing 128 | [enable_api]: https://console.cloud.google.com/flows/enableapi?apiid={{api_id}} 129 | [auth]: https://cloud.google.com/docs/authentication/getting-started 130 | -------------------------------------------------------------------------------- /templates/lib_samples_readme.tpl: -------------------------------------------------------------------------------- 1 | [//]: # "This README.md file is auto-generated, all changes to this file will be lost." 2 | [//]: # "To regenerate it, use `npm run generate-scaffolding`." 3 | Google Cloud Platform logo 4 | 5 | # {{name}}: {{display}} Samples 6 | 7 | [![Open in Cloud Shell][shell_img]][shell_link] 8 | 9 | {{description}} 10 | 11 | ## Table of Contents 12 | 13 | * [Before you begin](#before-you-begin) 14 | * [Samples](#samples) 15 | {{#each samples}} 16 | * [{{name}}](#{{slugify name}}) 17 | {{/each}} 18 | 19 | ## Before you begin 20 | 21 | Before running the samples, make sure you've followed the steps in the 22 | [Before you begin section](../README.md#before-you-begin) of the client 23 | library's README. 24 | 25 | ## Samples 26 | {{#each samples}} 27 | 28 | ### {{name}} 29 | {{#if body}} 30 | {{body}}{{else}} 31 | {{#if ref}} 32 | 33 | View the [README]({{ref}}). 34 | 35 | {{else}} 36 | View the [source code][{{id}}_{{@index}}_code]. 37 | 38 | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com{{../repoPath}}&page=editor&open_in_editor=samples/{{file}},samples/README.md){{#if description}} 39 | 40 | {{{description}}}{{/if}}{{#if usage}} 41 | 42 | __Usage:__ `{{{usage.text}}}` 43 | {{/if}} 44 | {{#if help}} 45 | 46 | ``` 47 | {{{trim help}}} 48 | ``` 49 | 50 | {{/if}} 51 | [{{id}}_{{@index}}_docs]: {{docs_link}} 52 | [{{id}}_{{@index}}_code]: {{file}}{{/if}}{{/if}} 53 | {{/each}} 54 | 55 | [shell_img]: https://gstatic.com/cloudssh/images/open-btn.png 56 | [shell_link]: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com{{repoPath}}&page=editor&open_in_editor=samples/README.md 57 | -------------------------------------------------------------------------------- /templates/license.tpl: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /templates/nodejs/contributing.tpl: -------------------------------------------------------------------------------- 1 | # How to become a contributor and submit your own code 2 | 3 | **Table of contents** 4 | 5 | * [Contributor License Agreements](#contributor-license-agreements) 6 | * [Contributing a patch](#contributing-a-patch) 7 | * [Running the tests](#running-the-tests) 8 | * [Releasing the library](#releasing-the-library) 9 | 10 | ## Contributor License Agreements 11 | 12 | We'd love to accept your sample apps and patches! Before we can take them, we 13 | have to jump a couple of legal hurdles. 14 | 15 | Please fill out either the individual or corporate Contributor License Agreement 16 | (CLA). 17 | 18 | * If you are an individual writing original source code and you're sure you 19 | own the intellectual property, then you'll need to sign an [individual CLA] 20 | (https://developers.google.com/open-source/cla/individual). 21 | * If you work for a company that wants to allow you to contribute your work, 22 | then you'll need to sign a [corporate CLA] 23 | (https://developers.google.com/open-source/cla/corporate). 24 | 25 | Follow either of the two links above to access the appropriate CLA and 26 | instructions for how to sign and return it. Once we receive it, we'll be able to 27 | accept your pull requests. 28 | 29 | ## Contributing A Patch 30 | 31 | 1. Submit an issue describing your proposed change to the repo in question. 32 | 1. The repo owner will respond to your issue promptly. 33 | 1. If your proposed change is accepted, and you haven't already done so, sign a 34 | Contributor License Agreement (see details above). 35 | 1. Fork the desired repo, develop and test your code changes. 36 | 1. Ensure that your code adheres to the existing style in the code to which 37 | you are contributing. 38 | 1. Ensure that your code has an appropriate set of tests which all pass. 39 | 1. Submit a pull request. 40 | 41 | ## Running the tests 42 | 43 | 1. [Prepare your environment for Node.js setup][setup]. 44 | 45 | 1. Install dependencies: 46 | 47 | npm install 48 | 49 | 1. Run the tests: 50 | 51 | npm test 52 | 53 | [setup]: https://cloud.google.com/nodejs/docs/setup 54 | -------------------------------------------------------------------------------- /templates/nodejs/eslintignore.tpl: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | src/**/doc/* 3 | build/ 4 | -------------------------------------------------------------------------------- /templates/nodejs/eslintrc.tpl: -------------------------------------------------------------------------------- 1 | --- 2 | extends: 3 | - 'eslint:recommended' 4 | - 'plugin:node/recommended' 5 | - prettier 6 | plugins: 7 | - node 8 | - prettier 9 | rules: 10 | prettier/prettier: error 11 | block-scoped-var: error 12 | eqeqeq: error 13 | no-warning-comments: warn 14 | no-var: error 15 | prefer-const: error 16 | -------------------------------------------------------------------------------- /templates/nodejs/eslintrc_samples.tpl: -------------------------------------------------------------------------------- 1 | --- 2 | rules: 3 | no-console: off 4 | -------------------------------------------------------------------------------- /templates/nodejs/eslintrc_samples_test.tpl: -------------------------------------------------------------------------------- 1 | --- 2 | rules: 3 | node/no-unpublished-require: off 4 | no-empty: off 5 | -------------------------------------------------------------------------------- /templates/nodejs/eslintrc_systemtest.tpl: -------------------------------------------------------------------------------- 1 | --- 2 | env: 3 | mocha: true 4 | rules: 5 | node/no-unpublished-require: off 6 | no-console: off 7 | -------------------------------------------------------------------------------- /templates/nodejs/eslintrc_test.tpl: -------------------------------------------------------------------------------- 1 | --- 2 | env: 3 | mocha: true 4 | rules: 5 | node/no-unpublished-require: off 6 | -------------------------------------------------------------------------------- /templates/nodejs/gitignore.tpl: -------------------------------------------------------------------------------- 1 | **/*.log 2 | **/node_modules 3 | .coverage 4 | .nyc_output 5 | docs/ 6 | out/ 7 | build/ 8 | system-test/secrets.js 9 | system-test/*key.json 10 | *.lock 11 | .DS_Store 12 | google-cloud-logging-winston-*.tgz 13 | google-cloud-logging-bunyan-*.tgz 14 | -------------------------------------------------------------------------------- /templates/nodejs/issue_template.tpl: -------------------------------------------------------------------------------- 1 | Thanks for stopping by to let us know something could be better! 2 | 3 | Please run down the following list and make sure you've tried the usual "quick 4 | fixes": 5 | 6 | - Search the issues already opened: https://github.com{{repoPath}}/issues 7 | - Search the issues on our "catch-all" repository: https://github.com/GoogleCloudPlatform/google-cloud-node 8 | - Search StackOverflow: http://stackoverflow.com/questions/tagged/google-cloud-platform+node.js 9 | 10 | If you are still having issues, please be sure to include as much information as 11 | possible: 12 | 13 | #### Environment details 14 | 15 | - OS: 16 | - Node.js version: 17 | - npm version: 18 | - `{{lib_pkg_name}}` version: 19 | 20 | #### Steps to reproduce 21 | 22 | 1. ??? 23 | 2. ??? 24 | 25 | Following these steps will guarantee the quickest resolution possible. 26 | 27 | Thanks! 28 | -------------------------------------------------------------------------------- /templates/nodejs/jsdoc.tpl: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright 2018 Google LLC. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | module.exports = { 20 | opts: { 21 | readme: './README.md', 22 | package: './package.json', 23 | template: './node_modules/ink-docstrap/template', 24 | recurse: true, 25 | verbose: true, 26 | destination: './docs/' 27 | }, 28 | plugins: [ 29 | 'plugins/markdown' 30 | ], 31 | source: { 32 | excludePattern: '(^|\\/|\\\\)[._]', 33 | include: [ 34 | 'src' 35 | ], 36 | includePattern: '\\.js$' 37 | }, 38 | templates: { 39 | copyright: 'Copyright 2018 Google, LLC.', 40 | includeDate: false, 41 | sourceFiles: false, 42 | systemName: '{{lib_pkg_name}}', 43 | theme: 'lumen' 44 | } 45 | }; 46 | -------------------------------------------------------------------------------- /templates/nodejs/nycrc.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "report-dir": "./.coverage", 3 | "exclude": [ 4 | "src/*{/*,/**/*}.js", 5 | "src/*/v*/*.js", 6 | "test/**/*.js" 7 | ], 8 | "watermarks": { 9 | "branches": [ 10 | 95, 11 | 100 12 | ], 13 | "functions": [ 14 | 95, 15 | 100 16 | ], 17 | "lines": [ 18 | 95, 19 | 100 20 | ], 21 | "statements": [ 22 | 95, 23 | 100 24 | ] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /templates/nodejs/pkgjson.tpl: -------------------------------------------------------------------------------- 1 | {{{formattedPkgjson}}} 2 | -------------------------------------------------------------------------------- /templates/nodejs/prettierignore.tpl: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | samples/node_modules/* 3 | src/**/doc/* 4 | -------------------------------------------------------------------------------- /templates/nodejs/prettierrc.tpl: -------------------------------------------------------------------------------- 1 | --- 2 | bracketSpacing: false 3 | printWidth: 80 4 | semi: true 5 | singleQuote: true 6 | tabWidth: 2 7 | trailingComma: es5 8 | useTabs: false 9 | -------------------------------------------------------------------------------- /templates/pr_template.tpl: -------------------------------------------------------------------------------- 1 | Fixes # (it's a good idea to open an issue first for discussion) 2 | 3 | - [ ] Tests and linter pass 4 | - [ ] Code coverage does not decrease (if any source code was changed) 5 | - [ ] Appropriate docs were updated (if necessary) 6 | -------------------------------------------------------------------------------- /templates/python/gitignore.tpl: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | *.sw[op] 3 | 4 | # C extensions 5 | *.so 6 | 7 | # Packages 8 | *.egg 9 | *.egg-info 10 | dist 11 | build 12 | eggs 13 | parts 14 | bin 15 | var 16 | sdist 17 | develop-eggs 18 | .installed.cfg 19 | lib 20 | lib64 21 | __pycache__ 22 | 23 | # Installer logs 24 | pip-log.txt 25 | 26 | # Unit test / coverage reports 27 | .coverage 28 | .nox 29 | .tox 30 | .cache 31 | htmlcov 32 | 33 | # Translations 34 | *.mo 35 | 36 | # Mac 37 | .DS_Store 38 | 39 | # Mr Developer 40 | .mr.developer.cfg 41 | .project 42 | .pydevproject 43 | 44 | # JetBrains 45 | .idea 46 | 47 | # Built documentation 48 | docs/_build 49 | docs/_build_doc2dash 50 | 51 | # Virtual environment 52 | env/ 53 | coverage.xml 54 | 55 | # System test environment variables. 56 | system_tests/local_test_setup 57 | 58 | # Make sure a generated file isn't accidentally committed. 59 | pylintrc 60 | pylintrc.test 61 | 62 | # Directories used for creating generated PB2 files 63 | generated_python/ 64 | cloud-bigtable-client/ 65 | googleapis-pb/ 66 | grpc_python_venv/ 67 | -------------------------------------------------------------------------------- /templates/python/lib_readme.tpl: -------------------------------------------------------------------------------- 1 | .. image:: https://avatars2.githubusercontent.com/u/2810941?v=3&s=96 2 | :height: 96px 3 | :width: 96px 4 | :alt: Google Cloud Platform logo 5 | :align: right 6 | 7 | `{{name}}: {{display}} Client `__ 8 | ========================================================================= 9 | 10 | |release level| |CircleCI| |AppVeyor| |codecov| 11 | 12 | {{display}} idiomatic client for 13 | `{{short_name}} <{{docs_url}}>`__. 14 | 15 | {{description}} 16 | 17 | {{#if deprecated}} 18 | \| :warning: Deprecated Module \| 19 | \| — \| 20 | \| This library is **deprecated**. {{deprecated}} \| 21 | {{/if}} 22 | 23 | - `{{short_name}} {{display}} Client API Reference <{{client_reference_url}}>`__ 24 | - `github.com{{repoPath}} `__ 25 | - `{{short_name}} Documentation <{{docs_url}}>`__ 26 | 27 | Read more about the client libraries for Cloud APIs, including the older 28 | Google APIs Client Libraries, in `Client Libraries 29 | Explained `__. 30 | 31 | **Table of contents:** 32 | 33 | - `Quickstart <#quickstart>`__ 34 | 35 | - `Before you begin <#before-you-begin>`__ 36 | - `Installing the client library <#installing-the-client-library>`__ 37 | - `Using the client library <#using-the-client-library>`__ 38 | 39 | {{#if samples.length}} 40 | - `Samples <#samples>`__ 41 | {{/if}} 42 | - `Versioning <#versioning>`__ 43 | - `Contributing <#contributing>`__ 44 | - `License <#license>`__ 45 | 46 | Quickstart 47 | ---------- 48 | 49 | Before you begin 50 | ~~~~~~~~~~~~~~~~ 51 | 52 | 1. Select or create a Cloud Platform project. 53 | 54 | `Go to the projects page`_ 55 | 56 | {{#unless suppress_billing}} 57 | 1. Enable billing for your project. 58 | 59 | `Enable billing`_ 60 | {{/unless}} 61 | 62 | {{#if api_id}} 63 | 1. Enable the {{name}} API. 64 | 65 | `Enable the API`_ 66 | {{/if}} 67 | 68 | 1. `Set up authentication with a service account`_ so you 69 | can access the API from your local workstation. 70 | 71 | .. _Go to the projects page: https://console.cloud.google.com/project 72 | .. _Enable billing: https://support.google.com/cloud/answer/6293499#enable-billing 73 | .. _Enable the API: https://console.cloud.google.com/flows/enableapi?apiid={{api_id}} 74 | .. _Set up authentication with a service account: https://cloud.google.com/docs/authentication/getting-started 75 | 76 | 77 | Installing the client library 78 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 79 | 80 | {{lib_install_cmd}} 81 | 82 | .. note:: 83 | 84 | We highly recommend that you install this library in a 85 | `virtualenv `_. 86 | 87 | {{#if quickstart}} 88 | 89 | Using the client library 90 | ~~~~~~~~~~~~~~~~~~~~~~~~ 91 | 92 | .. code:: {{syntax_highlighting_ext}} 93 | 94 | {{{quickstart}}} 95 | {{/if}} 96 | 97 | {{#if samples.length}} 98 | 99 | Samples 100 | ~~~~~~~ 101 | 102 | Samples are in the `samples\ `__ 103 | directory. The samples’ ``README.md`` has instructions for running the 104 | samples. 105 | 106 | +--------+-------------+--------+ 107 | | Sample | Source Code | Try it | 108 | +========+=============+========+ 109 | +--------+-------------+--------+ 110 | 111 | {{#each samples}} 112 | \| {{name}} \| `source code `__ \| |Open in Cloud Shell {{name}}| \| 113 | 114 | .. |Open in Cloud Shell {{name}}| image:: https://gstatic.com/cloudssh/images/open-btn.png 115 | :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com{{../repoPath}}&page=editor&open_in_editor=samples/{{file}},samples/README.md 116 | 117 | {{/each}} 118 | {{/if}} 119 | 120 | The `{{short_name}} {{display}} Client API 121 | Reference <{{client_reference_url}}>`__ documentation also 122 | contains samples. 123 | 124 | Versioning 125 | ---------- 126 | 127 | This library follows `Semantic Versioning `__. 128 | 129 | {{#if_eq release_quality ‘ga’}} 130 | This library is considered to be 131 | **General Availability (GA)**. This means it is stable; the code surface 132 | will not change in backwards-incompatible ways unless absolutely 133 | necessary (e.g. because of critical security issues) or with an 134 | extensive deprecation period. Issues and requests against **GA** 135 | libraries are addressed with the highest priority. 136 | {{/if_eq}} 137 | {{#if_eq release_quality ‘beta’}} 138 | This library is considered to be in **beta**. 139 | This means it is expected to be mostly stable while we work toward a 140 | general availability release; however, complete stability is not 141 | guaranteed. We will address issues and requests against beta libraries 142 | with a high priority. 143 | {{/if_eq}} 144 | {{#if_eq release_quality ‘alpha’}} 145 | This library is considered to be in **alpha**. This means it is still a 146 | work-in-progress and under active development. Any release is subject to 147 | backwards-incompatible changes at any time. 148 | {{/if_eq}} 149 | {{#if_eq release_quality ‘deprecated’}} This library is **deprecated**. This means that it is no longer being actively maintained and the only 150 | updates the library will receive will be for critical security issues.{{#if deprecated}}{{deprecated}}{{/if}} 151 | {{/if_eq}} 152 | 153 | More Information: `Google Cloud Platform Launch 154 | Stages `__ 155 | 156 | Contributing 157 | ------------ 158 | 159 | Contributions welcome! See the `Contributing 160 | Guide `__. 161 | 162 | License 163 | ------- 164 | 165 | Apache Version 2.0 166 | 167 | See 168 | `LICENSE `__ 169 | 170 | 171 | .. |release level| image:: https://img.shields.io/badge/release%20level-general%20availability%20%28GA%29-brightgreen.svg?style=flat 172 | :target: https://cloud.google.com/terms/launch-stages 173 | .. |CircleCI| image:: https://img.shields.io/circleci/project/github{{repoPath}}.svg?style=flat 174 | :target: https://circleci.com/gh{{repoPath}} 175 | .. |AppVeyor| image:: https://ci.appveyor.com/api/projects/status/github{{repoPath}}?branch=master&svg=true 176 | :target: https://ci.appveyor.com/project{{repoPath}} 177 | .. |codecov| image:: https://img.shields.io/codecov/c/github{{repoPath}}/master.svg?style=flat 178 | :target: https://codecov.io/gh{{repoPath}} 179 | -------------------------------------------------------------------------------- /templates/python/lib_samples_readme.tpl: -------------------------------------------------------------------------------- 1 | .. image:: https://avatars2.githubusercontent.com/u/2810941?v=3&s=96 2 | :height: 96px 3 | :width: 96px 4 | :alt: Google Cloud Platform logo 5 | :align: right 6 | 7 | {{name}}: {{display}} Samples 8 | ============================= 9 | 10 | |Open in Cloud Shell| 11 | 12 | {{description}} 13 | 14 | Table of Contents 15 | ----------------- 16 | 17 | - `Before you begin <#before-you-begin>`__ 18 | - `Samples <#samples>`__ 19 | {{#each samples}} 20 | - `{{name}} <#{{slugify name}}>`__ 21 | {{/each}} 22 | 23 | Before you begin 24 | ---------------- 25 | 26 | Before running the samples, make sure you’ve followed the steps in the 27 | `Before you begin section <../README.md#before-you-begin>`__ of the 28 | client library’s README. 29 | 30 | Samples 31 | ------- 32 | 33 | {{#each samples}} 34 | 35 | {{name}} 36 | ~~~~~~~~ 37 | {{#if body}} 38 | {{body}}{{else}} 39 | {{#if ref}} 40 | 41 | View the `README <{{ref}}>`__. 42 | 43 | {{else}} 44 | View the `source code <{{file}}>`__. 45 | 46 | |Open in Cloud Shell {{name}}| 47 | 48 | .. |Open in Cloud Shell {{name}}| image:: //gstatic.com/cloudssh/images/open-btn.png 49 | :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com{{../repoPath}}&page=editor&open_in_editor=samples/{{file}},samples/README.md 50 | 51 | 52 | {{#if description}} 53 | 54 | {{{description}}}{{/if}}{{#if usage}} 55 | 56 | **Usage:** ``{{{usage.text}}}`` 57 | {{/if}} 58 | {{#if help}} 59 | 60 | :: 61 | 62 | {{{trim help}}} 63 | 64 | {{/if}} 65 | {{/if}} 66 | {{/if}} 67 | {{/each}} 68 | 69 | .. |Open in Cloud Shell| image:: https://gstatic.com/cloudssh/images/open-btn.png 70 | :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com{{repoPath}}&page=editor&open_in_editor=samples/README.md 71 | -------------------------------------------------------------------------------- /templates/samples_readme.tpl: -------------------------------------------------------------------------------- 1 | Google Cloud Platform logo 2 | 3 | # {{name}}: {{display}} Samples 4 | 5 | [![Build](https://storage.googleapis.com/{{badgeUri}}.svg)]() 6 | 7 | {{description}} 8 | 9 | ## Table of Contents 10 | 11 | * [Setup](#setup) 12 | * [Samples](#samples) 13 | {{#each samples}} 14 | * [{{name}}](#{{slugify name}}) 15 | {{/each}} 16 | * [Running the tests](#running-the-tests) 17 | 18 | ## Setup 19 | {{readme.setup}} 20 | 21 | ## Samples 22 | {{#each samples}} 23 | 24 | ### {{name}} 25 | {{#if body}} 26 | {{body}}{{else}} 27 | {{#if ref}} 28 | 29 | View the [README]({{ref}}). 30 | 31 | {{else}} 32 | View the [documentation][{{id}}_{{@index}}_docs] or the [source code][{{id}}_{{@index}}_code].{{#if description}} 33 | 34 | {{{description}}}{{/if}}{{#if usage}} 35 | 36 | __Usage:__ `{{{usage.text}}}` 37 | {{/if}} 38 | {{#if help}} 39 | 40 | ``` 41 | {{{trim help}}} 42 | ``` 43 | 44 | {{/if}} 45 | [{{id}}_{{@index}}_docs]: {{docs_link}} 46 | [{{id}}_{{@index}}_code]: {{file}}{{/if}}{{/if}} 47 | {{/each}} 48 | 49 | ## Running the tests 50 | {{readme.tests}} 51 | -------------------------------------------------------------------------------- /test/cli/nodejs/bump.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017, Google, Inc. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | const fs = require('fs'); 17 | const path = require('path'); 18 | const test = require('ava'); 19 | 20 | const toolsPath = path.join(__dirname, '../../../'); 21 | const tools = require(toolsPath); 22 | 23 | const binPath = path.join(toolsPath, 'bin/tools'); 24 | const samplePath = path.join(toolsPath, 'test/samples/nodejs/bump'); 25 | const appName = 'test-samples-nodejs-app'; 26 | 27 | function setVersion(version, withSamples) { 28 | let packageJson = JSON.parse( 29 | fs.readFileSync(path.join(samplePath, 'package.json.default')) 30 | ); 31 | packageJson['version'] = version; 32 | packageJson['name'] = appName; 33 | fs.writeFileSync( 34 | path.join(samplePath, 'package.json'), 35 | JSON.stringify(packageJson, null, ' ') 36 | ); 37 | 38 | if (withSamples) { 39 | let samplesPackageJson = JSON.parse( 40 | fs.readFileSync(path.join(samplePath, 'samples', 'package.json.default')) 41 | ); 42 | samplesPackageJson['dependencies'][appName] = version; 43 | fs.writeFileSync( 44 | path.join(samplePath, 'samples', 'package.json'), 45 | JSON.stringify(samplesPackageJson, null, ' ') 46 | ); 47 | } 48 | } 49 | 50 | test.serial('should do a dry run bump patch with samples', async t => { 51 | setVersion('1.2.3', true); 52 | const output = await tools.runAsync( 53 | `${binPath} bump patch --dry-run`, 54 | samplePath 55 | ); 56 | 57 | t.regex(output, new RegExp(`bump: Beginning dry run.`)); 58 | t.regex( 59 | output, 60 | new RegExp(`bump: Version will be bumped from 1.2.3 to 1.2.4.`) 61 | ); 62 | t.regex( 63 | output, 64 | new RegExp(`bump: Version in package.json will be set to 1.2.4.`) 65 | ); 66 | t.regex( 67 | output, 68 | new RegExp( 69 | `bump: samples/package.json will depend on '${appName}': '1.2.4'.` 70 | ) 71 | ); 72 | t.regex(output, new RegExp(`bump: Dry run complete.`)); 73 | 74 | // dry run should not change version 75 | let packageJson = JSON.parse( 76 | fs.readFileSync(path.join(samplePath, 'package.json')) 77 | ); 78 | let samplesPackageJson = JSON.parse( 79 | fs.readFileSync(path.join(samplePath, 'samples', 'package.json')) 80 | ); 81 | t.deepEqual('1.2.3', packageJson['version']); 82 | t.deepEqual('1.2.3', samplesPackageJson['dependencies'][appName]); 83 | }); 84 | 85 | test.serial('should do a dry run bump minor with samples', async t => { 86 | setVersion('1.2.3', true); 87 | const output = await tools.runAsync( 88 | `${binPath} bump minor --dry-run`, 89 | samplePath 90 | ); 91 | 92 | t.regex(output, new RegExp(`bump: Beginning dry run.`)); 93 | t.regex( 94 | output, 95 | new RegExp(`bump: Version will be bumped from 1.2.3 to 1.3.0.`) 96 | ); 97 | t.regex( 98 | output, 99 | new RegExp(`bump: Version in package.json will be set to 1.3.0.`) 100 | ); 101 | t.regex( 102 | output, 103 | new RegExp( 104 | `bump: samples/package.json will depend on '${appName}': '1.3.0'.` 105 | ) 106 | ); 107 | t.regex(output, new RegExp(`bump: Dry run complete.`)); 108 | 109 | // dry run should not change version 110 | let packageJson = JSON.parse( 111 | fs.readFileSync(path.join(samplePath, 'package.json')) 112 | ); 113 | let samplesPackageJson = JSON.parse( 114 | fs.readFileSync(path.join(samplePath, 'samples', 'package.json')) 115 | ); 116 | t.deepEqual('1.2.3', packageJson['version']); 117 | t.deepEqual('1.2.3', samplesPackageJson['dependencies'][appName]); 118 | }); 119 | 120 | test.serial('should do a dry run bump major with samples', async t => { 121 | setVersion('1.2.3', true); 122 | const output = await tools.runAsync( 123 | `${binPath} bump major --dry-run`, 124 | samplePath 125 | ); 126 | 127 | t.regex(output, new RegExp(`bump: Beginning dry run.`)); 128 | t.regex( 129 | output, 130 | new RegExp(`bump: Version will be bumped from 1.2.3 to 2.0.0.`) 131 | ); 132 | t.regex( 133 | output, 134 | new RegExp(`bump: Version in package.json will be set to 2.0.0.`) 135 | ); 136 | t.regex( 137 | output, 138 | new RegExp( 139 | `bump: samples/package.json will depend on '${appName}': '2.0.0'.` 140 | ) 141 | ); 142 | t.regex(output, new RegExp(`bump: Dry run complete.`)); 143 | 144 | // dry run should not change version 145 | let packageJson = JSON.parse( 146 | fs.readFileSync(path.join(samplePath, 'package.json')) 147 | ); 148 | let samplesPackageJson = JSON.parse( 149 | fs.readFileSync(path.join(samplePath, 'samples', 'package.json')) 150 | ); 151 | t.deepEqual('1.2.3', packageJson['version']); 152 | t.deepEqual('1.2.3', samplesPackageJson['dependencies'][appName]); 153 | }); 154 | 155 | test.serial('should bump patch with samples', async t => { 156 | setVersion('1.2.3', true); 157 | const output = await tools.runAsync(`${binPath} bump patch`, samplePath); 158 | 159 | t.regex( 160 | output, 161 | new RegExp(`bump: Version will be bumped from 1.2.3 to 1.2.4.`) 162 | ); 163 | t.regex( 164 | output, 165 | new RegExp(`bump: Version in package.json will be set to 1.2.4.`) 166 | ); 167 | t.regex( 168 | output, 169 | new RegExp( 170 | `bump: samples/package.json will depend on '${appName}': '1.2.4'.` 171 | ) 172 | ); 173 | 174 | let packageJson = JSON.parse( 175 | fs.readFileSync(path.join(samplePath, 'package.json')) 176 | ); 177 | let samplesPackageJson = JSON.parse( 178 | fs.readFileSync(path.join(samplePath, 'samples', 'package.json')) 179 | ); 180 | t.deepEqual('1.2.4', packageJson['version']); 181 | t.deepEqual('1.2.4', samplesPackageJson['dependencies'][appName]); 182 | }); 183 | 184 | test.serial('should bump minor with samples', async t => { 185 | setVersion('1.2.3', true); 186 | const output = await tools.runAsync(`${binPath} bump minor`, samplePath); 187 | 188 | t.regex( 189 | output, 190 | new RegExp(`bump: Version will be bumped from 1.2.3 to 1.3.0.`) 191 | ); 192 | t.regex( 193 | output, 194 | new RegExp(`bump: Version in package.json will be set to 1.3.0.`) 195 | ); 196 | t.regex( 197 | output, 198 | new RegExp( 199 | `bump: samples/package.json will depend on '${appName}': '1.3.0'.` 200 | ) 201 | ); 202 | 203 | let packageJson = JSON.parse( 204 | fs.readFileSync(path.join(samplePath, 'package.json')) 205 | ); 206 | let samplesPackageJson = JSON.parse( 207 | fs.readFileSync(path.join(samplePath, 'samples', 'package.json')) 208 | ); 209 | t.deepEqual('1.3.0', packageJson['version']); 210 | t.deepEqual('1.3.0', samplesPackageJson['dependencies'][appName]); 211 | }); 212 | 213 | test.serial('should bump major with samples', async t => { 214 | setVersion('1.2.3', true); 215 | const output = await tools.runAsync(`${binPath} bump major`, samplePath); 216 | 217 | t.regex( 218 | output, 219 | new RegExp(`bump: Version will be bumped from 1.2.3 to 2.0.0.`) 220 | ); 221 | t.regex( 222 | output, 223 | new RegExp(`bump: Version in package.json will be set to 2.0.0.`) 224 | ); 225 | t.regex( 226 | output, 227 | new RegExp( 228 | `bump: samples/package.json will depend on '${appName}': '2.0.0'.` 229 | ) 230 | ); 231 | 232 | let packageJson = JSON.parse( 233 | fs.readFileSync(path.join(samplePath, 'package.json')) 234 | ); 235 | let samplesPackageJson = JSON.parse( 236 | fs.readFileSync(path.join(samplePath, 'samples', 'package.json')) 237 | ); 238 | t.deepEqual('2.0.0', packageJson['version']); 239 | t.deepEqual('2.0.0', samplesPackageJson['dependencies'][appName]); 240 | }); 241 | 242 | test.serial('should work without samples', async t => { 243 | setVersion('1.2.3', false); 244 | const output = await tools.runAsync(`${binPath} bump major`, samplePath); 245 | 246 | t.regex( 247 | output, 248 | new RegExp(`bump: Version will be bumped from 1.2.3 to 2.0.0.`) 249 | ); 250 | t.regex( 251 | output, 252 | new RegExp(`bump: Version in package.json will be set to 2.0.0.`) 253 | ); 254 | 255 | let packageJson = JSON.parse( 256 | fs.readFileSync(path.join(samplePath, 'package.json')) 257 | ); 258 | t.deepEqual('2.0.0', packageJson['version']); 259 | }); 260 | -------------------------------------------------------------------------------- /test/cli/test_commands/nodejs/app.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017, Google, Inc. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | const fs = require('fs-extra'); 17 | const path = require('path'); 18 | const test = require('ava'); 19 | 20 | const toolsPath = path.join(__dirname, '../../../../'); 21 | const tools = require(toolsPath); 22 | 23 | const binPath = path.join(toolsPath, 'bin/tools'); 24 | const samplePath = path.join(toolsPath, 'test/samples/nodejs/app'); 25 | const NodejsBuildPack = require(toolsPath).buildPacks.NodejsBuildPack; 26 | const buildPack = new NodejsBuildPack(); 27 | 28 | // test install 29 | 30 | test.serial('should do a dry run install', async t => { 31 | const output = await tools.runAsync( 32 | `${binPath} test install --dry-run`, 33 | samplePath 34 | ); 35 | 36 | t.regex(output, new RegExp(`install: Beginning dry run.`)); 37 | t.regex( 38 | output, 39 | new RegExp(`install: Installing dependencies in: ${samplePath}`) 40 | ); 41 | t.regex( 42 | output, 43 | new RegExp( 44 | `install: Running: ${ 45 | buildPack.config.test.install.cmd 46 | } ${buildPack.config.test.install.args.join(' ')}` 47 | ) 48 | ); 49 | t.regex(output, new RegExp(`install: Dry run complete.`)); 50 | }); 51 | 52 | test.serial('should install with overrides', async t => { 53 | const cmd = 'npm'; 54 | const args = 'install --no-optional --dry-run'; 55 | 56 | await fs.remove(path.join(samplePath, 'node_modules')); 57 | const output = await tools.runAsync( 58 | `${binPath} test install --cmd=${cmd} -- ${args}`, 59 | samplePath 60 | ); 61 | 62 | t.regex( 63 | output, 64 | new RegExp(`install: Installing dependencies in: ${samplePath}`) 65 | ); 66 | t.regex(output, new RegExp(`install: Running: ${cmd} ${args}`)); 67 | t.regex(output, new RegExp(`install: Success!`)); 68 | }); 69 | 70 | test.serial('should install with defaults', async t => { 71 | await fs.remove(path.join(samplePath, 'node_modules')); 72 | const output = await tools.runAsync(`${binPath} test install`, samplePath); 73 | 74 | t.regex( 75 | output, 76 | new RegExp(`install: Installing dependencies in: ${samplePath}`) 77 | ); 78 | t.regex( 79 | output, 80 | new RegExp( 81 | `install: Running: ${ 82 | buildPack.config.test.install.cmd 83 | } ${buildPack.config.test.install.args.join(' ')}` 84 | ) 85 | ); 86 | t.regex(output, new RegExp(`install: Success!`)); 87 | }); 88 | 89 | // test run 90 | 91 | test.serial('should do a dry run test', async t => { 92 | const output = await tools.runAsync( 93 | `${binPath} test run --dry-run`, 94 | samplePath 95 | ); 96 | 97 | t.regex(output, new RegExp(`run: Beginning dry run.`)); 98 | t.regex(output, new RegExp(`run: Executing tests in: ${samplePath}`)); 99 | t.regex( 100 | output, 101 | new RegExp( 102 | `run: Running: ${ 103 | buildPack.config.test.run.cmd 104 | } ${buildPack.config.test.run.args.join(' ')}` 105 | ) 106 | ); 107 | t.regex(output, new RegExp(`run: Dry run complete.`)); 108 | }); 109 | 110 | test.serial('should run test with defaults', async t => { 111 | const results = await tools.spawnAsyncWithIO( 112 | binPath, 113 | ['test', 'run'], 114 | samplePath 115 | ); 116 | 117 | t.regex(results.output, new RegExp(`run: Executing tests in: ${samplePath}`)); 118 | t.regex( 119 | results.output, 120 | new RegExp( 121 | `run: Running: ${ 122 | buildPack.config.test.run.cmd 123 | } ${buildPack.config.test.run.args.join(' ')}` 124 | ) 125 | ); 126 | t.regex(results.output, new RegExp(`run: Success!`)); 127 | }); 128 | 129 | test.serial('should run test with overrides', async t => { 130 | const cmd = 'npm'; 131 | const results = await tools.spawnAsyncWithIO( 132 | binPath, 133 | ['test', 'run', `--cmd=${cmd}`, '--', 'run', 'test', '--foo="bar"'], 134 | samplePath 135 | ); 136 | 137 | t.regex(results.output, new RegExp(`run: Executing tests in: ${samplePath}`)); 138 | t.regex(results.output, new RegExp(`run: Running: npm run test --foo=bar`)); 139 | t.regex(results.output, new RegExp(`run: Success!`)); 140 | }); 141 | 142 | // test app 143 | 144 | test.serial('should do a dry run web app test', async t => { 145 | const results = await tools.spawnAsyncWithIO( 146 | binPath, 147 | ['test', 'app', '--dry-run'], 148 | samplePath 149 | ); 150 | 151 | t.regex(results.output, new RegExp(`app: Starting app in: ${samplePath}`)); 152 | t.regex(results.output, new RegExp(`app: Using port:`)); 153 | t.regex( 154 | results.output, 155 | new RegExp( 156 | `app: Running: ${ 157 | buildPack.config.test.app.cmd 158 | } ${buildPack.config.test.app.args.join(' ')}` 159 | ) 160 | ); 161 | t.regex(results.output, new RegExp(`app: Verifying: http://localhost:`)); 162 | t.regex(results.output, new RegExp(`app: Dry run complete.`)); 163 | }); 164 | 165 | test.serial('should test web app with defaults', async t => { 166 | const results = await tools.spawnAsyncWithIO( 167 | binPath, 168 | ['test', 'app'], 169 | samplePath 170 | ); 171 | 172 | t.regex(results.output, new RegExp(`app: Starting app in: ${samplePath}`)); 173 | t.regex(results.output, new RegExp(`app: Using port:`)); 174 | t.regex( 175 | results.output, 176 | new RegExp( 177 | `app: Running: ${ 178 | buildPack.config.test.app.cmd 179 | } ${buildPack.config.test.app.args.join(' ')}` 180 | ) 181 | ); 182 | t.regex(results.output, new RegExp(`app: Verifying: http://localhost:`)); 183 | t.regex(results.output, new RegExp(`app: Success!`)); 184 | }); 185 | 186 | test.serial('should test web app with overrides', async t => { 187 | const cmd = 'node'; 188 | const results = await tools.spawnAsyncWithIO( 189 | binPath, 190 | ['test', 'app', `--cmd=${cmd}`, '--', 'app.js', '--foo', '"bar"'], 191 | samplePath 192 | ); 193 | 194 | t.regex(results.output, new RegExp(`app: Starting app in: ${samplePath}`)); 195 | t.regex(results.output, new RegExp(`app: Using port:`)); 196 | t.regex(results.output, new RegExp(`app: Running: node app.js --foo bar`)); 197 | t.regex(results.output, new RegExp(`app: Verifying: http://localhost:`)); 198 | t.regex(results.output, new RegExp(`app: Success!`)); 199 | }); 200 | 201 | // test build 202 | 203 | test.serial('should do a dry run build with defaults', async t => { 204 | const results = await tools.spawnAsyncWithIO( 205 | binPath, 206 | [ 207 | 'test', 208 | 'build', 209 | '--dry-run', 210 | '--config', 211 | 'package.json', 212 | '--config-key', 213 | 'cloud-repo-tools', 214 | ], 215 | samplePath 216 | ); 217 | let keyFilePath = process.env.GOOGLE_APPLICATION_CREDENTIALS; 218 | const output = results.output; 219 | 220 | if (keyFilePath) { 221 | keyFilePath = path.parse(keyFilePath).base; 222 | } 223 | 224 | t.regex( 225 | output, 226 | new RegExp( 227 | `build: Detected repository: /GoogleCloudPlatform/nodejs-repo-tools` 228 | ) 229 | ); 230 | t.regex(output, new RegExp(`build: Detected SHA:`)); 231 | if (process.env.GOOGLE_APPLICATION_CREDENTIALS) { 232 | t.regex( 233 | output, 234 | new RegExp( 235 | `build: Copying: ${path.resolve( 236 | process.env.GOOGLE_APPLICATION_CREDENTIALS 237 | )}` 238 | ) 239 | ); 240 | } 241 | if (process.env.GCLOUD_PROJECT || process.env.GOOGLE_CLOUD_PROJECT) { 242 | t.regex( 243 | output, 244 | new RegExp( 245 | `build: Setting build project ID to: ${process.env.GCLOUD_PROJECT || 246 | process.env.GOOGLE_CLOUD_PROJECT}` 247 | ) 248 | ); 249 | } 250 | t.regex( 251 | output, 252 | new RegExp( 253 | `build: Compiling: ${path.join(samplePath, 'repo-tools-cloudbuild.yaml')}` 254 | ) 255 | ); 256 | t.regex( 257 | output, 258 | new RegExp( 259 | `build: Printing: ${path.join(samplePath, 'repo-tools-cloudbuild.yaml')}` 260 | ) 261 | ); 262 | t.regex(output, new RegExp(`steps:`)); 263 | t.is(output.includes(`- name: 'gcr.io/$PROJECT_ID/nodejs'`), true); 264 | t.is(output.includes(` env: [`), true); 265 | t.regex(output, new RegExp(` 'CLOUD_BUILD=true',`)); 266 | t.regex(output, new RegExp(` 'SHA=`)); 267 | t.regex( 268 | output, 269 | new RegExp(` 'REPO_PATH=/GoogleCloudPlatform/nodejs-repo-tools',`) 270 | ); 271 | t.regex(output, new RegExp(` 'CI=`)); 272 | t.regex(output, new RegExp(` 'CONTEXT=test-samples-nodejs-app',`)); 273 | t.regex( 274 | output, 275 | new RegExp(` 'GOOGLE_APPLICATION_CREDENTIALS=${keyFilePath || ''}',`) 276 | ); 277 | t.regex( 278 | output, 279 | new RegExp( 280 | ` 'GCLOUD_PROJECT=${process.env.GCLOUD_PROJECT || 281 | process.env.GOOGLE_CLOUD_PROJECT || 282 | ''}',` 283 | ) 284 | ); 285 | t.regex( 286 | output, 287 | new RegExp( 288 | ` 'GOOGLE_CLOUD_PROJECT=${process.env.GCLOUD_PROJECT || 289 | process.env.GOOGLE_CLOUD_PROJECT || 290 | ''}'` 291 | ) 292 | ); 293 | t.is(output.includes(` ]`), true); 294 | t.regex(output, new RegExp(` entrypoint: 'samples'`)); 295 | t.is( 296 | output.includes( 297 | ` args: ['test', 'install', '--cmd', 'npm', '--', 'install']` 298 | ), 299 | true 300 | ); 301 | t.is(output.includes(`- name: 'gcr.io/$PROJECT_ID/nodejs'`), true); 302 | t.is(output.includes(` env: [`), true); 303 | t.regex(output, new RegExp(` 'CLOUD_BUILD=true',`)); 304 | t.regex( 305 | output, 306 | new RegExp(` 'GOOGLE_APPLICATION_CREDENTIALS=${keyFilePath || ''}',`) 307 | ); 308 | t.regex( 309 | output, 310 | new RegExp( 311 | ` 'GCLOUD_PROJECT=${process.env.GCLOUD_PROJECT || 312 | process.env.GOOGLE_CLOUD_PROJECT || 313 | ''}',` 314 | ) 315 | ); 316 | t.regex( 317 | output, 318 | new RegExp( 319 | ` 'GOOGLE_CLOUD_PROJECT=${process.env.GCLOUD_PROJECT || 320 | process.env.GOOGLE_CLOUD_PROJECT || 321 | ''}'` 322 | ) 323 | ); 324 | t.is(output.includes(` ]`), true); 325 | t.regex(output, new RegExp(` entrypoint: 'samples'`)); 326 | t.is( 327 | output.includes(` args: ['test', 'run', '--cmd', 'npm', '--', 'test']`), 328 | true 329 | ); 330 | t.regex(output, new RegExp(`build: Dry run complete.`)); 331 | }); 332 | -------------------------------------------------------------------------------- /test/cli/test_commands/nodejs/snippet.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017, Google, Inc. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | const fs = require('fs-extra'); 17 | const path = require('path'); 18 | const test = require('ava'); 19 | 20 | const toolsPath = path.join(__dirname, '../../../../'); 21 | const tools = require(toolsPath); 22 | 23 | const binPath = path.join(toolsPath, 'bin/tools'); 24 | const samplePath = path.join(toolsPath, 'test/samples/nodejs/snippet'); 25 | const NodejsBuildPack = require(toolsPath).buildPacks.NodejsBuildPack; 26 | const buildPack = new NodejsBuildPack(); 27 | 28 | test.serial('should do a dry run install', async t => { 29 | const output = await tools.runAsync( 30 | `${binPath} test install --dry-run`, 31 | samplePath 32 | ); 33 | 34 | t.regex(output, new RegExp(`install: Beginning dry run.`)); 35 | t.regex( 36 | output, 37 | new RegExp(`install: Installing dependencies in: ${samplePath}`) 38 | ); 39 | t.regex( 40 | output, 41 | new RegExp( 42 | `install: Running: ${ 43 | buildPack.config.test.install.cmd 44 | } ${buildPack.config.test.install.args.join(' ')}` 45 | ) 46 | ); 47 | t.regex(output, new RegExp(`install: Dry run complete.`)); 48 | }); 49 | 50 | test.serial('should install with overrides', async t => { 51 | const cmd = 'npm'; 52 | const args = 'install --no-optional --dry-run'; 53 | 54 | await fs.remove(path.join(samplePath, 'node_modules')); 55 | const output = await tools.runAsync( 56 | `${binPath} test install --cmd=${cmd} -- ${args}`, 57 | samplePath 58 | ); 59 | 60 | t.regex( 61 | output, 62 | new RegExp(`install: Installing dependencies in: ${samplePath}`) 63 | ); 64 | t.regex(output, new RegExp(`install: Running: ${cmd} ${args}`)); 65 | t.regex(output, new RegExp(`install: Success!`)); 66 | }); 67 | 68 | test.serial('should install with defaults', async t => { 69 | await fs.remove(path.join(samplePath, 'node_modules')); 70 | const output = await tools.runAsync(`${binPath} test install`, samplePath); 71 | 72 | t.regex( 73 | output, 74 | new RegExp(`install: Installing dependencies in: ${samplePath}`) 75 | ); 76 | t.regex( 77 | output, 78 | new RegExp( 79 | `install: Running: ${ 80 | buildPack.config.test.install.cmd 81 | } ${buildPack.config.test.install.args.join(' ')}` 82 | ) 83 | ); 84 | t.regex(output, new RegExp(`install: Success!`)); 85 | }); 86 | 87 | test.serial('should do a dry run test', async t => { 88 | const output = await tools.runAsync( 89 | `${binPath} test run --dry-run`, 90 | samplePath 91 | ); 92 | 93 | t.regex(output, new RegExp(`run: Beginning dry run.`)); 94 | t.regex(output, new RegExp(`run: Executing tests in: ${samplePath}`)); 95 | t.regex( 96 | output, 97 | new RegExp( 98 | `run: Running: ${ 99 | buildPack.config.test.run.cmd 100 | } ${buildPack.config.test.run.args.join(' ')}` 101 | ) 102 | ); 103 | t.regex(output, new RegExp(`run: Dry run complete.`)); 104 | }); 105 | 106 | test.serial('should run test with defaults', async t => { 107 | const output = await tools.runAsync(`${binPath} test run`, samplePath); 108 | 109 | t.regex(output, new RegExp(`run: Executing tests in: ${samplePath}`)); 110 | t.regex( 111 | output, 112 | new RegExp( 113 | `run: Running: ${ 114 | buildPack.config.test.run.cmd 115 | } ${buildPack.config.test.run.args.join(' ')}` 116 | ) 117 | ); 118 | t.regex(output, new RegExp(`run: Success!`)); 119 | }); 120 | 121 | test.serial('should run test with overrides', async t => { 122 | const cmd = 'npm'; 123 | const args = 'run test --foo="bar"'; 124 | 125 | const output = await tools.runAsync( 126 | `${binPath} test run --cmd=${cmd} -- ${args}`, 127 | samplePath 128 | ); 129 | 130 | t.regex(output, new RegExp(`run: Executing tests in: ${samplePath}`)); 131 | t.regex(output, new RegExp(`run: Running: ${cmd} run test --foo=bar`)); 132 | t.regex(output, new RegExp(`run: Success!`)); 133 | }); 134 | -------------------------------------------------------------------------------- /test/index.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017, Google, Inc. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | const test = require('ava'); 17 | 18 | test('has correct exports', t => { 19 | const repoTools = require('../'); 20 | 21 | t.is(typeof repoTools.getRequest, 'function'); 22 | }); 23 | -------------------------------------------------------------------------------- /test/samples/nodejs/app/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017, Google, Inc. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | const express = require('express'); 17 | 18 | const app = express(); 19 | app.enable('trust proxy'); 20 | 21 | app.use((req, res) => { 22 | res.send('Hello, world!').end(); 23 | }); 24 | 25 | const PORT = process.env.PORT || 8080; 26 | app.listen(PORT, () => { 27 | console.log(`App listening on port ${PORT}`); 28 | console.log('Press Ctrl+C to quit.'); 29 | }); 30 | -------------------------------------------------------------------------------- /test/samples/nodejs/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-samples-nodejs-app", 3 | "version": "0.0.1", 4 | "private": true, 5 | "license": "Apache-2.0", 6 | "author": "Google Inc.", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/GoogleCloudPlatform/nodejs-repo-tools.git" 10 | }, 11 | "engines": { 12 | "node": ">=4" 13 | }, 14 | "scripts": { 15 | "test": "../../../../bin/tools test app" 16 | }, 17 | "dependencies": { 18 | "express": "4.15.2" 19 | }, 20 | "cloud-repo-tools": { 21 | "requiresKeyFile": true, 22 | "requiresProjectId": true, 23 | "test": { 24 | "app": { 25 | "args": [ 26 | "app.js" 27 | ], 28 | "msg": "Hello, world!" 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/samples/nodejs/bump/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017, Google, Inc. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | // nothing here 17 | -------------------------------------------------------------------------------- /test/samples/nodejs/bump/package.json.default: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-samples-nodejs-app", 3 | "version": "0.0.1", 4 | "private": true, 5 | "license": "Apache-2.0", 6 | "author": "Google Inc.", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/GoogleCloudPlatform/nodejs-repo-tools.git" 10 | }, 11 | "engines": { 12 | "node": ">=4" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/samples/nodejs/bump/samples/package.json.default: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-samples-nodejs-app-samples", 3 | "version": "0.0.1", 4 | "private": true, 5 | "license": "Apache-2.0", 6 | "author": "Google Inc.", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/GoogleCloudPlatform/nodejs-repo-tools.git" 10 | }, 11 | "engines": { 12 | "node": ">=4" 13 | }, 14 | "dependencies": { 15 | "test-samples-nodejs-app": "0.0.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/samples/nodejs/snippet/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017, Google, Inc. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | 'use strict'; 17 | 18 | var translate = require('@google-cloud/translate')(); 19 | 20 | var text = 'Hello, world!'; 21 | 22 | translate.translate(text, 'ru') 23 | .then(function (results) { 24 | var translation = results[0]; 25 | console.log('Text: ' + text); 26 | console.log('Translation: ' + translation); 27 | }) 28 | .catch(function (err) { 29 | console.error('ERROR:', err); 30 | }); 31 | -------------------------------------------------------------------------------- /test/samples/nodejs/snippet/lint_error.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017, Google, Inc. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | console.log('foo') // missing semicolon 17 | -------------------------------------------------------------------------------- /test/samples/nodejs/snippet/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-samples-nodejs-snippet", 3 | "version": "0.0.1", 4 | "private": true, 5 | "license": "Apache-2.0", 6 | "author": "Google Inc.", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/GoogleCloudPlatform/nodejs-repo-tools.git" 10 | }, 11 | "engines": { 12 | "node": ">=4" 13 | }, 14 | "scripts": { 15 | "mocha": "mocha --timeout 20000 test/*.test.js", 16 | "test": "../../../../bin/tools test run --cmd npm -- run mocha" 17 | }, 18 | "dependencies": { 19 | "@google-cloud/translate": "1.0.0" 20 | }, 21 | "devDependencies": { 22 | "mocha": "3.5.3" 23 | }, 24 | "cloud-repo-tools": { 25 | "requiresKeyFile": true, 26 | "requiresProjectId": true 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/samples/nodejs/snippet/test/index.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017, Google, Inc. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | 'use strict'; 17 | 18 | /* global before, describe, it */ 19 | 20 | var assert = require('assert'); 21 | var path = require('path'); 22 | // eslint-disable-next-line 23 | var tools = require('../../../../../'); 24 | var translate = require('@google-cloud/translate')(); 25 | 26 | var cwd = path.join(__dirname, '..'); 27 | var cmd = 'node index.js'; 28 | var lang = 'ru'; 29 | var text = 'Hello, world!'; 30 | 31 | before(tools.checkCredentials); 32 | 33 | describe('snippet', function() { 34 | it('should work', function() { 35 | return Promise.all([ 36 | tools.runAsync(cmd, cwd), 37 | translate.translate(text, lang), 38 | ]).then(function(results) { 39 | var output = results[0]; 40 | var translation = results[1][0]; 41 | assert(output.includes('Text: ' + text)); 42 | assert(output.includes('Translation: ' + translation)); 43 | }); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /test/utils/index.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017, Google, Inc. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | const path = require('path'); 17 | const test = require('ava'); 18 | 19 | const toolsPath = path.join(__dirname, '../../'); 20 | const utils = require(toolsPath).utils; 21 | 22 | test.serial('parseArgs: should parse args', t => { 23 | t.deepEqual(utils.parseArgs(`foo`), [`foo`]); 24 | t.deepEqual(utils.parseArgs(`foo bar`), [`foo`, `bar`]); 25 | t.deepEqual(utils.parseArgs(`"foo" 'bar'`), [`"foo"`, `'bar'`]); 26 | t.deepEqual(utils.parseArgs(`fo'oba'r`), [`fo'oba'r`]); 27 | t.deepEqual(utils.parseArgs(`install --prod`), [`install`, `--prod`]); 28 | t.deepEqual(utils.parseArgs(`some cmd --foo='bar' -b h"ell"o`), [ 29 | `some`, 30 | `cmd`, 31 | `--foo='bar'`, 32 | `-b`, 33 | `h"ell"o`, 34 | ]); 35 | t.throws(() => utils.parseArgs(`some cmd --foo='bar`)); 36 | }); 37 | --------------------------------------------------------------------------------