├── .gitignore ├── lib ├── config.js ├── confirm.js ├── package.js ├── help.txt ├── commit.js └── github.js ├── .npmignore ├── test.js ├── LICENSE.md ├── package.json ├── README.md └── cmd.js /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | -------------------------------------------------------------------------------- /lib/config.js: -------------------------------------------------------------------------------- 1 | var conf = require('npmconf') 2 | 3 | module.exports = function config(cb) { 4 | conf.load({}, cb) 5 | } -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | test 7 | test.js 8 | demo/ 9 | .npmignore 10 | LICENSE.md -------------------------------------------------------------------------------- /lib/confirm.js: -------------------------------------------------------------------------------- 1 | var inquirer = require("inquirer") 2 | 3 | module.exports = function(message, cb) { 4 | inquirer.prompt([{ 5 | type: 'confirm', 6 | name: 'confirm', 7 | message: message 8 | }], function(answers) { 9 | if (answers.confirm) 10 | cb(null) 11 | else 12 | cb(new Error('cancelled')) 13 | }) 14 | } -------------------------------------------------------------------------------- /lib/package.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var path = require('path') 3 | var noop = require('no-op') 4 | 5 | module.exports = function(cb) { 6 | cb = cb||noop 7 | var cwd = process.cwd() 8 | fs.readFile(path.join(cwd, 'package.json'), 'utf8', function(err, data) { 9 | if (err) return cb(err) 10 | try { 11 | data = JSON.parse(data) 12 | } catch (e) { 13 | return cb(e) 14 | } 15 | cb(null, data) 16 | }) 17 | } -------------------------------------------------------------------------------- /lib/help.txt: -------------------------------------------------------------------------------- 1 | Usage: 2 | ghrepo [opts] 3 | 4 | Options: 5 | --help show help 6 | --version, -v prints version number 7 | --name, -n the name of the repository 8 | --description, -d the description 9 | --homepage, -h the homepage URL 10 | --private, -p mark the repository as private (default false) 11 | --message, -m the commit message (default "first commit") 12 | --bare, -b do not run any git commands after creating the repo 13 | --no-open do not open the GitHub repo in the browser after creation 14 | --org, -o the organization to create the repo in (optional) 15 | 16 | Example: 17 | ghrepo -p --org stackgl -m "first commit" 18 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var spawn = require('win-spawn') 3 | var test = require('tape') 4 | var concat = require('concat-stream') 5 | 6 | var cliPath = path.resolve('./cmd.js') 7 | var expected = require('./package.json') 8 | 9 | test('should dry run and get correct package details', function(t) { 10 | var proc = spawn(cliPath, ['--dry-run']) 11 | proc.stdout.pipe(concat(function(body) { 12 | var data = JSON.parse(body.toString()) 13 | t.equal(data.name, expected.name, 'matches name') 14 | t.equal(data.description, expected.description, 'matches description') 15 | t.equal(data.homepage, '', 'homepage is the same as repo so ignored') 16 | })) 17 | proc.on('exit', function() { 18 | t.ok(true, 'closed') 19 | }) 20 | t.plan(4) 21 | }) -------------------------------------------------------------------------------- /lib/commit.js: -------------------------------------------------------------------------------- 1 | var spawn = require('npm-execspawn') 2 | var escape = require('jsesc') 3 | var noop = require('no-op') 4 | 5 | //git add + git commit + git push 6 | module.exports = function(opt, cb) { 7 | cb = cb || noop 8 | var message = opt.message || 'first commit' 9 | message = "'"+escape(message)+"'" 10 | 11 | var cmd = [ 12 | 'git init', 13 | 'git add .', 14 | 'git commit -m ' + message, 15 | 'git remote add origin ' + opt.url, 16 | 'git push -u origin master' 17 | ].join(' && ') 18 | var proc = spawn(cmd) 19 | proc.stdout.pipe(process.stdout) 20 | proc.stderr.pipe(process.stderr) 21 | proc.on('exit', function(err) { 22 | if (err === 0) 23 | cb(null) 24 | else 25 | cb(new Error('git command failed')) 26 | }) 27 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2015 Matt DesLauriers 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /lib/github.js: -------------------------------------------------------------------------------- 1 | var auth = require('ghauth') 2 | var GitHubApi = require('github') 3 | 4 | var github = new GitHubApi({ 5 | debug: false, 6 | host: 'api.github.com', 7 | protocol: 'https', 8 | version: '3.0.0' 9 | }) 10 | 11 | //Creates a new repo on user/org 12 | module.exports.publish = function(opt, cb) { 13 | var result = function(err, data) { 14 | //not sure why github returns a JSON string?? 15 | if (err) { 16 | console.error(err) 17 | if (typeof err.message === 'string') { 18 | try { 19 | var errDat = JSON.parse(err.message) || {} 20 | var err0 = (errDat.errors && errDat.errors[0] && errDat.errors[0].message) 21 | err = err0 ? [errDat.message, err0].join(': ') : errDat.message 22 | } catch (e) { 23 | err = 'Could not publish to GitHub' 24 | } 25 | } 26 | return cb(new Error(err), data) 27 | } 28 | cb(null, data) 29 | } 30 | 31 | if (opt.org) { 32 | github.repos.createFromOrg(opt, result) 33 | } 34 | else 35 | github.repos.create(opt, result) 36 | } 37 | 38 | module.exports.auth = function(opt, cb) { 39 | auth(opt, function(err, data) { 40 | if (err) return cb(err) 41 | github.authenticate({ 42 | type: 'oauth', 43 | token: data.token 44 | }) 45 | cb(null, data) 46 | }) 47 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ghrepo", 3 | "version": "1.3.2", 4 | "description": "create a new GitHub repo from your current folder", 5 | "main": "./cmd.js", 6 | "license": "MIT", 7 | "bin": { 8 | "ghrepo": "./cmd.js" 9 | }, 10 | "author": { 11 | "name": "Matt DesLauriers", 12 | "email": "dave.des@gmail.com", 13 | "url": "https://github.com/mattdesl" 14 | }, 15 | "dependencies": { 16 | "bluebird": "^2.9.14", 17 | "chalk": "^1.0.0", 18 | "ghauth": "^2.0.0", 19 | "github": "^0.2.3", 20 | "github-url-to-object": "^1.5.2", 21 | "inquirer": "^0.8.0", 22 | "jsesc": "^0.5.0", 23 | "minimist": "^1.1.0", 24 | "no-op": "^1.0.3", 25 | "npm-execspawn": "^1.0.6", 26 | "npmconf": "^2.1.1", 27 | "opn": "^1.0.1", 28 | "require-package-name": "^2.0.0" 29 | }, 30 | "devDependencies": { 31 | "concat-stream": "^1.4.8", 32 | "tape": "^4.0.0", 33 | "win-spawn": "^2.0.0" 34 | }, 35 | "scripts": { 36 | "test": "node test.js" 37 | }, 38 | "keywords": [ 39 | "tool", 40 | "utility", 41 | "gh", 42 | "repo", 43 | "repository", 44 | "new", 45 | "make", 46 | "create", 47 | "github", 48 | "git", 49 | "hub", 50 | "module", 51 | "npm", 52 | "package" 53 | ], 54 | "repository": { 55 | "type": "git", 56 | "url": "git://github.com/mattdesl/ghrepo.git" 57 | }, 58 | "homepage": "https://github.com/mattdesl/ghrepo", 59 | "bugs": { 60 | "url": "https://github.com/mattdesl/ghrepo/issues" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ghrepo 2 | 3 | [![stable](http://badges.github.io/stability-badges/dist/stable.svg)](http://github.com/badges/stability-badges) 4 | 5 | Creates a new GitHub repository from your current folder, and then runs the initial `git` commands to commit and push the contents. If the folder has a `package.json` it will pull the repo's `description`, `name` and `homepage` from that. 6 | 7 | Install: 8 | 9 | ```sh 10 | npm install ghrepo -g 11 | ``` 12 | 13 | The example below pushes the contents of `my-module` to a new GitHub repository with the specified commit message. On first run, it will [prompt for authentication](https://github.com/rvagg/ghauth). 14 | 15 | ```sh 16 | #make a new module 17 | mkdir my-module 18 | cd my-module 19 | npm init 20 | 21 | #create a new GitHub repository 22 | ghrepo -m 'first commit yolo' 23 | ``` 24 | 25 | This should open the browser to the newly created GitHub repository on your account, and push the current contents of the folder. 26 | 27 | ![result](http://i.imgur.com/5bz7JCW.png) 28 | 29 | ## Usage 30 | 31 | [![NPM](https://nodei.co/npm/ghrepo.png)](https://www.npmjs.com/package/ghrepo) 32 | 33 | ``` 34 | Usage: 35 | ghrepo [opts] 36 | 37 | Options: 38 | --help show help 39 | --version, -v prints version number 40 | --name, -n the name of the repository 41 | --description, -d the description 42 | --homepage, -h the homepage URL 43 | --private, -p mark the repository as private (default false) 44 | --message, -m the commit message (default "first commit") 45 | --bare, -b do not run any git commands after creating the repo 46 | --no-open do not open the GitHub repo in the browser after creation 47 | --org, -o the organization to create the repo in (optional) 48 | ``` 49 | 50 | The `--name`, `--description`, and `--homepage` will default to `package.json`. If no `package.json` is found, `--name` defaults to the current folder's name. 51 | 52 | The `--org` will default to the same user as specified in the `repository` URL in package.json. Otherwise, it will default to the username specified from the initial authentication. 53 | 54 | #### Default Commit Message 55 | 56 | You can personalize the default commit message with npm config: 57 | 58 | ```sh 59 | npm config set init.ghrepo.message 'YOLO!' 60 | ``` 61 | 62 | Now `--message` will default to `'YOLO!'`. 63 | 64 | ## License 65 | 66 | MIT, see [LICENSE.md](http://github.com/mattdesl/ghrepo/blob/master/LICENSE.md) for details. 67 | -------------------------------------------------------------------------------- /cmd.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const Promise = require('bluebird') 3 | const argv = require('minimist')(process.argv.slice(2)) 4 | const chalk = require('chalk') 5 | const github = require('./lib/github') 6 | const open = require('opn') 7 | const path = require('path') 8 | const noop = require('no-op') 9 | const baseName = require('require-package-name').base 10 | const githubUrl = require('github-url-to-object') 11 | 12 | const publish = Promise.promisify(github.publish) 13 | const auth = Promise.promisify(github.auth) 14 | const config = Promise.promisify(require('./lib/config')) 15 | const gitCommit = Promise.promisify(require('./lib/commit')) 16 | const confirm = Promise.promisify(require('./lib/confirm')) 17 | const loadPackage = Promise.promisify(require('./lib/package')) 18 | 19 | //get organization 20 | var org = argv.o || argv.org 21 | if (org && typeof org !== 'string') { 22 | console.error("No --org specified") 23 | process.exit(1) 24 | } 25 | 26 | 27 | if (argv.v || argv.version) { 28 | var version = require('./package.json').version 29 | console.log(version) 30 | return 31 | } 32 | 33 | if (argv.help) { 34 | require('fs').createReadStream(path.join(__dirname, 'lib', 'help.txt')) 35 | .pipe(process.stdout) 36 | return 37 | } 38 | 39 | var dryRun = argv['dry-run'] 40 | if (dryRun) { 41 | console.error(chalk.grey("(dry run)")) 42 | start() 43 | .then(function(result) { 44 | console.log(JSON.stringify(result)) 45 | }) 46 | .catch(err()) 47 | } else { 48 | start() 49 | .then(publish) 50 | .then(commit) 51 | .then(argv.open !== false ? open : noop, err()) 52 | .catch(err()) 53 | } 54 | 55 | function start() { 56 | return getOpts().then(request) 57 | } 58 | 59 | function request(opt) { 60 | var pkg = opt.package 61 | var name = argv.n || argv.name || baseName(pkg.name) 62 | var description = argv.d || argv.description || pkg.description || '' 63 | var homepage = argv.h || argv.homepage || pkg.homepage || '' 64 | 65 | if (!name) { 66 | console.error("No name in package.json") 67 | process.exit(1) 68 | } 69 | 70 | if (homepage && homepage.indexOf('https://github.com/') === 0) 71 | homepage = '' 72 | 73 | var user = opt.org 74 | // try to glean default username from package.json repository URL 75 | if (!user && pkg.repository && pkg.repository.url) { 76 | var urlObj = githubUrl(pkg.repository.url) 77 | if (urlObj) { 78 | user = urlObj.user 79 | } 80 | } 81 | user = user || opt.auth.user 82 | 83 | var org = user === opt.auth.user ? undefined : user 84 | var repo = [user, name].join('/') 85 | var url = 'https://github.com/' + repo + '.git' 86 | repo = chalk.magenta(repo) 87 | var info = 'Publish new repo as ' + repo + '?' 88 | var data = { 89 | org: org, 90 | name: name, 91 | description: description, 92 | homepage: homepage, 93 | private: argv.p || argv.private, 94 | team_id: argv.team 95 | } 96 | 97 | if (dryRun) { 98 | return data 99 | } 100 | 101 | return confirm(info) 102 | .then(function() { 103 | return data 104 | }, function() { 105 | // user exited early 106 | process.exit(0) 107 | }) 108 | } 109 | 110 | //commits current working dir, resolves to html_url 111 | function commit(result) { 112 | var url = result.html_url 113 | //user opted not to commit anything 114 | if (argv.b || argv.bare) { 115 | return Promise.resolve(url) 116 | } 117 | return getMessage().then(function(message) { 118 | return gitCommit({ 119 | message: message, 120 | url: url + '.git' 121 | }).catch(function() { 122 | console.warn(chalk.dim("git commands ignored")) 123 | return Promise.resolve(url) 124 | }).then(function() { 125 | return url 126 | }) 127 | }) 128 | } 129 | 130 | function getMessage() { 131 | var msg = argv.m || argv.message 132 | if (msg) 133 | return Promise.resolve(msg) 134 | var def = 'first commit' 135 | //try getting it from config 136 | return config().then(function(conf) { 137 | return conf.get('init.ghrepo.message') || def 138 | }, function(err) { 139 | //default 140 | return Promise.resolve(def) 141 | }) 142 | } 143 | 144 | function getOpts() { 145 | return auth({ 146 | configName: 'ghrepo', 147 | note: 'ghrepo - repo creation tool', 148 | scopes: ['user', 'repo'] 149 | }) 150 | .then(function(auth) { 151 | return [ auth, getPackage() ] 152 | }, err('Could not authenticate')) 153 | .spread(function(auth, pkg) { 154 | return { 155 | package: pkg, 156 | org: org, 157 | auth: auth 158 | } 159 | }, err('Error reading package.json')) 160 | } 161 | 162 | function getPackage() { 163 | return loadPackage() 164 | .then(null, function(err) { 165 | console.warn(chalk.bgYellow("WARN"), chalk.magenta("could not open package.json")) 166 | console.warn(chalk.dim(err.message)) 167 | return Promise.resolve({ 168 | name: path.basename(process.cwd()) 169 | }) 170 | }) 171 | } 172 | 173 | function err(msg) { 174 | msg = msg||'' 175 | return function(err) { 176 | console.error([msg, err.message].join(' ').trim()) 177 | process.exit(1) 178 | } 179 | } --------------------------------------------------------------------------------