From 256c5b2445ccfc312d098355fdcc323ad32867d2 Mon Sep 17 00:00:00 2001 From: Ramon Gebben Date: Thu, 29 Mar 2018 15:37:45 +0200 Subject: [PATCH] Added config file which solves #1 --- .gitignore | 1 + README.md | 7 +- index.js | 150 ++++++++++++++++++--------------- lib/printMergeRequest/index.js | 30 +++++++ lib/readConfig/index.js | 21 +++++ lib/writeConfig/index.js | 15 ++++ package.json | 1 + yarn.lock | 101 +++++++++++++++++++++- 8 files changed, 249 insertions(+), 77 deletions(-) create mode 100644 lib/printMergeRequest/index.js create mode 100644 lib/readConfig/index.js create mode 100644 lib/writeConfig/index.js diff --git a/.gitignore b/.gitignore index 3c3629e..598a2e6 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules +.config diff --git a/README.md b/README.md index 61222f1..d6cc060 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,14 @@ A very *very* simple cli to check merge requests on Gitlab. ## Install ``` -yarn add @pindakaasman/mergify --global +yarn global add @pindakaasman/mergify ``` -Add `GITLAB_USER_ID` and `GITLAB_PRIVATE_TOKEN` to your `.profile` or `.bashrc` and `source` it. +To complete setup you will need to run the `configure` command to provide access to the Gitlab API. Documentation on how to get a private token can be [found at Gitlab](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html). ``` -export GITLAB_PRIVATE_TOKEN=TOKEN_HERE -export GITLAB_ID=USER_ID +mergify --configure ``` ## Usage diff --git a/index.js b/index.js index 06dedda..e9d719d 100755 --- a/index.js +++ b/index.js @@ -4,23 +4,12 @@ const program = require('commander'); const R = require('ramda'); const chalk = require('chalk'); +const inquirer = require('inquirer'); const pack = require('./package.json'); const { getMergeRequests } = require('./lib/getMergeRequests'); - -const { - GITLAB_USER_ID = 0, - GITLAB_PRIVATE_TOKEN, -} = process.env; - -if (!GITLAB_PRIVATE_TOKEN || GITLAB_USER_ID === 0) { - throw new Error( -`It looks like \`mergify\` was not properly configured. -Please add \`GITLAB_USER_ID\` and \`GITLAB_PRIVATE_TOKEN\` to the environment`); -} - -const currentUserId = parseInt(GITLAB_USER_ID); - -const padRight = (str, n, fill = '0') => str.length < n ? padRight(str + fill, n, fill) : str; +const { printMergeRequest } = require('./lib/printMergeRequest'); +const { writeConfig } = require('./lib/writeConfig'); +const { readConfig } = require('./lib/readConfig'); const simplifyMergeRequest = R.pick([ 'title', @@ -33,62 +22,64 @@ const simplifyMergeRequest = R.pick([ 'web_url' ]); -const states = { - merged: chalk.blue, - closed: chalk.red, - opened: chalk.green, +const getAll = async () => { + console.log('#getAll') + const mergeRequests = await getMergeRequests(); + const simplifiedMergeRequest = R.map(simplifyMergeRequest)(mergeRequests); + simplifiedMergeRequest + .forEach(printMergeRequest); }; -const printMergeRequest = ({ - title, - state, - author: { username: author }, - assignee: { username: assignee }, - source_branch, - web_url, -}) => { - console.log( -`${padRight(`[${states[state](state)}]`, 15, ' ')} -${title} -Branch: ${chalk.cyan(source_branch)} -Assignee: ${chalk.cyan(assignee)} -Author: ${chalk.cyan(author)} -${chalk.gray(web_url)} -`, - ); +const getAllAssigned = async ({ userId }) => { + console.log('#getAllAssigned', userId) + const mergeRequests = await getMergeRequests(); + const simplifiedMergeRequest = R.map(simplifyMergeRequest)(mergeRequests); + simplifiedMergeRequest + .filter(({ state, assignee: { id: assignee } }) => state !== 'merged' && assignee === userId) + .forEach(printMergeRequest); +} + +const getAllSubmitted = async ({ userId }) => { + console.log('#getAllSubmitted', userId) + const mergeRequests = await getMergeRequests(); + const simplifiedMergeRequest = R.map(simplifyMergeRequest)(mergeRequests); + simplifiedMergeRequest + .filter(({ state, author: { id: author } }) => author === userId && state !== 'merged') + .forEach(printMergeRequest); +} + +const configure = async () => { + try { + const answers = await inquirer.prompt([ + { + type: 'input', + name: 'userId', + message: 'What is your Gitlab User ID?' + }, + { + type: 'password', + name: 'privateToken', + message: 'What private token shall we use?', + }, + { + type: 'input', + name: 'domain', + default: 'gitlab.com', + message: 'On what domain is your Gitlab instance?' + } + ]); + + return writeConfig(answers); + } catch (error) { + throw new Error(error); + process.exit(1); + } } program .name('mergify') .version(pack.version) -const getAll = async () => { - const mergeRequests = await getMergeRequests(); - const simplifiedMergeRequest = R.map(simplifyMergeRequest)(mergeRequests); - simplifiedMergeRequest - .forEach(printMergeRequest); -}; - -const getAllAssigned = async () => { - if (currentUserId === 1) throw new Error('No user ID configured.'); - - const mergeRequests = await getMergeRequests(); - const simplifiedMergeRequest = R.map(simplifyMergeRequest)(mergeRequests); - simplifiedMergeRequest - .filter(({ state, assignee: { id: assignee } }) => state !== 'merged' && assignee === currentUserId) - .forEach(printMergeRequest); -} - -const getAllSubmitted = async () => { - if (currentUserId === 1) throw new Error('No user ID configured.'); - - const mergeRequests = await getMergeRequests(); - const simplifiedMergeRequest = R.map(simplifyMergeRequest)(mergeRequests); - simplifiedMergeRequest - .filter(({ state, author: { id: author } }) => author === currentUserId && state !== 'merged') - .forEach(printMergeRequest); -} - const options = [ { trigger: '-a --all', @@ -105,11 +96,32 @@ const options = [ description: 'Get all open merge request submitted to you', fn: getAllSubmitted }, -] + { + trigger: '-c --configure', + description: 'Setup or update required config', + fn: configure + }, +]; -options.forEach(({ trigger, description, fn }) => { - program.option(trigger, description, fn); -}); +const run = async() => { + let config = await readConfig(); -// Start -program.parse(process.argv); + const { + userId, + privateToken + } = config; + + if (!userId || !privateToken) { + await configure(); + config = await readConfig(); + console.log('🎉 All set, your ready to mergify!'); + } + + options.forEach(({ trigger, description, fn }) => { + program.option(trigger, description, (...args) => fn(config, ...args)); + }); + + return program +}; + +run().then(p => p.parse(process.argv)); diff --git a/lib/printMergeRequest/index.js b/lib/printMergeRequest/index.js new file mode 100644 index 0000000..babd34d --- /dev/null +++ b/lib/printMergeRequest/index.js @@ -0,0 +1,30 @@ +const chalk = require('chalk'); + +const states = { + merged: chalk.blue, + closed: chalk.red, + opened: chalk.green, +}; + +const printMergeRequest = ({ + title, + state, + author: { username: author }, + assignee: { username: assignee }, + source_branch, + web_url, +}) => { + console.log( + ` +${`[${states[state](state)}]`} +${title} +Branch: ${chalk.cyan(source_branch)} +Assignee: ${chalk.cyan(assignee)} +Author: ${chalk.cyan(author)} +${chalk.gray(web_url)}`, + ); +} + +module.exports = { + printMergeRequest +}; diff --git a/lib/readConfig/index.js b/lib/readConfig/index.js new file mode 100644 index 0000000..b9fd175 --- /dev/null +++ b/lib/readConfig/index.js @@ -0,0 +1,21 @@ +const { readFile } = require('fs'); +const { promisify } = require('util'); +const readFileAsync = promisify(readFile); + +async function readConfig() { + const configFileName = `${process.cwd()}/.config`; + try { + const config = await readFileAsync(configFileName, 'utf8'); + return JSON.parse(config); + } catch(_) { + console.log(` +Oh no, \`mergify\` is not configured yet. +Let configure it +`); + return {}; + } +} + +module.exports = { + readConfig +}; diff --git a/lib/writeConfig/index.js b/lib/writeConfig/index.js new file mode 100644 index 0000000..f70fbd8 --- /dev/null +++ b/lib/writeConfig/index.js @@ -0,0 +1,15 @@ +const { writeFile } = require('fs'); +const { promisify } = require('util'); +const writeFileAsync = promisify(writeFile); + +async function writeConfig(config) { + const configFileName = `${process.cwd()}/.config`; + config.userId = parseInt(config.userId); + + const body = JSON.stringify(config, null, 2); + return writeFileAsync(configFileName, body, 'utf8'); +} + +module.exports = { + writeConfig +}; diff --git a/package.json b/package.json index 49c238e..5690a3e 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "dependencies": { "chalk": "^2.3.2", "commander": "^2.15.1", + "inquirer": "^5.2.0", "node-fetch": "^2.1.2", "ramda": "^0.25.0" }, diff --git a/yarn.lock b/yarn.lock index 2547dd1..8668553 100644 --- a/yarn.lock +++ b/yarn.lock @@ -499,6 +499,10 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +chardet@^0.4.0: + version "0.4.2" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" + ci-info@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.1.3.tgz#710193264bb05c77b8c90d02f5aaf22216a667b2" @@ -512,6 +516,16 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + dependencies: + restore-cursor "^2.0.0" + +cli-width@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" + cliui@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" @@ -862,6 +876,14 @@ extend@~3.0.0, extend@~3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" +external-editor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.1.0.tgz#3d026a21b7f95b5726387d4200ac160d372c3b48" + dependencies: + chardet "^0.4.0" + iconv-lite "^0.4.17" + tmp "^0.0.33" + extglob@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" @@ -907,6 +929,12 @@ fb-watchman@^2.0.0: dependencies: bser "^2.0.0" +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + dependencies: + escape-string-regexp "^1.0.5" + filename-regex@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" @@ -1231,7 +1259,7 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" -iconv-lite@0.4.19: +iconv-lite@0.4.19, iconv-lite@^0.4.17: version "0.4.19" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" @@ -1261,6 +1289,24 @@ ini@~1.3.0: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" +inquirer@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-5.2.0.tgz#db350c2b73daca77ff1243962e9f22f099685726" + dependencies: + ansi-escapes "^3.0.0" + chalk "^2.0.0" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^2.1.0" + figures "^2.0.0" + lodash "^4.3.0" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^5.5.2" + string-width "^2.1.0" + strip-ansi "^4.0.0" + through "^2.3.6" + invariant@^2.2.2: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" @@ -1425,6 +1471,10 @@ is-primitive@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" +is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + is-regex@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" @@ -1957,7 +2007,7 @@ lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" -lodash@^4.13.1, lodash@^4.14.0, lodash@^4.17.4: +lodash@^4.13.1, lodash@^4.14.0, lodash@^4.17.4, lodash@^4.3.0: version "4.17.5" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" @@ -2095,6 +2145,10 @@ ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + nan@^2.3.0: version "2.10.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" @@ -2250,6 +2304,12 @@ once@^1.3.0, once@^1.3.3, once@^1.4.0: dependencies: wrappy "1" +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + dependencies: + mimic-fn "^1.0.0" + optimist@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" @@ -2280,7 +2340,7 @@ os-locale@^2.0.0: lcid "^1.0.0" mem "^1.1.0" -os-tmpdir@^1.0.0, os-tmpdir@^1.0.1: +os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -2625,6 +2685,13 @@ resolve@1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" @@ -2641,6 +2708,18 @@ rimraf@2, rimraf@^2.5.1, rimraf@^2.5.4, rimraf@^2.6.1: dependencies: glob "^7.0.5" +run-async@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" + dependencies: + is-promise "^2.1.0" + +rxjs@^5.5.2: + version "5.5.8" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.8.tgz#b2b0809a57614ad6254c03d7446dea0d83ca3791" + dependencies: + symbol-observable "1.0.1" + safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" @@ -2872,7 +2951,7 @@ string-width@^1.0.1, string-width@^1.0.2: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -string-width@^2.0.0, string-width@^2.1.1: +string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" dependencies: @@ -2935,6 +3014,10 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" +symbol-observable@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" + symbol-tree@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" @@ -2974,6 +3057,16 @@ throat@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + dependencies: + os-tmpdir "~1.0.2" + tmpl@1.0.x: version "1.0.4" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1"