├── cli.ts
├── bin
└── kibibit-cli.js
├── .gitignore
├── lib
├── errors
│ ├── error-handler.js
│ └── KbError.js
├── kb-info.js
├── kb-git-root.js
├── kibibit.js
├── gitflow
│ ├── master.js
│ ├── develop.js
│ ├── init.js
│ ├── finish.js
│ ├── sync.js
│ ├── hotfix.js
│ ├── feature.js
│ ├── utility.js
│ ├── release.js
│ ├── commit.js
│ └── status.js
├── services
│ ├── kb-user.js
│ ├── kb-github.js
│ └── kb-repo.js
├── kb-string.js
└── commandDefinitions.js
├── package.json
├── .eslintrc
├── README.md
└── tsconfig.json
/cli.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/bin/kibibit-cli.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | require('manakin').global;
3 |
4 | var kibibit = require('../lib/kibibit.js');
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | node_modules/
3 | logs/
4 | mochawesome-reports/
5 | coverage/
6 | .nyc_output/
7 | .idea/
8 |
9 | monkeyDB.json
10 |
11 | mochawesome-report/
12 |
13 | privateConfig.json
14 | test.js
15 |
16 | # dist folder
17 | dist/
18 |
--------------------------------------------------------------------------------
/lib/errors/error-handler.js:
--------------------------------------------------------------------------------
1 | /*eslint-env es6*/
2 | var _ = require('lodash');
3 |
4 | module.exports = function errorHandlerClosure(prefix) {
5 | // console.log('error handler created for ', prefix);
6 | return function errorHandler(error) {
7 | var logError = error.exitStatus === 0 || error.noTrace ?
8 | console.error : console.trace;
9 | var exitStatus = _.isNumber(error.exitStatus) ? error.exitStatus : 1;
10 |
11 | prefix = _.isString(prefix) ? _.trim(prefix) + ' ' : '';
12 |
13 | logError(prefix + error);
14 |
15 | process.exit(exitStatus);
16 | };
17 | };
18 |
--------------------------------------------------------------------------------
/lib/kb-info.js:
--------------------------------------------------------------------------------
1 | var os = require('os');
2 | var kbString = require('kb-string');
3 | var gitRoot = require('kb-git-root');
4 |
5 | module.export = info;
6 |
7 | var info = {
8 | runFolder: process.cwd(),
9 | gitRoot: gitRoot,
10 | os: {
11 | username: os.userInfo().username,
12 | hostname: os.hostname(),
13 | type: os.type()
14 | },
15 | signInAnimation: [
16 | kbString.info(kbString.warning('/'), ' Signing in'),
17 | kbString.info(kbString.warning('|'), ' Signing in..'),
18 | kbString.info(kbString.warning('\\'), ' Signing in..'),
19 | kbString.info(kbString.warning('-'), ' Signing in...')
20 | ]
21 | };
22 |
--------------------------------------------------------------------------------
/lib/errors/KbError.js:
--------------------------------------------------------------------------------
1 | /*eslint-env es6*/
2 | var _ = require('lodash');
3 |
4 | module.exports = class KbError extends Error {
5 | constructor(message, exitStatus, noTrace) {
6 |
7 | // Calling parent constructor of base Error class.
8 | super(message);
9 |
10 | // Saving class name in the property of our custom error as a shortcut.
11 | this.name = this.constructor.name;
12 | this.noTrace = noTrace || false;
13 |
14 | // Capturing stack trace, excluding constructor call from it.
15 | Error.captureStackTrace(this, this.constructor);
16 |
17 | // process.exit exit status.
18 | // default is to throw an error
19 | // set to 0 if you want to show error gracefully (intentional)
20 | this.exitStatus = _.isNumber(exitStatus) ? exitStatus : 1;
21 |
22 | }
23 | };
24 |
--------------------------------------------------------------------------------
/lib/kb-git-root.js:
--------------------------------------------------------------------------------
1 | var KbError = require('./errors/KbError');
2 | var currentFolder = process.cwd();
3 | var path = require('path');
4 | var fs = require('fs');
5 | var Q = require('q');
6 |
7 | var findRoot = require('find-root');
8 |
9 | var kbGitRoot = {};
10 |
11 | kbGitRoot.getGitRoot = getGitRoot;
12 |
13 | var deferred = Q.defer();
14 |
15 | try {
16 | var foundRoot = findRoot(currentFolder, function (dir) {
17 | return fs.existsSync(path.resolve(dir, '.git'));
18 | });
19 | // console.log('found git root', foundRoot);
20 | deferred.resolve(foundRoot);
21 | } catch (error) {
22 | // console.error('no git root found', error);
23 | // gitRoot = null;
24 | var err = new KbError('git repo not found', 1, true);
25 | deferred.reject(err);
26 | }
27 |
28 | // console.log('this is gitRoot', gitRoot);
29 |
30 | module.exports = kbGitRoot;
31 |
32 | function getGitRoot() {
33 | // console.log('yo');
34 | return deferred.promise;
35 | }
36 |
--------------------------------------------------------------------------------
/lib/kibibit.js:
--------------------------------------------------------------------------------
1 | // var pkginfo = require('pkginfo')(module);
2 | var shell = require('shelljs');
3 | var program = require('gitlike-cli');
4 | // var Confirm = require('prompt-confirm');
5 | // var currentFolder = process.cwd();
6 | var kbString = require('./kb-string');
7 | var commandDefinitions = require('./commandDefinitions');
8 |
9 | if (!shell.which('git')) {
10 | shell.echo('Sorry, kibibit requires git');
11 | shell.exit(1);
12 | }
13 |
14 | var gitflowUrl = 'https://datasift.github.io/gitflow/IntroducingGitFlow.html';
15 |
16 | program
17 | .version(module.exports.version)
18 | .description(kbString.build(
19 | kbString.header([
20 | kbString.success('~= '),
21 | kbString.kibibitLogo(),
22 | ' cli tool for development using hubflow(gitflow) ',
23 | kbString.success('=~')
24 | ]),
25 | kbString.build([
26 | '\n',
27 | bodyLine('this cli should eventually be used to work on '),
28 | bodyLine('different kibibit projects. read more about gitflow here:'),
29 | bodyLine(kbString.info(gitflowUrl))
30 | ])
31 | ));
32 |
33 | commandDefinitions.attach(program);
34 |
35 | program.parse(process.argv);
36 |
37 | process.on('uncaughtException', function (error) {
38 | console.log(error.stack);
39 | });
40 |
41 | function bodyLine(str) {
42 | return ' ' + str + '\n';
43 | }
44 |
--------------------------------------------------------------------------------
/lib/gitflow/master.js:
--------------------------------------------------------------------------------
1 | // var kbString = require('../kb-string');
2 | var gitRoot = require('../kb-git-root');
3 | // var fs = require('fs');
4 | // var currentFolder = process.cwd();
5 | var NodeGit = require('nodegit-flow')(require('nodegit'));
6 | // var Table = require('cli-table');
7 | // var _ = require('lodash');
8 | // var path = require('path');
9 | // var colorize = require('json-colorizer');
10 | // var Q = require('q');
11 | // var findRoot = require('find-root');
12 | // var countFiles = require('count-files');
13 |
14 | var developGitFlow = {};
15 |
16 | developGitFlow.develop = develop;
17 |
18 | module.exports = developGitFlow;
19 |
20 | var gRepo;
21 |
22 |
23 | function develop() {
24 | gitRoot.getGitRoot()
25 | .then(function(_gitRoot) {
26 | return NodeGit.Repository.open(_gitRoot);
27 | })
28 | .then(function(repo) {
29 | gRepo = repo;
30 | return NodeGit.Flow.getConfig(repo);
31 | })
32 | .then(function(config) {
33 | return gRepo.getBranch(config['gitflow.branch.master'] || 'master');
34 | })
35 | .then(function(reference) {
36 | //checkout branch
37 | return gRepo.checkoutRef(reference);
38 | })
39 | .then(function() {
40 | process.exit(0);
41 | })
42 | .catch(function(error) {
43 | console.error(error);
44 | process.exit(1);
45 | });
46 | }
47 |
--------------------------------------------------------------------------------
/lib/gitflow/develop.js:
--------------------------------------------------------------------------------
1 | // var kbString = require('../kb-string');
2 | var gitRoot = require('../kb-git-root');
3 | // var fs = require('fs');
4 | // var currentFolder = process.cwd();
5 | var NodeGit = require('nodegit-flow')(require('nodegit'));
6 | // var Table = require('cli-table');
7 | // var _ = require('lodash');
8 | // var path = require('path');
9 | // var colorize = require('json-colorizer');
10 | // var Q = require('q');
11 | // var findRoot = require('find-root');
12 | // var countFiles = require('count-files');
13 |
14 | var developGitFlow = {};
15 |
16 | developGitFlow.develop = develop;
17 |
18 | module.exports = developGitFlow;
19 |
20 | var gRepo;
21 |
22 |
23 | function develop() {
24 | gitRoot.getGitRoot()
25 | .then(function(_gitRoot) {
26 | return NodeGit.Repository.open(_gitRoot);
27 | })
28 | .then(function(repo) {
29 | gRepo = repo;
30 | return NodeGit.Flow.getConfig(repo);
31 | })
32 | .then(function(config) {
33 | return gRepo.getBranch(config['gitflow.branch.develop'] || 'develop');
34 | })
35 | .then(function(reference) {
36 | //checkout branch
37 | return gRepo.checkoutRef(reference);
38 | })
39 | .then(function() {
40 | process.exit(0);
41 | })
42 | .catch(function(error) {
43 | console.error(error);
44 | process.exit(1);
45 | });
46 | }
47 |
--------------------------------------------------------------------------------
/lib/gitflow/init.js:
--------------------------------------------------------------------------------
1 | var kbString = require('../kb-string');
2 | var gitRoot = require('../kb-git-root');
3 | var kbUser = require('../services/kb-user');
4 | var kbRepo = require('../services/kb-repo');
5 | var errorHandler = require('../errors/error-handler')('[INIT-FLOW]');
6 | var currentFolder = process.cwd();
7 |
8 | var initGitflow = {};
9 |
10 | initGitflow.init = init;
11 |
12 | module.exports = initGitflow;
13 |
14 | function init() {
15 | var GLOBAL = {};
16 |
17 | gitRoot.getGitRoot()
18 | .then(function(root) {
19 | GLOBAL.root = root;
20 |
21 | return kbRepo.openGit();
22 | })
23 | .catch(function() {
24 | console.info(kbString.error('git repo not found'));
25 | console.log('initializing...');
26 |
27 | GLOBAL.root = currentFolder;
28 |
29 | return kbRepo.initGit(GLOBAL.root);
30 | })
31 | .then(function(repo) {
32 | GLOBAL.repo = repo;
33 |
34 | return kbRepo.ensureGitFlowNotInitialized(GLOBAL.repo);
35 | })
36 | .then(kbString.printFlash)
37 | .then(kbUser.questions.selectOrCreateUser)
38 | .then(function(user) {
39 | GLOBAL.user = user;
40 | return kbRepo.setGitConfigUser(GLOBAL.repo, GLOBAL.user);
41 | })
42 | .then(function() {
43 | return kbRepo.ensureBasicGitFlowDetails(GLOBAL.root,
44 | GLOBAL.repo, GLOBAL.user);
45 | })
46 | .then(function() {
47 | return kbRepo.questions.createRepoOrigin(GLOBAL.repo, GLOBAL.user);
48 | })
49 | .then(function() {
50 | console.log('git flow repo initialized');
51 | process.exit(0);
52 | })
53 | .catch(errorHandler);
54 | }
55 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "kibibit-cli",
3 | "version": "0.1.0",
4 | "description": "cli tool for development using hubflow(gitflow + GitHub)",
5 | "bin": {
6 | "kibibit": "bin/kibibit-cli.js"
7 | },
8 | "main": "./lib/kibibit.js",
9 | "scripts": {
10 | "test": "echo \"Error: no test specified\" && exit 1"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/Kibibit/kibibit-cli.git"
15 | },
16 | "author": "neilkalman@gmail.com",
17 | "license": "ISC",
18 | "bugs": {
19 | "url": "https://github.com/Kibibit/kibibit-cli/issues"
20 | },
21 | "homepage": "https://github.com/Kibibit/kibibit-cli#readme",
22 | "dependencies": {
23 | "async": "^2.6.0",
24 | "blessed": "^0.1.81",
25 | "cardinal": "^1.0.0",
26 | "chalk": "^2.3.0",
27 | "cli-color": "^1.2.0",
28 | "cli-table": "^0.3.1",
29 | "count-files": "^2.6.2",
30 | "eslint": "^5.14.1",
31 | "eslint-plugin-kibibit": "github:kibibit/eslint-plugin-kibibit",
32 | "eslint-plugin-lodash": "^5.1.0",
33 | "find-root": "^1.1.0",
34 | "fs-readfile-promise": "^3.0.1",
35 | "gitlike-cli": "^0.1.0",
36 | "highlight.js": "^9.14.2",
37 | "home-config": "^0.1.0",
38 | "indent-string": "^3.2.0",
39 | "inquirer": "^4.0.0",
40 | "json-colorizer": "^2.1.2",
41 | "keytar": "^4.1.0",
42 | "lodash": "^4.17.4",
43 | "manakin": "^0.5.1",
44 | "marked": "^0.6.1",
45 | "moment": "^2.19.3",
46 | "node-emoji": "^1.8.1",
47 | "nodegit": "^0.25.0-alpha.7",
48 | "nodegit-flow": "^2.0.0",
49 | "octonode": "git+https://github.com/Thatkookooguy/octonode.git",
50 | "pkginfo": "^0.4.1",
51 | "prompt-checkbox": "^2.2.0",
52 | "prompt-confirm": "^1.2.0",
53 | "q": "^1.5.1",
54 | "sanitize-html": "^1.16.3",
55 | "shelljs": "^0.8.3",
56 | "strip-ansi": "^5.0.0",
57 | "unescape": "^1.0.1",
58 | "wrap-ansi": "^4.0.0",
59 | "write-file-promise": "^1.0.0"
60 | },
61 | "devDependencies": {
62 | "npm-check": "^5.9.0",
63 | "npm-install-peers": "^1.2.1",
64 | "typescript": "^3.3.3333"
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/lib/gitflow/finish.js:
--------------------------------------------------------------------------------
1 | var NodeGit = require('nodegit-flow')(require('nodegit'));
2 | var gitRoot = require('../kb-git-root');
3 |
4 | var GLOB = {
5 | config: {},
6 | repo: {}
7 | };
8 |
9 | var finishGitflow = {};
10 |
11 | finishGitflow.finish = finish;
12 |
13 | module.exports = finishGitflow;
14 |
15 | function finish() {
16 | gitRoot.getGitRoot()
17 | .then(function(_gitRoot) {
18 | if (!_gitRoot) {
19 | console.info(kbString.error('git repo not found'));
20 | process.exit(1);
21 | }
22 |
23 | GLOB.gitRoot = _gitRoot;
24 |
25 | return NodeGit.Repository.open(GLOB.gitRoot);
26 | })
27 | .then(function(repo) {
28 | GLOB.repo = repo;
29 | return NodeGit.Flow.getConfig(repo);
30 | })
31 | .then(function(config) {
32 | GLOB.config = config;
33 | console.log(config);
34 | return GLOB.repo.getCurrentBranch();
35 | })
36 | .then(function(branchRef) {
37 | return branchRef.name();
38 | })
39 | .then(function(branchName) {
40 | console.log(branchName);
41 | if (branchName.indexOf(GLOB.config['gitflow.prefix.feature']) >= 0) {
42 | console.log('feature branch');
43 | var featureName = branchName.replace(/.*\//gi, '');
44 | return NodeGit.Flow.finishFeature(GLOB.repo, featureName);
45 | } else
46 | if (branchName.indexOf(GLOB.config['gitflow.prefix.hotfix']) >= 0) {
47 | console.log('hotfix branch');
48 | var hotfixName = branchName.replace(/.*\//gi, '');
49 | return NodeGit.Flow.finishHotfix(GLOB.repo, hotfixName);
50 | } else
51 | if (branchName.indexOf(GLOB.config['gitflow.prefix.release']) >= 0) {
52 | console.log('release branch');
53 | var releaseName = branchName.replace(/.*\//gi, '');
54 | return NodeGit.Flow.finishRelease(GLOB.repo, releaseName);
55 | } else {
56 | console.log('not a branch you can finish!');
57 | }
58 | process.exit(0);
59 | })
60 | .then(function(mergeCommit) {
61 | console.log(mergeCommit); // => the sha of the newly created commit
62 | process.exit(0);
63 | })
64 | .catch(function(error) {
65 | console.error(error);
66 | process.exit(1);
67 | });
68 | }
69 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | 'plugins': ['lodash', 'kibibit'],
3 | 'rules': {
4 | 'lodash/prefer-is-nil': 2,
5 | 'lodash/prefer-lodash-typecheck': 2,
6 | 'lodash/prefer-startswith': 2,
7 | 'lodash/prefer-includes': 2,
8 | 'lodash/prefer-noop': 2,
9 | 'kibibit/lodash-typecheck-instead-of-angular': 1,
10 | 'kibibit/angular-modules-functions-order': 1,
11 | 'accessor-pairs': 'error',
12 | 'no-unused-vars': 'error',
13 | 'default-case': 'error',
14 | 'eqeqeq': 'error',
15 | 'no-extend-native': 'error',
16 | 'curly': [
17 | 2,
18 | 'all'
19 | ],
20 | 'operator-linebreak': [
21 | 2,
22 | 'after'
23 | ],
24 | 'camelcase': [
25 | 2,
26 | {
27 | 'properties': 'always'
28 | }
29 | ],
30 | 'max-len': [
31 | 2,
32 | 80
33 | ],
34 | 'indent': [
35 | 2,
36 | 2,
37 | {
38 | 'SwitchCase': 1
39 | }
40 | ],
41 | 'quotes': [
42 | 2,
43 | 'single'
44 | ],
45 | 'no-multi-str': 2,
46 | 'no-mixed-spaces-and-tabs': 2,
47 | 'no-trailing-spaces': 2,
48 | 'space-unary-ops': [
49 | 2,
50 | {
51 | 'nonwords': false,
52 | 'overrides': {}
53 | }
54 | ],
55 | 'one-var': [
56 | 2,
57 | {
58 | 'uninitialized': 'always',
59 | 'initialized': 'never'
60 | }
61 | ],
62 | 'brace-style': [
63 | 2,
64 | '1tbs',
65 | {
66 | 'allowSingleLine': true
67 | }
68 | ],
69 | 'keyword-spacing': [
70 | 2,
71 | {}
72 | ],
73 | 'space-infix-ops': 2,
74 | 'space-before-blocks': [
75 | 2,
76 | 'always'
77 | ],
78 | 'eol-last': 2,
79 | 'space-before-function-paren': [
80 | 2,
81 | {
82 | 'anonymous': 'ignore',
83 | 'named': 'never'
84 | }
85 | ],
86 | 'array-bracket-spacing': [
87 | 2,
88 | 'never',
89 | {
90 | 'singleValue': true
91 | }
92 | ],
93 | 'space-in-parens': [
94 | 2,
95 | 'never'
96 | ],
97 | 'no-multiple-empty-lines': 2,
98 | 'no-with': 2,
99 | 'no-spaced-func': 2,
100 | 'key-spacing': [
101 | 2,
102 | {
103 | 'beforeColon': false,
104 | 'afterColon': true
105 | }
106 | ],
107 | 'dot-notation': 2,
108 | 'semi': [
109 | 2,
110 | 'always'
111 | ],
112 | 'valid-jsdoc': 2
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | kibibit-cli (aka bit)
5 |
6 |
7 |
8 |
12 |
13 |
14 |
15 | A git-flow + GitHub replacement for git and git flow
16 |
17 |
18 |
19 | ### installation
20 | run `npm install -g https://github.com/Kibibit/kibibit-cli.git`
21 | `kibibit status`
22 |
23 | This is intended to replace your `git` with `bit` (right now it's `kibibit`).
24 |
25 | `bit` uses the git-flow methodology to optimize team work on GitHub.
26 |
27 | - `git status` -> `bit status`
28 | - `git checkout master` -> `bit master`
29 | - `git checkout develop` -> `bit develop`
30 |
31 | ```
32 | Usage: kibibit-cli.js [options] [null]
33 |
34 | Commands:
35 |
36 | commit clone a remote repository
37 | status show current branch status
38 | init initialize the gitflow tools for the current repo. (GitHub login, etc.)
39 | clone clone a remote repository
40 | feature start or continue a feature (will be prompted). If no featureName is given, returns all ongoing features
41 | hotfix start or continue a hotfix (will be prompted). If no hotfixName is given, returns all ongoing hotfixes
42 | finish use GitHub to issue a pull request to origin/develop.
43 | release When you have enough completed features in origin/develop, create a release branch, test it and fix it, and then merge it into origin/master
44 | update keep up-to-date with completed features on GitHub
45 | push push your feature branch back to GitHub as you make progress and want to save your work
46 | master checkout master branch
47 | develop checkout develop branch
48 |
49 | Options:
50 |
51 | -h, --help output help information
52 | -v, --version output version information
53 | ```
54 |
--------------------------------------------------------------------------------
/lib/services/kb-user.js:
--------------------------------------------------------------------------------
1 | var KbError = require('../errors/KbError');
2 | var globalBitConfig = require('home-config').load('.kibibit-bit');
3 | var keytar = require('keytar');
4 | var _ = require('lodash');
5 | var Q = require('q');
6 | var inquirer = require('inquirer');
7 | var kbGithub = require('../services/kb-github');
8 |
9 | var kbUser = {};
10 |
11 | kbUser.getExistingBitUsers = getExistingBitUsers;
12 | kbUser.getBitUserToken = getBitUserToken;
13 | kbUser.addBitUser = addBitUser;
14 |
15 | kbUser.questions = {};
16 | kbUser.questions.selectOrCreateUser = selectOrCreateUser;
17 | kbUser.questions.loginNewUser = loginNewUser;
18 |
19 | initBitUsersArray();
20 |
21 | module.exports = kbUser;
22 |
23 | function initBitUsersArray() {
24 | if (!_.isArray(globalBitConfig.users)) {
25 | globalBitConfig.users = [];
26 |
27 | globalBitConfig.save();
28 | }
29 | }
30 |
31 | function loginNewUser() {
32 | return kbGithub.questions.userQuestionsGithubLogin()
33 | .then(function(newUser) {
34 | console.log('user logged in', newUser);
35 |
36 | return addBitUser(newUser.username, newUser.token);
37 | });
38 | }
39 |
40 | function selectOrCreateUser() {
41 | if (!globalBitConfig.users.length) {
42 | return Q.fcall(function() {
43 | return kbUser.questions.loginNewUser();
44 | });
45 | }
46 |
47 | var username, isSelectedExistingUser;
48 |
49 | return inquirer.prompt([ {
50 | type: 'list',
51 | name: 'selectUser',
52 | message: 'Found registered users. Select a user for this repo',
53 | choices: globalBitConfig.users.concat([
54 | new inquirer.Separator(),
55 | 'Add a new user'
56 | ])
57 | } ])
58 | .then(function(answers) {
59 | isSelectedExistingUser =
60 | globalBitConfig.users.indexOf(answers.selectUser) > -1;
61 | console.log('isSelectedExistingUser?', isSelectedExistingUser);
62 | username = answers.selectUser;
63 | return isSelectedExistingUser ?
64 | getBitUserToken(username) :
65 | kbUser.questions.loginNewUser();
66 | })
67 | .then(function(data) {
68 | return isSelectedExistingUser ? {
69 | username: username,
70 | token: data
71 | } : data;
72 | })
73 | .then(function(user) {
74 | return kbGithub.getGitHubUserData(user.username, user.token);
75 | });
76 | }
77 |
78 | function getExistingBitUsers() {
79 | return Q.fcall(function() {
80 | return globalBitConfig.users;
81 | });
82 | }
83 |
84 | function getBitUserToken(username) {
85 | return keytar.getPassword('kibibit-cli', username);
86 | }
87 |
88 | function addBitUser(gitHubUsername, token) {
89 | if (globalBitConfig.users.indexOf(gitHubUsername) === -1) {
90 | globalBitConfig.users.push(gitHubUsername);
91 | }
92 |
93 | return keytar.setPassword('kibibit-cli', gitHubUsername, token)
94 | .then(function() {
95 | return globalBitConfig.save();
96 | })
97 | .then(function() {
98 | return gitHubUsername;
99 | })
100 | .catch(function() {
101 | throw new KbError('something went wrong', 1);
102 | });
103 | }
104 |
--------------------------------------------------------------------------------
/lib/gitflow/sync.js:
--------------------------------------------------------------------------------
1 | var GLOBAL = {};
2 | var NodeGit = require('nodegit-flow')(require('nodegit'));
3 | var gitRoot = require('../kb-git-root');
4 | var keytar = require('keytar');
5 | // var kbString = require('../kb-string');
6 |
7 | var syncGitflow = {};
8 |
9 | syncGitflow.sync = sync;
10 |
11 | module.exports = syncGitflow;
12 |
13 | var cloneOptions = {
14 | fetchOpts: {
15 | callbacks: {
16 | certificateCheck: function() {
17 | return 1;
18 | },
19 | credentials: function() {
20 | console.log('asked for credentials', GLOBAL.token);
21 | return NodeGit.Cred.userpassPlaintextNew(GLOBAL.token, 'x-oauth-basic');
22 | }
23 | }
24 | }
25 | };
26 |
27 | function sync() {
28 | console.log('get started!');
29 | gitRoot.getGitRoot()
30 | .then(function(_gitRoot) {
31 | console.log('gitRoot passed');
32 | if (!_gitRoot) {
33 | console.info('git repo not found');
34 | process.exit(1);
35 | } else {
36 | GLOBAL.gitRoot = _gitRoot;
37 | // open the git repo if it exists
38 | console.log('opened repo');
39 | return NodeGit.Repository.open(GLOBAL.gitRoot);
40 | }
41 | })
42 | .then(function(repo) {
43 | GLOBAL.repo = repo;
44 | console.log('found repo. trying to fetch');
45 | return NodeGit.Remote.list(GLOBAL.repo);
46 | })
47 | .then(function(remoteList) {
48 | console.log('here are all the remotes:', remoteList);
49 |
50 | if (!remoteList.length) {
51 | console.log('no remotes found. need to add one to update');
52 | process.exit(1);
53 | }
54 |
55 | return GLOBAL.repo.config()
56 | .then(function(config) {
57 | return config.getString('kibibit.user');
58 | });
59 | // process.exit(0);
60 |
61 | // return GLOBAL.repo.fetch('origin', cloneOptions.fetchOpts);
62 | })
63 | .then(function(userToUse) {
64 | GLOBAL.username = userToUse;
65 | console.log('user to use', GLOBAL.username);
66 | return keytar.getPassword('kibibit-cli', GLOBAL.username);
67 | })
68 | .then(function(token) {
69 | GLOBAL.token = token;
70 |
71 | console.log('your token', token);
72 | return GLOBAL.repo.fetchAll(cloneOptions.fetchOpts);
73 | process.exit(1);
74 | })
75 | .then(function() {
76 | return NodeGit.Flow.getConfig(GLOBAL.repo);
77 | })
78 | .then(function(config) {
79 | GLOBAL.gitflowConfig = config;
80 | return GLOBAL.repo.mergeBranches(
81 | GLOBAL.gitflowConfig['gitflow.branch.master'],
82 | 'origin/' + GLOBAL.gitflowConfig['gitflow.branch.master']);
83 | })
84 | .then(function() {
85 | return GLOBAL.repo.mergeBranches(
86 | GLOBAL.gitflowConfig['gitflow.branch.develop'],
87 | 'origin/' + GLOBAL.gitflowConfig['gitflow.branch.develop']);
88 | })
89 | // TODO(thatkookooguy): also update current branch!
90 | // .done(function() {
91 | // console.log('all done!');
92 | // process.exit(0);
93 | // })
94 | .then(function(result) {
95 | console.log('the result?', result);
96 | process.exit(0);
97 | })
98 | .catch(function(error) {
99 | console.trace(error);
100 | process.exit(1);
101 | });
102 | }
103 |
--------------------------------------------------------------------------------
/lib/kb-string.js:
--------------------------------------------------------------------------------
1 | var kbString = {};
2 |
3 | var clc = require('cli-color');
4 | var _ = require('lodash');
5 |
6 | var msgColors = clc.xterm(45).bgXterm(236);
7 | var headerColors = clc.bgXterm(0);
8 | var warningColors = clc.xterm(184);
9 | var errorColors = clc.xterm(196);
10 | var infoColors = clc.xterm(39);
11 | var successColors = clc.xterm(42);
12 | var whiteColors = clc.xterm(15);
13 | var ohNoColors = clc.bgXterm(9).xterm(15);
14 |
15 | kbString.important = buildImportant;
16 | kbString.msg = buildMsg;
17 | kbString.header = buildHeader;
18 | kbString.warning = buildWarning;
19 | kbString.error = buildError;
20 | kbString.info = buildInfo;
21 | kbString.success = buildSuccess;
22 | kbString.white = buildWhite;
23 | kbString.ohNo = buildOhNo;
24 | kbString.param = buildParam;
25 | kbString.kibibitLogo = kibibitLogo;
26 | kbString.build = build;
27 | kbString.printFlash = printFlash;
28 |
29 | module.exports = kbString;
30 |
31 | function buildImportant() {
32 | return clc.bold.underline.blink(kbString.build.apply(this, arguments));
33 | }
34 |
35 | function buildParam() {
36 | return clc.cyan(kbString.build.apply(this, arguments));
37 | }
38 |
39 | function buildMsg() {
40 | return msgColors(kbString.build.apply(this, arguments));
41 | }
42 |
43 | function buildHeader() {
44 | return headerColors(kbString.build.apply(this, arguments));
45 | }
46 |
47 | function buildWarning() {
48 | return warningColors(kbString.build.apply(this, arguments));
49 | }
50 |
51 | function buildError() {
52 | return errorColors(kbString.build.apply(this, arguments));
53 | }
54 |
55 | function buildInfo() {
56 | return infoColors(kbString.build.apply(this, arguments));
57 | }
58 |
59 | function buildSuccess() {
60 | return successColors(kbString.build.apply(this, arguments));
61 | }
62 |
63 | function buildWhite() {
64 | return whiteColors(kbString.build.apply(this, arguments));
65 | }
66 |
67 | function buildOhNo() {
68 | return ohNoColors(kbString.build.apply(this, arguments));
69 | }
70 |
71 | function printFlash() {
72 | console.log(kbString.kibibitLogo(true));
73 | console.log(kbString.success(' gitflow + github cli '));
74 | console.log('======================');
75 | console.log('');
76 |
77 | return;
78 | }
79 |
80 | function kibibitLogo(big) {
81 | if (big) {
82 | return kbString.build([
83 | ' _ ', kbString.error('_'), ' _ ', kbString.info('_'),
84 | ' _ ', kbString.warning('_'), ' _ \n',
85 | '| | _', kbString.error('(_)'), ' |__ ', kbString.info('(_)'),
86 | ' |__ ', kbString.warning('(_)'), ' |_ \n',
87 | '| |/ / ', kbString.error('|'), ' \'_ \\', kbString.info('| |'),
88 | ' \'_ \\', kbString.warning('| |'), ' __|\n',
89 | '| <', kbString.error('| |'), ' |_) ', kbString.info('| |'),
90 | ' |_) ', kbString.warning('| |'), ' |_ \n',
91 | '|_|\\_\\', kbString.error('_|'), '_.__/', kbString.info('|_|'),
92 | '_.__/', kbString.warning('|_|'), '\\__|\n'
93 | ]);
94 | }
95 | return kbString.build([
96 | kbString.white('k'),
97 | kbString.error('i'),
98 | kbString.white('b'),
99 | kbString.info('i'),
100 | kbString.white('b'),
101 | kbString.warning('i'),
102 | kbString.white('t')
103 | ]);
104 | }
105 |
106 | function build() {
107 | var array = _.isArray(arguments[0]) ? arguments[0] : arguments;
108 |
109 | return _.join(array, '');
110 | }
111 |
--------------------------------------------------------------------------------
/lib/gitflow/hotfix.js:
--------------------------------------------------------------------------------
1 | var kbString = require('../kb-string');
2 | var _ = require('lodash');
3 | var Q = require('q');
4 | var NodeGit = require('nodegit-flow')(require('nodegit'));
5 | var moment = require('moment');
6 | var gitRoot = require('../kb-git-root');
7 |
8 | var GLOBAL = {
9 | repo: null,
10 | featureBranches: null
11 | };
12 |
13 | var hotfixGitflow = {};
14 |
15 | hotfixGitflow.hotfix = hotfix;
16 |
17 | module.exports = hotfixGitflow;
18 |
19 | function startHotfix(args) {
20 | return NodeGit.Flow.startHotfix(
21 | GLOBAL.repo,
22 | args.hotfixName
23 | )
24 | .then(function(branch) {
25 | console
26 | .log('creating hotfix named', args.hotfixName);
27 | return branch;
28 | })
29 | .catch(function(/*error*/) {
30 | // console.trace(error);
31 | return NodeGit.Flow.getConfig(GLOBAL.repo)
32 | .then(function(config) {
33 | // console.log(config);
34 | GLOBAL.currHotfixBranch =
35 | config['gitflow.prefix.hotfix'] + args.hotfixName;
36 | console
37 | .log('checking out existing hotfix ', GLOBAL.currHotfixBranch);
38 | return GLOBAL.repo.checkoutBranch(GLOBAL.currHotfixBranch);
39 | // process.exit(1);
40 | })
41 | .then(function() {
42 | return GLOBAL.repo.getBranch(GLOBAL.currHotfixBranch);
43 | });
44 | });
45 | }
46 |
47 | function hotfix(args, options) {
48 |
49 | gitRoot.getGitRoot()
50 | .then(function(_gitRoot) {
51 | if (!_gitRoot) {
52 | console.info(kbString.error('git repo not found'));
53 | process.exit(1);
54 | } else {
55 | GLOBAL.gitRoot = _gitRoot;
56 | // open the git repo if it exists
57 | return NodeGit.Repository.open(GLOBAL.gitRoot);
58 | }
59 | })
60 | .then(function(repo) {
61 | GLOBAL.repo = repo;
62 |
63 | if (!args.hotfixName) {
64 | return GLOBAL.repo.getReferenceNames(NodeGit.Reference.TYPE.LISTALL);
65 | } else {
66 | return startHotfix(args)
67 | .then(function(/* featureBranch */) {
68 | // upload branch to github
69 | // (either empty or with an empty init commit)
70 | // console.log(featureBranch.shorthand()); // => feautre/my-feature
71 | process.exit(0);
72 | });
73 | }
74 | })
75 | .then(function(allBranches) {
76 | var regexItem = options.remote ?
77 | /^refs\/remotes\/.*?\/hotfix\// :
78 | /^refs\/heads\/hotfix\//;
79 |
80 | allBranches = _.filter(allBranches, function(branch) {
81 | return regexItem.test(branch); // feature\/
82 | });
83 |
84 | var hotfixBranches = _.map(allBranches, function(branch) {
85 | return branch.replace(regexItem, '');
86 | });
87 |
88 | GLOBAL.hotfixBranches = hotfixBranches;
89 |
90 | console.log(kbString.success([
91 | 'Found ',
92 | hotfixBranches.length, ' ',
93 | kbString.important(options.remote ? 'remote' : 'local'),
94 | ' hotfixes:\n',
95 | '======================'
96 | ]));
97 |
98 | // todo(thatkookooguy): simplify this!
99 | return Q.all(_.map(allBranches, function(branch) {
100 | return GLOBAL.repo.getBranchCommit(branch)
101 | .then(function(result) {
102 | return result;
103 | }, function(error) {
104 | var deferred = Q.defer();
105 | deferred.resolve(error);
106 |
107 | return deferred.promise;
108 | });
109 | }));
110 | })
111 | .then(function(allLastCommits) {
112 | _.forEach(allLastCommits, function(commit, index) {
113 | if (commit.errno) {
114 | console.log(kbString.build([
115 | kbString.info([
116 | '[',
117 | 'hotfix/',
118 | GLOBAL.hotfixBranches[index],
119 | ']'
120 | ]),
121 | ' ',
122 | kbString.error('no commits yet...'),
123 | ' ---ERROR: ', commit.message
124 | ]));
125 | return;
126 | }
127 | console.log(kbString.build([
128 | kbString.info('[', 'hotfix/', GLOBAL.hotfixBranches[index], ']'),
129 | ' ',
130 | kbString.white('(', commit.author(), ')'),
131 | kbString.error(' > '),
132 | kbString.success(commit.message().trim().split('\n', 1)[0]),
133 | kbString.warning(' (', moment(commit.date()).fromNow(), ')')
134 | ]));
135 | });
136 | process.exit(0);
137 | })
138 | .catch(function(error) {
139 | console.trace('oops.... something went wrong...', error);
140 | process.exit(1);
141 | });
142 | }
143 |
--------------------------------------------------------------------------------
/lib/gitflow/feature.js:
--------------------------------------------------------------------------------
1 | var kbString = require('../kb-string');
2 | var _ = require('lodash');
3 | var Q = require('q');
4 | var NodeGit = require('nodegit-flow')(require('nodegit'));
5 | var moment = require('moment');
6 | var gitRoot = require('../kb-git-root');
7 |
8 | var GLOBAL = {
9 | repo: null,
10 | featureBranches: null
11 | };
12 |
13 | var featureGitflow = {};
14 |
15 | featureGitflow.feature = feature;
16 |
17 | module.exports = featureGitflow;
18 |
19 | function startFeature(args) {
20 | return NodeGit.Flow.startFeature(
21 | GLOBAL.repo,
22 | args.featureName
23 | )
24 | .then(function(branch) {
25 | console
26 | .log('creating feature named', args.featureName);
27 | return branch;
28 | })
29 | .catch(function(/*error*/) {
30 | // console.trace(error);
31 | return NodeGit.Flow.getConfig(GLOBAL.repo)
32 | .then(function(config) {
33 | // console.log(config);
34 | GLOBAL.currFeatureBranch =
35 | config['gitflow.prefix.feature'] + args.featureName;
36 | console
37 | .log('checking out existing feature ', GLOBAL.currFeatureBranch);
38 | return GLOBAL.repo.checkoutBranch(GLOBAL.currFeatureBranch);
39 | // process.exit(1);
40 | })
41 | .then(function() {
42 | return GLOBAL.repo.getBranch(GLOBAL.currFeatureBranch);
43 | });
44 | });
45 | }
46 |
47 | function feature(args, options) {
48 |
49 | gitRoot.getGitRoot()
50 | .then(function(_gitRoot) {
51 | if (!_gitRoot) {
52 | console.info(kbString.error('git repo not found'));
53 | process.exit(1);
54 | } else {
55 | GLOBAL.gitRoot = _gitRoot;
56 | // open the git repo if it exists
57 | return NodeGit.Repository.open(GLOBAL.gitRoot);
58 | }
59 | })
60 | .then(function(repo) {
61 | GLOBAL.repo = repo;
62 |
63 | if (!args.featureName) {
64 | return GLOBAL.repo.getReferenceNames(NodeGit.Reference.TYPE.LISTALL);
65 | } else {
66 | return startFeature(args)
67 | .then(function(/* featureBranch */) {
68 | // upload branch to github
69 | // (either empty or with an empty init commit)
70 | // console.log(featureBranch.shorthand()); // => feautre/my-feature
71 | process.exit(0);
72 | });
73 | }
74 | })
75 | .then(function(allBranches) {
76 | var regexItem = options.remote ?
77 | /^refs\/remotes\/.*?\/feature\// :
78 | /^refs\/heads\/feature\//;
79 |
80 | allBranches = _.filter(allBranches, function(branch) {
81 | return regexItem.test(branch); // feature\/
82 | });
83 |
84 | var featureBranches = _.map(allBranches, function(branch) {
85 | return branch.replace(regexItem, '');
86 | });
87 |
88 | GLOBAL.featureBranches = featureBranches;
89 |
90 | console.log(kbString.success([
91 | 'Found ',
92 | featureBranches.length, ' ',
93 | kbString.important(options.remote ? 'remote' : 'local'),
94 | ' features:\n',
95 | '======================'
96 | ]));
97 |
98 | // todo(thatkookooguy): simplify this!
99 | return Q.all(_.map(allBranches, function(branch) {
100 | return GLOBAL.repo.getBranchCommit(branch)
101 | .then(function(result) {
102 | return result;
103 | }, function(error) {
104 | var deferred = Q.defer();
105 | deferred.resolve(error);
106 |
107 | return deferred.promise;
108 | });
109 | }));
110 | })
111 | .then(function(allLastCommits) {
112 | _.forEach(allLastCommits, function(commit, index) {
113 | if (commit.errno) {
114 | console.log(kbString.build([
115 | kbString.info([
116 | '[',
117 | 'feature/',
118 | GLOBAL.featureBranches[index],
119 | ']'
120 | ]),
121 | ' ',
122 | kbString.error('no commits yet...'),
123 | ' ---ERROR: ', commit.message
124 | ]));
125 | return;
126 | }
127 | console.log(kbString.build([
128 | kbString.info('[', 'feature/', GLOBAL.featureBranches[index], ']'),
129 | ' ',
130 | kbString.white('(', commit.author(), ')'),
131 | kbString.error(' > '),
132 | kbString.success(commit.message().trim().split('\n', 1)[0]),
133 | kbString.warning(' (', moment(commit.date()).fromNow(), ')')
134 | ]));
135 | });
136 | process.exit(0);
137 | })
138 | .catch(function(error) {
139 | console.trace('oops.... something went wrong...', error);
140 | process.exit(1);
141 | });
142 | }
143 |
--------------------------------------------------------------------------------
/lib/gitflow/utility.js:
--------------------------------------------------------------------------------
1 | var kbString = require('../kb-string');
2 | var _ = require('lodash');
3 | var Q = require('q');
4 | var colorize = require('json-colorizer');
5 | var github = require('octonode');
6 |
7 | var util = {};
8 |
9 | util.addFilesToIndex = addFilesToIndex;
10 | util.openIndex = openIndex;
11 | util.writeFilesInIndex = writeFilesInIndex;
12 | util.writeIndexTree = writeIndexTree;
13 | util.getRootCommit = getRootCommit;
14 | util.getGitHubUserData = getGitHubUserData;
15 | util.getOrgs = getOrgs;
16 | util.createRepoUser = createRepoUser;
17 | util.createRepoOrg = createRepoOrg;
18 | util.signingInAnimation = [
19 | kbString.info(kbString.warning('/'), ' Signing in'),
20 | kbString.info(kbString.warning('|'), ' Signing in..'),
21 | kbString.info(kbString.warning('\\'), ' Signing in..'),
22 | kbString.info(kbString.warning('-'), ' Signing in...')
23 | ];
24 | util.signingInSteps = 4;
25 |
26 | module.exports = util;
27 |
28 | var client;
29 |
30 | function createRepoUser(name, description) {
31 | var deferred = Q.defer();
32 |
33 | description = description || '';
34 |
35 | if (!name) {
36 | deferred.reject('name must be given');
37 |
38 | return deferred.promise;
39 | }
40 |
41 | var ghme = client.me();
42 |
43 | ghme.repo({
44 | 'name': name,
45 | 'description': description,
46 | }, function(err, body) {
47 | if (err) {
48 | // console.log('ERROR!', err);
49 |
50 | deferred.reject('can\'t create user repo');
51 | } else {
52 | deferred.resolve(body);
53 | }
54 | }); //repo
55 |
56 | return deferred.promise;
57 | }
58 |
59 | function createRepoOrg(orgName, name, description) {
60 | var deferred = Q.defer();
61 |
62 | description = description || '';
63 |
64 | if (!name || !orgName) {
65 | deferred.reject('orgName & name must be given');
66 |
67 | return deferred.promise;
68 | }
69 |
70 | var ghorg = client.org(orgName);
71 |
72 | ghorg.repo({
73 | name: name,
74 | description: description
75 | }, function(err, body) {
76 | if (err) {
77 | // console.log('ERROR!', err);
78 | deferred.reject('can\'t create organization repo')
79 | } else {
80 | deferred.resolve(body);
81 | }
82 | });
83 |
84 | return deferred.promise;
85 | }
86 |
87 | function getOrgs() {
88 | var deferred = Q.defer();
89 |
90 | var ghme = client.me();
91 |
92 | ghme.orgs(function(err, body) {
93 | if (err) {
94 | // console.log('ERROR!', err);
95 | deferred.reject('can\'t get organizations data from github');
96 | } else {
97 | deferred.resolve(body);
98 | }
99 | });
100 |
101 | return deferred.promise;
102 | }
103 |
104 | function getGitHubUserData(username, token) {
105 | console.log('trying to login to github', token);
106 | var deferred = Q.defer();
107 |
108 | client = github.client(token);
109 |
110 | client.get('/user', {}, function (err, status, body) {
111 | if (err) {
112 | // console.log('ERROR!', err);
113 | deferred.reject('can\'t get user data from github');
114 | } else {
115 | // console.log(body); //json object
116 | deferred.resolve(body);
117 | }
118 | });
119 |
120 | return deferred.promise;
121 | }
122 |
123 | function addFilesToIndex(index, filesToCommit) {
124 | var fName = arguments.callee.toString().match(/function ([^\(]+)/)[1];
125 | if (!index) { return Q.reject(fName + ' expects an index arg'); }
126 | // add files
127 | return Q
128 | .allSettled(_.map(filesToCommit, function(file) {
129 | return index.addByPath(file);
130 | }));
131 | }
132 |
133 | // Load up the repository index
134 | function openIndex(repository) {
135 | var fName = arguments.callee.toString().match(/function ([^\(]+)/)[1];
136 | if (!repository) { return Q.reject(fName + ' expects a repository arg'); }
137 |
138 | return repository.refreshIndex();
139 | }
140 |
141 | // this will write all files to the index
142 | function writeFilesInIndex(index) {
143 | var fName = arguments.callee.toString().match(/function ([^\(]+)/)[1];
144 | if (!index) { return Q.reject(fName + ' expects an index arg'); }
145 | if (!index.write) { report(index); }
146 |
147 | return index.write();
148 | }
149 |
150 | function writeIndexTree(index) {
151 | var fName = arguments.callee.toString().match(/function ([^\(]+)/)[1];
152 | if (!index) { return Q.reject(fName + ' expects an index arg'); }
153 |
154 | return index.writeTree();
155 | }
156 |
157 | function getRootCommit(repo, head) {
158 | var fName = arguments.callee.toString().match(/function ([^\(]+)/)[1];
159 |
160 | if (!repo || !head) {
161 | return Q.reject(fName + ' expects a repo and an index arg');
162 | }
163 |
164 | return repo.getCommit(head);
165 | }
166 |
167 | function report(what, vars) {
168 | console.error(kbString.error(what),
169 | vars ? colorize(JSON.stringify(vars, null, 2)) : '');
170 | }
171 |
--------------------------------------------------------------------------------
/lib/gitflow/release.js:
--------------------------------------------------------------------------------
1 | var kbString = require('../kb-string');
2 | var _ = require('lodash');
3 | var Q = require('q');
4 | var NodeGit = require('nodegit-flow')(require('nodegit'));
5 | var moment = require('moment');
6 | var gitRoot = require('../kb-git-root');
7 |
8 | var GLOBAL = {
9 | repo: null,
10 | releaseBranches: null
11 | };
12 |
13 | var releaseGitflow = {};
14 |
15 | releaseGitflow.release = release;
16 |
17 | module.exports = releaseGitflow;
18 |
19 | function startRelease(args) {
20 | return NodeGit.Flow.startRelease(
21 | GLOBAL.repo,
22 | args.releaseName
23 | )
24 | .then(function(branch) {
25 | console
26 | .log('creating release named', args.releaseName);
27 | return branch;
28 | })
29 | .catch(function(/*error*/) {
30 | // console.trace(error);
31 | return NodeGit.Flow.getConfig(GLOBAL.repo)
32 | .then(function(config) {
33 | // console.log(config);
34 | GLOBAL.currReleaseBranch =
35 | config['gitflow.prefix.release'] + args.releaseName;
36 | console
37 | .log('checking out existing release ', GLOBAL.currReleaseBranch);
38 | return GLOBAL.repo.checkoutBranch(GLOBAL.currReleaseBranch);
39 | // process.exit(1);
40 | })
41 | .then(function() {
42 | return GLOBAL.repo.getBranch(GLOBAL.currReleaseBranch);
43 | });
44 | });
45 | }
46 |
47 | function release(args, options) {
48 |
49 | gitRoot.getGitRoot()
50 | .then(function(_gitRoot) {
51 | if (!_gitRoot) {
52 | console.info(kbString.error('git repo not found'));
53 | process.exit(1);
54 | } else {
55 | GLOBAL.gitRoot = _gitRoot;
56 | // open the git repo if it exists
57 | return NodeGit.Repository.open(GLOBAL.gitRoot);
58 | }
59 | })
60 | .then(function(repo) {
61 | GLOBAL.repo = repo;
62 |
63 | return NodeGit.Tag.list(GLOBAL.repo).then(function(array) {
64 | console.log('all tags!', array);
65 | var releaseTags = _.filter(array, function(tag) {
66 | return /^v\d+\.\d+\.\d+$/gi.test(tag);
67 | });
68 | console.log('only release tags', releaseTags);
69 | process.exit(1);
70 | return NodeGit.Tag.lookupPrefix(GLOBAL.repo, 'v', 1);
71 | });
72 | })
73 | .then(function() {
74 |
75 | if (!args.releaseName) {
76 | return GLOBAL.repo.getReferenceNames(NodeGit.Reference.TYPE.LISTALL);
77 | } else {
78 | return startRelease(args)
79 | .then(function(/* releaseBranch */) {
80 | // upload branch to github
81 | // (either empty or with an empty init commit)
82 | // console.log(releaseBranch.shorthand()); // => release/my-release
83 | process.exit(0);
84 | });
85 | }
86 | })
87 | .then(function(allBranches) {
88 | var regexItem = options.remote ?
89 | /^refs\/remotes\/.*?\/release\// :
90 | /^refs\/heads\/release\//;
91 |
92 | allBranches = _.filter(allBranches, function(branch) {
93 | return regexItem.test(branch); // release\/
94 | });
95 |
96 | var releaseBranches = _.map(allBranches, function(branch) {
97 | return branch.replace(regexItem, '');
98 | });
99 |
100 | GLOBAL.releaseBranches = releaseBranches;
101 |
102 | console.log(kbString.success([
103 | 'Found ',
104 | releaseBranches.length, ' ',
105 | kbString.important(options.remote ? 'remote' : 'local'),
106 | ' releases:\n',
107 | '======================'
108 | ]));
109 |
110 | // todo(thatkookooguy): simplify this!
111 | return Q.all(_.map(allBranches, function(branch) {
112 | return GLOBAL.repo.getBranchCommit(branch)
113 | .then(function(result) {
114 | return result;
115 | }, function(error) {
116 | var deferred = Q.defer();
117 | deferred.resolve(error);
118 |
119 | return deferred.promise;
120 | });
121 | }));
122 | })
123 | .then(function(allLastCommits) {
124 | _.forEach(allLastCommits, function(commit, index) {
125 | if (commit.errno) {
126 | console.log(kbString.build([
127 | kbString.info([
128 | '[',
129 | 'release/',
130 | GLOBAL.releaseBranches[index],
131 | ']'
132 | ]),
133 | ' ',
134 | kbString.error('no commits yet...'),
135 | ' ---ERROR: ', commit.message
136 | ]));
137 | return;
138 | }
139 | console.log(kbString.build([
140 | kbString.info('[', 'release/', GLOBAL.releaseBranches[index], ']'),
141 | ' ',
142 | kbString.white('(', commit.author(), ')'),
143 | kbString.error(' > '),
144 | kbString.success(commit.message().trim().split('\n', 1)[0]),
145 | kbString.warning(' (', moment(commit.date()).fromNow(), ')')
146 | ]));
147 | });
148 | process.exit(0);
149 | })
150 | .catch(function(error) {
151 | console.trace('oops.... something went wrong...', error);
152 | process.exit(1);
153 | });
154 | }
155 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Basic Options */
4 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
5 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
6 | // "lib": [], /* Specify library files to be included in the compilation. */
7 | // "allowJs": true, /* Allow javascript files to be compiled. */
8 | // "checkJs": true, /* Report errors in .js files. */
9 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
10 | // "declaration": true, /* Generates corresponding '.d.ts' file. */
11 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
12 | "sourceMap": true, /* Generates corresponding '.map' file. */
13 | // "outFile": "./", /* Concatenate and emit output to single file. */
14 | "outDir": "./dist", /* Redirect output structure to the directory. */
15 | "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
16 | // "composite": true, /* Enable project compilation */
17 | // "removeComments": true, /* Do not emit comments to output. */
18 | // "noEmit": true, /* Do not emit outputs. */
19 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
20 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
21 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
22 | /* Strict Type-Checking Options */
23 | "strict": true, /* Enable all strict type-checking options. */
24 | "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
25 | // "strictNullChecks": true, /* Enable strict null checks. */
26 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
27 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
28 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
29 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
30 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
31 | /* Additional Checks */
32 | // "noUnusedLocals": true, /* Report errors on unused locals. */
33 | "noUnusedParameters": true, /* Report errors on unused parameters. */
34 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
35 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
36 | /* Module Resolution Options */
37 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
38 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
39 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
40 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
41 | // "typeRoots": [], /* List of folders to include type definitions from. */
42 | // "types": [], /* Type declaration files to be included in compilation. */
43 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
44 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
45 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
46 | /* Source Map Options */
47 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
48 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
49 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
50 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
51 | /* Experimental Options */
52 | "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
53 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/lib/gitflow/commit.js:
--------------------------------------------------------------------------------
1 | /*eslint-env es6*/
2 | var KbError = require('../errors/KbError');
3 | var errorHandler = require('../errors/error-handler')('[COMMIT-FLOW]');
4 | var kbString = require('../kb-string');
5 | var Prompt = require('prompt-checkbox');
6 | var _ = require('lodash');
7 | var kbRepo = require('../services/kb-repo');
8 | var stripAnsi = require('strip-ansi');
9 | var Table = require('cli-table');
10 | var inquirer = require('inquirer');
11 |
12 |
13 | var commitGitflow = {};
14 |
15 | commitGitflow.commit = commit;
16 |
17 | commitGitflow.questions = [];
18 |
19 | module.exports = commitGitflow;
20 |
21 | function commit() {
22 | var data = {};
23 |
24 | return kbRepo.openGit()
25 | .then(function(repo) {
26 | data.repo = repo;
27 |
28 | return kbRepo.getCurrentGitFilesStatus(data.repo,
29 | false /* shouldAddFolderEntries */);
30 | })
31 | .then(function(statuses) {
32 |
33 | return selectFilesToCommit(statuses);
34 | })
35 | .then(function(filesToCommit) {
36 | data.filesToCommit = filesToCommit;
37 |
38 | return askForCommitDetails();
39 | })
40 | .then(function(commitDetails) {
41 | data.commitDetails = commitDetails;
42 | return kbRepo.commitFilesByPath(data.repo, {
43 | filesToCommit: data.filesToCommit,
44 | commitMessage: commitDetails.commitMessage
45 | });
46 | })
47 | .then(function(commitDetails) {
48 | data.commitDetails = commitDetails;
49 |
50 | var commitSummaryTable = new Table({
51 | chars: {
52 | 'top': '═',
53 | 'top-mid': '╤',
54 | 'top-left': '╔',
55 | 'top-right': '╗',
56 | 'bottom': '═',
57 | 'bottom-mid': '╧',
58 | 'bottom-left': '╚',
59 | 'bottom-right': '╝',
60 | 'left': '║',
61 | 'left-mid': '╟',
62 | 'mid': '─',
63 | 'mid-mid': '┼',
64 | 'right': '║',
65 | 'right-mid': '╢',
66 | 'middle': '│'
67 | }
68 | });
69 |
70 | commitSummaryTable.push([
71 | kbString.info('id'),
72 | kbString.white(data.commitDetails.commit)
73 | ]);
74 |
75 | commitSummaryTable.push([
76 | kbString.info('push to branch'),
77 | kbString.success(data.commitDetails.branch)
78 | ]);
79 |
80 | commitSummaryTable.push([
81 | kbString.info('author'),
82 | kbString.build([
83 | kbString.success(data.commitDetails.authorName),
84 | kbString.warning('<', data.commitDetails.authorEmail, '>')
85 | ])
86 | ]);
87 |
88 | commitSummaryTable.push([
89 | kbString.info('commit message'),
90 | data.commitDetails.commitMessage
91 | ]);
92 |
93 | commitSummaryTable.push([
94 | kbString.info('parent commit'),
95 | kbString.white(data.commitDetails.parents[0])
96 | ]);
97 |
98 | return commitSummaryTable;
99 | })
100 | .then(function(commitSummaryTable) {
101 | console.log(kbString.success([
102 | '\n committed succesfully \n',
103 | '======================='
104 | ]));
105 |
106 | console.log(commitSummaryTable.toString());
107 |
108 | process.exit(0);
109 | })
110 | .catch(errorHandler);
111 | }
112 |
113 | function selectFilesToCommit(statuses) {
114 | var stagedChoices = _.map(statuses.staged, function(status) {
115 | return {
116 | name: kbString.warning(status.path) + ' ' + status.statusStr,
117 | checked: true,
118 | folder: status.folder + '/',
119 | value: kbString.warning(status.path) + ' ' + status.statusStr
120 | };
121 | });
122 |
123 | var unstagedChoices = _.map(statuses.unstaged, function(status) {
124 | return {
125 | name: kbString.warning(status.path) + ' ' + status.statusStr,
126 | checked: false,
127 | folder: status.folder + '/',
128 | value: kbString.warning(status.path) + ' ' + status.statusStr
129 | };
130 | });
131 |
132 | var allOptions =
133 | _.groupBy(stagedChoices.concat(unstagedChoices), 'folder');
134 |
135 | if (_.isEmpty(allOptions)) {
136 | console.log(kbString.error([
137 | 'no changes were found.\n',
138 | 'change some files in order to commit them.'
139 | ]));
140 | process.exit(126);
141 | }
142 |
143 | var prompt = new Prompt({
144 | name: 'commitFiles',
145 | message: kbString.info(
146 | '==================================================',
147 | kbString.param(' ?\n? '),
148 | 'Which files do you want to include in this commit?',
149 | kbString.param(' ?\n? '),
150 | '==================================================',
151 | kbString.param(' ?\n'),
152 | kbString.warning(
153 | '\n- select files with ', kbString.important('space bar'),
154 | ', confirm selection with ', kbString.important('enter'), ' -\n'
155 | )
156 | ),
157 | radio: true,
158 | choices: allOptions
159 | });
160 |
161 | return prompt.run()
162 | .then(function(answers) {
163 | var filesToCommit = _.map(answers, function(answer) {
164 | return stripAnsi(answer).replace(/\s\[.*?\]/, '');
165 | });
166 | // console.log(colorize(JSON.stringify(filesToCommit, null, 2)));
167 |
168 | if (!filesToCommit.length) {
169 | throw new KbError('no files were selected', 0);
170 | }
171 |
172 | return filesToCommit;
173 | });
174 | }
175 |
176 | function askForCommitDetails(branch) {
177 | return inquirer.prompt([
178 | askForCommitTitle(branch),
179 | editCommitMessageQuestion()
180 | ])
181 | .then(function(answers) {
182 | var markedMessage;
183 | var completeMessage = [
184 | answers.commitTitle,
185 | '\n\n',
186 | answers.commitMessage
187 | ].join('');
188 |
189 | try {
190 | markedMessage = [
191 | '# ',
192 | answers.commitTitle,
193 | '\n\n',
194 | marked(answers.commitMessage, {
195 | renderer: renderer
196 | })
197 | ].join('');
198 | } catch (e) {
199 | console.error('MARKDOWN ERROR!', e);
200 | }
201 |
202 | return {
203 | commitMessage: completeMessage,
204 | markedCommitMessage: markedMessage
205 | };
206 | });
207 | }
208 |
209 | function editCommitMessageQuestion() {
210 | return {
211 | type: 'editor',
212 | name: 'commitMessage',
213 | message: 'Please write a short commit message',
214 | validate: function(text) {
215 | if (text && text.split('\n')[0].length > 72) {
216 | return kbString.build([
217 | 'each line in the commit message must be ',
218 | 'no more than 72 characters long.\n',
219 | 'For more info: ',
220 | 'http://tbaggery.com/2008/04/19/',
221 | 'a-note-about-git-commit-messages.html\n\n'
222 | ]);
223 | }
224 |
225 | return true;
226 | },
227 | default: function() {
228 | return 'master';
229 | }
230 | };
231 | }
232 |
233 | function askForCommitTitle(branch) {
234 | return {
235 | type: 'input',
236 | name: 'commitTitle',
237 | message: 'Please enter a title for your commit - ' +
238 | kbString.param('[', branch || '', ']: '),
239 | validate: function(commitTitle) {
240 | var isTitleShortEnough = commitTitle.length <= 50;
241 | if (!_.isEmpty(commitTitle) && isTitleShortEnough) {
242 | return true;
243 | }
244 |
245 | if (!isTitleShortEnough) {
246 | return kbString.build([
247 | 'The summary of your commit message must ',
248 | 'be maximum 50 characters long.\n',
249 | 'For more info: ',
250 | 'http://tbaggery.com/2008/04/19/',
251 | 'a-note-about-git-commit-messages.html\n\n'
252 | ]);
253 | }
254 |
255 | return 'no input detected. please try again';
256 | }
257 | };
258 | }
259 |
--------------------------------------------------------------------------------
/lib/services/kb-github.js:
--------------------------------------------------------------------------------
1 | var github = require('octonode');
2 | var _ = require('lodash');
3 | var Q = require('q');
4 | var inquirer = require('inquirer');
5 | var os = require('os');
6 | var kbString = require('../kb-string');
7 |
8 | var kbGithub = {};
9 |
10 | kbGithub.loginGitHub = loginGitHub;
11 | kbGithub.getGitHubUserData = getGitHubUserData;
12 | kbGithub.getGitHubBasicUserData = getGitHubBasicUserData;
13 | kbGithub.getGitHubUserOrgs = getGitHubUserOrgs;
14 | kbGithub.createRepoOrg = createRepoOrg;
15 | kbGithub.createRepoUser = createRepoUser;
16 |
17 | kbGithub.questions = {};
18 | kbGithub.questions.askForGitHubUser = askForGitHubUser;
19 | kbGithub.questions.askForGitHubPassword = askForGitHubPassword;
20 | kbGithub.questions.askForOTPCode = askForOTPCode;
21 | kbGithub.questions.userQuestionsGithubLogin = userQuestionsGithubLogin;
22 |
23 | module.exports = kbGithub;
24 |
25 | function askForGitHubUser() {
26 | return {
27 | type: 'input',
28 | name: 'GitHubUsername',
29 | message: 'GitHub username:',
30 | validate: function(currUsername) {
31 | if (!_.isEmpty(currUsername)) {
32 | return true;
33 | }
34 |
35 | return 'no input detected. please try again';
36 | }
37 | };
38 | }
39 |
40 | function askForGitHubPassword(data) {
41 | return {
42 | type: 'password',
43 | name: 'GitHubPassword',
44 | message: 'GitHub password:',
45 | validate: function(currPassword, userAnswers) {
46 | var done = this.async();
47 |
48 | if (!_.isEmpty(currPassword)) {
49 | kbGithub.loginGitHub(userAnswers.GitHubUsername, currPassword)
50 | .then(function(loginObj) {
51 | data.shouldAskForOTPCode = loginObj.needOTP;
52 | data.OTPMethod = loginObj.OTPMethod;
53 | data.token = loginObj.token;
54 | done(null, true);
55 | }, function(err) {
56 | done(err, false);
57 | });
58 | }
59 | }
60 | };
61 | }
62 |
63 | function askForOTPCode(data) {
64 | return {
65 | when: function() {
66 | if (data.shouldAskForOTPCode) {
67 | console.log(kbString.success([
68 | 'OTP detected. You should recieve a temp code to authenticate[',
69 | data.OTPMethod,
70 | '].'
71 | ]));
72 | }
73 | return data.shouldAskForOTPCode;
74 | },
75 | type: 'password',
76 | name: 'GitHubOTP',
77 | message: 'enter two-factor code:',
78 | validate: function(currOTP, userAnswers) {
79 | var done = this.async();
80 | var pass = currOTP.match(/^\d+$/);
81 |
82 | if (_.isEmpty(currOTP)) {
83 | done('OTP should not be empty', false);
84 | return;
85 | }
86 |
87 | if (!pass) {
88 | done('OTP should contain only numbers', false);
89 | return;
90 | }
91 |
92 | kbGithub.loginGitHub(
93 | userAnswers.GitHubUsername,
94 | userAnswers.GitHubPassword,
95 | currOTP)
96 | .then(function(loginObj) {
97 | data.token = loginObj.token;
98 | done(null, true);
99 | }, function(err) {
100 | done(err, false);
101 | });
102 | }
103 | };
104 | }
105 |
106 | function getGitHubUserData(username, token) {
107 | var user = {};
108 | return kbGithub.getGitHubBasicUserData(username, token)
109 | .then(function(githubUser) {
110 | user.email = githubUser.email;
111 | return kbGithub.getGitHubUserOrgs(username, token);
112 | })
113 | .then(function(userOrgs) {
114 | user.orgs = userOrgs;
115 | user.username = username;
116 | user.token = token;
117 |
118 | return user;
119 | });
120 | }
121 |
122 | function getGitHubBasicUserData(username, token) {
123 | var deferred = Q.defer();
124 |
125 | var githubUserClient = github.client(token);
126 |
127 | githubUserClient.get('/user', {}, function (err, status, body) {
128 | if (err) {
129 | // console.log('ERROR!', err);
130 | deferred.reject('can\'t get user data from github: ' + err);
131 | } else {
132 | // console.log(body); //json object
133 | deferred.resolve(body);
134 | }
135 | });
136 |
137 | return deferred.promise;
138 | }
139 |
140 | function getGitHubUserOrgs(username, token) {
141 | var deferred = Q.defer();
142 |
143 | var githubUserClient = github.client(token);
144 |
145 | var ghme = githubUserClient.me();
146 |
147 | ghme.orgs(function(err, body) {
148 | if (err) {
149 | // console.log('ERROR!', err);
150 | deferred.reject('can\'t get organizations data from github');
151 | } else {
152 | var orgNames = _.map(body, function(org) {
153 | return org.login;
154 | });
155 | deferred.resolve(orgNames);
156 | }
157 | });
158 |
159 | return deferred.promise;
160 | }
161 |
162 | function userQuestionsGithubLogin() {
163 | var data = {};
164 | return inquirer.prompt([
165 | kbGithub.questions.askForGitHubUser(data),
166 | kbGithub.questions.askForGitHubPassword(data),
167 | kbGithub.questions.askForOTPCode(data)
168 | ])
169 | .then(function(answers) {
170 | data.user = {
171 | username: answers.GitHubUsername,
172 | token: data.token
173 | };
174 |
175 | console.log(data);
176 |
177 | return kbGithub.getGitHubUserData(data.user.username, data.user.token);
178 | });
179 | }
180 |
181 | function loginGitHub(username, password, otpCode) {
182 | var deferred = Q.defer();
183 |
184 | var scopes = {
185 | 'scopes': ['user', 'repo', 'gist', 'read:org'],
186 | 'note': [
187 | 'kibibit cli','@',
188 | os.hostname(),
189 | ' on ', os.type()
190 | ].join('')
191 | };
192 |
193 | var data = {
194 | username: username,
195 | password: password
196 | };
197 |
198 | if (otpCode) {
199 | data.otp = otpCode;
200 | }
201 |
202 | github.auth.config(data).login(scopes, function(err, id, token, headers) {
203 | var err = _.get(err, 'message');
204 | if (_.isEmpty(err)) {
205 | deferred.resolve({
206 | msg: null,
207 | needOTP: false,
208 | token: token
209 | });
210 |
211 | return deferred.promise;
212 | }
213 | if (err && err.indexOf('OTP code') > -1) {
214 | deferred.resolve({
215 | msg: 'OTP detected. You should recieve a temp code to authenticate.',
216 | needOTP: true,
217 | OTPMethod: headers['x-github-otp'].replace('required; ', '')
218 | });
219 |
220 | return deferred.promise;
221 | } else {
222 | deferred.reject(err);
223 |
224 | return deferred.promise;
225 | }
226 | });
227 |
228 | return deferred.promise;
229 | }
230 |
231 | function createRepoUser(user, name, description) {
232 | var deferred = Q.defer();
233 |
234 | var client = github.client(user.token);
235 |
236 | description = description || '';
237 |
238 | if (!name) {
239 | deferred.reject('name must be given');
240 |
241 | return deferred.promise;
242 | }
243 |
244 | var ghme = client.me();
245 |
246 | ghme.repo({
247 | 'name': name,
248 | 'description': description,
249 | }, function(err, body) {
250 | if (err) {
251 | // console.log('ERROR!', err);
252 |
253 | deferred.reject('can\'t create user repo');
254 | } else {
255 | deferred.resolve(body);
256 | }
257 | }); //repo
258 |
259 | return deferred.promise;
260 | }
261 |
262 | function createRepoOrg(user, orgName, name, description) {
263 | var deferred = Q.defer();
264 |
265 | var client = github.client(user.token);
266 |
267 | description = description || '';
268 |
269 | if (!name || !orgName) {
270 | deferred.reject('orgName & name must be given');
271 |
272 | return deferred.promise;
273 | }
274 |
275 | var ghorg = client.org(orgName);
276 |
277 | ghorg.repo({
278 | name: name,
279 | description: description
280 | }, function(err, body) {
281 | if (err) {
282 | // console.log('ERROR!', err);
283 | deferred.reject('can\'t create organization repo');
284 | } else {
285 | deferred.resolve(body);
286 | }
287 | });
288 |
289 | return deferred.promise;
290 | }
291 |
--------------------------------------------------------------------------------
/lib/gitflow/status.js:
--------------------------------------------------------------------------------
1 | var kbString = require('../kb-string');
2 | var gitRoot = require('../kb-git-root');
3 | // var fs = require('fs');
4 | // var currentFolder = process.cwd();
5 | var NodeGit = require('nodegit-flow')(require('nodegit'));
6 | var Table = require('cli-table');
7 | var _ = require('lodash');
8 | var path = require('path');
9 | // var colorize = require('json-colorizer');
10 | var Q = require('q');
11 | // var findRoot = require('find-root');
12 | // var countFiles = require('count-files');
13 |
14 | var GLOBAL = {};
15 |
16 | var initGitflow = {};
17 |
18 | initGitflow.status = status;
19 | initGitflow.getStatusAsArray = getStatus;
20 |
21 | var globalRepo, globalDiff;
22 |
23 | module.exports = initGitflow;
24 |
25 | function status() {
26 | gitRoot.getGitRoot()
27 | .then(function(_gitRoot) {
28 | if (!_gitRoot) {
29 | console.info(kbString.error('git repo not found'));
30 | process.exit(1);
31 | }
32 |
33 | GLOBAL.gitRoot = _gitRoot;
34 |
35 | GLOBAL.stagedTable = new Table({
36 | chars: {
37 | 'top': '═',
38 | 'top-mid': '╤',
39 | 'top-left': '╔',
40 | 'top-right': '╗',
41 | 'bottom': '═',
42 | 'bottom-mid': '╧',
43 | 'bottom-left': '╚',
44 | 'bottom-right': '╝',
45 | 'left': '║',
46 | 'left-mid': '╟',
47 | 'mid': '─',
48 | 'mid-mid': '┼',
49 | 'right': '║',
50 | 'right-mid': '╢',
51 | 'middle': '│'
52 | },
53 | head: ['filename', 'state', 'diff']
54 | });
55 |
56 | GLOBAL.unstagedTable = new Table({
57 | chars: {
58 | 'top': '═',
59 | 'top-mid': '╤',
60 | 'top-left': '╔',
61 | 'top-right': '╗',
62 | 'bottom': '═',
63 | 'bottom-mid': '╧',
64 | 'bottom-left': '╚',
65 | 'bottom-right': '╝',
66 | 'left': '║',
67 | 'left-mid': '╟',
68 | 'mid': '─',
69 | 'mid-mid': '┼',
70 | 'right': '║',
71 | 'right-mid': '╢',
72 | 'middle': '│'
73 | },
74 | head: ['filename', 'state', 'diff']
75 | });
76 |
77 | return getStatus();
78 | })
79 | .then(function(statuses) {
80 | console.log(kbString.success(
81 | 'BRANCH NAME: [',
82 | kbString.white(statuses.branchName),
83 | ']'
84 | ));
85 |
86 | var seenFolderStaged = false;
87 | _.forEach(statuses.staged, function(file) {
88 | seenFolderStaged = seenFolderStaged || file.isFolder;
89 | var colored = file.isFolder ? kbString.success : kbString.info;
90 | GLOBAL.stagedTable.push([
91 | colored(!file.isFolder && seenFolderStaged ? ' ' : '', file.path),
92 | file.statusStr,
93 | file.changes ? kbString.build([
94 | kbString.success('++', file.changes[0]),
95 | ' | ',
96 | kbString.error('++', file.changes[1])
97 | ]) : ''
98 | ]);
99 | });
100 |
101 | var seenFolderUnstaged = false;
102 | _.forEach(statuses.unstaged, function(file) {
103 | seenFolderUnstaged = seenFolderUnstaged || file.isFolder;
104 | var colored = file.isFolder ? kbString.success : kbString.info;
105 | GLOBAL.unstagedTable.push([
106 | colored(!file.isFolder && seenFolderUnstaged ? ' ' : '', file.path),
107 | file.statusStr,
108 | file.changes ? kbString.build([
109 | kbString.success('++', file.changes[0]),
110 | ' | ',
111 | kbString.error('--', file.changes[1])
112 | ]) : ''
113 | ]);
114 | });
115 |
116 | console.log(kbString.build([
117 | '\n================\n',
118 | '= ', kbString.info('STAGED FILES'), ' =\n',
119 | '================'
120 | ]));
121 | console.log(GLOBAL.stagedTable.length ?
122 | GLOBAL.stagedTable.toString() :
123 | kbString.info('nothing to show here'));
124 | console.log(kbString.build([
125 | '\n==================\n',
126 | '= ', kbString.warning('UNSTAGED FILES'), ' =\n',
127 | '=================='
128 | ]));
129 | console.log(GLOBAL.unstagedTable.length ?
130 | GLOBAL.unstagedTable.toString() :
131 | kbString.info('nothing to show here'));
132 |
133 | process.exit(0);
134 | })
135 | .catch(function(error) {
136 | console.log(error);
137 | process.exit(1);
138 | });
139 | }
140 |
141 | function getStatus(shouldAddFolderEntries, shouldGroupBy) {
142 | shouldGroupBy = shouldGroupBy || 'isStaged';
143 | var deferred = Q.defer();
144 |
145 | // GLOBAL PROCESS VARIALBES
146 | var currentBranchName = '';
147 | var currentBranchStatuses;
148 |
149 | var files = [];
150 | gitRoot.getGitRoot()
151 | .then(function(_gitRoot) {
152 | return NodeGit.Repository.open(_gitRoot);
153 | })
154 | .then(function(repo) {
155 | globalRepo = repo;
156 |
157 | return Q.all([
158 | globalRepo.getStatus(),
159 | globalRepo.getCurrentBranch(),
160 | getDiff(globalRepo)
161 | ]);
162 | })
163 | .then(function(result) {
164 | currentBranchStatuses = result[0];
165 | globalDiff = result[2];
166 | return NodeGit.Branch.name(result[1]);
167 | })
168 | .then(function(currBranchName) {
169 | var statuses = currentBranchStatuses;
170 | currentBranchName = currBranchName;
171 |
172 | statuses.forEach(function(file) {
173 | files.push({
174 | path: file.path(),
175 | status: statusToArray(file),
176 | statusStr: statusToText(file),
177 | folder: path.dirname(file.path()),
178 | isStaged: file.status()[0].indexOf('INDEX') >= 0,
179 | changes: globalDiff[file.path()]
180 | });
181 | });
182 |
183 | var sortedFiles = _.sortBy(files, [ 'path' ]);
184 |
185 | // separate to staged vs unstaged
186 | if (shouldGroupBy) {
187 | var separatedByStage = _.groupBy(sortedFiles, shouldGroupBy);
188 | }
189 |
190 | if (shouldAddFolderEntries) {
191 | separatedByStage.true = addFolderEntries(separatedByStage.true);
192 | separatedByStage.false = addFolderEntries(separatedByStage.false);
193 | }
194 |
195 | deferred.resolve({
196 | staged: separatedByStage.true,
197 | unstaged: separatedByStage.false,
198 | branchName: currentBranchName
199 | });
200 | })
201 | .catch(function(err) {
202 | console.error('couldn\'t open repo', err);
203 | throw err;
204 | deferred.reject(err);
205 | });
206 |
207 | return deferred.promise;
208 | }
209 |
210 | function addFolderEntries(files) {
211 | files = _.sortBy(files, [ 'path' ]);
212 |
213 | var filesWithFolders = [];
214 | var currentFolder = '.';
215 | _.forEach(files, function(file) {
216 | if (file.folder === currentFolder || file.folder === currentFolder + '/') {
217 | filesWithFolders.push(file);
218 | } else {
219 | filesWithFolders.push({
220 | path: file.folder + '/',
221 | status: '',
222 | statusStr: '',
223 | isFolder: true
224 | });
225 | currentFolder = file.folder;
226 | filesWithFolders.push(file);
227 | }
228 | });
229 |
230 | return filesWithFolders;
231 | }
232 |
233 | function statusToArray(status) {
234 | var words = [];
235 | if (status.isNew()) {
236 | words.push('NEW');
237 | }
238 | if (status.isModified()) {
239 | words.push('MODIFIED');
240 | }
241 | if (status.isTypechange()) {
242 | words.push('TYPECHANGE');
243 | }
244 | if (status.isRenamed()) {
245 | words.push('RENAMED');
246 | }
247 | if (status.isIgnored()) {
248 | words.push('IGNORED');
249 | }
250 |
251 | return words;
252 | }
253 |
254 | function statusToText(status) {
255 | var words = [];
256 | if (status.isNew()) {
257 | words.push(kbString.build(
258 | kbString.white('['),
259 | kbString.info('NEW'),
260 | kbString.white(']')
261 | ));
262 | }
263 | if (status.isModified()) {
264 | words.push(kbString.build(
265 | kbString.white('['),
266 | kbString.warning('MODIFIED'),
267 | kbString.white(']')
268 | ));
269 | }
270 | if (status.isTypechange()) {
271 | words.push(kbString.build(
272 | kbString.white('['),
273 | kbString.warning('TYPECHANGE'),
274 | kbString.white(']')
275 | ));
276 | }
277 | if (status.isRenamed()) {
278 | words.push(kbString.build(
279 | kbString.white('['),
280 | kbString.error('RENAMED'),
281 | kbString.white(']')
282 | ));
283 | }
284 | if (status.isIgnored()) {
285 | words.push(kbString.build(
286 | kbString.white('['),
287 | kbString.white('IGNORED'),
288 | kbString.white(']')
289 | ));
290 | }
291 |
292 | return words.join(' ');
293 | }
294 |
295 | function getDiff(repo) {
296 | var deferred = Q.defer();
297 |
298 | var result = {};
299 |
300 | NodeGit.Diff.indexToWorkdir(repo)
301 | .then(function(diff) {
302 | return diff.patches();
303 | })
304 | .then(function(patches) {
305 | _.forEach(patches, function(patch) {
306 | var lineStats = patch.lineStats();
307 |
308 | result[patch.newFile().path()] = [
309 | lineStats.total_additions,
310 | lineStats.total_deletions
311 | ];
312 | });
313 |
314 | deferred.resolve(result);
315 | })
316 | .catch(function(error) {
317 | deferred.reject(error);
318 | console.error(error);
319 | });
320 |
321 | return deferred.promise;
322 | }
323 |
--------------------------------------------------------------------------------
/lib/commandDefinitions.js:
--------------------------------------------------------------------------------
1 | var kbString = require('./kb-string');
2 | var initGitflow = require('./gitflow/init');
3 | var statusGitflow = require('./gitflow/status');
4 | var commitGitflow = require('./gitflow/commit');
5 | var finishGitflow = require('./gitflow/finish');
6 | var featureGitflow = require('./gitflow/feature');
7 | var releaseGitflow = require('./gitflow/release');
8 | var hotfixGitflow = require('./gitflow/hotfix');
9 | var developGitflow = require('./gitflow/develop');
10 | var syncGitflow = require('./gitflow/sync');
11 | var masterGitflow = require('./gitflow/master');
12 | // var inquirer = require('inquirer');
13 | var shell = require('shelljs');
14 | // var Q = require('q');
15 | // var Git = require('nodegit');
16 | // var colorize = require('json-colorizer');
17 | // var _ = require('lodash');
18 | // var github = require('octonode');
19 | // var currentFolder = process.cwd();
20 | // var signingIn = [
21 | // kbString.info(kbString.warning('/'), ' Signing in'),
22 | // kbString.info(kbString.warning('|'), ' Signing in..'),
23 | // kbString.info(kbString.warning('\\'), ' Signing in..'),
24 | // kbString.info(kbString.warning('-'), ' Signing in...')
25 | // ];
26 | // var signingInSteps = 4;
27 |
28 | // var client = github.client();
29 |
30 | var commandDefinitions = {};
31 |
32 | commandDefinitions.attach = function attachCommands(program) {
33 | addSubCommandCommit(program);
34 | addSubCommandStatus(program);
35 | addSubCommandInit(program);
36 | addSubCommandClone(program);
37 | addSubCommandFeature(program);
38 | addSubCommandHotfix(program);
39 | addSubCommandFinish(program);
40 | addSubCommandRelease(program);
41 | addSubCommandUpdate(program);
42 | addSubCommandPush(program);
43 | addSubCommandMaster(program);
44 | addSubCommandDevelop(program);
45 | };
46 |
47 | function addSubCommandMaster(program) {
48 | program
49 | .command('master')
50 | .description(kbString.info('checkout master branch'))
51 | .action(masterGitflow.develop)
52 | .on('help', function(cmd) {
53 | cmd.outputIndented('Examples', [
54 | kbString.build(
55 | kbString.success('$ '),
56 | kbString.kibibitLogo(),
57 | kbString.success(' master ')
58 | )
59 | ]);
60 | }).parent;
61 | }
62 |
63 | function addSubCommandDevelop(program) {
64 | program
65 | .command('develop')
66 | .description(kbString.info('checkout develop branch'))
67 | .action(developGitflow.develop)
68 | .on('help', function(cmd) {
69 | cmd.outputIndented('Examples', [
70 | kbString.build(
71 | kbString.success('$ '),
72 | kbString.kibibitLogo(),
73 | kbString.success(' develop ')
74 | )
75 | ]);
76 | }).parent;
77 | }
78 |
79 | function addSubCommandCommit(program) {
80 | program
81 | .command('commit')
82 | .description(kbString.info('clone a remote repository'))
83 | .action(commitGitflow.commit)
84 | .option('-v, --verbose', 'only show my features')
85 | .option('-S, --gpg-sign', 'GPG sign commit')
86 | .on('help', function(cmd) {
87 | cmd.outputIndented('Examples', [
88 | kbString.build(
89 | kbString.success('$ '),
90 | kbString.kibibitLogo(),
91 | kbString.success(' commit '),
92 | kbString.param('search-db')
93 | ),
94 | kbString.build(
95 | kbString.success('$ '),
96 | kbString.kibibitLogo(),
97 | kbString.success(' commit')
98 | ),
99 | kbString.build(
100 | kbString.success('$ '),
101 | kbString.kibibitLogo(),
102 | kbString.success(' commit '),
103 | '-v'
104 | )
105 | ]);
106 | }).parent;
107 | }
108 |
109 | function addSubCommandClone(program) {
110 | program
111 | .command('clone ')
112 | .description(kbString.info('clone a remote repository'))
113 | .action(clone)
114 | .option('-v, --verbose', 'only show my features')
115 | .option('-S, --gpg-sign', 'GPG sign commit')
116 | .on('help', function(cmd) {
117 | cmd.outputIndented('Examples', [
118 | kbString.build(
119 | kbString.success('$ '),
120 | kbString.kibibitLogo(),
121 | kbString.success(' commit '),
122 | kbString.param('search-db')
123 | ),
124 | kbString.build(
125 | kbString.success('$ '),
126 | kbString.kibibitLogo(),
127 | kbString.success(' commit')
128 | ),
129 | kbString.build(
130 | kbString.success('$ '),
131 | kbString.kibibitLogo(),
132 | kbString.success(' commit '),
133 | '-v'
134 | )
135 | ]);
136 | }).parent;
137 | }
138 |
139 | function addSubCommandStatus(program) {
140 | program
141 | .command('status')
142 | .description(kbString.info('show current branch status'))
143 | .action(statusGitflow.status)
144 | .option('-v, --verbose', 'only show my features')
145 | .option('-S, --gpg-sign', 'GPG sign commit')
146 | .on('help', function(cmd) {
147 | cmd.outputIndented('Examples', [
148 | kbString.build(
149 | kbString.success('$ '),
150 | kbString.kibibitLogo(),
151 | kbString.success(' status ')
152 | ),
153 | kbString.build(
154 | kbString.success('$ '),
155 | kbString.kibibitLogo(),
156 | kbString.success(' status')
157 | ),
158 | kbString.build(
159 | kbString.success('$ '),
160 | kbString.kibibitLogo(),
161 | kbString.success(' status ')
162 | )
163 | ]);
164 | }).parent;
165 | }
166 |
167 | function addSubCommandFeature(program) {
168 | program
169 | .command('feature [featureName]')
170 | .description(kbString.build([
171 | kbString.info('start or continue a feature '),
172 | kbString.warning('(will be prompted)'),
173 | kbString.info(
174 | '. If no featureName is given, returns all ongoing features'
175 | )
176 | ]))
177 | .action(featureGitflow.feature)
178 | .option('-m, --mine', 'only show my features')
179 | .option('-r, --remote', 'only show remote features')
180 | .on('help', function(cmd) {
181 | cmd.outputIndented('Examples', [
182 | kbString.build(
183 | kbString.success('$ '),
184 | kbString.kibibitLogo(),
185 | kbString.success(' feature '),
186 | kbString.param('search-db')
187 | ),
188 | kbString.build(
189 | kbString.success('$ '),
190 | kbString.kibibitLogo(),
191 | kbString.success(' feature')
192 | ),
193 | kbString.build(
194 | kbString.success('$ '),
195 | kbString.kibibitLogo(),
196 | kbString.success(' feature '),
197 | '-m'
198 | )
199 | ]);
200 | }).parent;
201 | }
202 |
203 | function addSubCommandHotfix(program) {
204 | program
205 | .command('hotfix [hotfixName]')
206 | .description(kbString.build([
207 | kbString.info('start or continue a hotfix '),
208 | kbString.warning('(will be prompted)'),
209 | kbString.info('. If no hotfixName is given, returns all ongoing hotfixes')
210 | ]))
211 | .action(hotfixGitflow.hotfix)
212 | .option('-m, --mine', 'only show my hotfixes')
213 | .on('help', function(cmd) {
214 | cmd.outputIndented('Examples', [
215 | kbString.build(
216 | kbString.success('$ '),
217 | kbString.kibibitLogo(),
218 | kbString.success(' hotfix '),
219 | kbString.param('search-db-edge-case-no-query')
220 | ),
221 | kbString.build(
222 | kbString.success('$ '),
223 | kbString.kibibitLogo(),
224 | kbString.success(' hotfix')
225 | ),
226 | kbString.build(
227 | kbString.success('$ '),
228 | kbString.kibibitLogo(),
229 | kbString.success(' hotfix '),
230 | '-m'
231 | )
232 | ]);
233 | }).parent;
234 | }
235 |
236 | function addSubCommandFinish(program) {
237 | program
238 | .command('finish')
239 | .description(
240 | kbString.info('use GitHub to issue a pull request to origin/develop.')
241 | )
242 | .action(finishGitflow.finish)
243 | .on('help', function(cmd) {
244 | cmd.outputIndented('Examples', [
245 | kbString.build(
246 | kbString.success('$ '),
247 | kbString.kibibitLogo(),
248 | kbString.success(' finish ')
249 | )
250 | ]);
251 | }).parent;
252 | }
253 |
254 | function addSubCommandRelease(program) {
255 | program
256 | .command('release [releaseName]')
257 | .description(kbString.build(
258 | kbString.info(
259 | 'When you have enough completed features in origin/develop, '
260 | ),
261 | kbString.info('create a release branch, test it and fix it, '),
262 | kbString.info('and then merge it into origin/master')
263 | ))
264 | .action(releaseGitflow.release)
265 | .on('help', function(cmd) {
266 | cmd.outputIndented('Examples', [
267 | kbString.build(
268 | kbString.success('$ '),
269 | kbString.kibibitLogo(),
270 | kbString.success(' release '),
271 | kbString.param('start')
272 | ),
273 | kbString.success(''),
274 | kbString.build(
275 | kbString.success('$ '),
276 | kbString.kibibitLogo(),
277 | kbString.success(' release '),
278 | kbString.param('finish')
279 | )
280 | ]);
281 | }).parent;
282 | }
283 |
284 | function addSubCommandUpdate(program) {
285 | program
286 | .command('update')
287 | .description(
288 | kbString.info('keep up-to-date with completed features on GitHub')
289 | )
290 | .action(syncGitflow.sync)
291 | .on('help', function(cmd) {
292 | cmd.outputIndented('Examples', [
293 | kbString.build(
294 | kbString.success('$ '),
295 | kbString.kibibitLogo(),
296 | kbString.success(' update')
297 | )
298 | ]);
299 | }).parent;
300 | }
301 |
302 | function addSubCommandPush(program) {
303 | program
304 | .command('push')
305 | .description(
306 | kbString.info([
307 | 'push your feature branch back to GitHub as you make progress ',
308 | 'and want to save your work'
309 | ])
310 | )
311 | .action(push)
312 | .on('help', function(cmd) {
313 | cmd.outputIndented('Examples', [
314 | kbString.build(
315 | kbString.success('$ '),
316 | kbString.kibibitLogo(),
317 | kbString.success(' push')
318 | )
319 | ]);
320 | }).parent;
321 | }
322 |
323 | function addSubCommandInit(program) {
324 | program
325 | .command('init')
326 | .description(
327 | kbString.info('initialize the gitflow tools for the current repo. ') +
328 | kbString.warning('(GitHub login, etc.)')
329 | )
330 | .option('-f, --force', kbString.build([
331 | 'force setting of hubflow branches, even if ',
332 | 'already configured (default: false)'
333 | ]))
334 | .option('-a, --ask', 'ask for branch naming conventions (default: false)')
335 | .action(initGitflow.init)
336 | .on('help', function(cmd) {
337 | cmd.outputIndented('Examples', [
338 | kbString.build(
339 | kbString.success('$ '),
340 | kbString.kibibitLogo(),
341 | kbString.success(' init ')
342 | ),
343 | kbString.build(
344 | kbString.success('$ '),
345 | kbString.kibibitLogo(),
346 | kbString.success(' init '),
347 | '-f'
348 | ),
349 | kbString.build(
350 | kbString.success('$ '),
351 | kbString.kibibitLogo(),
352 | kbString.success(' init '),
353 | '--ask'
354 | )
355 | ]);
356 | }).parent;
357 | }
358 |
359 | module.exports = commandDefinitions;
360 |
361 | function clone() {
362 | console.error('clone!');
363 | }
364 |
365 | function push() {
366 | kbExec('git hf push');
367 | }
368 |
369 | // function update() {
370 | // kbExec('git hf update');
371 | // }
372 |
373 | function kbExec(command) {
374 | if (shell.exec(command).code !== 0) {
375 | shell.echo('Error: ' + kbString.error(command) + ' failed');
376 | shell.exit(1);
377 | }
378 | }
379 |
--------------------------------------------------------------------------------
/lib/services/kb-repo.js:
--------------------------------------------------------------------------------
1 | /*eslint-env es6*/
2 | var KbError = require('../errors/KbError');
3 | var gitRoot = require('../kb-git-root');
4 | var NodeGit = require('nodegit-flow')(require('nodegit'));
5 | var inquirer = require('inquirer');
6 | var colorize = require('json-colorizer');
7 | var kbString = require('../kb-string');
8 | var kbGithub = require('./kb-github');
9 | var _ = require('lodash');
10 | var writeFile = require('write-file-promise');
11 | var readFile = require('fs-readfile-promise');
12 | var Q = require('q');
13 | var path = require('path');
14 |
15 | Q.longStackSupport = true;
16 |
17 | var kbRepo = {};
18 |
19 | kbRepo.getCurrentGitFilesStatus = getStatus;
20 | kbRepo.commitFilesByPath = commitFilesByPath;
21 | kbRepo.getBitConfig = getBitConfig;
22 | kbRepo.saveBitConfig = saveBitConfig;
23 | kbRepo.openGit = openGit;
24 | kbRepo.initGit = initGit;
25 | kbRepo.ensureGitFlowNotInitialized = ensureGitFlowNotInitialized;
26 | kbRepo.ensureBasicGitFlowDetails = ensureBasicGitFlowDetails;
27 | kbRepo.setGitConfigUser = setGitConfigUser;
28 |
29 | kbRepo.questions = {};
30 | kbRepo.questions.masterBranch = askNameOfMasterBranch();
31 | kbRepo.questions.developBranch = askNameOfDevelopBranch();
32 | kbRepo.questions.featureBranchPrefix = askPrefixOfFeatureBranches();
33 | kbRepo.questions.hotfixBranchPrefix = askPrefixOfHotfixBranches();
34 | kbRepo.questions.releaseBranchPrefix = askPrefixOfReleaseBranches();
35 | kbRepo.questions.shouldSaveBitConfig = askIfShouldSaveBitConfig();
36 | kbRepo.questions.createRepoOrigin = createRepoOrigin;
37 |
38 | module.exports = kbRepo;
39 |
40 | function getBitConfig(rootFolder) {
41 | console.log('trying to read file', rootFolder + '/.bit.json');
42 | return readFile(rootFolder + '/.bit.json', 'utf8');
43 | }
44 |
45 | function saveBitConfig(rootFolder, config) {
46 | var configStr = JSON.stringify(config, null, 2);
47 | return writeFile(rootFolder + '/.bit.json', configStr)
48 | .catch(function(error) {
49 | console.error('something went wrong!', error);
50 | });
51 | }
52 |
53 | function setGitConfigUser(repo, user) {
54 | var data = {};
55 | return repo.config()
56 | .then(function(config) {
57 | data.config = config;
58 | return data.config.setString('user.name', user.username);
59 | })
60 | .then(function() {
61 | return data.config.setString('user.email', user.email);
62 | });
63 | }
64 |
65 | function ensureBasicGitFlowDetails(rootFolder, repo, user) {
66 | var data = {};
67 | console.log('getting bitconfig');
68 | // check if bit config exists. if so, use it. else, ask user for details
69 | // and check if he wants to save those details globally for all users
70 | return kbRepo.getBitConfig(rootFolder)
71 | .then(function(repoBitConfig) {
72 | console.log('found config file', repoBitConfig);
73 | console.log('checking validity...');
74 |
75 | return checkConfigSchema(repoBitConfig);
76 | })
77 | .then(function(config) {
78 | data.userOptions = config;
79 | })
80 | .catch(function() {
81 | console.log('valid bit config not found');
82 | return inquirer.prompt([
83 | kbRepo.questions.masterBranch,
84 | kbRepo.questions.developBranch,
85 | kbRepo.questions.featureBranchPrefix,
86 | kbRepo.questions.hotfixBranchPrefix,
87 | kbRepo.questions.releaseBranchPrefix,
88 | kbRepo.questions.shouldSaveBitConfig
89 | ])
90 | .then(function(answers) {
91 |
92 | answers.featureBranchesPrefix =
93 | ensureSlash(answers.featureBranchesPrefix);
94 | answers.releaseBranchesPrefix =
95 | ensureSlash(answers.releaseBranchesPrefix);
96 | answers.hotfixBranchesPrefix =
97 | ensureSlash(answers.hotfixBranchesPrefix);
98 |
99 | console.log(kbString.build([
100 | '\nnew ', kbString.kibibitLogo(), ' gitflow repo will be ',
101 | 'initialized with these settings:'
102 | ]));
103 | console.log(colorize(JSON.stringify(answers, null, ' ')));
104 |
105 | data.userOptions = {
106 | 'gitflow.branch.master': answers.masterBranch,
107 | 'gitflow.branch.develop': answers.developBranch,
108 | 'gitflow.prefix.feature': answers.featureBranchesPrefix,
109 | 'gitflow.prefix.release': answers.releaseBranchesPrefix,
110 | 'gitflow.prefix.hotfix': answers.hotfixBranchesPrefix,
111 | 'gitflow.prefix.versiontag': ''
112 | };
113 |
114 | // console.log('should save?', answers.shouldSaveBitConfig);
115 | data.shouldSaveBitConfig = answers.shouldSaveBitConfig;
116 | data.configPath = rootFolder + '/.bit.json';
117 |
118 | return data.shouldSaveBitConfig &&
119 | kbRepo.saveBitConfig(rootFolder, data.userOptions);
120 | });
121 | })
122 | .then(function() {
123 |
124 | // here, we need to make sure that master branch exists
125 | // and have an initial commit
126 | return repo.getBranchCommit('master')
127 | // no initial commit?
128 | // no problem. make one!
129 | .catch(makeInitialCommit(repo,
130 | user, data.shouldSaveBitConfig, data.configPath));
131 | })
132 | .then(function(commit) {
133 | commit = commit.commit || commit;
134 | if (data.userOptions['gitflow.branch.master'] !== 'master') {
135 | return repo.createBranch(
136 | data.userOptions['gitflow.branch.master'], commit);
137 | }
138 |
139 | return commit;
140 | })
141 | .then(function(/* commit */) {
142 | // we now have all the user preferences and we can
143 | // initialize git-flow into the repo
144 | return NodeGit.Flow.init(repo, data.userOptions);
145 | })
146 | .then(function() {
147 | return repo.checkoutBranch(data.userOptions['gitflow.branch.master'])
148 | .then(function() {
149 | if (data.userOptions['gitflow.branch.master'] !== 'master') {
150 | return repo.getBranch('master');
151 | }
152 |
153 | return;
154 | })
155 | .then(function(oldMasterBranch) {
156 | if (!oldMasterBranch) { return; }
157 | var result = NodeGit.Branch.delete(oldMasterBranch);
158 | console.log('deleted old master branch', result);
159 | });
160 | });
161 | }
162 |
163 | function createRepoOrigin(repo, user) {
164 | var data = {};
165 | return inquirer.prompt([{
166 | type: 'list',
167 | name: 'whereToCreate',
168 | message: 'Where should repo be created?',
169 | choices: [
170 | user.username,
171 | new inquirer.Separator()
172 | ].concat(user.orgs)
173 | },
174 | {
175 | type: 'input',
176 | name: 'repoName',
177 | message: 'what do you want to name the repo?',
178 | },
179 | {
180 | type: 'input',
181 | name: 'repoDescription',
182 | message: 'give the repo a short description (optional)',
183 | }])
184 | .then(function(answers) {
185 | data.repoName = answers.repoName;
186 | data.repoDescription = answers.repoDescription;
187 |
188 | if (answers.whereToCreate === user.username) {
189 | return kbGithub.createRepoUser(user,
190 | data.repoName, data.repoDescription);
191 | } else {
192 | return kbGithub.createRepoOrg(user, answers.whereToCreate,
193 | data.repoName, data.repoDescription);
194 | }
195 | })
196 | .then(function(body) {
197 | console.log('created repo', body.clone_url);
198 |
199 | // OR ORIGIN!!!!!! INSTEAD OF NAME
200 | return NodeGit.Remote
201 | .create(repo, 'origin', body.clone_url);
202 | })
203 | .then(function(remote) {
204 | data.remote = remote;
205 | // console.log('successfully added a new remote!', data.remote.name());
206 |
207 | return NodeGit.Flow.getConfig(repo);
208 | })
209 | .then(function(config) {
210 | var master = config['gitflow.branch.master'];
211 | var develop = config['gitflow.branch.develop'];
212 | return data.remote.push([
213 | `refs/heads/${master}:refs/heads/${master}`,
214 | `refs/heads/${develop}:refs/heads/${develop}`
215 | ], {
216 | callbacks: {
217 | certificateCheck: function() {
218 | return 1;
219 | },
220 | credentials: function() {
221 | // console.log('asked for credentials', user.token);
222 | return NodeGit.Cred
223 | .userpassPlaintextNew(user.token, 'x-oauth-basic');
224 | }
225 | }
226 | });
227 | });
228 | }
229 |
230 | function commitFilesByPath(repo, options) {
231 | var index, parents;
232 | var returnData = {};
233 | return repo.refreshIndex()
234 | .then(function(idx) {
235 | index = idx;
236 |
237 | return options.filesToCommit ?
238 | addFilesToIndex(index, options.filesToCommit) :
239 | true;
240 | })
241 | .then(function() {
242 | return index.write();
243 | })
244 | .then(function() {
245 | return repo.getCurrentBranch()
246 | .catch(function() {
247 | return 'refs/heads/master';
248 | });
249 | })
250 | .then(function(currentBranchRef) {
251 | returnData.branch = currentBranchRef.name ?
252 | currentBranchRef.name() :
253 | currentBranchRef;
254 | return repo.getHeadCommit()
255 | .then(function(commitId) {
256 | return commitId ? [ commitId ] : [];
257 | })
258 | .catch(function() {
259 | // wasn't able to get the last commit.
260 | // this means this is the first commit.
261 | return [];
262 | });
263 | })
264 | .then(function(parentArray) {
265 | parents = parentArray;
266 | // console.log('file added to index?');
267 | return index.writeTree();
268 | })
269 | .then(function(oid) {
270 | var signature = options.user ?
271 | NodeGit.Signature.now(options.user.username, options.user.email) :
272 | NodeGit.Signature.default(repo);
273 |
274 | returnData.oid = oid;
275 | returnData.parents = _.map(parents, (branch) => { return branch.id(); });
276 | returnData.commitMessage = options.commitMessage || 'initial commit';
277 | returnData.authorName = signature.name();
278 | returnData.authorEmail = signature.email();
279 | returnData.branch = _.replace(returnData.branch, 'refs/heads/', '');
280 |
281 | return repo.createCommit('HEAD',
282 | signature,
283 | signature,
284 | options.commitMessage || 'initial commit',
285 | oid,
286 | parents);
287 | })
288 | .then(function(newCommit) {
289 | returnData.commit = newCommit;
290 | return returnData;
291 | });
292 | }
293 |
294 | function addFilesToIndex(index, filesToCommit) {
295 | filesToCommit = _.isString(filesToCommit) ? [ filesToCommit ] : filesToCommit;
296 | var fName = arguments.callee.toString().match(/function ([^\(]+)/)[1];
297 | if (!index) { return Q.reject(fName + ' expects an index arg'); }
298 | // add files
299 | return Q
300 | .allSettled(_.map(filesToCommit, function(file) {
301 | return index.addByPath(file);
302 | }));
303 | }
304 |
305 | function makeInitialCommit(repo, user, shouldSaveBitConfig) {
306 | var filesToCommit = shouldSaveBitConfig ? '.bit.json' : undefined;
307 | return function() {
308 | return kbRepo.commitFilesByPath(repo, {
309 | filesToCommit: filesToCommit,
310 | user: user
311 | });
312 | };
313 | }
314 |
315 | function checkConfigSchema(configStr) {
316 | var parsedConfig = JSON.parse(configStr);
317 | if (notEmptyString(parsedConfig['gitflow.branch.master']) &&
318 | notEmptyString(parsedConfig['gitflow.branch.develop']) &&
319 | notEmptyString(parsedConfig['gitflow.prefix.feature']) &&
320 | notEmptyString(parsedConfig['gitflow.prefix.release']) &&
321 | notEmptyString(parsedConfig['gitflow.prefix.hotfix'])) {
322 | return parsedConfig;
323 | } else {
324 | throw new KbError('bit config file is messed up', 0);
325 | }
326 | }
327 |
328 | function notEmptyString(str) {
329 | return !_.isEmpty(str) && _.isString(str);
330 | }
331 |
332 | function askIfShouldSaveBitConfig() {
333 | return {
334 | type: 'confirm',
335 | name: 'shouldSaveBitConfig',
336 | message: 'should we save this setting globaly so everyone will use it?',
337 | default: true
338 | };
339 | }
340 |
341 | function askPrefixOfReleaseBranches() {
342 | return {
343 | type: 'input',
344 | name: 'releaseBranchesPrefix',
345 | message: 'Release branches?',
346 | default: 'release/'
347 | };
348 | }
349 |
350 | function askPrefixOfHotfixBranches() {
351 | return {
352 | type: 'input',
353 | name: 'hotfixBranchesPrefix',
354 | message: 'Hotfix branches?',
355 | default: 'hotfix/'
356 | };
357 | }
358 |
359 | function askPrefixOfFeatureBranches() {
360 | return {
361 | // when: writeBefore('How to name your supporting branch prefixes?'),
362 | type: 'input',
363 | name: 'featureBranchesPrefix',
364 | message: 'Feature branches?',
365 | default: 'feature/'
366 | };
367 | }
368 |
369 | function askNameOfDevelopBranch() {
370 | return {
371 | type: 'input',
372 | name: 'developBranch',
373 | message: 'Branch name for "next release" development?',
374 | default: 'develop'
375 | };
376 | }
377 |
378 | function askNameOfMasterBranch() {
379 | return {
380 | type: 'input',
381 | name: 'masterBranch',
382 | message: 'Branch name for production releases?',
383 | default: 'master'
384 | };
385 | }
386 |
387 | function ensureGitFlowNotInitialized(repo) {
388 | return NodeGit.Flow.isInitialized(repo)
389 | .then(function(isInit) {
390 | if (isInit) {
391 | throw new Error('repo is already a git flow repo', 1, true);
392 | }
393 |
394 | return isInit;
395 | });
396 | }
397 |
398 | function initGit(currentFolder) {
399 | return NodeGit.Repository.init(currentFolder, 0);
400 | }
401 |
402 | function openGit() {
403 | return gitRoot.getGitRoot()
404 | .then(function(root) {
405 | console.log('got the root!', root);
406 | return NodeGit.Repository.open(root);
407 | });
408 | }
409 |
410 | function ensureSlash(prefix) {
411 | return _.endsWith(prefix, '/') ? prefix : prefix + '/';
412 | }
413 |
414 | function getStatus(repo, shouldAddFolderEntries, shouldGroupBy) {
415 | shouldGroupBy = shouldGroupBy || 'isStaged';
416 |
417 | // GLOBAL PROCESS VARIALBES
418 | var currentBranchName, currentBranchStatuses, globalDiff;
419 |
420 | var files = [];
421 | return Q.all([
422 | repo.getStatus(),
423 | repo.getCurrentBranch(),
424 | getDiff(repo)
425 | ])
426 | .then(function(result) {
427 | currentBranchStatuses = result[0];
428 | globalDiff = result[2];
429 | return NodeGit.Branch.name(result[1]);
430 | })
431 | .then(function(currBranchName) {
432 | var statuses = currentBranchStatuses;
433 | currentBranchName = currBranchName;
434 |
435 | statuses.forEach(function(file) {
436 | files.push({
437 | path: file.path(),
438 | status: statusToArray(file),
439 | statusStr: statusToText(file),
440 | folder: path.dirname(file.path()),
441 | isStaged: file.status()[0].indexOf('INDEX') >= 0,
442 | changes: globalDiff[file.path()]
443 | });
444 | });
445 |
446 | // sort files alphabetically
447 | var sortedFiles = _.sortBy(files, [ 'path' ]);
448 |
449 | // separate to staged vs unstaged
450 | if (shouldGroupBy) {
451 | var separatedByStage = _.groupBy(sortedFiles, shouldGroupBy);
452 | }
453 |
454 | // should we show folders as well?
455 | /* TODO(thatkookooguy): update according
456 | to https://github.com/nodegit/nodegit/issues/1416 */
457 | if (shouldAddFolderEntries) {
458 | separatedByStage.true = addFolderEntries(separatedByStage.true);
459 | separatedByStage.false = addFolderEntries(separatedByStage.false);
460 | }
461 |
462 | var data = {};
463 | data.staged = separatedByStage.true;
464 | data.unstaged = separatedByStage.false;
465 | data.branchName = currentBranchName;
466 | // data.all = _.groupBy(data.staged.concat(data.unstaged), 'folder');
467 |
468 | // console.log('this is what I got!', data);
469 |
470 | //if no changes
471 | if (!data.staged && !data.unstaged) {
472 | throw new KbError(kbString.build([
473 | 'no changes were found.\n',
474 | 'change some files in order to commit them.'
475 | ]), 1, true);
476 | }
477 |
478 | return data;
479 | })
480 | .catch(function(err) {
481 | throw err;
482 | });
483 | }
484 |
485 | function getDiff(repo) {
486 | var deferred = Q.defer();
487 |
488 | var result = {};
489 |
490 | NodeGit.Diff.indexToWorkdir(repo)
491 | .then(function(diff) {
492 | return diff.patches();
493 | })
494 | .then(function(patches) {
495 | _.forEach(patches, function(patch) {
496 | var lineStats = patch.lineStats();
497 |
498 | result[patch.newFile().path()] = [
499 | lineStats.total_additions,
500 | lineStats.total_deletions
501 | ];
502 | });
503 |
504 | deferred.resolve(result);
505 | })
506 | .catch(function(error) {
507 | deferred.reject(error);
508 | console.error(error);
509 | });
510 |
511 | return deferred.promise;
512 | }
513 |
514 | function statusToArray(status) {
515 | var words = [];
516 | if (status.isNew()) {
517 | words.push('NEW');
518 | }
519 | if (status.isModified()) {
520 | words.push('MODIFIED');
521 | }
522 | if (status.isTypechange()) {
523 | words.push('TYPECHANGE');
524 | }
525 | if (status.isRenamed()) {
526 | words.push('RENAMED');
527 | }
528 | if (status.isIgnored()) {
529 | words.push('IGNORED');
530 | }
531 |
532 | return words;
533 | }
534 |
535 | function statusToText(status) {
536 | var words = [];
537 | if (status.isNew()) {
538 | words.push(kbString.build(
539 | kbString.white('['),
540 | kbString.info('NEW'),
541 | kbString.white(']')
542 | ));
543 | }
544 | if (status.isModified()) {
545 | words.push(kbString.build(
546 | kbString.white('['),
547 | kbString.warning('MODIFIED'),
548 | kbString.white(']')
549 | ));
550 | }
551 | if (status.isTypechange()) {
552 | words.push(kbString.build(
553 | kbString.white('['),
554 | kbString.warning('TYPECHANGE'),
555 | kbString.white(']')
556 | ));
557 | }
558 | if (status.isRenamed()) {
559 | words.push(kbString.build(
560 | kbString.white('['),
561 | kbString.error('RENAMED'),
562 | kbString.white(']')
563 | ));
564 | }
565 | if (status.isIgnored()) {
566 | words.push(kbString.build(
567 | kbString.white('['),
568 | kbString.white('IGNORED'),
569 | kbString.white(']')
570 | ));
571 | }
572 |
573 | return words.join(' ');
574 | }
575 |
--------------------------------------------------------------------------------