GitHub - Pull Request - Open external status links externally

Open external links in the "Checks" section in a new tab

  1. // ==UserScript==
  2. // @name GitHub - Pull Request - Open external status links externally
  3. // @namespace http://hear.com
  4. // @version 1.10
  5. // @description Open external links in the "Checks" section in a new tab
  6. // @author nate.clark@hear.com
  7. // @match https://github.com/*/*/pull/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=github.com
  9. // @tag productivity
  10. // @grant none
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. const nonGitHubTextKeys = ['sonarqube']
  17. const getRowText = (link) => link.parentNode.closest('.merge-status-item')?.innerText ?? link.parentNode.closest('.Box-row')?.innerText ?? ''
  18.  
  19. const isSonarQubeLink = (link) => {
  20. const text = getRowText(link)
  21.  
  22. return text.toLowerCase().indexOf('sonarqube') > -1
  23. }
  24.  
  25. const isNotGitHubLink = (link) => {
  26. const href = link.getAttribute('href')
  27. const text = getRowText(link)
  28. const isNonGitHub = href.startsWith('/') === false || nonGitHubTextKeys.some((key) => text.toLowerCase().indexOf(key.toLowerCase()) > -1)
  29.  
  30. isNotGitHubLink && console.log('isNotGitHubLink', { link, href, text, isNonGitHub })
  31.  
  32. return isNonGitHub
  33. }
  34.  
  35. const hasNoTarget = (link) => link.hasAttribute('target') === false
  36. const hasDetailsText = (link) => link.textContent === 'Details'
  37.  
  38. const updateSonarQubeLink = (link) => {
  39. const pathnameParts = window.location.pathname.split('/')
  40. const repo = pathnameParts.at(2)
  41. const pullRequest = pathnameParts.at(4)
  42. const url = new URL('https://sonarqube.service-production.audibene.net/dashboard')
  43.  
  44. url.searchParams.set('id', repo)
  45. url.searchParams.set('pullRequest', pullRequest)
  46. link.setAttribute('href', url.href)
  47. }
  48.  
  49. const makeLinkExternal = (link) => {
  50. link.setAttribute('target', '_blank')
  51. link.innerHTML = `<span>Details</span><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16"><path fill="currentColor" d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2m6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03L9.28 7.78a.75.75 0 0 1-1.042-.018a.75.75 0 0 1-.018-1.042l3.75-3.75l-1.543-1.543A.25.25 0 0 1 10.604 1"/></svg>`
  52. link.style.display = 'grid'
  53. link.style.alignItems = 'center'
  54. link.style.gap = '0.25rem'
  55. link.style.gridAutoFlow = 'column'
  56.  
  57. if (isSonarQubeLink(link)) {
  58. updateSonarQubeLink(link)
  59. }
  60. }
  61.  
  62. const setLinkTargets = () => {
  63. const allLinks = [...document.querySelectorAll('.merge-status-item a'), ...document.querySelectorAll('.Details-content--hidden * a')]
  64. const links = [...allLinks].filter(hasNoTarget).filter(hasDetailsText).filter(isNotGitHubLink)
  65.  
  66. links.forEach(makeLinkExternal)
  67. }
  68.  
  69. // Because when a build is running, these links are constantly updated/refreshed
  70. setInterval(setLinkTargets, 500)
  71. })();