前言
这篇文章次要是对于公司近期对于git提交标准进行了限度之后产出,其次要目标是为了实现本公司的提交标准的实现,以及简略不便化集体操作,本文的代码是由git-cz根底上进行的批改。注:本文会写上次要代码各位能够自行下载git-cz源码批改后创立本人的独特的git提交管理工具,也会附上一些api接口(如果有想要我来帮忙批改的也能够私聊我哦)
实现成果
这就是进行批改后的提交流程逻辑,如果在失常对于jira号和提交信息对应的状况下咱们能够不必输出任何信息就能够实现提交,并且生成信息也是齐全能够依照咱们制订的标准来进行提交,所以这样做能够对于咱们的提交信息进行治理并且还能够把咱们的提交信息和jira号关联对应上,如下图:
这样其实就简略的实现咱们这样做的最根本需要,就是标准提交信息让咱们的jira和git提交对应起来,并且前期能够通过git log来生成咱们需要的日志文件。然而,我既然都写到这了怎么不再把gitlab上的操作再省略一些呢:
是的,咱们一次性实现了全副操作间接把gitlab上的mr申请都干掉了间接提交这样岂不美滋滋,再也不必关上jira网页查看jira号和gitlab发送mr申请了!
具体实现
其实整体下来说有两种形式能够实现这种成果:
- 利用puppeteer自动化无头浏览器来实现
- 利用jira和gitlab的接口来实现
puppeteer
什么是puppeteerPuppeteer是一个Node库,它提供了高级API来通过DevTools协定管制Chrome或Chromium 。
Puppeteer 默认状况下无头运行,但能够配置为运行残缺(无头)的Chrome或Chromium。
好吧其实这是官网的介绍,简略来说就是给咱们提供了一个能够操控的浏览器,而咱们的目标就是通过这个浏览器的dom来获取咱们的信息
var PCR = require('puppeteer');const browser = await PCR.launch({ // executablePath: this.pcr.executablePath, headless: true, // 设置超时工夫 timeout: 120000, // 如果是拜访https页面 此属性会疏忽https谬误 ignoreHTTPSErrors: true, // 关上开发者工具, 当此值为true时, headless总为false devtools: true, defaultViewport: { width: 1900, height: 900, hasTouch: true, isMobile: true, deviceScaleFactor: 3 }, // 敞开headless模式, 不会关上浏览器 // headless: enableChromeDebug !== 'Y', args: ['--no-sandbox'] }); let json = {}; try { json = fs.readFileSync(_path.default.resolve(__dirname + '/../../cz-cli-git.json'), 'utf8'); json = JSON.parse(json); } catch (e) { console.log(e); } if (!json.username || !json.password) { const getMessage = await _inquirer.default.prompt([{ type: 'input', name: 'username', message: '请输入您的账号' }, { type: 'input', name: 'password', message: '请输入您的明码' }]); json = { username: getMessage.username, password: getMessage.password }; fs.writeFileSync(_path.default.resolve(__dirname + '/../../cz-cli-git.json'), JSON.stringify(json)); } let loading = ora(); loading.start(`正在获取分支信息`); const page = await browser.newPage(); await page.goto('公司的jira地址/users/sign_in'); await page.waitFor(1000); const elUsername = await page.$('#user_login'); const elPassword = await page.$('#user_password'); const elSubmit = await page.$('.move-submit-down'); await elUsername.type(json.username); await elPassword.type(json.password); await elSubmit.click(); await page.waitFor(1000); await page.goto(gitUrl + '/merge_requests/new'); await page.waitFor(1000); const sourceBranch = await page.$('.js-source-branch'); const targetBranch = await page.$('.js-target-branch'); await sourceBranch.click(); await page.waitFor(1000); const AllBranch = await page.$$eval('.js-source-branch-dropdown a', el => el.map(x => { return { name: x.innerText, value: x.getAttribute('data-ref') }; })); loading.succeed('分支信息获取胜利'); console.log(AllBranch);
看着这么长一串的await其实就是获取dom节点和实现dom操作而已,这样咱们就能获取到AllBranch了(当然咱们公司的jira版本号是7.3.8)
尽管这样就能够获取到咱们的jira信息了,然而有以下几个问题
- 整个npm库较重,应为利用了无头浏览器,所以npm会给你下载一个Chromium自动化测试浏览器(大概134MB)
- 装置npm的时候很慢很慢,因为有这么大一个浏览器下载经常出现下载失败和下载很慢的问题
- 应用的时候获取效率较慢,因为是采纳模仿浏览器操作所以有页面加载工夫须要进行期待
这个计划代码写起来简略轻松然而问题还是挺多的,然而如果需要不高这样操作也不失是一个方法,因为逻辑上来说这样能够代替你的所有操作,并且不必放心接口权限等问题
接口申请
这个字眼看着就是简略粗犷的做法了,看起来很简略其实并没有,最要害的问题在于:找接口!
是的,对于一个不相熟jira和gitlab接口的人来说(本菜鸡),官网提供的一大片接口几乎辣眼睛,jira上还没有官网提供的都是去页面上本人试出来的。那么这样的作法的长处当然也很显著了:
- npm包较轻,无浏览器装置
- 相应速度块,能够疾速获取接口信息展现
- 异样捕捉容易,能够较为轻松的实现异常情况的解决
如果可能应用接口申请的状况下我感觉还是尽量应用接口来进步应用品质和异样解决,当然如果有接口无奈实现的工作当我没说。要害说一个对于gitlab的拜访令牌的阐明的,因为gitlab的平安验证比较复杂所以应用了简略的拜访令牌来实现,具体如果生成令牌能够点击这个生成令牌查看,并且所有接口都须要在头部增加
jira就不展现代码了毕竟只有一个接口,这里就把gitlab代码展现进去吧(jira版本: 7.3.8,gitlab如同没太大版本之分)
获取未实现jira列表: url: /rest/issueNav/1/issueTable type: post params: { layoutKey: 'split-view', // 固定填写 jql: 'assignee = currentUser() AND resolution = Unresolved order by updated DESC', // 固定填写 os_username: json.username, // 用户账号 os_password: json.password // 用户明码 }
const reg = /http(.*?)\s/; const git = await execSync( 'git remote -v' ).toString().trim(); //姓名 const gitUrl = git.match( reg )[ 0 ].replace( /\s/g , '' ).replace('https://域名地址', 'http://ip地址').replace('.git', ''); let response try { response = await axios.get( 'https://域名地址/api/v4/projects' , { params : { search : gitUrl.split('/')[gitUrl.split('/').length - 1] } , headers : { 'Authorization' : `Bearer ${json.Authorization}` , 'Content-Type' : 'application/x-www-form-urlencoded' } } ); } catch ( e ) { throw Error('获取我的项目信息失败') } response = response.data.filter( ( item ) => item.web_url === gitUrl ); if ( response.length > 1 ) { throw Error('项目管理有问题,请分割jira我的项目管理员'); } const projectId = response[0].id; let forksRes; try { forksRes = await axios.get( `https://域名地址/api/v4/projects/${projectId}/repository/branches` , { headers : { 'Authorization' : `Bearer ${json.Authorization}` , 'Content-Type' : 'application/x-www-form-urlencoded' } } ); } catch ( e ) { throw Error('申请分支失败请重试') } const branchList = forksRes.data.map(item => { return { name: item.name, value: item.name } }) let menberRes; try { menberRes = await axios.get( `https://域名地址/api/v4/projects/${projectId}/members/all` , { headers : { 'Authorization' : `Bearer ${json.Authorization}` , 'Content-Type' : 'application/x-www-form-urlencoded' } } ); } catch ( e ) { throw Error('获取检查用户失败请重试') } const menberList = menberRes.data.map(item => { return { name: item.name, value: item.id } }) loading.succeed(`填写信息获取胜利`); console.warn('以后分支为:' + await execSync( 'git name-rev --name-only HEAD' ).toString().trim() + ' (为确保操作准确性,只容许在以后分支发动mr申请)'); const getInputB = await inquirer .prompt([ { type: 'input', name: 'branchIn', message: '输出合并入的主分支如果想手动抉择能够间接回车进入下一步', } ]) const proptList = [{ type: 'list', name: 'branch', message: '请抉择合入的主分支', choices: branchList }, { type: 'input', name: 'title', message: '请输出合并题目', default: '默认合并申请信息' }, { type: 'input', name: 'desc', message: '请输出合并形容' }, { type: 'list', name: 'users', message: '抉择合并用户', choices: menberList }] getInputB.branchIn && proptList.splice(0, 1) const getBranch = await inquirer .prompt(proptList) try { let mergeRes = await axios.post( `https://域名地址/api/v4/projects/${projectId}/merge_requests` , qs.stringify({ source_branch: await execSync( 'git name-rev --name-only HEAD' ).toString().trim(), target_branch: getInputB.branchIn ? getInputB.branchIn : getBranch.branch, title: getBranch.title, assignee_id: getBranch.users, description: getBranch.desc, remove_source_branch: true }), { headers : { 'Authorization' : `Bearer ${json.Authorization}` , 'Content-Type' : 'application/x-www-form-urlencoded' } } ); if (mergeRes.status === 201) { console.log('创立胜利'); console.log('拜访地址:' + mergeRes.data.web_url); } else { console.log('请查看分支抉择和题目不能为空'); } } catch ( e ) { throw Error('提交失败,请查看分支抉择和题目不能为空,或者线上曾经存在一样合并申请了无需再提交'); }
其余问题
账号治理
如果看了下面的代码其实有个货色就是账号治理没有看到,这个中央我应用的是第一次填写后将账号密码保留至文件种,前面再拜访就通过文件获取
let json = {}; try { json = fs.readFileSync(path.resolve(__dirname + '/cz-cli.json'), 'utf8') json = JSON.parse(json); } catch ( e ) { // console.log(e); } if (!json.username || !json.password) { const getMessage = await inquirer .prompt([ { type: 'input', name: 'username', message: '请输入您的账号' }, { type: 'input', name: 'password', message: '请输入您的明码' } ]) json = { username: getMessage.username, password: getMessage.password } fs.writeFileSync(path.resolve(__dirname + '/cz-cli.json'), JSON.stringify(json)) }
这个代码其实很简略就不再做形容了。
git cz
其实说实话这个才是最要害的货色,然而我感觉既然都看到这了大佬可能都不缺这点实力来读读git-cz的源码了,我就简略说一下咱们应该怎么批改让咱们的性能增加上
首先这个基本上就是git cz执行的根底逻辑了,真正在代码中的重复办法调用,办法传递较深所以理论看起来代码没有那么简单明了,然而也能够说的是在代码中的获取发问信息是通过require第三方包而后加载实现的(同步获取!),是的他是属于同步获取,所以咱们的接口申请无奈期待,那么就要咱们进行一点点的魔改了
第一处
#!src/commitizen/adapter.js// 142行本来代码function getPrompter (adapterPath) { // Resolve the adapter path let resolvedAdapterPath = resolveAdapterPath(adapterPath); // Load the adapter let adapter = require(resolvedAdapterPath); /* istanbul ignore next */ if (adapter && adapter.prompter && isFunction(adapter.prompter)) { return adapter.prompter; } else if (adapter && adapter.default && adapter.default.prompter && isFunction(adapter.default.prompter)) { return adapter.default.prompter; } else { throw new Error(`Could not find prompter method in the provided adapter module: ${adapterPath}`); }}//批改为function getPrompter (adapterPath) { // Resolve the adapter path return new Promise(async (resolve) => { let resolvedAdapterPath = resolveAdapterPath(adapterPath); // Load the adapter let adapter = await require(resolvedAdapterPath); /* istanbul ignore next */ if (adapter && adapter.prompter && isFunction(adapter.prompter)) { resolve(adapter.prompter); } else if (adapter && adapter.default && adapter.default.prompter && isFunction(adapter.default.prompter)) { resolve(adapter.default.prompter); } else { throw new Error(`Could not find prompter method in the provided adapter module: ${adapterPath}`); } })}
咱们将本来的间接执行代码给改成了异步执行返回Primose回调了,而后只须要进行上面一步就能够把获取改成异步了
#!src/commitizen/adapter.js// 将第43行的代码let prompter = getPrompter(adapterConfig.path);// 批改为(并且将办法改为async就能够了)let prompter = await getPrompter(adapterConfig.path);
这样咱们就能够进行jira的异步获取了,那么对于解决实现后咱们的操作呢?
在/src/strategies/git-cz.js文件中的第57行的commit办法执行中有一个done的回调办法,咱们能够把后续操作增加到这个办法内执行就能够了
commit(inquirer, process.cwd(), prompter, { args: parsedGitCzArgs, disableAppendPaths: true, emitData: true, quiet: false, retryLastCommit, hookMode }, function (error) { // 就是这个办法 if (error) { throw error; } });
最初一步
其实到这了基本上看了整篇文章就能够实现你想自主批改的git cz了,而后咱们增加一个git mr指令来让咱们在没有执行提交的时候也能够进行gitlab的merge request申请,在更目录下的bin文件夹中增加文件而后再package.jso中配置以下即可(其实这就是减少指令就是创立脚手架的,如果不相熟脚手架能够查看我的历史文章中有一篇对于脚手架的实战)
// package.json"bin": { "git-cz": "./bin/git-cz", "git-mr": "./bin/git-mr", "commitizen": "./bin/commitizen" },//git-mr#!/usr/bin/env noderequire('./git-mr.js');//git-mr.jsprocess.on('uncaughtException', function (err) { console.error(err.message || err); process.exit(1);})require('../dist/cli/git-cz.js').MrApi();//git-mr.cmd@node "%~dpn0" %*
结尾
这就是本文全部内容了,如果有任何问题或者想让我帮忙进行开发欢送进行评论的私聊我,上面贴上自己微信二维码。