GitLab: copy commit reference

Adds a "Copy commit reference" button to every commit page on GitLab.

目前为 2023-10-05 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name GitLab: copy commit reference
  3. // @namespace https://andrybak.dev
  4. // @license AGPL-3.0-only
  5. // @version 1
  6. // @description Adds a "Copy commit reference" button to every commit page on GitLab.
  7. // @homepageURL https://gitlab.com/andrybak/copy-commit-reference-userscript
  8. // @supportURL https://gitlab.com/andrybak/copy-commit-reference-userscript/-/issues
  9. // @author Andrei Rybak
  10. // @match https://gitlab.com/*/-/commit/*
  11. // @match https://invent.kde.org/*/-/commit/*
  12. // @icon https://gitlab.com/assets/favicon-72a2cad5025aa931d6ea56c3201d1f18e68a8cd39788c7c80d5b2b82aa5143ef.png
  13. // @require https://cdn.jsdelivr.net/gh/rybak/userscript-libs@e86c722f2c9cc2a96298c8511028f15c45180185/waitForElement.js
  14. // @require https://cdn.jsdelivr.net/gh/rybak/copy-commit-reference-userscript@c7f2c3b96fd199ceee46de4ba7eb6315659b34e3/copy-commit-reference-lib.js
  15. // @grant none
  16. // ==/UserScript==
  17.  
  18. /*
  19. * Copyright (C) 2023 Andrei Rybak
  20. *
  21. * This program is free software: you can redistribute it and/or modify
  22. * it under the terms of the GNU Affero General Public License as published
  23. * by the Free Software Foundation, version 3.
  24. *
  25. * This program is distributed in the hope that it will be useful,
  26. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  27. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  28. * GNU Affero General Public License for more details.
  29. *
  30. * You should have received a copy of the GNU Affero General Public License
  31. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  32. */
  33.  
  34. (function () {
  35. 'use strict';
  36.  
  37. /*
  38. * Implementation for GitLab.
  39. *
  40. * Example URLs for testing:
  41. * - https://gitlab.com/andrybak/resoday/-/commit/b82824ec6dc3f14c3711104bf0ffd792c86d19ba
  42. * - https://invent.kde.org/education/kturtle/-/commit/8beecff6f76a4afc74879c46517d00657d8426f9
  43. */
  44. class GitLab extends GitHosting {
  45. static #HEADER_SELECTOR = 'main#content-body .page-content-header > .header-main-content';
  46.  
  47. getTargetSelector() {
  48. return GitLab.#HEADER_SELECTOR;
  49. }
  50.  
  51. getButtonTagName() {
  52. return 'button'; // like GitLab's "Copy commit SHA"
  53. }
  54.  
  55. wrapButton(button) {
  56. const copyShaButtonIcon = document.querySelector(`${GitLab.#HEADER_SELECTOR} > button > svg[data-testid="copy-to-clipboard-icon"]`);
  57. const icon = copyShaButtonIcon.cloneNode(true);
  58. button.replaceChildren(icon); // is just icon enough?
  59. button.classList.add('btn-sm', 'btn-default', 'btn-default-tertiary', 'btn-icon', 'btn', 'btn-clipboard', 'gl-button');
  60. button.setAttribute('data-toggle', 'tooltip'); // this is needed to have a fancy tooltip in style of other UI
  61. button.setAttribute('data-placement', 'bottom'); // this is needed so that the fancy tooltip appears below the button
  62. button.style = 'border: 1px solid darkgray;';
  63. button.title = this.getButtonText() + " to clipboard";
  64. return button;
  65. }
  66.  
  67. getFullHash() {
  68. const copyShaButton = document.querySelector(`${GitLab.#HEADER_SELECTOR} > button`);
  69. return copyShaButton.getAttribute('data-clipboard-text');
  70. }
  71.  
  72. getDateIso(hash) {
  73. // careful not to select <time> tag for "Committed by"
  74. const authorTimeTag = document.querySelector(`${GitLab.#HEADER_SELECTOR} > .d-sm-inline + time`);
  75. return authorTimeTag.getAttribute('datetime').slice(0, 'YYYY-MM-DD'.length);
  76. }
  77.  
  78. getCommitMessage(hash) {
  79. /*
  80. * Even though vast majority will only need `subj`, gather everything and
  81. * let downstream code handle paragraph splitting.
  82. */
  83. const subj = document.querySelector('.commit-box .commit-title').innerText;
  84. const maybeBody = document.querySelector('.commit-box .commit-description');
  85. if (maybeBody == null) { // some commits have only a single-line message
  86. return subj;
  87. }
  88. const body = maybeBody.innerText;
  89. return subj + '\n\n' + body;
  90. }
  91.  
  92. addButtonContainerToTarget(target, buttonContainer) {
  93. const authoredSpanTag = target.querySelector('span.d-sm-inline');
  94. target.insertBefore(buttonContainer, authoredSpanTag);
  95. // add spacer to make text "authored" not stick to the button
  96. target.insertBefore(document.createTextNode(" "), authoredSpanTag);
  97. }
  98.  
  99. /*
  100. * GitLab has a complex interaction with library ClipboardJS:
  101. *
  102. * - https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/helpers/button_helper.rb#L31-68
  103. * - https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/behaviors/copy_to_clipboard.js#L63-94
  104. *
  105. * and the native tooltips are even more complicated.
  106. */
  107. createCheckmark() {
  108. const checkmark = super.createCheckmark();
  109. checkmark.style.left = 'calc(100% + 0.3rem)';
  110. checkmark.style.lineHeight = '1.5';
  111. checkmark.style.padding = '0.5rem 1.5rem';
  112. checkmark.style.textAlign = 'center';
  113. checkmark.style.width = 'auto';
  114. checkmark.style.borderRadius = '3px';
  115. checkmark.style.fontSize = '0.75rem';
  116. checkmark.style.fontFamily = '"Segoe UI", Roboto, "Noto Sans", Ubuntu, Cantarell, "Helvetica Neue", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"';
  117. if (document.body.classList.contains('gl-dark')) {
  118. checkmark.style.backgroundColor = '#dcdcde';
  119. checkmark.style.color = '#1f1e24';
  120. } else {
  121. checkmark.style.backgroundColor = '#000';
  122. checkmark.style.color = '#fff';
  123. }
  124. return checkmark;
  125. }
  126. }
  127.  
  128. CopyCommitReference.runForGitHostings(new GitLab());
  129. })();