JIRA Create Branch Name

Adds a shortcut to create a branch name from a ticket and adds it to the clipboard. Usage: [Control] + [Alt] + Click on a ticket on the board.

  1. // ==UserScript==
  2. // @name JIRA Create Branch Name
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1
  5. // @description Adds a shortcut to create a branch name from a ticket and adds it to the clipboard. Usage: [Control] + [Alt] + Click on a ticket on the board.
  6. // @author cartok
  7. // @match https://*.atlassian.net/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=atlassian.net
  9. // @grant none
  10. // @license MIT
  11.  
  12. // ==/UserScript==
  13. (function() {
  14. const DELIMITER = '-'
  15.  
  16. const itemQueries = {
  17. sprintItem: target => {
  18. const wrapper = target.closest('.ghx-issue')
  19. if(!wrapper){
  20. console.warn('[user-script] Could not find ticket wrapper on sprint board')
  21. return
  22. }
  23.  
  24. const prefix = (() => {
  25. const element = wrapper.querySelector('.ghx-highlighted-field')
  26. // not every task is related to an epic
  27. if(!element) return 'feature'
  28. return element.textContent.trim().toLowerCase().replace(/\s+/g, '-')
  29. })()
  30.  
  31. const id = (() => {
  32. const element = wrapper.querySelector('a.ghx-key')
  33. if(!element) console.warn(`[user-script] Could not find ticket's issue key element`)
  34. return wrapper.querySelector('a.ghx-key').href.replace(/.*\/(.*)$/, '$1')
  35. })()
  36.  
  37. const name = (() => {
  38. const element = wrapper.querySelector('.ghx-summary')
  39. if(!element) console.warn(`[user-script] Could not find ticket's summary element`)
  40. return element.textContent.trim().toLowerCase()
  41. })()
  42.  
  43. return { prefix, id, name }
  44. },
  45. backglogItem: target => {
  46. const wrapper = target.closest('.js-issue')
  47. if(!wrapper){
  48. console.warn('[user-script] Could not find ticket wrapper on backlog')
  49. return
  50. }
  51.  
  52. const prefix = (() => {
  53. const element = wrapper.querySelector('.ghx-label[data-epickey]')
  54. // not every task is related to an epic
  55. if(!element) return 'feature'
  56. return element.textContent.trim().toLowerCase().replace(/\s+/g, '-')
  57. })()
  58.  
  59. const id = (() => {
  60. const element = wrapper.querySelector('a.ghx-key')
  61. if(!element) console.warn(`[user-script] Could not find ticket's issue key element`)
  62. return element.href.replace(/.*\/(.*)$/, '$1')
  63. })()
  64.  
  65. const name = (() => {
  66. const element = wrapper.querySelector('.ghx-summary')
  67. if(!element) console.warn(`[user-script] Could not find ticket's summary element`)
  68. return element.textContent.trim().toLowerCase()
  69. })()
  70.  
  71. return { prefix, id, name }
  72. }
  73. }
  74.  
  75. function toBranchName ({ prefix, id, name }) {
  76. name = name
  77. .replace(/^(\[.*?\])/m, '')
  78. .replace(/^[^\w]+/, '')
  79. .replace(/[^\w]+$/, '')
  80. .replace(/[^\w]+/g, DELIMITER)
  81.  
  82. return `${prefix}/${id}-${name}`
  83. }
  84.  
  85. document.addEventListener('click', async event => {
  86. event.stopPropagation()
  87. if(event.ctrlKey && event.altKey){
  88. event.preventDefault()
  89. }
  90.  
  91.  
  92. // first check targets, do not override any other possible bindings
  93. const queryResults = Object.values(itemQueries).map(query => query(event.target)).find(Boolean)
  94. if(!queryResults || !event.ctrlKey || !event.altKey) return
  95.  
  96. // do not open the ticket in a new tab when clicking on the link
  97. event.preventDefault()
  98.  
  99. const branchName = toBranchName(queryResults)
  100. await navigator.clipboard.writeText(branchName)
  101. console.log(`[user-script] Branchname '${branchName}' copied to clipboard`)
  102. }, false)
  103. })();
  104.  

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址