├── .circleci └── config.yml ├── .eslintrc.json ├── .gitignore ├── .jshintrc ├── .nvmrc ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE.md ├── README.md ├── VERSION ├── app ├── base-generator.js ├── index.js └── prompts │ └── index.js ├── bin ├── alan ├── brand ├── deploy ├── help ├── init ├── scan ├── setup ├── upgrade └── validate ├── cf-manifest ├── index.js └── templates │ ├── Procfile │ ├── manifest_base.yml │ ├── manifest_dev.yml │ └── manifest_prod.yml ├── gitignores ├── __tests__ │ └── index.test.js └── index.js ├── install ├── license └── index.js ├── main ├── newrelic ├── index.js └── templates │ ├── javascript-low-security.js.template │ ├── python-low-security.ini │ └── ruby-low-security.yml ├── npm └── index.js ├── package-lock.json ├── package.json ├── readme └── index.js └── scripts ├── cf-db ├── deploy └── latest-commit /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | - image: circleci/node:8 11 | 12 | steps: 13 | - checkout 14 | - setup_remote_docker 15 | 16 | # Download and cache dependencies 17 | - restore_cache: 18 | keys: 19 | - v1-dependencies-{{ checksum "package.json" }} 20 | # fallback to using the latest cache if no exact match is found 21 | - v1-dependencies- 22 | 23 | - run: npm install 24 | 25 | - save_cache: 26 | paths: 27 | - node_modules 28 | key: v1-dependencies-{{ checksum "package.json" }} 29 | 30 | # run tests! 31 | - run: npm test 32 | 33 | - run: docker build -t 18fcli . 34 | - run: 35 | name: Integration test for license 36 | command: | 37 | docker run --name license --entrypoint yo 18fcli 18f:license 38 | docker cp license:/workdir /tmp/license 39 | [ -f /tmp/license/LICENSE.md ] 40 | - run: 41 | name: Integration test for README 42 | command: | 43 | echo "ANew ProjProj" | docker run --name readme -i --entrypoint yo 18fcli 18f:readme 44 | docker cp readme:/workdir /tmp/readme 45 | grep "ANew ProjProj" /tmp/readme/README.md 46 | 47 | publish: 48 | docker: 49 | # specify the version you desire here 50 | - image: circleci/node:8 51 | 52 | steps: 53 | - checkout 54 | 55 | - run: 56 | name: Write NPM Token to ~/.npmrc 57 | command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc 58 | 59 | - run: 60 | name: Publish to NPM 61 | command: npm publish --access=public 62 | 63 | workflows: 64 | version: 2 65 | 66 | build_and_maybe_publish: 67 | jobs: 68 | - build: 69 | filters: 70 | tags: 71 | only: /.*/ 72 | branches: 73 | only: /.*/ 74 | - publish: 75 | filters: 76 | tags: 77 | only: /v[0-9]+(\.[0-9]+)*/ 78 | branches: 79 | ignore: /.*/ 80 | requires: 81 | - build 82 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true, 6 | "node": true, 7 | "jest/globals": true 8 | }, 9 | "extends": ["airbnb-base", "plugin:jest/recommended"], 10 | "parserOptions": { 11 | "ecmaFeatures": { 12 | "jsx": false 13 | }, 14 | "sourceType": "module" 15 | }, 16 | "plugins": [ 17 | "jest" 18 | ], 19 | "rules": { 20 | "no-underscore-dangle": 0 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | orgname 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "esversion": 6 4 | } -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v6.10.2 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Welcome! 2 | 3 | We're so glad you're thinking about contributing to an 18F open source project! If you're unsure about anything, just ask -- or submit the issue or pull request anyway. The worst that can happen is you'll be politely asked to change something. We love all friendly contributions. 4 | 5 | We want to ensure a welcoming environment for all of our projects. Our staff follow the [18F Code of Conduct](https://github.com/18F/code-of-conduct/blob/master/code-of-conduct.md) and all contributors should do the same. 6 | 7 | We encourage you to read this project's CONTRIBUTING policy (you are here), its [LICENSE](LICENSE.md), and its [README](README.md). 8 | 9 | If you have any questions or want to read more, check out the [18F Open Source Policy GitHub repository]( https://github.com/18f/open-source-policy), or just [shoot us an email](mailto:18f@gsa.gov). 10 | 11 | ## Public domain 12 | 13 | This project is in the public domain within the United States, and 14 | copyright and related rights in the work worldwide are waived through 15 | the [CC0 1.0 Universal public domain dedication](https://creativecommons.org/publicdomain/zero/1.0/). 16 | 17 | All contributions to this project will be released under the CC0 18 | dedication. By submitting a pull request, you are agreeing to comply 19 | with this waiver of copyright interest. 20 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:8.9.4-alpine 2 | 3 | RUN adduser -h /home -D -H yeoman \ 4 | && chown yeoman:yeoman /home \ 5 | && mkdir /workdir \ 6 | && chown yeoman:yeoman /workdir 7 | USER yeoman 8 | 9 | RUN npm config set prefix='/home/global' 10 | ENV NODE_PATH=/home/node_modules \ 11 | PATH=/home/global/bin/:$PATH 12 | RUN npm install -q -g yo 13 | RUN mkdir -p /home/.config/configstore/ \ 14 | && echo '{"clientId": 0, "optOut": true}' > /home/.config/configstore/insight-yo.json 15 | 16 | WORKDIR /home/ 17 | 18 | COPY ["package.json", "/home/"] 19 | COPY ["app", "/home/app"] 20 | COPY ["cf-manifest", "/home/cf-manifest"] 21 | COPY ["gitignores", "/home/gitignores"] 22 | COPY ["license", "/home/license"] 23 | COPY ["newrelic", "/home/newrelic"] 24 | COPY ["npm", "/home/npm"] 25 | COPY ["readme", "/home/readme"] 26 | 27 | RUN npm link -q --production 28 | 29 | WORKDIR /workdir/ 30 | VOLUME /workdir/ 31 | 32 | ENTRYPOINT ["yo", "18f"] 33 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | As a work of the United States Government, this project is in the 2 | public domain within the United States. 3 | 4 | Additionally, we waive copyright and related rights in the work 5 | worldwide through the CC0 1.0 Universal public domain dedication. 6 | 7 | ## CC0 1.0 Universal Summary 8 | 9 | This is a human-readable summary of the [Legal Code (read the full text)](https://creativecommons.org/publicdomain/zero/1.0/legalcode). 10 | 11 | ### No Copyright 12 | 13 | The person who associated a work with this deed has dedicated the work to 14 | the public domain by waiving all of his or her rights to the work worldwide 15 | under copyright law, including all related and neighboring rights, to the 16 | extent allowed by law. 17 | 18 | You can copy, modify, distribute and perform the work, even for commercial 19 | purposes, all without asking permission. 20 | 21 | ### Other Information 22 | 23 | In no way are the patent or trademark rights of any person affected by CC0, 24 | nor are the rights that other persons may have in the work or in how the 25 | work is used, such as publicity or privacy rights. 26 | 27 | Unless expressly stated otherwise, the person who associated a work with 28 | this deed makes no warranties about the work, and disclaims liability for 29 | all uses of the work, to the fullest extent permitted by applicable law. 30 | When using or citing the work, you should not imply endorsement by the 31 | author or the affirmer. 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The 18F Generator 2 | 3 | This project contains a set of [Yeoman](http://yeoman.io/) scaffolds to set up 4 | common project needs. To run them, you'll either need 5 | [Node.js](https://nodejs.org/en/download/) 6.x or 6 | [Docker](docker.com/products/overview#install_the_platform) 7 | 8 | ## Install via Node 9 | 10 | Once you have Node installed, you'll also need to install yeoman and this 11 | package from the public NPM repository: 12 | 13 | ```sh 14 | npm install -g yo # install yeoman 15 | npm install -g generator-18f # installs this package 16 | ``` 17 | 18 | Then, run the generators: 19 | 20 | ```sh 21 | cd /somewhere/else 22 | 23 | yo 18f # all generators 24 | # or individually: 25 | yo 18f:license 26 | yo 18f:readme 27 | yo 18f:gitignores 28 | yo 18f:npm 29 | yo 18f:cf-manifest 30 | yo 18f:newrelic 31 | ``` 32 | 33 | ## Install via Docker 34 | 35 | Once you have Docker installed, you'll want to run it with the current 36 | directory mounted: 37 | 38 | ```sh 39 | cd /somewhere/else 40 | 41 | docker run --rm -it -v $PWD:/workdir 18fgsa/generator 42 | ``` 43 | 44 | That will run all of the generators. To run them individually, use e.g. 45 | 46 | ```sh 47 | docker run --rm -it -v $PWD:/workdir --entrypoint yo 18fgsa/generator 18f:license 48 | ``` 49 | 50 | ## Scaffolds 51 | 52 | ### License 53 | 54 | Pulls file content from https://github.com/18F/open-source-policy 55 | 56 | ```sh 57 | $ yo 18f:license 58 | $ tree 59 | . 60 | ├── CONTRIBUTING.md 61 | └── LICENSE.md 62 | ``` 63 | 64 | ### Readme 65 | 66 | Pulls README content from 67 | https://github.com/18F/open-source-policy/blob/master/README_TEMPLATE.md 68 | 69 | ```sh 70 | $ yo 18f:readme 71 | ? What is the project's full name? My Project 72 | 73 | $ tree 74 | . 75 | └── README.md 76 | ``` 77 | 78 | ### Gitignores 79 | 80 | Pulls Gitignore content from https://github.com/github/gitignore 81 | 82 | ```sh 83 | $ yo 18f:gitignores 84 | ? What languages will this project use? 85 | ◯ Go 86 | ◉ Node 87 | ◯ Python 88 | ❯◉ Ruby 89 | 90 | $ tree 91 | . 92 | └── .gitignore 93 | ``` 94 | 95 | ### NPM 96 | 97 | Creates a package.json file. Based on 98 | https://github.com/caseywebb/generator-npm-init 99 | 100 | ```sh 101 | $ yo 18f:npm 102 | ? Will this project have front end libraries/dependencies Yes 103 | Configuring npm now... 104 | ? name: generator-18f 105 | ? version: 0.2.0 106 | ? description: Scaffolding for 18F projects 107 | ? main point: index.js 108 | ? test command: echo "Error: no test specified" && exit 1 109 | ? git repository: https://github.com/18F/18f-cli 110 | ? keywords (space-delimited): 111 | ? author: 18f 112 | ? license: CC0-1.0 113 | 114 | $ tree 115 | . 116 | └── package.json 117 | ``` 118 | 119 | ### Cloud.gov Manifests 120 | 121 | Creates files for a cloud.gov deploy, depending on your primary application 122 | language. Requires modification after generation. 123 | 124 | ```sh 125 | $ yo 18f:cf-manifest 126 | ? What's the repo name? (A short name that acts as the project identifier) my-repo 127 | ? What is the primary language (for Travis, Cloud.gov, etc.)? (Use arrow keys) 128 | ❯ Go 129 | Node 130 | Python 131 | Ruby 132 | ? What is your application run command (e.g. "node app.js" or "gunicorn my_module.wsgi")? @TODO 133 | ? Will this project need any of these services? 134 | ❯◯ database 135 | ◯ secret credentials 136 | 137 | $ tree 138 | . 139 | ├── Procfile 140 | ├── TODO.txt 141 | ├── manifest_base.yml 142 | ├── manifest_dev.yml 143 | └── manifest_prod.yml 144 | ``` 145 | 146 | ### New Relic 147 | 148 | Generates a New Relic config files with 18F-recommended defaults. 149 | 150 | ```sh 151 | $ yo 18f:newrelic 152 | ? What's the repo name? (A short name that acts as the project identifier) @TODO 153 | ? What is the primary language (for Travis, Cloud.gov, etc.)? (Use arrow keys) 154 | ❯ Go 155 | Node 156 | Python 157 | Ruby 158 | ? What languages will this project use? 159 | ◯ Go 160 | ◉ Node 161 | ◯ Python 162 | ❯◉ Ruby 163 | 164 | $ tree 165 | . 166 | ├── newrelic.ini 167 | ├── newrelic.js 168 | └── newrelic.yml 169 | ``` 170 | 171 | ## Local Development 172 | 173 | You'll most likely want to `npm link` the in-progress project to add it to 174 | make it visible to `yo`: 175 | 176 | ```sh 177 | cd /path/to/18f-cli # navigate to project root 178 | npm install -g yo # install yeoman 179 | npm install # install the required libs 180 | npm link # install the 18f-cli generators globally 181 | ``` 182 | 183 | ## The 18F Command 184 | 185 | This repository also contains an earlier effort to standardize common 18F 186 | functions in the form of a set of command line utilities. 187 | 188 | ### Installation 189 | 190 | ``` 191 | curl -sO https://raw.githubusercontent.com/18f/18f-cli/release/install 192 | bash ./install 193 | ``` 194 | 195 | If you'd like to keep a copy of the `git` repository around: 196 | 197 | ``` 198 | git clone https://github.com/18f/18f-cli/ 199 | cd 18f-cli 200 | bash ./install 201 | ``` 202 | 203 | ### Usage 204 | 205 | ``` 206 | 18f [options] [subcommand options] 207 | ``` 208 | 209 | ### `18f init` 210 | 211 | Turns the current directory into a `git` repo with proper `LICENSE.md`, `CONTRIBUTING.md`, `README.md`, and `.about.yml` files. 212 | 213 | ### `18f setup` 214 | 215 | Runs the setup script found in https://github.com/18f/laptop. Used to set up a new computer or update an existing one. See that repo for more information about customizing the script through `~/.laptop.local`. 216 | 217 | ### `18f validate` 218 | 219 | Checks a repo to see whether it has the standard 18F files, as created by `18f init`. 220 | 221 | ### `18f deploy` 222 | 223 | Deploys a project to Cloud Foundry (and, more specifically, cloud.gov). 224 | 225 | ### `18f scan` 226 | 227 | Allows a variety of pre-configured scans to be run on a project. Currently supports: 228 | - Accessibility (§ 508) 229 | 230 | ### `18f brand` 231 | 232 | Downloads the 18F brand assets, either in whole or in part. Useful as a periodic check to keep assets up-to-date. 233 | 234 | ### `18f upgrade` 235 | 236 | Upgrade the `18f` command to the latest version 237 | 238 | ### Extra scripts 239 | 240 | This repo also includes some scripts for standalone use, either in continuous integration services or on cloud.gov. 241 | - `cf-db.sh`: get a `psql` binary for use on cloud.gov and connect to the database. 242 | - `deploy.sh`: a deployment script for deploying from a continuous integration service 243 | 244 | ### Local development 245 | 246 | This script is really a bundle of scripts: one main script and a bunch of subcommands. The main script—called `main`—looks for scripts named `18f-whatever` in `/usr/local/bin/` and makes them available as `18f whatever`. 247 | 248 | New scripts should be added to this repository's `bin` folder without the `18f-` prefix, as that will be added during installation. They can be written in any language, but care should be taken to choose a language that most users will have. Similarly, there is a preference for avoiding additional dependencies in new scripts. 249 | 250 | ## Contributing 251 | 252 | See [CONTRIBUTING](CONTRIBUTING.md) for additional information. 253 | 254 | 255 | ## Public domain 256 | 257 | This project is in the worldwide [public domain](LICENSE.md). As stated in [CONTRIBUTING](CONTRIBUTING.md): 258 | 259 | > This project is in the public domain within the United States, and copyright and related rights in the work worldwide are waived through the [CC0 1.0 Universal public domain dedication](https://creativecommons.org/publicdomain/zero/1.0/). 260 | > 261 | > All contributions to this project will be released under the CC0 dedication. By submitting a pull request, you are agreeing to comply with this waiver of copyright interest. 262 | 263 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.4.0 2 | -------------------------------------------------------------------------------- /app/base-generator.js: -------------------------------------------------------------------------------- 1 | const Generator = require('yeoman-generator'); 2 | 3 | module.exports = class BaseGenerator extends Generator { 4 | constructor(args, opts) { 5 | super(args, opts); 6 | this.prompts = []; 7 | } 8 | 9 | // Note that this will not be called automatically. See 10 | // http://yeoman.io/authoring/running-context.html 11 | async askAndSavePrompts() { 12 | const newPrompts = this.prompts.filter(p => !this.config.get(p.name)); 13 | const props = await this.prompt(newPrompts); 14 | this.prompts.forEach((prompt) => { 15 | const value = props[prompt.name]; 16 | if (value !== undefined) { 17 | this.config.set(prompt.name, value); 18 | } 19 | }); 20 | } 21 | 22 | /* Manages a TODO list which we generate for users. */ 23 | addTodos(newTodos) { 24 | const todos = this.config.get('todo') || {}; 25 | Object.assign(todos, newTodos); 26 | this.config.set('todo', todos); 27 | 28 | const content = Object.keys(todos) 29 | .map(key => [`## ${key}`].concat(todos[key]).join('\n[ ] ')) 30 | .join('\n\n\n'); 31 | this.fs.write('TODO.txt', content); 32 | } 33 | }; 34 | 35 | -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | const Generator = require('yeoman-generator'); 2 | 3 | module.exports = class extends Generator { 4 | initializing() { 5 | this.composeWith(require.resolve('../license')); 6 | this.composeWith(require.resolve('../readme')); 7 | this.composeWith(require.resolve('../gitignores')); 8 | this.composeWith(require.resolve('../npm')); 9 | this.composeWith(require.resolve('../cf-manifest')); 10 | this.composeWith(require.resolve('../newrelic')); 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /app/prompts/index.js: -------------------------------------------------------------------------------- 1 | /* Prompts which are shared across multiple sub-generators. We'd like to only 2 | * ask once. 3 | */ 4 | const path = require('path'); 5 | 6 | const supportedLanguages = ['Go', 'Node', 'Python', 'Ruby']; 7 | const languageChoices = supportedLanguages.map(name => ({ name })); 8 | 9 | const prompts = { 10 | cloudGovServices: { 11 | type: 'checkbox', 12 | message: 'Will this project need any of these services?', 13 | choices: ['database', 'secrets'], 14 | }, 15 | description: { 16 | type: 'input', 17 | message: 'What is the problem your project solves? What is the solution?', 18 | default: '@TODO', 19 | }, 20 | frontendDeps: { 21 | type: 'confirm', 22 | message: 'Will this project have front end libraries/dependencies', 23 | default: true, 24 | }, 25 | languages: { 26 | type: 'checkbox', 27 | message: 'What languages will this project use?', 28 | choices: languageChoices, 29 | }, 30 | licenseShortName: { 31 | type: 'input', 32 | message: 'What license is this under? Use the SPDX name.', 33 | default: 'CC0-1.0', 34 | }, 35 | primaryLanguage: { 36 | type: 'list', 37 | message: 'What is the primary language (for Travis, Cloud.gov, etc.)?', 38 | choices: languageChoices, 39 | }, 40 | projectFullName: { 41 | type: 'input', 42 | message: 'What\'s the project title? (A few words, title cased, describing the project)', 43 | default: '@TODO', 44 | }, 45 | repoName: { 46 | type: 'input', 47 | message: 'What\'s the repo name? (A short name that acts as the project identifier)', 48 | default: process.cwd().split(path.sep).pop(), 49 | }, 50 | runCommand: { 51 | type: 'input', 52 | message: 'What is your application run command (e.g. "node app.js" or "gunicorn my_module.wsgi")?', 53 | default: '@TODO', 54 | }, 55 | }; 56 | 57 | // Standardize the prompt names 58 | Object.keys(prompts).forEach((key) => { 59 | prompts[key].name = key; 60 | }); 61 | 62 | module.exports = prompts; 63 | -------------------------------------------------------------------------------- /bin/alan: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | usage(){ 4 | echo -e "\nUsage:\n 18f alan [options]\n" 5 | echo " Options:" 6 | echo " -h, --help Display this help message" 7 | echo " -n, --number How many Alans" 8 | } 9 | 10 | NUMBER=2000 11 | 12 | # Convert known long options to short options 13 | for arg in "$@"; do 14 | shift 15 | case "$arg" in 16 | --help) 17 | set -- "$@" "-h" 18 | ;; 19 | --number) 20 | set -- "$@" "-n" 21 | ;; 22 | *) 23 | set -- "$@" "$arg" 24 | ;; 25 | esac 26 | done 27 | 28 | # Reset to beginning of arguments 29 | OPTIND=1 30 | 31 | # Process option flags 32 | while getopts "hn:" opt; do 33 | case $opt in 34 | h ) 35 | usage 36 | exit 0 37 | ;; 38 | n ) 39 | if [[ $OPTARG -gt 0 ]]; then 40 | NUMBER=$OPTARG 41 | else 42 | echo "Enter a number above 0" 43 | exit 1 44 | fi 45 | ;; 46 | esac 47 | done 48 | shift $((OPTIND -1)) 49 | 50 | H=$(( $(tput lines) - 3 )) 51 | W=$(( $(tput cols) - 4 )) 52 | 53 | clear 54 | for _ in $(seq 0 $NUMBER); do 55 | tput cup $(($RANDOM % $H)) $(($RANDOM % $W)) 56 | echo "ALAN!" 57 | done 58 | tput cup $H 0 59 | -------------------------------------------------------------------------------- /bin/brand: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | usage(){ 4 | echo -e "\nUsage:\n 18f brand [options]\n" 5 | echo " Options:" 6 | echo " -h, --help Display this help message" 7 | echo " -t, --type The file type to grab (png, svg, favicon, all)" 8 | echo " -s, --size The logo size to grab ((s)mall, (m)edium, (l)arge)" 9 | echo " -c, --color The logo color to grab (black (default) or blue)" 10 | echo " -q, --quiet Quiet mode" 11 | echo "" 12 | } 13 | 14 | # The default logo color of black has no color indicated in the filename, 15 | # so an empty string will work 16 | PNG_COLOR="" 17 | SVG_COLOR="Black" 18 | 19 | # The default output location is the current directory 20 | OUTPUT="." 21 | 22 | # The default size is medium 23 | SIZE="M" 24 | 25 | # Convert known long options to short options 26 | for arg in "$@"; do 27 | shift 28 | case "$arg" in 29 | --help) 30 | set -- "$@" "-h" 31 | ;; 32 | --quiet) 33 | set -- "$@" "-q" 34 | ;; 35 | --color) 36 | set -- "$@" "-c" 37 | ;; 38 | --type) 39 | set -- "$@" "-t" 40 | ;; 41 | --size) 42 | set -- "$@" "-s" 43 | ;; 44 | *) 45 | set -- "$@" "$arg" 46 | ;; 47 | esac 48 | done 49 | 50 | # Reset to beginning of arguments 51 | OPTIND=1 52 | 53 | # Process option flags 54 | shopt -s nocasematch 55 | while getopts ":hqt:s:c:" opt; do 56 | case "$opt" in 57 | h ) 58 | usage 59 | exit 0 60 | ;; 61 | q ) 62 | exec 1>/dev/null 2>/dev/null 63 | ;; 64 | c ) 65 | if [[ $OPTARG =~ blue ]]; then 66 | PNG_COLOR="Bright-" 67 | SVG_COLOR="Blue" 68 | fi 69 | ;; 70 | t ) 71 | if [[ $OPTARG =~ (png|svg|(fav(icon)?|icon)) ]]; then 72 | TYPE=$OPTARG 73 | else 74 | echo "filetypes are png, svg, favicon, and all" 75 | usage 76 | exit 1 77 | fi 78 | ;; 79 | s ) 80 | if [[ $OPTARG =~ s(m|mall)? ]]; then 81 | SIZE="S" 82 | elif [[ $OPTARG =~ m(ed|edium)? ]]; then 83 | SIZE="M" 84 | elif [[ $OPTARG =~ l(g|rg|arge)? ]]; then 85 | SIZE="L" 86 | else 87 | echo "size options are (s)mall, (m)edium, and (l)arge" 88 | usage 89 | exit 1 90 | fi 91 | ;; 92 | \? ) 93 | echo "Unrecognized option: -$OPTARG\n" 94 | usage 95 | exit 1 96 | ;; 97 | : ) 98 | echo "The -$OPTARG option requires an argument" 99 | usage 100 | exit 1 101 | ;; 102 | esac 103 | done 104 | shift $((OPTIND -1)) 105 | 106 | echo "downloading latest logo zipfile" 107 | curl --silent --location --remote-name https://github.com/18F/brand/raw/18f-pages-staging/assets/dist/18F_Logo.zip 108 | unzip -qo 18F_Logo.zip -d /tmp 109 | if [[ $TYPE == "png" ]]; then 110 | echo "copying the .png file to ${OUTPUT}" 111 | cp /tmp/18F_Logo/PNG/18F-Logo-${PNG_COLOR}${SIZE}.png ${OUTPUT} 112 | elif [[ $TYPE = "svg" ]]; then 113 | echo "copying the .svg file to ${OUTPUT}" 114 | cp /tmp/18F_Logo/SVG/18F-Logo-2016-${SVG_COLOR}.svg ${OUTPUT} 115 | elif [[ $TYPE =~ (fav(icon)?|icon) ]]; then 116 | echo "copying favicons to ${OUTPUT}" 117 | cp /tmp/18F_Logo/favicons/favicon-16x16.png ${OUTPUT} 118 | cp /tmp/18F_Logo/favicons/favicon-32x32.png ${OUTPUT} 119 | cp /tmp/18F_Logo/favicons/favicon.ico ${OUTPUT} 120 | else 121 | echo "copying all files to ${OUTPUT}" 122 | cp /tmp/18F_Logo/*/* ${OUTPUT} 123 | fi 124 | rm 18F_Logo.zip 125 | rm -rf /tmp/18F_Logo 126 | 127 | # TODO: support font? 128 | # curl --silent https://pages-staging.18f.gov/brand/assets/dist/18F_Nimbus.zip 129 | -------------------------------------------------------------------------------- /bin/deploy: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | type cf >/dev/null 2>&1 || { 4 | echo " ERROR: cf not found:" 5 | echo " please install cf" 6 | exit 1 7 | } 8 | 9 | if [[ $(bc <<< "$(jq --version | cut -d'-' -f2)>=1.5") -ne 1 ]]; then 10 | if [[ $TRAVIS ]]; then 11 | mkdir /tmp/jq-1.5 12 | wget https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux64 -O /tmp/jq-1.5/jq 13 | chmod +x /tmp/jq-1.5/jq 14 | export PATH=/tmp/jq-1.5:$PATH 15 | else 16 | echo " ERROR: jq is not installed or is below version 1.5" 17 | echo " please update jq" 18 | exit 1 19 | fi 20 | fi 21 | 22 | usage(){ 23 | echo -e "\nUsage:\n 18f deploy [options] \n" 24 | echo " Options:" 25 | echo " -h, --help Display this help message" 26 | echo " -t, --timid Confirm org and space settings before pushing" 27 | echo " -f, --force Skip check for environment variables set outside of a UPS" 28 | echo " -d, --some-downtime Perform a some-downtime deploy (default: zero-downtime)" 29 | echo " -m, --manifest Specify a local manifest.yml instead of building from the app" 30 | echo " -c, --clean Do not pull environment variables from the running app" 31 | echo " -n, --new Create a new app rather than searching for an app to update" 32 | echo "" 33 | echo " If the app name is not unique:" 34 | echo " -o, --org The organization to target" 35 | echo " -s, --space The space in the organization to target" 36 | echo "" 37 | } 38 | 39 | API="https://api.cloud.gov" 40 | 41 | # Check whether logged in 42 | CURRENT_TARGET=$(cf target) 43 | if echo $CURRENT_TARGET | grep -q "Not logged in"; then 44 | echo " Not logged into cf" 45 | if [[ $CF_USERNAME && $CF_PASSWORD ]]; then 46 | # Log in to the sandbox to avoid the org/space selection prompt 47 | # The script will target the correct org/space in a few lines 48 | cf login -a $API -u $CF_USERNAME -p $CF_PASSWORD -o sandbox 49 | else 50 | cf login 51 | fi 52 | else 53 | OLD_ORG=$(echo $CURRENT_TARGET | cut -d' ' -f10) 54 | OLD_SPACE=$(echo $CURRENT_TARGET | cut -d' ' -f12) 55 | fi 56 | 57 | #TODO: consider using a single case statement, which would allow the argument to precede 58 | # the option flags 59 | 60 | # Convert known long options to short options 61 | for arg in "$@"; do 62 | shift 63 | case "$arg" in 64 | --help) 65 | set -- "$@" "-h" 66 | ;; 67 | --some-downtime) 68 | # Why this option? It may be useful for projects that don't want to allocate 69 | # the memory to run two apps, as required for zero-downtime deploys 70 | set -- "$@" "-d" 71 | ;; 72 | --timid) 73 | set -- "$@" "-t" 74 | ;; 75 | --force) 76 | set -- "$@" "-f" 77 | ;; 78 | --manifest) 79 | set -- "$@" "-m" 80 | ;; 81 | --new) 82 | set -- "$@" "-n" 83 | ;; 84 | --org) 85 | set -- "$@" "-o" 86 | ;; 87 | --space) 88 | set -- "$@" "-s" 89 | ;; 90 | *) 91 | set -- "$@" "$arg" 92 | ;; 93 | esac 94 | done 95 | 96 | # Reset to beginning of arguments 97 | OPTIND=1 98 | 99 | # Process option flags 100 | while getopts "hdtmo:s:cn" opt; do 101 | case $opt in 102 | h ) 103 | usage 104 | exit 0 105 | ;; 106 | t ) 107 | TIMID=true 108 | ;; 109 | d ) 110 | DOWNTIME=true 111 | ;; 112 | f ) 113 | FORCE=true 114 | ;; 115 | m ) 116 | ORIGINAL=$OPTARG 117 | ;; 118 | c ) 119 | CLEAN=true 120 | ;; 121 | n ) 122 | NEW=true 123 | ;; 124 | o ) 125 | CF_ORG=$OPTARG 126 | ;; 127 | s ) 128 | CF_SPACE=$OPTARG 129 | ;; 130 | esac 131 | done 132 | shift $((OPTIND -1)) 133 | 134 | # Check whether autopilot is installed for zero-downtime deploys 135 | if [[ ! $DOWNTIME ]]; then 136 | if cf plugins | grep -q "autopilot"; then 137 | : 138 | else 139 | echo "Install autopilot to use zero-downtime deploys. For installation instructions, see:" 140 | echo "https://github.com/concourse/autopilot#installation" 141 | fi 142 | fi 143 | 144 | if [ $# -ne 1 ]; then 145 | echo "Error: please supply the app name" 146 | usage 147 | exit 0 148 | fi 149 | 150 | NAME=$1 151 | if [[ $NEW ]]; then 152 | if [[ ! $CF_ORG ]] || [[ ! $CF_SPACE ]] || [[ ! $ORIGINAL ]]; then 153 | echo "For new deployments, you must set an org, space, and manifest" 154 | exit 1 155 | fi 156 | fi 157 | 158 | echo "Looking for the right org and space to deploy to" 159 | if [[ ! $CF_ORG ]] || [[ ! $CF_SPACE ]]; then 160 | CF_APP_DATA=$(cf curl "/v2/apps" -X GET -H "Content-Type: application/x-www-form-urlencoded" | jq '.resources[] | select(.entity.name == "'${NAME}'")') 161 | fi 162 | if [[ ! $CF_SPACE ]]; then 163 | SPACE_URL=$(echo $CF_APP_DATA | jq '.entity.space_url' | tr -d '"') 164 | if [[ $(echo $SPACE_URL | wc -w) -gt 1 ]]; then 165 | echo "You have access to multiple apps with that name! Ensure that the app names are unique and try again" 166 | exit 1 167 | fi 168 | if [[ $SPACE_URL == "null" ]] && [[ ! $NEW ]]; then 169 | echo "No app named ${NAME} found! Check the application name and try again" 170 | echo "If you are deploying a new app, use the --new flag" 171 | exit 1 172 | fi 173 | CF_SPACE_DATA=$(cf curl "${SPACE_URL}" -X GET -H "Content-Type: application/x-www-form-urlencoded") 174 | CF_SPACE=$(echo $CF_SPACE_DATA | jq '.entity.name' | tr -d '"') 175 | fi 176 | if [[ $CF_SPACE ]] && [[ ! $CF_ORG ]]; then 177 | ORG_URL=$(echo $CF_SPACE_DATA | jq '.entity.organization_url' | tr -d '"') 178 | CF_ORG=$(cf curl ${ORG_URL} -X GET -H "Content-Type: application/x-www-form-urlencoded" | jq '.entity.name' | tr -d '"') 179 | fi 180 | echo "Using space $CF_SPACE in org $CF_ORG" 181 | 182 | # Point to the appropriate CF org and space 183 | if [[ $TIMID ]]; then 184 | echo -n "Pushing to org ${CF_ORG} and space ${CF_SPACE}. Is that correct? [y/n] " 185 | read response 186 | if [[ $response =~ y(es)? ]]; then 187 | echo "Targeting space $CF_SPACE in org $CF_ORG" 188 | cf target -o $CF_ORG -s $CF_SPACE > /dev/null 189 | else 190 | exit 0 191 | fi 192 | else 193 | echo "Targeting space $CF_SPACE in org $CF_ORG" 194 | cf target -o $CF_ORG -s $CF_SPACE > /dev/null 2>&1 195 | fi 196 | 197 | 198 | #TODO: get app name, maybe from original manifest.yml? 199 | #TODO: figure out the right space and org 200 | if [[ $ORIGINAL ]]; then 201 | echo "Copying $ORIGINAL to a temporary manifest for deploy" 202 | cp "$ORIGINAL" 18f-deploy.yml 203 | else 204 | echo "Creating an app manifest" 205 | cf create-app-manifest $NAME -p 18f-deploy.yml > /dev/null 206 | fi 207 | MANIFEST="18f-deploy.yml" 208 | 209 | if [[ ! $CLEAN ]]; then 210 | #TODO: create flags for these options 211 | SHORTEN_VCAP=${SHORTEN_VCAP:-"false"} 212 | UPPERCASE=${UPPERCASE:-"true"} 213 | 214 | # Add user-provided variables as entries in yml env 215 | echo "Gathering env vars from user-provided services" 216 | VCAP_SERVICES=$(cf env $NAME | tr -d "\n" | awk -F"System-Provided:" '{$0=$2}1' | awk -F"{ \"VCAP_APPLICATION" '{$0=$1}1' | jq '.["VCAP_SERVICES"]') 217 | # Find the number of user-provided services 218 | LENGTH=$(echo $VCAP_SERVICES | jq '.["user-provided"] | length') 219 | echo "Found $LENGTH services" 220 | if [[ $LENGTH -gt 0 ]]; then 221 | LENGTH=$(($LENGTH - 1)) 222 | 223 | for n in $(seq 0 $LENGTH); do 224 | # The name of the service is nested in VCAP_SERVICES, so pull that out 225 | SERVICE=$(echo $VCAP_SERVICES | jq -r --argjson n $n '.["user-provided"][$n]["name"]') 226 | echo "Getting settings for $SERVICE" 227 | 228 | # Find out how many keys are in this service 229 | KEYS_LENGTH=$(echo $VCAP_SERVICES | jq -r --argjson n $n '.["user-provided"][$n]["credentials"] | keys | length') 230 | 231 | # Get each key-value pair and create an export statement 232 | KEYS_LENGTH=$(($KEYS_LENGTH - 1)) 233 | for i in $(seq 0 $KEYS_LENGTH); do 234 | KEY="$(echo $VCAP_SERVICES | jq -r --argjson n $n --argjson i $i '.["user-provided"][$n]["credentials"] | keys[$i]')" 235 | VALUE="$(echo $VCAP_SERVICES | jq -r --argjson n $n --arg key $KEY '.["user-provided"][$n]["credentials"][$key]')" 236 | 237 | if [[ "$UPPERCASE" == "true" ]]; then 238 | # Convert KEY and SERVICE to uppercase 239 | KEY=${KEY^^} 240 | SERVICE=${SERVICE^^} 241 | fi 242 | 243 | # Create export statements 244 | if [[ "$SHORTEN_VCAP" == "true" ]]; then 245 | ENV_VAR="${KEY}=\: ${VALUE}" 246 | else 247 | # Dashes cause failures for bash variables, so convert them 248 | ENV_VAR="${SERVICE//-/_}_${KEY}: ${VALUE}" 249 | fi 250 | if cat 18f-deploy.yml | grep "env:" > /dev/null; then # If the "env" key already exists 251 | sed -i '' "s/ env:/env:\\`echo -e '\r'` ${ENV_VAR}/" 18f-deploy.yml 252 | else # Create the "env" key if there is none 253 | echo "env:\n${ENV_VAR}" >> 18f-deploy.yml 254 | fi 255 | done 256 | done 257 | fi 258 | fi 259 | 260 | # Check that no user-set environment variables will get lost in a zero-downtime deploy. 261 | # Since zero-downtime works by creating a new instance and then switching to it, environment 262 | # variables only persist if they are set by a User-Provided Service. 263 | if [[ $(echo $CF_APP_DATA | jq --raw-output '.entity.environment_json | length') -gt 0 ]] && [[ ! $DOWNTIME ]] && [[ ! $FORCE ]]; then 264 | echo "" 265 | if [[ $CLEAN ]]; then 266 | echo "The following environment variables will be lost! " 267 | echo $CF_APP_DATA | jq '.entity.environment_json | keys[]' 268 | echo -en "To keep them, set up a User-Provided Service and bind it to the app:\n http://docs.cloudfoundry.org/devguide/services/user-provided.html\nStill proceed with push? [y/n] " 269 | read response 270 | if [[ $response =~ y(es)? ]]; then 271 | : 272 | else 273 | exit 0 274 | fi 275 | else 276 | echo "WARNING: The following environment variables are set manually!" 277 | echo $CF_APP_DATA | jq '.entity.environment_json | keys[]' 278 | echo "It's recommended set up a User-Provided Service and bind it to the app:" 279 | echo "http://docs.cloudfoundry.org/devguide/services/user-provided.html" 280 | echo "This will ensure that the variables aren't lost during a zero-downtime push" 281 | fi 282 | fi 283 | 284 | LATEST_COMMIT=$(find . -name latest-commit.txt) 285 | 286 | if [[ -n $LATEST_COMMIT ]]; then 287 | git rev-parse --abbrev-ref HEAD > $LATEST_COMMIT 288 | git rev-parse HEAD >> $LATEST_COMMIT 289 | fi 290 | 291 | # Push the app 292 | if [[ ! $DOWNTIME ]]; then 293 | cf zero-downtime-push $NAME -f $MANIFEST 294 | else 295 | cf push -f $MANIFEST 296 | fi 297 | 298 | # Return to the previous state, if applicable 299 | if [[ -n $OLD_ORG ]] && [[ -n $OLD_SPACE ]]; then 300 | echo "Returning to space $CF_SPACE in org $CF_ORG" 301 | cf target -o $OLD_ORG -s $OLD_SPACE > /dev/null 2>&1 302 | fi 303 | 304 | # Clean up 305 | rm 18f-deploy.yml 306 | -------------------------------------------------------------------------------- /bin/help: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | usage(){ 4 | echo -e "\nUsage:\n 18f help [subcommand]\n" 5 | echo " Options:" 6 | echo " -h, --help Display this help message" 7 | echo "" 8 | } 9 | 10 | # Convert known long options to short options 11 | for arg in "$@"; do 12 | shift 13 | case "$arg" in 14 | --help) 15 | set -- "$@" "-h" 16 | ;; 17 | *) 18 | set -- "$@" "$arg" 19 | esac 20 | done 21 | 22 | # Reset to beginning of arguments 23 | OPTIND=1 24 | 25 | # Process option flags 26 | while getopts ":hq" opt; do 27 | case "$opt" in 28 | h ) 29 | usage 30 | exit 0 31 | ;; 32 | \? ) 33 | echo "Unrecognized option: -$OPTARG\n" 34 | usage 35 | exit 1 36 | ;; 37 | esac 38 | done 39 | shift $((OPTIND -1)) 40 | 41 | 18f $1 --help 42 | -------------------------------------------------------------------------------- /bin/init: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | LOCATION="$(pwd)" 4 | NAME="$(/usr/bin/basename $LOCATION)" 5 | 6 | # Poirot pattern location 7 | PATTERNS="https://raw.githubusercontent.com/DCgov/poirot-patterns/master/default.txt" 8 | # Two other patterns that can be searched by Poirot 9 | # https://raw.githubusercontent.com/DCgov/poirot-patterns/master/financial.txt 10 | # https://raw.githubusercontent.com/DCgov/poirot-patterns/master/ids.txt 11 | 12 | # Check installations 13 | type git >/dev/null 2>&1 || { 14 | echo " ERROR: git not found:" 15 | echo " please install git" 16 | exit 1 17 | } 18 | 19 | type about_yml_generate >/dev/null 2>&1 || { 20 | echo " ERROR: about_yml_generate not found:" 21 | echo " gem install about_yml should do the trick" 22 | exit 1 23 | } 24 | 25 | type poirot >/dev/null 2>&1 || { 26 | echo " ERROR: poirot not found:" 27 | echo " pip install poirot should do the trick" 28 | exit 1 29 | } 30 | 31 | usage(){ 32 | echo -e "\nUsage:\n 18f init [options]\n" 33 | echo " Options:" 34 | echo " -d, --dest [DIR] Repository location (default: current directory)" 35 | echo " -h, --help Display this help message" 36 | echo " -n, --name [NAME] Project name (default: current directory name)" 37 | echo " -l, --lang [LANG] Add language-specific files" 38 | echo " -f, --force Overwrite any existing files" 39 | echo "" 40 | } 41 | 42 | # Convert known long options to short options 43 | for arg in "$@"; do 44 | shift 45 | case "$arg" in 46 | --dest) 47 | set -- "$@" "-d" 48 | ;; 49 | --help) 50 | set -- "$@" "-h" 51 | ;; 52 | --name) 53 | set -- "$@" "-n" 54 | ;; 55 | --lang) 56 | set -- "$@" "-l" 57 | ;; 58 | --force) 59 | set -- "$@" "-f" 60 | ;; 61 | --quiet) 62 | set -- "$@" "-q" 63 | ;; 64 | *) 65 | set -- "$@" "$arg" 66 | esac 67 | done 68 | 69 | # Reset to beginning of arguments 70 | OPTIND=1 71 | 72 | # Process option flags 73 | while getopts "d:hfqn:l:" opt; do 74 | case $opt in 75 | d ) 76 | LOCATION="$OPTARG" 77 | ;; 78 | h ) 79 | usage 80 | exit 0 81 | ;; 82 | n ) 83 | NAME="$OPTARG" 84 | ;; 85 | l ) 86 | LANGUAGE="$OPTARG" 87 | case $LANGUAGE in 88 | py ) 89 | LANGUAGE="python" 90 | ;; 91 | rb ) 92 | LANGUAGE="ruby" 93 | ;; 94 | nodejs ) 95 | LANGUAGE="node" 96 | ;; 97 | esac 98 | ;; 99 | f ) 100 | echo -n "Are you sure that you want to overwrite existing files? [y/n] " 101 | read response 102 | if [[ $response =~ y(es)? ]]; then 103 | FORCE=true 104 | else 105 | exit 0 106 | fi 107 | ;; 108 | q ) 109 | exec 1>/dev/null 2>/dev/null 110 | ;; 111 | \? ) 112 | echo "Unrecognized option: -$OPTARG\n" 113 | usage 114 | exit 1 115 | ;; 116 | esac 117 | done 118 | shift $((OPTIND -1)) 119 | 120 | # Create project directory, if doesn't already exist, and move there 121 | mkdir -p "$LOCATION" 122 | cd "$LOCATION" || exit 1 123 | 124 | # Initialize git repo in this directory 125 | git init >/dev/null 126 | 127 | # Grab LICENSE.md and CONTRIBUTING.md 128 | if [[ ! -e LICENSE.md ]] || [[ $FORCE ]]; then 129 | echo " Getting LICENSE.md" 130 | curl -sO https://raw.githubusercontent.com/18F/open-source-policy/master/LICENSE.md 131 | fi 132 | if [[ ! -e CONTRIBUTING.md ]] || [[ $FORCE ]]; then 133 | echo " Getting CONTRIBUTING.md" 134 | curl -sO https://raw.githubusercontent.com/18F/open-source-policy/master/CONTRIBUTING.md 135 | fi 136 | 137 | # Create README.md 138 | if [[ ! -e README.md ]] || [[ $FORCE ]]; then 139 | echo " Creating README.md" 140 | curl -s https://raw.githubusercontent.com/18F/open-source-policy/master/README_TEMPLATE.md -o README.md 141 | sed -i "" 's/\[Repo Name\]/'$NAME'/' README.md 142 | else 143 | for file in README*; do 144 | if [[ -e $file ]]; then 145 | echo -n " Found an existing README: ${file}. Would you like to convert it to README.md? (y/n) " 146 | read response 147 | if [[ $response =~ y(es)? ]]; then 148 | pandoc $file -o README.md 149 | else 150 | echo -n " Would you like to create a separate README.md? (y/n) " 151 | read response 152 | if [[ $response =~ y(es)? ]]; then 153 | echo " Creating README.md" 154 | curl -s https://raw.githubusercontent.com/18F/open-source-policy/master/README_TEMPLATE.md -o README.md 155 | sed -i "" 's/\[Repo Name\]/'$NAME'/' README.md 156 | fi 157 | fi 158 | fi 159 | done 160 | fi 161 | 162 | # Create initial .about.yml 163 | if [[ ! -e .about.yml ]] || [[ $FORCE ]]; then 164 | echo " Initializing .about.yml" 165 | about_yml_generate > .about.yml 166 | fi 167 | 168 | # Install git hooks 169 | if [[ ! -e .git/hooks/pre-commit ]] || [[ $FORCE ]]; then 170 | echo " Creating empty pre-commit git hook" 171 | touch .git/hooks/pre-commit 172 | fi 173 | 174 | if grep -q "poirot" .git/hooks/pre-commit; then 175 | : 176 | else 177 | echo " Adding Poirot to git hooks" 178 | curl --silent https://raw.githubusercontent.com/DCgov/poirot/master/pre-commit-poirot -o .git/hooks/pre-commit-poirot 179 | echo ".git/hooks/pre-commit-poirot -f \"\" -p \"${PATTERNS}\"" >> .git/hooks/pre-commit 180 | chmod +x .git/hooks/pre-commit .git/hooks/pre-commit-poirot 181 | fi 182 | 183 | # about_yml_validate 184 | # linter(s) 185 | 186 | # Create system-security-plan.yml 187 | # Optional 188 | 189 | # Create initial CI metadata 190 | # Perhaps optional? Or with user input? 191 | 192 | # Language-specific additions 193 | shopt -s nocasematch 194 | if [[ -n $LANGUAGE ]]; then 195 | if [[ ! -e .gitignore ]] || [[ $FORCE ]]; then 196 | # Using gitignore.io because GitHub's gitignore API is sensitive to 197 | # capitalization, which causes problems 198 | echo " Adding .gitignore for ${LANGUAGE}" 199 | curl --silent https://www.gitignore.io/api/${LANGUAGE} -o .gitignore 200 | ln -s .gitignore .cfignore 201 | fi 202 | if [[ $LANGUAGE =~ node ]]; then 203 | if [[ ! -e package.json ]]; then 204 | echo " Initializing basic Node files" 205 | npm init --yes > /dev/null 2>&1 206 | sed -i "" -e 's/ISC/CC0-1.0/;s/### Public domain//' package.json 207 | fi 208 | curl --silent -O https://raw.githubusercontent.com/airbnb/javascript/master/linters/.jshintrc 209 | elif [[ $LANGUAGE =~ ruby ]]; then 210 | echo " Initializing basic Ruby files" 211 | touch Gemfile 212 | elif [[ $LANGUAGE =~ python ]]; then 213 | echo " Initializing basic Python files" 214 | if which pyenv > /dev/null; then 215 | echo " Attempting to create virtualenv" 216 | eval "$(pyenv init -)" 217 | pyenv virtualenvwrapper 218 | mkvirtualenv $NAME 219 | fi 220 | fi 221 | fi 222 | -------------------------------------------------------------------------------- /bin/scan: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | # OWASP 4 | # other security things 5 | # pa11y 6 | 7 | SCANS=() 8 | 9 | check() { 10 | cmd=$1 11 | type $cmd >/dev/null 2>&1 || { 12 | echo " ERROR: ${cmd} not found:" 13 | echo " please install ${cmd}" 14 | exit 1 15 | } 16 | } 17 | 18 | usage(){ 19 | echo -e "\nUsage:\n 18f scan [options]\n" 20 | echo " Options:" 21 | echo " -h, --help Display this help message" 22 | echo " -a, --accessibility Scan site accessibility using pa11y" 23 | echo " -u, --url The URL to scan" 24 | echo " -q, --quiet Quiet mode" 25 | echo "" 26 | } 27 | 28 | if [[ $# -eq 0 ]]; then 29 | usage 30 | exit 1 31 | fi 32 | 33 | # Convert known long options to short options 34 | for arg in "$@"; do 35 | shift 36 | case "$arg" in 37 | --help) 38 | set -- "$@" "-h" 39 | ;; 40 | --quiet) 41 | set -- "$@" "-q" 42 | ;; 43 | --accessibility) 44 | set -- "$@" "-a" 45 | ;; 46 | --url) 47 | set -- "$@" "-u" 48 | ;; 49 | *) 50 | set -- "$@" "$arg" 51 | esac 52 | done 53 | 54 | # Reset to beginning of arguments 55 | OPTIND=1 56 | 57 | # Process background option flags first 58 | while getopts ":hqu:a" opt; do 59 | case "$opt" in 60 | h ) 61 | usage 62 | exit 0 63 | ;; 64 | q ) 65 | exec 1>/dev/null 2>/dev/null 66 | ;; 67 | u ) 68 | URL=$OPTARG 69 | ;; 70 | a ) 71 | SCANS+=(a11y) 72 | ;; 73 | \? ) 74 | echo "Unrecognized option: -$OPTARG\n" 75 | usage 76 | exit 1 77 | ;; 78 | : ) 79 | echo "The -$OPTARG option requires an argument" 80 | usage 81 | exit 1 82 | ;; 83 | esac 84 | done 85 | shift $((OPTIND -1)) 86 | 87 | for scan in ${SCANS[@]}; do 88 | case "$scan" in 89 | a11y ) 90 | check pa11y 91 | if [[ -n URL ]]; then 92 | pa11y --standard WCAG2AA $URL 93 | else 94 | echo "No URL specified. Use --url to set one." 95 | usage 96 | exit 1 97 | fi 98 | ;; 99 | esac 100 | done 101 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | usage(){ 4 | echo -e "\nUsage:\n 18f setup [options]\n" 5 | echo " Options:" 6 | echo " -h, --help Display this help message" 7 | echo " -s, --system The operating system of this machine" 8 | echo "" 9 | } 10 | 11 | setup(){ 12 | shopt -s nocasematch 13 | if [[ $SYSTEM =~ (osx|mac) ]]; then 14 | curl --silent --remote-name https://raw.githubusercontent.com/18F/laptop/master/mac 15 | echo "This uses the script found at https://github.com/18F/laptop/blob/master/mac" 16 | echo -n "Ready to run the setup script? [y/n] " 17 | read response 18 | if [[ $response =~ y(es)? ]]; then 19 | bash mac 2>&1 | tee ~/laptop.log 20 | fi 21 | rm mac 22 | else 23 | echo "Supported system options are OSX or Mac" 24 | echo "To add more, check out https://github.com/18F/laptop" 25 | fi 26 | } 27 | 28 | if [[ $# -eq 0 ]]; then 29 | usage 30 | exit 1 31 | fi 32 | 33 | # Convert known long options to short options 34 | for arg in "$@"; do 35 | shift 36 | case "$arg" in 37 | "--help") set -- "$@" "-h" ;; 38 | "--system") set -- "$@" "-s" ;; 39 | *) set -- "$@" "$arg" ;; 40 | esac 41 | done 42 | 43 | # Reset to beginning of arguments 44 | OPTIND=1 45 | 46 | # Process option flags 47 | while getopts ":hs:" opt; do 48 | case "$opt" in 49 | h ) 50 | usage 51 | exit 0 52 | ;; 53 | s ) 54 | SYSTEM=$OPTARG 55 | setup 56 | ;; 57 | \? ) 58 | echo "Unrecognized option: -$OPTARG\n" 59 | usage 60 | exit 1 61 | ;; 62 | : ) 63 | echo "The -$OPTARG option requires an argument" 64 | usage 65 | exit 1 66 | ;; 67 | esac 68 | done 69 | shift $((OPTIND -1)) 70 | -------------------------------------------------------------------------------- /bin/upgrade: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | usage(){ 4 | echo -e "\nUsage:\n 18f upgrade [options]\n" 5 | echo " Options:" 6 | echo " -h, --help Display this help message" 7 | echo "" 8 | } 9 | 10 | # Convert known long options to short options 11 | for arg in "$@"; do 12 | shift 13 | case "$arg" in 14 | "--help") set -- "$@" "-h" ;; 15 | *) set -- "$@" "$arg" ;; 16 | esac 17 | done 18 | 19 | # Reset to beginning of arguments 20 | OPTIND=1 21 | 22 | # Process option flags 23 | while getopts ":hs:" opt; do 24 | case "$opt" in 25 | h ) 26 | usage 27 | exit 0 28 | ;; 29 | \? ) 30 | echo "Unrecognized option: -$OPTARG\n" 31 | usage 32 | exit 1 33 | ;; 34 | : ) 35 | echo "The -$OPTARG option requires an argument" 36 | usage 37 | exit 1 38 | ;; 39 | esac 40 | done 41 | shift $((OPTIND -1)) 42 | 43 | REMOTE=$(curl -s https://raw.githubusercontent.com/18f/18f-cli/release/VERSION) 44 | LOCAL=$(18f --version | cut -d' ' -f2) 45 | 46 | if [[ "$REMOTE" != "$LOCAL" ]]; then 47 | curl -s https://raw.githubusercontent.com/18f/18f-cli/release/install | bash 48 | else 49 | echo "Already at the latest version!" 50 | fi 51 | -------------------------------------------------------------------------------- /bin/validate: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | usage(){ 4 | echo -e "\nUsage:\n 18f validate [options]\n" 5 | echo " Options:" 6 | echo " -h, --help Display this help message" 7 | echo " -q, --quiet Quiet mode" 8 | echo "" 9 | } 10 | 11 | # Check installations 12 | # TODO: check these during Makefile installation 13 | type about_yml_validate >/dev/null 2>&1 || { 14 | echo " ERROR: about_yml_validate not found:" 15 | echo " gem install about_yml should do the trick" 16 | exit 1 17 | } 18 | 19 | type poirot >/dev/null 2>&1 || { 20 | echo " ERROR: poirot not found:" 21 | echo " pip install poirot should do the trick" 22 | } 23 | 24 | # Convert known long options to short options 25 | for arg in "$@"; do 26 | shift 27 | case "$arg" in 28 | --help) 29 | set -- "$@" "-h" 30 | ;; 31 | --quiet) 32 | set -- "$@" "-q" 33 | ;; 34 | *) 35 | set -- "$@" "$arg" 36 | esac 37 | done 38 | 39 | # Reset to beginning of arguments 40 | OPTIND=1 41 | 42 | # Process option flags 43 | while getopts ":hq" opt; do 44 | case "$opt" in 45 | h ) 46 | usage 47 | exit 0 48 | ;; 49 | q ) 50 | exec 1>/dev/null 2>/dev/null 51 | ;; 52 | \? ) 53 | echo "Unrecognized option: -$OPTARG\n" 54 | usage 55 | exit 1 56 | ;; 57 | esac 58 | done 59 | shift $((OPTIND -1)) 60 | 61 | # TODO: about_yml_validate 62 | 63 | # Check LICENSE.md 64 | curl --silent https://raw.githubusercontent.com/18F/open-source-policy/master/LICENSE.md | cmp --silent - LICENSE.md 65 | if [[ $? == 0 ]]; then 66 | echo "✔︎ LICENSE.md is up to date" 67 | else 68 | echo "𝘅 LICENSE.md is different than expected" 69 | echo " Please run curl -sO https://raw.githubusercontent.com/18F/open-source-policy/master/LICENSE.md" 70 | fi 71 | 72 | # Check CONTRIBUTING.d 73 | curl --silent https://raw.githubusercontent.com/18F/open-source-policy/master/CONTRIBUTING.md | cmp --silent - CONTRIBUTING.md 74 | if [[ $? == 0 ]]; then 75 | echo "✔︎ CONTRIBUTING.md is up to date" 76 | else 77 | echo "𝘅 CONTRIBUTING.md is different than expected" 78 | echo " Please run curl -sO https://raw.githubusercontent.com/18F/open-source-policy/master/CONTRIBUTING.md" 79 | fi 80 | 81 | if cat README.md | awk "### Public domain\n\nThis project is in the worldwide [public domain](LICENSE.md). As stated in [CONTRIBUTING](CONTRIBUTING.md):\n\n> This project is in the public domain within the United States, and copyright and related rights in the work worldwide are waived through the [CC0 1.0 Universal public domain dedication](https://creativecommons.org/publicdomain/zero/1.0/).\n>\n> All contributions to this project will be released under the CC0 dedication. By submitting a pull request, you are agreeing to comply with this waiver of copyright interest."; then 82 | echo "✔︎ Licensing language in README.md" 83 | else 84 | echo "𝘅 Licensing language is missing from README.md" 85 | echo " Please add the public domain statement from https://github.com/18F/open-source-policy/blob/master/README.md#public-domain" 86 | fi 87 | -------------------------------------------------------------------------------- /cf-manifest/index.js: -------------------------------------------------------------------------------- 1 | const allPrompts = require('../app/prompts'); 2 | const BaseGenerator = require('../app/base-generator'); 3 | 4 | const serviceCreation = { 5 | database: 'create-service aws-rds medium-psql database', 6 | secrets: 'create-user-provided-service credentials -p \'{"my": "secret"}\'', 7 | }; 8 | 9 | 10 | module.exports = class extends BaseGenerator { 11 | constructor(args, opts) { 12 | super(args, opts); 13 | this.prompts = [ 14 | allPrompts.repoName, 15 | allPrompts.primaryLanguage, 16 | allPrompts.runCommand, 17 | allPrompts.cloudGovServices, 18 | ]; 19 | } 20 | 21 | prompting() { 22 | return this.askAndSavePrompts(); 23 | } 24 | 25 | writing() { 26 | if (this.config.get('runCommand') === allPrompts.runCommand.default) { 27 | this.addTodos({ 'Cloud.gov Manifests': ['Procfile\'s "run" command'] }); 28 | } 29 | const serviceTodos = this.config.get('cloudGovServices').map(service => 30 | `Run \`cf ${serviceCreation[service]}\` in each env`); 31 | if (serviceTodos.length > 0) { 32 | this.addTodos({ 'Cloud.gov Manifests': serviceTodos }); 33 | } 34 | 35 | this.fs.copyTpl( 36 | this.templatePath('**'), 37 | this.destinationPath(), 38 | this.config.getAll(), 39 | ); 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /cf-manifest/templates/Procfile: -------------------------------------------------------------------------------- 1 | web: <%= runCommand %> 2 | -------------------------------------------------------------------------------- /cf-manifest/templates/manifest_base.yml: -------------------------------------------------------------------------------- 1 | --- 2 | stack: cflinuxfs2 3 | buildpack: <%= primaryLanguage.toLowerCase() %>_buildpack 4 | applications: 5 | - name: web 6 | <% if (cloudGovServices.length > 0) { -%> 7 | services: 8 | <% if (cloudGovServices.includes('database')) { -%> 9 | - database # aws-rds medium-psql 10 | <% } 11 | if (cloudGovServices.includes('secret credentials')) { -%> 12 | - credentials # ups with keys: "my" 13 | <% } 14 | } -%> 15 | -------------------------------------------------------------------------------- /cf-manifest/templates/manifest_dev.yml: -------------------------------------------------------------------------------- 1 | --- 2 | inherit: manifest_base.yml 3 | host: <%= repoName %>-dev 4 | -------------------------------------------------------------------------------- /cf-manifest/templates/manifest_prod.yml: -------------------------------------------------------------------------------- 1 | --- 2 | inherit: manifest_base.yml 3 | host: <%= repoName %> 4 | instances: 2 5 | -------------------------------------------------------------------------------- /gitignores/__tests__/index.test.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | const axios = require('axios'); 5 | const helpers = require('yeoman-test'); 6 | 7 | jest.mock('axios'); 8 | 9 | describe('18f:gitignores', () => { 10 | const toRun = path.join(__dirname, '..'); 11 | beforeEach(() => { 12 | axios.get = jest.fn(() => Promise.resolve({ data: 'File\nExample' })); 13 | }); 14 | 15 | it('generates .gitignore', () => helpers.run(toRun) 16 | .withPrompts({ languages: ['Go'] }) 17 | .then((dir) => { 18 | expect(fs.existsSync(path.join(dir, '.gitignore'))).toBeTruthy(); 19 | })); 20 | 21 | it('hits github', () => helpers.run(toRun) 22 | .withPrompts({ languages: ['Node', 'Python'] }) 23 | .then(() => { 24 | expect(axios.get).toHaveBeenCalledTimes(2); // one per language 25 | expect(axios.get).toHaveBeenCalledWith( 26 | expect.stringMatching(/.*github.*Node.gitignore/)); 27 | expect(axios.get).toHaveBeenCalledWith( 28 | expect.stringMatching(/.*github.*Python.gitignore/)); 29 | })); 30 | 31 | it('writes the correct content', () => helpers.run(toRun) 32 | .withPrompts({ languages: ['Go', 'Ruby'] }) 33 | .then((dir) => { 34 | const result = fs.readFileSync(path.join(dir, '.gitignore'), 'utf-8'); 35 | expect(result).toEqual(expect.stringMatching(/# === Go ===/)); 36 | expect(result).toEqual(expect.stringMatching(/# === Ruby ===/)); 37 | expect(result).toEqual(expect.stringMatching(/File\nExample/)); 38 | })); 39 | 40 | it('does not overwrite content', () => { 41 | const originalContent = 'Stuff\n\n# === Python ===\n\nalready defined'; 42 | helpers.run(toRun) 43 | .inTmpDir((dir) => { 44 | fs.writeFileSync(path.join(dir, '.gitignore'), originalContent); 45 | }) 46 | .withPrompts({ languages: ['Python', 'Ruby'] }) 47 | .then((dir) => { 48 | const result = fs.readFileSync(path.join(dir, '.gitignore'), 'utf-8'); 49 | expect(result).toEqual( 50 | `${originalContent}\n\n# === Ruby ===\nFile\nExample`); 51 | }); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /gitignores/index.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | 3 | const allPrompts = require('../app/prompts'); 4 | const BaseGenerator = require('../app/base-generator'); 5 | 6 | 7 | function fetchAndWriteGitignore(language, fs, gitignorePath, logError) { 8 | const url = `https://raw.githubusercontent.com/github/gitignore/master/${language}.gitignore`; 9 | 10 | return axios.get(url).then((response) => { 11 | let fileContents = fs.read(gitignorePath, { defaults: '' }); 12 | fileContents += `\n\n# === ${language} ===\n${response.data}`; 13 | fs.write(gitignorePath, fileContents); 14 | }).catch(logError); 15 | } 16 | 17 | function languagesAlreadyPresent(filePath, fs) { 18 | const languages = new Set(); 19 | const fileContents = fs.read(filePath, { defaults: '' }); 20 | const search = /^# === ([^=]+) ===$/gm; 21 | let match = search.exec(fileContents); 22 | 23 | while (match !== null) { 24 | languages.add(match[1]); 25 | match = search.exec(fileContents); 26 | } 27 | return languages; 28 | } 29 | 30 | 31 | module.exports = class extends BaseGenerator { 32 | constructor(args, opts) { 33 | super(args, opts); 34 | this.prompts = [allPrompts.languages]; 35 | } 36 | 37 | prompting() { 38 | return this.askAndSavePrompts(); 39 | } 40 | 41 | writing() { 42 | const filePath = this.destinationPath('.gitignore'); 43 | const existingLanguages = languagesAlreadyPresent(filePath, this.fs); 44 | const requests = this.config.get('languages') 45 | .filter(language => !existingLanguages.has(language)) 46 | .map(language => fetchAndWriteGitignore( 47 | language, this.fs, this.destinationPath('.gitignore'), 48 | this.env.error.bind(this.env))); 49 | return Promise.all(requests); 50 | } 51 | }; 52 | 53 | -------------------------------------------------------------------------------- /install: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | PREFIX=/usr/local/bin 4 | ORGNAME="18f" 5 | 6 | #TODO: make sure we're in the RIGHT repo 7 | if git status 2>&1 | grep fatal > /dev/null || ! [[ $(git remote show -n origin | grep Fetch | cut -d':' -f2- | tr -d ' ') == "git@github.com:18F/18f-cli.git" ]]; then 8 | CURL=true 9 | cd /tmp || exit 1 10 | git clone https://github.com/18f/18f-cli 11 | cd 18f-cli || exit 1 12 | fi 13 | 14 | echo "Installing main $ORGNAME command" 15 | cp main $PREFIX/$ORGNAME 16 | 17 | # Create version script 18 | echo -e "#! /usr/bin/env bash\n\necho version $(cat VERSION)" >> bin/version 19 | 20 | cd bin || exit 1 21 | chmod +x * 22 | BINS=$(find *) 23 | for bin in $BINS; do 24 | echo "Installing $bin subcommand" 25 | cp $bin $PREFIX/$ORGNAME-$bin 26 | done 27 | 28 | if [[ $CURL ]]; then 29 | cd /tmp || exit 1 30 | rm -rf 18f-cli 31 | fi 32 | -------------------------------------------------------------------------------- /license/index.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | const BaseGenerator = require('../app/base-generator'); 3 | 4 | module.exports = class extends BaseGenerator { 5 | writing() { 6 | const licenseReq = axios.get( 7 | 'https://raw.githubusercontent.com/18F/open-source-policy/master/LICENSE.md') 8 | .then((response) => { 9 | this.fs.write(this.destinationPath('LICENSE.md'), response.data); 10 | }); 11 | const contributingReq = axios.get( 12 | 'https://raw.githubusercontent.com/18F/open-source-policy/master/CONTRIBUTING.md') 13 | .then((response) => { 14 | this.fs.write(this.destinationPath('CONTRIBUTING.md'), response.data); 15 | }); 16 | return Promise.all([licenseReq, contributingReq]) 17 | .catch(this.env.error.bind(this.env)); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /main: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | CMD=$(basename "$0") 4 | ARGS="" 5 | 6 | usage(){ 7 | AVAILABLE=$(compgen -c 18f-) 8 | echo -e "\nUsage:\n $ORG [options] [subcommand options] \n" 9 | echo " Options:" 10 | echo " -h, --help Display this help message" 11 | echo " -v, --version Display version information" 12 | echo -e "\nAvailable subcommands:\n${AVAILABLE//18f-/- }\n" 13 | } 14 | 15 | ORG=$( basename "$0" | cut -d'-' -f1 ) 16 | 17 | if [[ $# -eq 0 ]]; then 18 | usage 19 | exit 1 20 | fi 21 | 22 | # Convert known long options to short options 23 | for arg in "$@"; do 24 | shift 25 | case "$arg" in 26 | --help) 27 | set -- "$@" "-h" 28 | ;; 29 | --version) 30 | set -- "$@" "-v" 31 | ;; 32 | *) 33 | set -- "$@" "$arg" 34 | ;; 35 | esac 36 | done 37 | 38 | # Reset to beginning of arguments 39 | OPTIND=1 40 | 41 | # Process option flags 42 | while getopts ":hv" opt; do 43 | case "$opt" in 44 | h ) 45 | usage 46 | exit 0 47 | ;; 48 | v ) 49 | 18f version 50 | exit 0 51 | ;; 52 | \? ) 53 | echo -e "Unrecognized option: -$OPTARG\n" 54 | usage 55 | exit 1 56 | ;; 57 | esac 58 | done 59 | shift $((OPTIND -1)) 60 | 61 | # Find subcommands and subcommand arguments 62 | SUB=$1; shift 63 | for var in "$@"; do 64 | if [[ $var == \-* ]] 65 | then 66 | ARGS+="$var " 67 | else 68 | ARGS+="\"$var\" " 69 | fi 70 | done 71 | 72 | # Send to subcommand 73 | eval "${CMD}-${SUB} ${ARGS}" 74 | -------------------------------------------------------------------------------- /newrelic/index.js: -------------------------------------------------------------------------------- 1 | const jsyaml = require('js-yaml'); 2 | 3 | const allPrompts = require('../app/prompts'); 4 | const BaseGenerator = require('../app/base-generator'); 5 | 6 | 7 | module.exports = class extends BaseGenerator { 8 | constructor(args, opts) { 9 | // Calling the super constructor is important so our generator is correctly set up 10 | super(args, opts); 11 | this.prompts = [ 12 | allPrompts.repoName, 13 | allPrompts.primaryLanguage, 14 | allPrompts.languages, 15 | ]; 16 | } 17 | 18 | // choices are dev, production 19 | _writeToManifest(environment) { 20 | const manifest = `manifest_${environment}.yml`; 21 | let manifestBody; 22 | if (this.fs.exists(manifest)) { 23 | manifestBody = this.fs.read(manifest, 'utf8'); 24 | const doc = jsyaml.safeLoad(manifestBody); 25 | if (!Object.keys(doc).includes('applications')) { 26 | doc.applications = {}; 27 | } 28 | doc.applications.env = doc.applications.env || []; 29 | const fileByLang = { 30 | Python: 'newrelic.ini', 31 | Ruby: 'newrelic.yml', 32 | Node: 'newrelic.js', 33 | }; 34 | const primaryLang = this.config.get('primaryLanguage'); 35 | if (Object.keys(fileByLang).includes(primaryLang)) { 36 | doc.applications.env.push({ 37 | NEW_RELIC_APP_NAME: `${this.config.get('repoName')} (${environment})`, 38 | NEW_RELIC_CONFIG_FILE: fileByLang[primaryLang], 39 | NEW_RELIC_ENV: environment, // note that we don't need the string template 40 | NEW_RELIC_LOG: 'stdout', 41 | }); 42 | } 43 | this.fs.write(manifest, jsyaml.safeDump(doc)); 44 | } else { 45 | this.log('Please run yo 18f:cf-manifest first'); 46 | } 47 | } 48 | 49 | _writeToRequirementsTxt() { 50 | const requirementsFile = 'requirements.txt'; 51 | if (this.fs.exists(requirementsFile)) { 52 | let requirementsBody = this.fs.read(requirementsFile, 'utf8'); 53 | if (requirementsBody.indexOf('newrelic') < 0) { 54 | requirementsBody += '\nnewrelic\n'; 55 | this.fs.write(requirementsFile, requirementsBody); 56 | } 57 | } else { 58 | this.log('Please create requirements.txt first'); 59 | } 60 | } 61 | 62 | _writeToPackagesJson() { 63 | const packageJson = 'package.json'; 64 | if (this.fs.exists(packageJson)) { 65 | const packageObj = this.fs.readJSON(packageJson); 66 | const deps = packageObj.dependencies || {}; 67 | if (!deps.newrelic) { 68 | deps.newrelic = 'latest'; 69 | packageObj.dependencies = deps; 70 | this.fs.writeJSON(packageJson, packageObj); 71 | } 72 | } else { 73 | this.log('Please create package.json first'); 74 | } 75 | } 76 | 77 | _writeToGemfile() { 78 | const gemfile = 'Gemfile'; 79 | if (this.fs.exists(gemfile)) { 80 | let gembody = this.fs.read(gemfile, 'utf8'); 81 | if (gembody.indexOf("source 'https://rubygems.org'") < 0) { 82 | gembody += "source 'https://rubygems.org'"; 83 | } 84 | if (gembody.indexOf('newrelic_rpm') < 0) { 85 | gembody += "\ngem 'newrelic_rpm'\n"; 86 | this.fs.append(gemfile, gembody, (err) => { 87 | if (err) throw err; 88 | }); 89 | } 90 | this.log("Don't forget to run bundle install!"); 91 | } else { 92 | this.log('Please create Gemfile first'); 93 | } 94 | } 95 | 96 | prompting() { 97 | return this.askAndSavePrompts(); 98 | } 99 | 100 | writing() { 101 | const languages = this.config.get('languages'); 102 | const context = this.config.getAll(); 103 | if (languages.includes('Python')) { 104 | this.fs.copyTpl(this.templatePath('python-low-security.ini'), this.destinationPath('newrelic.ini'), context); 105 | this._writeToRequirementsTxt(); 106 | } 107 | if (languages.includes('Ruby')) { 108 | this.fs.copyTpl(this.templatePath('ruby-low-security.yml'), this.destinationPath('newrelic.yml'), context); 109 | this._writeToGemfile(); 110 | } 111 | if (languages.includes('Node')) { 112 | this.fs.copyTpl(this.templatePath('javascript-low-security.js.template'), this.destinationPath('newrelic.js'), context); 113 | this._writeToPackagesJson(); 114 | } 115 | // update manifest_dev.yml 116 | this._writeToManifest('dev'); 117 | // update manifest_prod.yml 118 | this._writeToManifest('prod'); 119 | } 120 | }; 121 | -------------------------------------------------------------------------------- /newrelic/templates/javascript-low-security.js.template: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * New Relic agent configuration. 5 | * 6 | * See https://docs.newrelic.com/docs/agents/nodejs-agent/installation-configuration/nodejs-agent-configuration 7 | * for a more complete description of configuration variables and their potential values. 8 | */ 9 | exports.config = { 10 | /** 11 | * Array of application names. 12 | */ 13 | app_name: [ "<%= repoName%> " ], 14 | 15 | /** 16 | * Whether the module is enabled. 17 | * 18 | * --18F-- 19 | * This means that new relic will be running. You might not 20 | * want this to run on a testing environment, so override it using 21 | * environment variable: NEW_RELIC_ENABLED 22 | */ 23 | agent_enabled: true, 24 | 25 | /** 26 | * Whether or not to use SSL to connect to New Relic's servers. 27 | * 28 | * --18F-- 29 | * This needs to be true 30 | */ 31 | ssl: true, 32 | 33 | logging: { 34 | /** 35 | * Level at which to log. 'trace' is most useful to New Relic when diagnosing 36 | * issues with the agent, 'info' and higher will impose the least overhead on 37 | * production applications. 38 | */ 39 | level: 'info' 40 | }, 41 | 42 | /** 43 | * High Security 44 | * 45 | * High security mode (v2) is a setting which prevents any sensitive data from 46 | * being sent to New Relic. The local setting must match the server setting. 47 | * If there is a mismatch the agent will log a message and act as if it is 48 | * disabled. 49 | * 50 | * Attributes of high security mode (when enabled): 51 | * * requires SSL 52 | * * does not allow capturing of http params 53 | * * does not allow custom params 54 | * 55 | * To read more see: https://docs.newrelic.com/docs/subscriptions/high-security 56 | */ 57 | high_security: false, 58 | 59 | /** 60 | * Whether to capture parameters in the request URL in slow transaction 61 | * traces and error traces. Because this can pass sensitive data, it's 62 | * disabled by default. If there are specific parameters you want ignored, 63 | * use ignored_params. 64 | * 65 | * --18F-- 66 | * Keep as false, look for that level of detail in the local logs. 67 | */ 68 | capture_params: false, 69 | 70 | transaction_tracer: { 71 | /** 72 | * Whether to collect & submit slow transaction traces to New Relic. The 73 | * instrumentation is loaded regardless of this setting, as it's necessary 74 | * to gather metrics. Disable the agent to prevent the instrumentation from 75 | * loading. 76 | * 77 | * @env NEW_RELIC_TRACER_ENABLED 78 | */ 79 | enabled: true, 80 | /** 81 | * The duration at below which the slow transaction tracer should collect a 82 | * transaction trace. If set to 'apdex_f', the threshold will be set to 83 | * 4 * apdex_t, which with a default apdex_t value of 500 milliseconds will 84 | * be 2 seconds. 85 | * 86 | * If a time is provided, it is set in seconds. 87 | * 88 | * @env NEW_RELIC_TRACER_THRESHOLD 89 | */ 90 | transaction_threshold: 'apdex_f', 91 | /** 92 | * Increase this parameter to increase the diversity of the slow 93 | * transaction traces recorded by your application over time. Confused? 94 | * Read on. 95 | * 96 | * Transactions are named based on the request (see the README for the 97 | * details of how requests are mapped to transactions), and top_n refers to 98 | * the "top n slowest transactions" grouped by these names. The module will 99 | * only replace a recorded trace with a new trace if the new trace is 100 | * slower than the previous slowest trace of that name. The default value 101 | * for this setting is 20, as the transaction trace view page also defaults 102 | * to showing the 20 slowest transactions. 103 | * 104 | * If you want to record the absolute slowest transaction over the last 105 | * minute, set top_n to 0 or 1. This used to be the default, and has a 106 | * problem in that it will allow one very slow route to dominate your slow 107 | * transaction traces. 108 | * 109 | * The module will always record at least 5 different slow transactions in 110 | * the reporting periods after it starts up, and will reset its internal 111 | * slow trace aggregator if no slow transactions have been recorded for the 112 | * last 5 harvest cycles, restarting the aggregation process. 113 | * 114 | * @env NEW_RELIC_TRACER_TOP_N 115 | */ 116 | top_n: 20, 117 | 118 | /** 119 | * This option affects both slow-queries and record_sql for transaction 120 | * traces. It can have one of 3 values: 'off', 'obfuscated' or 'raw' 121 | * When it is 'off' no slow queries will be captured, and backtraces 122 | * and sql will not be included in transaction traces. If it is 'raw' 123 | * or 'obfuscated' and other criteria (slow_sql.enabled etc) are met 124 | * for a query. The raw or obfuscated sql will be included in the 125 | * transaction trace and a slow query sample will be collected. 126 | * 127 | * --18F-- 128 | * Don't change to raw, if you need to capture specific values 129 | * use something other than New Relic. 130 | */ 131 | record_sql: 'obfuscated', 132 | 133 | /** 134 | * This option affects both slow-queries and record_sql for transaction 135 | * traces. This is the minimum duration a query must take (in ms) for it 136 | * to be considered for for slow query and inclusion in transaction traces. 137 | */ 138 | explain_threshold: 500 139 | } 140 | 141 | } 142 | -------------------------------------------------------------------------------- /newrelic/templates/python-low-security.ini: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------- 2 | 3 | # 4 | # This file configures the New Relic Python Agent. 5 | # 6 | # The path to the configuration file should be supplied to the function 7 | # newrelic.agent.initialize() when the agent is being initialized. 8 | # 9 | # The configuration file follows a structure similar to what you would 10 | # find for Microsoft Windows INI files. For further information on the 11 | # configuration file format see the Python ConfigParser documentation at: 12 | # 13 | # http://docs.python.org/library/configparser.html 14 | # 15 | # For further discussion on the behavior of the Python agent that can 16 | # be configured via this configuration file see: 17 | # 18 | # http://newrelic.com/docs/python/python-agent-configuration 19 | # 20 | 21 | # --------------------------------------------------------------------------- 22 | 23 | # Here are the settings that are common to all environments. 24 | 25 | [newrelic] 26 | 27 | # You must specify the license key associated with your New 28 | # Relic account. This key binds the Python Agent's data to your 29 | # account in the New Relic service. 30 | 31 | # The appplication name. Set this to be the name of your 32 | # application as you would like it to show up in New Relic UI. 33 | # The UI will then auto-map instances of your application into a 34 | # entry on your home dashboard page. 35 | app_name = <%= repoName%> 36 | 37 | # When "true", the agent collects performance data about your 38 | # application and reports this data to the New Relic UI at 39 | # newrelic.com. This global switch is normally overridden for 40 | # each environment below. 41 | # --18F-- 42 | # This means that the new relic will be running. You might not 43 | # want this to run on a testing environment, so override it using 44 | # environment variables. 45 | monitor_mode = true 46 | 47 | # Sets the name of a file to log agent messages to. Useful for 48 | # debugging any issues with the agent. This is not set by 49 | # default as it is not known in advance what user your web 50 | # application processes will run as and where they have 51 | # permission to write to. Whatever you set this to you must 52 | # ensure that the permissions for the containing directory and 53 | # the file itself are correct, and that the user that your web 54 | # application runs as can write to the file. If not able to 55 | # write out a log file, it is also possible to say "stderr" and 56 | # output to standard error output. This would normally result in 57 | # output appearing in your web server log. 58 | log_file = /tmp/newrelic-python-agent.log 59 | 60 | # Sets the level of detail of messages sent to the log file, if 61 | # a log file location has been provided. Possible values, in 62 | # increasing order of detail, are: "critical", "error", "warning", 63 | # "info" and "debug". When reporting any agent issues to New 64 | # Relic technical support, the most useful setting for the 65 | # support engineers is "debug". However, this can generate a lot 66 | # of information very quickly, so it is best not to keep the 67 | # agent at this level for longer than it takes to reproduce the 68 | # problem you are experiencing. 69 | log_level = info 70 | 71 | # The Python Agent communicates with the New Relic service using 72 | # SSL by default. Note that this does result in an increase in 73 | # CPU overhead, over and above what would occur for a non SSL 74 | # connection, to perform the encryption involved in the SSL 75 | # communication. This work is though done in a distinct thread 76 | # to those handling your web requests, so it should not impact 77 | # response times. You can if you wish revert to using a non SSL 78 | # connection, but this will result in information being sent 79 | # over a plain socket connection and will not be as secure. 80 | # --18F-- 81 | # This needs to be true 82 | ssl = true 83 | 84 | # High Security Mode enforces certain security settings, and 85 | # prevents them from being overridden, so that no sensitive data 86 | # is sent to New Relic. Enabling High Security Mode means that 87 | # SSL is turned on, request parameters are not collected, and SQL 88 | # can not be sent to New Relic in its raw form. To activate High 89 | # Security Mode, it must be set to 'true' in this local .ini 90 | # configuration file AND be set to 'true' in the server-side 91 | # configuration in the New Relic user interface. For details, see 92 | # https://docs.newrelic.com/docs/subscriptions/high-security 93 | high_security = false 94 | 95 | # The Python Agent will attempt to connect directly to the New 96 | # Relic service. If there is an intermediate firewall between 97 | # your host and the New Relic service that requires you to use a 98 | # HTTP proxy, then you should set both the "proxy_host" and 99 | # "proxy_port" settings to the required values for the HTTP 100 | # proxy. The "proxy_user" and "proxy_pass" settings should 101 | # additionally be set if proxy authentication is implemented by 102 | # the HTTP proxy. The "proxy_scheme" setting dictates what 103 | # protocol scheme is used in talking to the HTTP proxy. This 104 | # would normally always be set as "http" which will result in the 105 | # agent then using a SSL tunnel through the HTTP proxy for end to 106 | # end encryption. 107 | # proxy_scheme = http 108 | # proxy_host = hostname 109 | # proxy_port = 8080 110 | # proxy_user = 111 | # proxy_pass = 112 | 113 | # Tells the transaction tracer and error collector (when 114 | # enabled) whether or not to capture the query string for the 115 | # URL and send it as the request parameters for display in the 116 | # UI. When "true", it is still possible to exclude specific 117 | # values from being captured using the "ignored_params" setting. 118 | # --18F-- 119 | # Keep as false, look for that level of detail in the local logs. 120 | capture_params = false 121 | 122 | # Space separated list of variables that should be removed from 123 | # the query string captured for display as the request 124 | # parameters in the UI. 125 | ignored_params = 126 | 127 | # The transaction tracer captures deep information about slow 128 | # transactions and sends this to the UI on a periodic basis. The 129 | # transaction tracer is enabled by default. Set this to "false" 130 | # to turn it off. 131 | transaction_tracer.enabled = true 132 | 133 | # Threshold in seconds for when to collect a transaction trace. 134 | # When the response time of a controller action exceeds this 135 | # threshold, a transaction trace will be recorded and sent to 136 | # the UI. Valid values are any positive float value, or (default) 137 | # "apdex_f", which will use the threshold for a dissatisfying 138 | # Apdex controller action - four times the Apdex T value. 139 | transaction_tracer.transaction_threshold = apdex_f 140 | 141 | # When the transaction tracer is on, SQL statements can 142 | # optionally be recorded. The recorder has three modes, "off" 143 | # which sends no SQL, "raw" which sends the SQL statement in its 144 | # original form, and "obfuscated", which strips out numeric and 145 | # string literals. 146 | # --18F-- 147 | # Don't change to raw, if you need to capture specific values 148 | # use something other than New Relic. 149 | transaction_tracer.record_sql = obfuscated 150 | 151 | # Threshold in seconds for when to collect stack trace for a SQL 152 | # call. In other words, when SQL statements exceed this 153 | # threshold, then capture and send to the UI the current stack 154 | # trace. This is helpful for pinpointing where long SQL calls 155 | # originate from in an application. 156 | transaction_tracer.stack_trace_threshold = 0.5 157 | 158 | # Determines whether the agent will capture query plans for slow 159 | # SQL queries. Only supported in MySQL and PostgreSQL. Set this 160 | # to "false" to turn it off. 161 | transaction_tracer.explain_enabled = true 162 | 163 | # Threshold for query execution time below which query plans 164 | # will not not be captured. Relevant only when "explain_enabled" 165 | # is true. 166 | transaction_tracer.explain_threshold = 0.5 167 | 168 | # Space separated list of function or method names in form 169 | # 'module:function' or 'module:class.function' for which 170 | # additional function timing instrumentation will be added. 171 | transaction_tracer.function_trace = 172 | 173 | # The error collector captures information about uncaught 174 | # exceptions or logged exceptions and sends them to UI for 175 | # viewing. The error collector is enabled by default. Set this 176 | # to "false" to turn it off. 177 | error_collector.enabled = true 178 | 179 | # To stop specific errors from reporting to the UI, set this to 180 | # a space separated list of the Python exception type names to 181 | # ignore. The exception name should be of the form 'module:class'. 182 | error_collector.ignore_errors = 183 | 184 | # Browser monitoring is the Real User Monitoring feature of the UI. 185 | # For those Python web frameworks that are supported, this 186 | # setting enables the auto-insertion of the browser monitoring 187 | # JavaScript fragments. 188 | # --18F-- 189 | # Weigh the security implications of inserting javascript into the 190 | # browser. 191 | browser_monitoring.auto_instrument = true 192 | 193 | # A thread profiling session can be scheduled via the UI when 194 | # this option is enabled. The thread profiler will periodically 195 | # capture a snapshot of the call stack for each active thread in 196 | # the application to construct a statistically representative 197 | # call tree. 198 | thread_profiler.enabled = true 199 | 200 | # --------------------------------------------------------------------------- 201 | 202 | # 203 | # The application environments. These are specific settings which 204 | # override the common environment settings. The settings related to a 205 | # specific environment will be used when the environment argument to the 206 | # newrelic.agent.initialize() function has been defined to be either 207 | # "development", "test", "staging" or "production". 208 | # 209 | 210 | [NEW_RELIC_ENV:test] 211 | monitor_mode = false 212 | 213 | [newrelic:development] 214 | monitor_mode = false 215 | 216 | [newrelic:staging] 217 | app_name = <%= repoName%> (Staging) 218 | monitor_mode = true 219 | 220 | [newrelic:production] 221 | app_name = <%= repoName%> (Production) 222 | monitor_mode = true 223 | 224 | # --------------------------------------------------------------------------- 225 | -------------------------------------------------------------------------------- /newrelic/templates/ruby-low-security.yml: -------------------------------------------------------------------------------- 1 | # 2 | # This file configures the New Relic Agent. New Relic monitors Ruby, Java, 3 | # .NET, PHP, Python and Node applications with deep visibility and low 4 | # overhead. For more information, visit www.newrelic.com. 5 | # 6 | # Generated June 20, 2017 7 | # 8 | # This configuration file is custom generated for General Services Administration_1 9 | # 10 | # For full documentation of agent configuration options, please refer to 11 | # https://docs.newrelic.com/docs/agents/ruby-agent/installation-configuration/ruby-agent-configuration 12 | 13 | common: &default_settings 14 | 15 | # Your application name. Renaming here affects where data displays in New 16 | # Relic. For more details, see https://docs.newrelic.com/docs/apm/new-relic-apm/maintenance/renaming-applications 17 | app_name: <%= repoName%> 18 | 19 | # To disable the agent regardless of other settings, uncomment the following: 20 | # agent_enabled: false 21 | 22 | # Sets the name of a file to log agent messages to. Useful for 23 | # debugging any issues with the agent. This is not set by 24 | # default as it is not known in advance what user your web 25 | # application processes will run as and where they have 26 | # permission to write to. Whatever you set this to you must 27 | # ensure that the permissions for the containing directory and 28 | # the file itself are correct, and that the user that your web 29 | # application runs as can write to the file. If not able to 30 | # write out a log file, it is also possible to say "stderr" and 31 | # output to standard error output. This would normally result in 32 | # output appearing in your web server log. 33 | log_file: /tmp/newrelic-ruby-agent.log 34 | 35 | # Logging level for log/newrelic_agent.log 36 | log_level: info 37 | 38 | # When "true", the agent collects performance data about your 39 | # application and reports this data to the New Relic UI at 40 | # newrelic.com. This global switch is normally overridden for 41 | # each environment below. 42 | monitor_mode: true 43 | 44 | # The Ruby Agent communicates with the New Relic service using 45 | # SSL by default. Note that this does result in an increase in 46 | # CPU overhead, over and above what would occur for a non SSL 47 | # connection, to perform the encryption involved in the SSL 48 | # communication. This work is though done in a distinct thread 49 | # to those handling your web requests, so it should not impact 50 | # response times. You can if you wish revert to using a non SSL 51 | # connection, but this will result in information being sent 52 | # over a plain socket connection and will not be as secure. 53 | ssl: true 54 | 55 | # High Security Mode enforces certain security settings, and 56 | # prevents them from being overridden, so that no sensitive data 57 | # is sent to New Relic. Enabling High Security Mode means that 58 | # SSL is turned on, request parameters are not collected, and SQL 59 | # can not be sent to New Relic in its raw form. To activate High 60 | # Security Mode, it must be set to 'true' in this local .ini 61 | # configuration file AND be set to 'true' in the server-side 62 | # configuration in the New Relic user interface. For details, see 63 | # https://docs.newrelic.com/docs/subscriptions/high-security 64 | high_security: false 65 | 66 | # The transaction tracer captures deep information about slow 67 | # transactions and sends this to the UI on a periodic basis. The 68 | # transaction tracer is enabled by default. Set this to "false" 69 | # to turn it off. 70 | transaction_tracer.enabled: true 71 | 72 | # Threshold in seconds for when to collect a transaction trace. 73 | # When the response time of a controller action exceeds this 74 | # threshold, a transaction trace will be recorded and sent to 75 | # the UI. Valid values are any positive float value, or (default) 76 | # "apdex_f", which will use the threshold for a dissatisfying 77 | # Apdex controller action - four times the Apdex T value. 78 | transaction_tracer.transaction_threshold: apdex_f 79 | 80 | # When the transaction tracer is on, SQL statements can 81 | # optionally be recorded. The recorder has three modes, "off" 82 | # which sends no SQL, "raw" which sends the SQL statement in its 83 | # original form, and "obfuscated", which strips out numeric and 84 | # string literals. 85 | transaction_tracer.record_sql: obfuscated 86 | 87 | # Threshold in seconds for when to collect stack trace for a SQL 88 | # call. In other words, when SQL statements exceed this 89 | # threshold, then capture and send to the UI the current stack 90 | # trace. This is helpful for pinpointing where long SQL calls 91 | # originate from in an application. 92 | transaction_tracer.stack_trace_threshold: 0.5 93 | 94 | # Determines whether the agent will capture query plans for slow 95 | # SQL queries. Only supported in MySQL and PostgreSQL. Set this 96 | # to "false" to turn it off. 97 | transaction_tracer.explain_enabled: true 98 | 99 | # Threshold for query execution time below which query plans 100 | # will not not be captured. Relevant only when "explain_enabled" 101 | # is true. 102 | transaction_tracer.explain_threshold: 0.5 103 | 104 | # Space separated list of function or method names in form 105 | # 'module:function' or 'module:class.function' for which 106 | # additional function timing instrumentation will be added. 107 | transaction_tracer.function_trace: 108 | 109 | # The error collector captures information about uncaught 110 | # exceptions or logged exceptions and sends them to UI for 111 | # viewing. The error collector is enabled by default. Set this 112 | # to "false" to turn it off. 113 | error_collector.enabled: true 114 | 115 | # To stop specific errors from reporting to the UI, set this to 116 | # a space separated list of the Python exception type names to 117 | # ignore. The exception name should be of the form 'module:class'. 118 | error_collector.ignore_errors: 119 | 120 | # Browser monitoring is the Real User Monitoring feature of the UI. 121 | # For those Python web frameworks that are supported, this 122 | # setting enables the auto-insertion of the browser monitoring 123 | # JavaScript fragments. 124 | browser_monitoring.auto_instrument: true 125 | 126 | # A thread profiling session can be scheduled via the UI when 127 | # this option is enabled. The thread profiler will periodically 128 | # capture a snapshot of the call stack for each active thread in 129 | # the application to construct a statistically representative 130 | # call tree. 131 | thread_profiler.enabled: true 132 | 133 | # Environment-specific settings are in this section. 134 | # RAILS_ENV or RACK_ENV (as appropriate) is used to determine the environment. 135 | # If your application has other named environments, configure them here. 136 | development: 137 | <<: *default_settings 138 | app_name: <%= repoName%> (Development) 139 | 140 | test: 141 | <<: *default_settings 142 | # It doesn't make sense to report to New Relic from automated test runs. 143 | monitor_mode: false 144 | 145 | staging: 146 | <<: *default_settings 147 | app_name: <%= repoName%> (Staging) 148 | 149 | production: 150 | <<: *default_settings 151 | app_name: <%= repoName%> (Production) 152 | -------------------------------------------------------------------------------- /npm/index.js: -------------------------------------------------------------------------------- 1 | const allPrompts = require('../app/prompts'); 2 | const BaseGenerator = require('../app/base-generator'); 3 | 4 | module.exports = class extends BaseGenerator { 5 | constructor(args, opts) { 6 | super(args, opts); 7 | this.prompts = [ 8 | allPrompts.repoName, 9 | allPrompts.description, 10 | allPrompts.frontendDeps, 11 | allPrompts.licenseShortName, 12 | ]; 13 | } 14 | 15 | prompting() { 16 | return this.askAndSavePrompts(); 17 | } 18 | 19 | configuring() { 20 | if (this.config.get('frontendDeps')) { 21 | this.log('Configuring npm now...'); 22 | this.composeWith(require.resolve('generator-npm-init/app'), { 23 | name: this.config.get('repoName'), 24 | description: this.config.get('description'), 25 | repo: `https://github.com/18F/${this.config.get('repoName')}`, 26 | author: '18f', 27 | license: this.config.get('licenseShortName'), 28 | }); 29 | } 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generator-18f", 3 | "version": "0.4.0", 4 | "description": "Scaffolding for 18F projects", 5 | "scripts": { 6 | "test": "eslint . && jest" 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/18F/18f-cli.git" 11 | }, 12 | "license": "CC0-1.0", 13 | "bugs": { 14 | "url": "https://github.com/18F/18f-cli/issues" 15 | }, 16 | "files": [ 17 | "app", 18 | "cf-manifest", 19 | "gitignores", 20 | "license", 21 | "newrelic", 22 | "npm", 23 | "readme", 24 | "newrelic" 25 | ], 26 | "homepage": "https://github.com/18F/18f-cli#readme", 27 | "dependencies": { 28 | "axios": "^0.15.3", 29 | "generator-npm-init": "^1.3.1", 30 | "js-yaml": "^3.8.4", 31 | "yeoman-generator": "^1.0.1" 32 | }, 33 | "devDependencies": { 34 | "eslint": "^3.15.0", 35 | "eslint-config-airbnb": "^15.0.1", 36 | "eslint-config-airbnb-base": "^11.1.1", 37 | "eslint-plugin-import": "^2.2.0", 38 | "eslint-plugin-jest": "^19.0.1", 39 | "jest": "^19.0.2", 40 | "yeoman-test": "^1.6.0" 41 | }, 42 | "engines": { 43 | "node": ">=8.0.0" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /readme/index.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | 3 | const allPrompts = require('../app/prompts'); 4 | const BaseGenerator = require('../app/base-generator'); 5 | 6 | 7 | module.exports = class extends BaseGenerator { 8 | constructor(args, options) { 9 | super(args, options); 10 | this.prompts = [allPrompts.projectFullName]; 11 | } 12 | 13 | prompting() { 14 | return this.askAndSavePrompts(); 15 | } 16 | 17 | writing() { 18 | return axios.get( 19 | 'https://raw.githubusercontent.com/18F/open-source-policy/master/README_TEMPLATE.md') 20 | .then((response) => { 21 | // Not EJS style, so we'll just search-and-replace 22 | const content = response.data.replace( 23 | '[Repo Name]', this.config.get('projectFullName')); 24 | this.fs.write(this.destinationPath('README.md'), content); 25 | }).catch(this.env.error.bind(this.env)); 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /scripts/cf-db: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | # This script creates a psql binary for use when ssh-ing into cloud.gov 4 | curl https://s3.amazonaws.com/18f-cf-cli/psql-9.4.4-ubuntu-14.04.tar.gz | tar xvz 5 | ./psql/bin/psql $DATABASE_URL 6 | -------------------------------------------------------------------------------- /scripts/deploy: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | set -e 4 | 5 | API="https://api.cloud.gov" 6 | cf login -a $API -u $CF_USERNAME -p $CF_PASSWORD 7 | 8 | if [[ $1 = "production" ]]; then 9 | MANIFEST="manifest.yml" 10 | else 11 | MANIFEST="manifest-${1}.yml" 12 | fi 13 | 14 | NAME=$(cat $MANIFEST | grep name | cut -d':' -f2 | tr -d ' ') 15 | if [[ ! $CF_ORG ]] || [[ ! $CF_SPACE ]]; then 16 | CF_APP_DATA=$(cf curl "/v2/apps" -X GET -H "Content-Type: application/x-www-form-urlencoded" | jq '.resources[] | select(.entity.name == "'${NAME}'")') 17 | SPACE_URL=$(echo $CF_APP_DATA | jq '.entity.space_url' | tr -d '"') 18 | CF_SPACE_DATA=$(cf curl "${SPACE_URL}" -X GET -H "Content-Type: application/x-www-form-urlencoded") 19 | CF_SPACE=$(echo $CF_SPACE_DATA | jq '.entity.name' | tr -d '"') 20 | ORG_URL=$(echo $CF_SPACE_DATA | jq '.entity.organization_url' | tr -d '"') 21 | CF_ORG=$(cf curl ${ORG_URL} -X GET -H "Content-Type: application/x-www-form-urlencoded" | jq '.entity.name' | tr -d '"') 22 | fi 23 | 24 | cf target -o $CF_ORG -s $CF_SPACE 25 | cf zero-downtime-push $NAME -f $MANIFEST 26 | -------------------------------------------------------------------------------- /scripts/latest-commit: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | cd "$(git rev-parse --show-toplevel)" || exit 1 4 | 5 | FILE=$(find . -name latest-commit.txt) 6 | 7 | if [[ -n $FILE ]]; then 8 | git rev-parse --abbrev-ref HEAD > $FILE 9 | git rev-parse HEAD >> $FILE 10 | else 11 | : 12 | fi 13 | --------------------------------------------------------------------------------