├── img └── example.gif ├── .eslintrc.json ├── .editorconfig ├── .travis.yml ├── tests ├── .eslintrc.json ├── options.spec.js ├── main.spec.js └── tasks.spec.js ├── wallaby.js ├── CONTRIBUTING.md ├── azure-pipelines.yml ├── bin ├── main.js ├── options.js ├── tasks.js └── gitIdle.js ├── src ├── main.js ├── options.js ├── tasks.js └── gitIdle.js ├── LICENSE.md ├── .gitignore ├── README.md └── package.json /img/example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cotts/git-idle/HEAD/img/example.gif -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb-base", 3 | "plugins": [ 4 | "import" 5 | ], 6 | "rules": { 7 | "no-console": 0 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "7" 4 | 5 | cache: 6 | directories: 7 | "node_modules" 8 | before_script: 9 | - npm run build 10 | 11 | after_success: 12 | - npm run coveralls 13 | -------------------------------------------------------------------------------- /tests/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "prefer-arrow-callback": "off", 4 | "func-names": "off", 5 | "no-unused-expressions": 0, 6 | "chai-friendly/no-unused-expressions": 2, 7 | "no-unused-vars": "off" 8 | }, 9 | "plugins": [ 10 | "mocha", 11 | "chai-friendly" 12 | ], 13 | "env": { 14 | "mocha": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /wallaby.js: -------------------------------------------------------------------------------- 1 | module.exports = wallaby => ({ 2 | files: [ 3 | 'src/**/*.js', 4 | ], 5 | 6 | tests: [ 7 | 'tests/**/*spec.js', 8 | ], 9 | env: { 10 | type: 'node', 11 | }, 12 | compilers: { 13 | '**/*.js': wallaby.compilers.babel({ 14 | }), 15 | }, 16 | setup() { 17 | const mocha = wallaby.testFramework; 18 | mocha.ui('tdd'); 19 | }, 20 | }); 21 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | 1. Fork it! 4 | 2. See the commands on [package.json](package.json) to build, test and run. 5 | 3. Create your feature branch: `git checkout -b my-new-feature` 6 | 4. Commit your changes: `git commit -m 'Add some feature'` 7 | 5. Push to the branch: `git push origin my-new-feature` 8 | 9 | *Remember that we have a pre-push hook with steps that analyzes and prevents mistakes.* 10 | 11 | **After your pull request is merged**, you can safely delete your branch. 12 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # Node.js with webpack 2 | # Build a Node.js project using the webpack CLI. 3 | # Add steps that analyze code, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/javascript 5 | 6 | trigger: 7 | - master 8 | 9 | pool: 10 | vmImage: 'ubuntu-latest' 11 | 12 | steps: 13 | - task: NodeTool@0 14 | inputs: 15 | versionSpec: '10.x' 16 | displayName: 'Install Node.js' 17 | 18 | - script: | 19 | npm install -g webpack webpack-cli --save-dev 20 | npm install 21 | npm run test 22 | displayName: 'npm install, run webpack' 23 | -------------------------------------------------------------------------------- /bin/main.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 4 | const program = require('commander'); 5 | const pkg = require('../package.json'); 6 | const { gitIdle } = require('./gitIdle'); 7 | 8 | program.version(pkg.version).description('List, clone and install dependencies from a github repository direct by terminal').usage(' [options]').arguments('user, ', 'Select Github User Repository', { required: true }).option('-f, --filter ', 'Filter for a specific name included').option('-n, --notforked', 'Show only not forked repositorires').option('-s, --skip', 'Skip install dependencies').parse(process.argv); 9 | 10 | if (!program.args[0]) { 11 | program.help(); 12 | } 13 | 14 | gitIdle(program.args[0], program.filter, program.notforked, program.skip); -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const program = require('commander'); 4 | const pkg = require('../package.json'); 5 | const { gitIdle } = require('./gitIdle'); 6 | 7 | 8 | program 9 | .version(pkg.version) 10 | .description('List, clone and install dependencies from a github repository direct by terminal') 11 | .usage(' [options]') 12 | .arguments('user, ', 'Select Github User Repository', { required: true }) 13 | .option('-f, --filter ', 'Filter for a specific name included') 14 | .option('-n, --notforked', 'Show only not forked repositorires') 15 | .option('-s, --skip', 'Skip install dependencies') 16 | .parse(process.argv); 17 | 18 | if (!program.args[0]) { 19 | program.help(); 20 | } 21 | 22 | gitIdle(program.args[0], program.filter, program.notforked, program.skip); 23 | 24 | -------------------------------------------------------------------------------- /bin/options.js: -------------------------------------------------------------------------------- 1 | const request = require('request-promise-native'); 2 | 3 | const reqParams = (page, user) => ({ 4 | method: 'GET', 5 | url: `https://api.github.com/users/${user}/repos?page=${page}&per_page=100`, 6 | headers: { 7 | 'User-Agent': 'git-idle CLI', 8 | host: 'api.github.com' 9 | } 10 | }); 11 | 12 | let responseFull = []; 13 | 14 | const requestPage = (page, user) => request(reqParams(page, user)).then(data => JSON.parse(data)); 15 | 16 | const getRepositories = async (page, user) => { 17 | const pagedResponse = await requestPage(page, user); 18 | responseFull = responseFull.concat(pagedResponse); 19 | if (pagedResponse.length === 100) { 20 | const size = page + 1; 21 | await getRepositories(size, user); 22 | } 23 | 24 | return responseFull; 25 | }; 26 | 27 | const filterRepositories = (data, param) => data.filter(item => item.name.includes(param)); 28 | 29 | const nonForked = data => data.filter(item => item.fork === false); 30 | 31 | module.exports = { getRepositories, filterRepositories, nonForked }; -------------------------------------------------------------------------------- /src/options.js: -------------------------------------------------------------------------------- 1 | const request = require('request-promise-native'); 2 | 3 | const reqParams = (page, user) => ({ 4 | method: 'GET', 5 | url: `https://api.github.com/users/${user}/repos?page=${page}&per_page=100`, 6 | headers: { 7 | 'User-Agent': 'git-idle CLI', 8 | host: 'api.github.com', 9 | }, 10 | }); 11 | 12 | let responseFull = []; 13 | 14 | 15 | const requestPage = (page, user) => request(reqParams(page, user)).then(data => JSON.parse(data)); 16 | 17 | const getRepositories = async (page, user) => { 18 | const pagedResponse = await requestPage(page, user); 19 | responseFull = responseFull.concat(pagedResponse); 20 | if (pagedResponse.length === 100) { 21 | const size = page + 1; 22 | await getRepositories(size, user); 23 | } 24 | 25 | return responseFull; 26 | }; 27 | 28 | const filterRepositories = (data, param) => data.filter(item => item.name.includes(param)); 29 | 30 | const nonForked = data => data.filter(item => item.fork === false); 31 | 32 | 33 | module.exports = { getRepositories, filterRepositories, nonForked }; 34 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | 4 | 5 | Copyright 2017 - Thadeu Cotts 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # Vscode Folder 61 | .vscode/ 62 | -------------------------------------------------------------------------------- /bin/tasks.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const inquirer = require('inquirer'); 3 | const chalk = require('chalk'); 4 | const execa = require('execa'); 5 | 6 | const taskChoose = data => inquirer.prompt({ 7 | type: 'list', 8 | name: 'name', 9 | message: `Select repository you wanna clone > ${chalk.green(data.length)} ${data.length > 1 ? 'repositories' : 'repository'} available`, 10 | choices: data 11 | }); 12 | 13 | const taskChangeName = path => inquirer.prompt({ 14 | type: 'input', 15 | name: 'name', 16 | message: 'Do you want to change the name?', 17 | default: () => path 18 | }); 19 | 20 | const existsPath = path => inquirer.prompt({ 21 | type: 'input', 22 | name: 'name', 23 | message: `path ${chalk.yellow(path)} already exists! Change name to continue`, 24 | default: () => path 25 | }); 26 | 27 | const execCheck = path => !!(fs.existsSync(path) && fs.lstatSync(path).isDirectory()); 28 | 29 | const execRename = path => execCheck(path) ? existsPath(path).then(data => execRename(data.name)) : path; 30 | 31 | const cloneGit = (user, repository, changed = null) => execa.stdout('git', ['clone', `https://github.com/${user}/${repository}.git`, `${changed || ''}`]); 32 | 33 | const skipInstall = path => !fs.existsSync(`${path}/package.json`); 34 | 35 | const installPackages = path => execa.stdout('npm', ['install'], { cwd: path }); 36 | 37 | module.exports = { 38 | taskChoose, 39 | taskChangeName, 40 | existsPath, 41 | execCheck, 42 | execRename, 43 | cloneGit, 44 | installPackages, 45 | skipInstall 46 | }; -------------------------------------------------------------------------------- /bin/gitIdle.js: -------------------------------------------------------------------------------- 1 | const Listr = require('listr'); 2 | const ora = require('ora'); 3 | const { getRepositories, filterRepositories, nonForked } = require('./options'); 4 | 5 | const { 6 | taskChoose, 7 | taskChangeName, 8 | execRename, 9 | cloneGit, 10 | installPackages, 11 | skipInstall 12 | } = require('./tasks'); 13 | 14 | const spinner = ora({ 15 | text: 'Retrieving User Repositories...', 16 | color: 'yellow' 17 | }); 18 | 19 | const gitIdle = async (user, filter = null, forked = null, skip) => { 20 | console.log('Starting git-idle'); 21 | spinner.start(); 22 | 23 | let list = await getRepositories(0, user); 24 | 25 | if (filter) list = await filterRepositories(list, filter); 26 | if (forked) list = await nonForked(list); 27 | 28 | spinner.stop(); 29 | 30 | const repository = await taskChoose(list); 31 | const isChanged = await taskChangeName(repository.name).then(data => execRename(data.name)); 32 | 33 | const tasks = new Listr([{ 34 | title: `Starting Clone ${repository.name} ${isChanged !== repository.name ? `into ${isChanged} ` : ''}from ${user}`, 35 | task: () => cloneGit(user, repository.name, isChanged) 36 | }, { 37 | title: 'Installing packages with NPM', 38 | skip: () => { 39 | if (skip) return 'Install dependencies skipped by user'; 40 | if (skipInstall(isChanged)) return 'No package.json found inside repository'; 41 | return ''; 42 | }, 43 | task: () => installPackages(isChanged) 44 | }]); 45 | 46 | tasks.run(); 47 | }; 48 | 49 | module.exports = { gitIdle }; -------------------------------------------------------------------------------- /src/tasks.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const inquirer = require('inquirer'); 3 | const chalk = require('chalk'); 4 | const execa = require('execa'); 5 | 6 | 7 | const taskChoose = data => inquirer.prompt({ 8 | type: 'list', 9 | name: 'name', 10 | message: `Select repository you wanna clone > ${chalk.green(data.length)} ${data.length > 1 ? 'repositories' : 'repository'} available`, 11 | choices: data, 12 | }); 13 | 14 | const taskChangeName = path => inquirer.prompt({ 15 | type: 'input', 16 | name: 'name', 17 | message: 'Do you want to change the name?', 18 | default: () => path, 19 | }); 20 | 21 | const existsPath = path => inquirer.prompt({ 22 | type: 'input', 23 | name: 'name', 24 | message: `path ${chalk.yellow(path)} already exists! Change name to continue`, 25 | default: () => path, 26 | }); 27 | 28 | const execCheck = path => 29 | (!!(fs.existsSync(path) && fs.lstatSync(path).isDirectory())); 30 | 31 | const execRename = path => 32 | (execCheck(path) ? existsPath(path).then(data => execRename(data.name)) : path); 33 | 34 | const cloneGit = (user, repository, changed = null) => 35 | execa.stdout('git', ['clone', `https://github.com/${user}/${repository}.git`, `${changed || ''}`]); 36 | 37 | const skipInstall = path => !fs.existsSync(`${path}/package.json`); 38 | 39 | const installPackages = path => 40 | execa.stdout('npm', ['install'], { cwd: path }); 41 | 42 | 43 | module.exports = { 44 | taskChoose, 45 | taskChangeName, 46 | existsPath, 47 | execCheck, 48 | execRename, 49 | cloneGit, 50 | installPackages, 51 | skipInstall, 52 | }; 53 | -------------------------------------------------------------------------------- /src/gitIdle.js: -------------------------------------------------------------------------------- 1 | const Listr = require('listr'); 2 | const ora = require('ora'); 3 | const { getRepositories, filterRepositories, nonForked } = require('./options'); 4 | 5 | const { 6 | taskChoose, 7 | taskChangeName, 8 | execRename, 9 | cloneGit, 10 | installPackages, 11 | skipInstall, 12 | } = require('./tasks'); 13 | 14 | const spinner = ora({ 15 | text: 'Retrieving User Repositories...', 16 | color: 'yellow', 17 | }); 18 | 19 | const gitIdle = async (user, filter = null, forked = null, skip) => { 20 | console.log('Starting git-idle'); 21 | spinner.start(); 22 | 23 | let list = await getRepositories(0, user); 24 | 25 | if (filter) list = await filterRepositories(list, filter); 26 | if (forked) list = await nonForked(list); 27 | 28 | spinner.stop(); 29 | 30 | const repository = await taskChoose(list); 31 | const isChanged = await taskChangeName(repository.name).then(data => execRename(data.name)); 32 | 33 | const tasks = new Listr([ 34 | { 35 | title: `Starting Clone ${repository.name} ${(isChanged !== repository.name) ? `into ${isChanged} ` : ''}from ${user}`, 36 | task: () => cloneGit(user, repository.name, isChanged), 37 | }, 38 | { 39 | title: 'Installing packages with NPM', 40 | skip: () => { 41 | if (skip) return 'Install dependencies skipped by user'; 42 | if (skipInstall(isChanged)) return 'No package.json found inside repository'; 43 | return ''; 44 | }, 45 | task: () => installPackages(isChanged), 46 | }, 47 | ]); 48 | 49 | tasks.run(); 50 | }; 51 | 52 | module.exports = { gitIdle }; 53 | -------------------------------------------------------------------------------- /tests/options.spec.js: -------------------------------------------------------------------------------- 1 | const nock = require('nock'); 2 | const chai = require('chai'); 3 | const { expect } = require('chai'); 4 | 5 | 6 | const { 7 | getRepositories, filterRepositories, nonForked, skipInstall, 8 | } = require('../src/options'); 9 | 10 | describe('Options Methods', () => { 11 | const responseMock = [ 12 | { name: 'AFNetworking', fork: true }, 13 | { name: 'aptly', fork: true }, 14 | { name: 'github', fork: false }, 15 | { name: 'desktop', fork: false }, 16 | ]; 17 | describe('Smoke Tests', () => { 18 | it('should exist the getRepositories Method', () => { 19 | expect(getRepositories).to.exist; 20 | }); 21 | it('should exist the filterRepositories Method', () => { 22 | expect(filterRepositories).to.exist; 23 | }); 24 | it('should exist the nonForked Method', () => { 25 | expect(nonForked).to.exist; 26 | }); 27 | }); 28 | 29 | describe('Generic List', () => { 30 | it('should return a list of repositories from a github user', async () => { 31 | nock('https://api.github.com/users/github/repos?page=0&per_page=100', { reqheaders: { 'User-Agent': 'git-idle CLI' } }) 32 | .get('') 33 | .query(true) 34 | .reply(200, responseMock); 35 | 36 | const repos = await getRepositories(0, 'github'); 37 | expect(repos).to.be.eql(responseMock); 38 | }); 39 | 40 | it('should filter when a parameter after --filter is passed', () => { 41 | const filtered = filterRepositories(responseMock, 'git'); 42 | expect(filtered).to.be.eql([{ name: 'github', fork: false }]); 43 | }); 44 | 45 | it('should filter for non-forked repositories when option --nf is passed', () => { 46 | const notFork = nonForked(responseMock); 47 | expect(notFork).to.be.eql([{ name: 'github', fork: false }, { name: 'desktop', fork: false }]); 48 | }); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Git IDLE 2 | 3 | [![Build Status](https://travis-ci.org/cotts/git-idle.svg?branch=master)](https://travis-ci.org/cotts/git-idle) 4 | [![Coverage Status](https://coveralls.io/repos/github/cotts/git-idle/badge.svg?branch=master)](https://coveralls.io/github/cotts/git-idle?branch=master) 5 | 6 | > A CLI to list, download and install dependencies direct from a github repository! 7 | 8 | ![Example CLI running](img/example.gif) 9 | 10 | 11 | ### Installing 12 | 13 | ``` 14 | $ npm install -g git-idle 15 | ``` 16 | 17 | ### How to use 18 | 19 | ```sh 20 | git-idle --help 21 | 22 | Usage: main [options] 23 | 24 | CLI to download a repository from a github user and install dependencies 25 | 26 | 27 | Options: 28 | 29 | -V, --version Output the version number 30 | -f, --filter Filter for a specific name included 31 | -n, --notforked Show only not forked repositorires 32 | -s, --skip Skip install dependencies 33 | -h, --help Output usage information 34 | 35 | ``` 36 | 37 | ## Built With 38 | 39 | * [Listr](https://github.com/SamVerschueren/listr) - Terminal task list 40 | * [Inquirer.js](https://github.com/SBoudrias/Inquirer.js) - A collection of common interactive command line user interfaces. 41 | * [Request](https://github.com/request/request) - Simplified HTTP request client. 42 | 43 | ## Contributing 44 | 45 | Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us. 46 | 47 | 48 | ## Versioning 49 | 50 | We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/cotts/git-idle/tags). 51 | 52 | ## Author 53 | 54 | * **Thadeu Cotts** - *Initial Project* 55 | 56 | ## License 57 | 58 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details 59 | 60 | 61 | ## Acknowledgments 62 | 63 | * [**Willian Justen**](https://github.com/willianjusten) - By helping me with a thousand of doubts when I was creating that CLI and how to make my TDD better. 64 | 65 | * [**Evandro Ribeiro**](https://github.com/ribeiroevandro) - For some ideas that helped me to create this CLI 66 | -------------------------------------------------------------------------------- /tests/main.spec.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const { expect } = require('chai'); 3 | const { exec } = require('child_process'); 4 | 5 | 6 | const gitIdle = './src/main.js'; 7 | const pkg = require('../package.json'); 8 | 9 | describe('Main Function git-idle', () => { 10 | it('should show help if no arguments is passed', (done) => { 11 | exec(`node ${gitIdle}`, (err, stdout, stderr) => { 12 | expect(stdout.includes('Usage: main [options]')).to.be.true; 13 | done(); 14 | }); 15 | }); 16 | 17 | it('should return version of git-idle', (done) => { 18 | exec(`node ${gitIdle} --version`, (err, stdout, stderr) => { 19 | if (err) throw err; 20 | expect(stdout.replace('\n', '')).to.be.equal(pkg.version); 21 | done(); 22 | }); 23 | }); 24 | 25 | it('should return the description when git-idle --help', (done) => { 26 | exec(`node ${gitIdle} --help`, (err, stdout, stderr) => { 27 | if (err) throw err; 28 | expect(stdout.includes('List, clone and install dependencies from a github repository direct by terminal')).to.be.true; 29 | done(); 30 | }); 31 | }); 32 | it('should return the user parameter when git-idle --help', (done) => { 33 | exec(`node ${gitIdle} --help`, (err, stdout, stderr) => { 34 | if (err) throw err; 35 | expect(stdout.includes('')).to.be.true; 36 | done(); 37 | }); 38 | }); 39 | 40 | it('should return the filter option when git-idle --help', (done) => { 41 | exec(`node ${gitIdle} --help`, (err, stdout, stderr) => { 42 | if (err) throw err; 43 | expect(stdout.includes('--filter')).to.be.true; 44 | done(); 45 | }); 46 | }); 47 | 48 | it('should return the not-forked option when git-idle --help', (done) => { 49 | exec(`node ${gitIdle} --help`, (err, stdout, stderr) => { 50 | if (err) throw err; 51 | expect(stdout.includes('--notforked')).to.be.true; 52 | done(); 53 | }); 54 | }); 55 | it('should return not install dependencies option when git-idle --help', (done) => { 56 | exec(`node ${gitIdle} --help`, (err, stdout, stderr) => { 57 | if (err) throw err; 58 | expect(stdout.includes('--skip')).to.be.true; 59 | done(); 60 | }); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "git-idle", 3 | "version": "1.0.2", 4 | "description": "List, clone and install dependencies from a github repository direct by terminal", 5 | "main": "index.js", 6 | "scripts": { 7 | "clear": "rimraf bin", 8 | "prebuild": "./node_modules/.bin/babel --out-dir bin src", 9 | "build": "npm run clear && npm run prebuild", 10 | "build:watch": "npm run build -- --watch", 11 | "lint": "./node_modules/.bin/eslint src/*.js", 12 | "prepush": "npm run lint", 13 | "test": "./node_modules/.bin/mocha tests/**/*.spec.js --compilers js:babel-core/register --require babel-polyfill", 14 | "test:tdd": "npm run test -- --watch", 15 | "test:coverage": "nyc npm test", 16 | "coveralls": "npm run test:coverage && nyc report --reporter=text-lcov | coveralls" 17 | }, 18 | "preferGlobal": true, 19 | "files": [ 20 | "bin" 21 | ], 22 | "bin": { 23 | "git-idle": "bin/main.js" 24 | }, 25 | "nyc": { 26 | "reporter": [ 27 | "text", 28 | "html" 29 | ], 30 | "exclude": [ 31 | "tests/**" 32 | ] 33 | }, 34 | "keywords": [ 35 | "js", 36 | "cli", 37 | "node", 38 | "github", 39 | "clone" 40 | ], 41 | "author": "Thadeu Cotts (https://thadeucotts.com/)", 42 | "license": "MIT", 43 | "devDependencies": { 44 | "babel-cli": "^6.24.1", 45 | "babel-plugin-transform-async-to-generator": "^6.24.1", 46 | "babel-polyfill": "^6.26.0", 47 | "babel-preset-env": "^1.3.2", 48 | "babel-register": "^6.24.0", 49 | "chai": "^3.5.0", 50 | "coveralls": "^3.0.0", 51 | "eslint": "^4.8.0", 52 | "eslint-config-airbnb-base": "^12.0.2", 53 | "eslint-plugin-chai-friendly": "^0.4.0", 54 | "eslint-plugin-import": "^2.7.0", 55 | "eslint-plugin-mocha": "^4.11.0", 56 | "husky": "^0.11.9", 57 | "mocha": "^3.2.0", 58 | "nock": "^9.0.22", 59 | "nyc": "^10.2.0", 60 | "rimraf": "^2.6.1", 61 | "sinon": "^2.3.6", 62 | "sinon-chai": "^2.11.0", 63 | "sinon-stub-promise": "^4.0.0" 64 | }, 65 | "dependencies": { 66 | "chalk": "^2.1.0", 67 | "commander": "^2.11.0", 68 | "execa": "^0.8.0", 69 | "inquirer": "^3.3.0", 70 | "listr": "^0.12.0", 71 | "ora": "^1.3.0", 72 | "request": "^2.83.0", 73 | "request-promise-native": "^1.0.5" 74 | }, 75 | "directories": { 76 | "test": "tests" 77 | }, 78 | "repository": { 79 | "type": "git", 80 | "url": "git+https://github.com/cotts/git-idle.git" 81 | }, 82 | "bugs": { 83 | "url": "https://github.com/cotts/git-idle/issues" 84 | }, 85 | "homepage": "https://github.com/cotts/git-idle#readme" 86 | } 87 | -------------------------------------------------------------------------------- /tests/tasks.spec.js: -------------------------------------------------------------------------------- 1 | const nock = require('nock'); 2 | const chai = require('chai'); 3 | const { expect } = require('chai'); 4 | const sinon = require('sinon'); 5 | const sinonChai = require('sinon-chai'); 6 | const sinonStubPromise = require('sinon-stub-promise'); 7 | const inquirer = require('inquirer'); 8 | const chalk = require('chalk'); 9 | const listr = require('listr'); 10 | const execa = require('execa'); 11 | 12 | const { 13 | taskChoose, 14 | taskChangeName, 15 | existsPath, 16 | execCheck, 17 | execRename, 18 | cloneGit, 19 | installPackages, 20 | skipInstall, 21 | } = require('../src/tasks'); 22 | 23 | chai.use(sinonChai); 24 | sinonStubPromise(sinon); 25 | 26 | describe('Tasks Methods', () => { 27 | const responseMock = [ 28 | { name: 'AFNetworking', fork: true }, 29 | { name: 'aptly', fork: true }, 30 | { name: 'github', fork: false }, 31 | { name: 'desktop', fork: false }, 32 | ]; 33 | 34 | const choose = { 35 | type: 'list', 36 | name: 'name', 37 | message: `Select repository you wanna clone > ${chalk.green(4)} repositories available`, 38 | choices: responseMock, 39 | }; 40 | 41 | const change = { 42 | type: 'input', 43 | name: 'name', 44 | message: 'Do you want to change the name?', 45 | }; 46 | 47 | const exists = { 48 | type: 'input', 49 | name: 'name', 50 | message: `path ${chalk.yellow('github')} already exists! Change name to continue`, 51 | }; 52 | 53 | let inquirerStub; 54 | let inquirerPromise; 55 | let execStub; 56 | 57 | beforeEach(() => { 58 | inquirerStub = sinon.stub(inquirer, 'prompt'); 59 | inquirerPromise = inquirerStub.returnsPromise(); 60 | execStub = sinon.stub(execa, 'stdout'); 61 | }); 62 | 63 | afterEach(() => { 64 | inquirerStub.restore(); 65 | execStub.restore(); 66 | }); 67 | 68 | describe('Smoke Tests', () => { 69 | it('should taskChoose method to exists', () => { 70 | expect(inquirerStub).to.exist; 71 | }); 72 | it('should taskChange method to exist', () => { 73 | expect(inquirerStub).to.exist; 74 | }); 75 | it('should existsPath method to exist', () => { 76 | expect(inquirerStub).exist; 77 | }); 78 | it('should execCheck method to exist', () => { 79 | expect(execCheck).to.exist; 80 | }); 81 | it('should execRename method to exist', () => { 82 | expect(execRename).to.exist; 83 | }); 84 | it('should cloneGit method to exist', () => { 85 | expect(cloneGit).to.exist; 86 | }); 87 | it('should exist the skipInstall Method', () => { 88 | expect(skipInstall).to.exist; 89 | }); 90 | it('should installPackages method to exist', () => { 91 | expect(installPackages).to.exist; 92 | }); 93 | }); 94 | 95 | describe('Prompt Methods Call working', () => { 96 | it('should return a list of repositories when the CLI started with a valid user', () => { 97 | const task = taskChoose(responseMock); 98 | expect(inquirerStub.getCall(0).args[0]).to.contains(choose); 99 | }); 100 | 101 | it('should show a prompt to change name if wanted', () => { 102 | const wannaChange = taskChangeName('github'); 103 | expect(inquirerStub.getCall(0).args[0]).to.contains(change); 104 | }); 105 | 106 | it('should return a prompt if path already exists', () => { 107 | const hasPath = existsPath('github'); 108 | expect(inquirerStub.getCall(0).args[0]).to.contains(exists); 109 | }); 110 | }); 111 | 112 | describe('Executable Runners', () => { 113 | context('Validate path', () => { 114 | it('should return false if a path does not exist', () => { 115 | const exist = execCheck('../node'); 116 | expect(exist).to.be.eql(false); 117 | }); 118 | it('should return path if path does not exist', () => { 119 | const rename = execRename('node'); 120 | expect(rename).to.be.eql('node'); 121 | }); 122 | }); 123 | 124 | it('should execute git-clone after the prompts finishes', () => { 125 | const clone = cloneGit('github', 'desktop'); 126 | expect(execStub).to.have.been.calledWith('git', ['clone', 'https://github.com/github/desktop.git', '']); 127 | }); 128 | 129 | context('should run npm install inside folder after git clone execute', () => { 130 | it('run inside the same folder if there is no changed name', () => { 131 | const install = installPackages('github'); 132 | expect(execStub).to.have.been.calledWith('npm', ['install'], { cwd: 'github' }); 133 | }); 134 | it('run inside the another folder when the name is changed', () => { 135 | const install = installPackages('desktop'); 136 | expect(execStub).to.have.been.calledWith('npm', ['install'], { cwd: 'desktop' }); 137 | }); 138 | }); 139 | }); 140 | }); 141 | --------------------------------------------------------------------------------