GitHub First Commit

Add a link to a GitHub repo's first commit

2021-01-05 يوللانغان نەشرى. ئەڭ يېڭى نەشرىنى كۆرۈش.

  1. // ==UserScript==
  2. // @name GitHub First Commit
  3. // @description Add a link to a GitHub repo's first commit
  4. // @author chocolateboy
  5. // @copyright chocolateboy
  6. // @version 2.7.2
  7. // @namespace https://github.com/chocolateboy/userscripts
  8. // @license GPL: https://www.gnu.org/copyleft/gpl.html
  9. // @include https://github.com/
  10. // @include https://github.com/*
  11. // @require https://cdn.jsdelivr.net/npm/cash-dom@8.1.0/dist/cash.min.js
  12. // @grant GM_log
  13. // @inject-into auto
  14. // ==/UserScript==
  15.  
  16. const COMMIT_BAR = 'div.js-details-container[data-issue-and-pr-hovercards-enabled] > *:last-child ul'
  17. const FIRST_COMMIT_LABEL = '<span aria-label="First commit"><strong>1st</strong> commit</span>'
  18.  
  19. /*
  20. * this function extracts the URL of the repo's first commit and navigates to it.
  21. * it is based on code by several developers, a list of whom can be found here:
  22. * https://github.com/FarhadG/init#contributors
  23. *
  24. * XXX it doesn't work on private repos. a way to do that can be found here,
  25. * but it requires an authentication token:
  26. * https://gist.github.com/simonewebdesign/a70f6c89ffd71e6ba4f7dcf7cc74ccf8
  27. */
  28. function openFirstCommit (user, repo) {
  29. return fetch(`https://api.github.com/repos/${user}/${repo}/commits`)
  30. // the `Link` header has additional URLs for paging.
  31. // parse the original JSON for the case where no other pages exist
  32. .then(res => Promise.all([res.headers.get('link'), res.json()]))
  33.  
  34. .then(([link, commits]) => {
  35. if (!link) {
  36. // if there's no link, we know we're on the only page
  37. return commits
  38. }
  39.  
  40. // the link header contains two URLs and has the following
  41. // format (wrapped for readability):
  42. //
  43. // <https://api.github.com/repositories/1234/commits?page=2>; rel="next",
  44. // <https://api.github.com/repositories/1234/commits?page=9>; rel="last"
  45.  
  46. // extract the URL of the last page (commits are ordered in
  47. // reverse chronological order, like the git CLI, so the oldest
  48. // commit is on the last page)
  49.  
  50. // @ts-ignore
  51. const lastPage = link.match(/^.+?<([^>]+)>;/)[1]
  52.  
  53. // fetch the last page of results
  54. return fetch(lastPage).then(res => res.json())
  55. })
  56.  
  57. // get the last commit and navigate to its target URL
  58. .then(commits => {
  59. if (Array.isArray(commits)) {
  60. location.href = commits[commits.length - 1].html_url
  61. } else {
  62. console.error(commits)
  63. }
  64. })
  65. }
  66.  
  67. /*
  68. * add the "First commit" link as the last child of the commit bar
  69. */
  70. function run () {
  71. const $commitBar = $(COMMIT_BAR)
  72.  
  73. // bail if it's not a repo page
  74. if (!$commitBar.length) {
  75. return
  76. }
  77.  
  78. // delete (i.e. replace) the (possibly inert/unresponsive) widget if it
  79. // already exists
  80. $commitBar.find('#first-commit').remove()
  81.  
  82. /*
  83. * This is the first LI in the commit bar (UL), which we clone to create the
  84. * "First commit" widget.
  85. *
  86. * <li class="ml-3">
  87. * <a data-pjax="" href="/foo/bar/commits/master" class="link-gray-dark no-underline">
  88. * <svg height="16">...</svg>
  89. *
  90. * <span class="d-none d-sm-inline">
  91. * <strong>42</strong>
  92. * <span aria-label="Commits on master">commits</span>
  93. * </span>
  94. * </a>
  95. * </li>
  96. */
  97.  
  98. // create it
  99. const $firstCommit = $commitBar
  100. .find('li')
  101. .eq(0)
  102. .clone()
  103. .attr('id', 'first-commit')
  104.  
  105. const $link = $firstCommit
  106. .find('a')
  107. .removeAttr('href')
  108. .css('cursor', 'pointer')
  109.  
  110. const $label = $(FIRST_COMMIT_LABEL)
  111.  
  112. $link.find(':scope > span').empty().append($label)
  113.  
  114. // @ts-ignore
  115. const [user, repo] = $('meta[name="octolytics-dimension-repository_network_root_nwo"]')
  116. .attr('content')
  117. .split('/')
  118.  
  119. $link.on('click', () => {
  120. $label.text('Loading...')
  121. openFirstCommit(user, repo)
  122. return false // stop processing the click
  123. })
  124.  
  125. $commitBar.append($firstCommit)
  126. }
  127.  
  128. $(document).on('pjax:end', run) // run on pjax page loads
  129. $(run) // run on full page loads