Enhance some features of the Self-managed Gitlab

Enhance some features of the Self-managed Gitlab, such as the CI/CD settings page, the merge request create/edit page etc.

当前为 2024-12-03 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Enhance some features of the Self-managed Gitlab
  3. // @name:zh 强化自托管 Gitlab 能力
  4. // @description Enhance some features of the Self-managed Gitlab, such as the CI/CD settings page, the merge request create/edit page etc.
  5. // @description:zh 强化自托管 Gitlab 的一些功能,如 CI/CD 设置页面、合并请求创建/编辑页面等。
  6. // @icon data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGhlaWdodD0iNTEycHgiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDUxMiA1MTI7IiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiB3aWR0aD0iNTEycHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxnIGlkPSJfeDMxXzQ0LWdpdGxhYiI+PGc+PGcgaWQ9IlhNTElEXzZfIj48Zz48Zz48cGF0aCBkPSJNNTIuNzUyLDIwNS41MDFsMjAzLjE4LDI2NC4wN2wtMjIyLjctMTY1LjI5Yy02LjExLTQuNTktOC43Mi0xMi41OC02LjM4LTE5Ljc3MWwyNS44Ny03OS4wNSAgICAgICBMNTIuNzUyLDIwNS41MDF6IiBzdHlsZT0iZmlsbDojRkNBMzI2OyIvPjwvZz48Zz48cG9seWdvbiBwb2ludHM9IjE3MS4zMDIsMjA1LjQ2MSAyNTYuMDEyLDQ2OS41NDEgMjU1LjkzMiw0NjkuNTcxIDUyLjc1MiwyMDUuNTAxIDUyLjgxMiwyMDUuNDYxICAgICAgICAgICAgICIgc3R5bGU9ImZpbGw6I0ZDNkQyNjsiLz48L2c+PGc+PHBvbHlnb24gcG9pbnRzPSIzNDAuNzMxLDIwNS40NjEgMjU2LjAyMSw0NjkuNTcxIDI1Ni4wMTIsNDY5LjU0MSAxNzEuMzAyLDIwNS40NjEgMTcxLjM5MiwyMDUuNDYxICAgICAgICAzNDAuNjQyLDIwNS40NjEgICAgICAiIHN0eWxlPSJmaWxsOiNFMjQzMjk7Ii8+PC9nPjxnPjxwb2x5Z29uIHBvaW50cz0iNDU5LjI5MiwyMDUuNTAxIDI1Ni4wMjEsNDY5LjU3MSAzNDAuNzMxLDIwNS40NjEgNDU5LjIzMSwyMDUuNDYxICAgICAgIiBzdHlsZT0iZmlsbDojRkM2RDI2OyIvPjwvZz48Zz48cGF0aCBkPSJNNDg1LjE5MSwyODQuNTExYzIuMjQsNy4xOS0wLjI3LDE1LjE4MS02LjQ3LDE5Ljc3MWwtMjIyLjcsMTY1LjI5bDIwMy4yNzEtMjY0LjA3bDAuMDI5LTAuMDQgICAgICAgTDQ4NS4xOTEsMjg0LjUxMXoiIHN0eWxlPSJmaWxsOiNGQ0EzMjY7Ii8+PC9nPjxnPjxwYXRoIGQ9Ik00MDguNDcyLDQ4LjQyMWw1MC43NiwxNTcuMDRoLTExOC41aC0wLjA5bDUwLjg1LTE1Ny4wNCAgICAgICBDMzk0LjM2MSw0MC40MzEsNDA1LjY4Miw0MC40MzEsNDA4LjQ3Miw0OC40MjF6IiBzdHlsZT0iZmlsbDojRTI0MzI5OyIvPjwvZz48Zz48cGF0aCBkPSJNMTcxLjM5MiwyMDUuNDYxaC0wLjA5SDUyLjgxMmw1MC43Ni0xNTcuMDRjMi44Ny03Ljk5LDE0LjE5LTcuOTksMTYuOTgsMCAgICAgICBDMTIwLjU1Miw0OC40MjEsMTcxLjMwMiwyMDUuNDYxLDE3MS4zOTIsMjA1LjQ2MXoiIHN0eWxlPSJmaWxsOiNFMjQzMjk7Ii8+PC9nPjwvZz48L2c+PC9nPjwvZz48ZyBpZD0iTGF5ZXJfMSIvPjwvc3ZnPg==
  7. // @version 5
  8. // @author Arylo Yeung <arylo.open@gmail.com>
  9. // @include /^https:\/\/(git(lab)?|code)\.[^/]+\/.*\/-\/settings\/ci_cd$/
  10. // @include /^https:\/\/(git(lab)?|code)\.[^/]+\/.*\/-\/merge_requests\/new\b/
  11. // @include /^https:\/\/(git(lab)?|code)\.[^/]+\/.*\/-\/merge_requests\/\d+/edit\b/
  12. // @include /^https:\/\/(git(lab)?|code)\.[^/]+\/dashboard\/merge_requests\b/
  13. // @license MIT
  14. // @homepage https://github.com/Arylo/scripts#readme
  15. // @supportURL https://github.com/Arylo/scripts/issues
  16. // @run-at document-end
  17. // @grant GM_addStyle
  18. // @grant GM_setClipboard
  19. // @namespace https://greasyfork.org/users/1133279
  20. // ==/UserScript==
  21. "use strict";
  22. (() => {
  23. // src/monkey/polyfill/GM.ts
  24. var thisGlobal = window;
  25. if (typeof thisGlobal.GM === "undefined") {
  26. thisGlobal.GM = {};
  27. }
  28. function getGMWindow() {
  29. return thisGlobal;
  30. }
  31.  
  32. // src/monkey/polyfill/GM_addStyle.ts
  33. var w = getGMWindow();
  34. if (typeof w.GM_addStyle === "undefined") {
  35. w.GM_addStyle = function GM_addStyle2(cssContent) {
  36. const head = document.getElementsByTagName("head")[0];
  37. if (head) {
  38. const styleElement = document.createElement("style");
  39. styleElement.setAttribute("type", "text/css");
  40. styleElement.textContent = cssContent;
  41. head.appendChild(styleElement);
  42. return styleElement;
  43. }
  44. return null;
  45. };
  46. }
  47. if (typeof w.GM.addStyle === "undefined") {
  48. w.GM.addStyle = GM_addStyle;
  49. }
  50.  
  51. // src/monkey/gitlab-enhance/settings/ci_cd.css
  52. var ci_cd_default = ".content-wrapper nav{max-width:100%}.content-wrapper .container-fluid{max-width:100%}.ci-variable-table table colgroup col:nth-child(3){width:100px}.ci-variable-table table colgroup col:nth-child(4){width:200px}.ci-variable-table table colgroup col:nth-child(5){width:50px}\n";
  53.  
  54. // src/monkey/gitlab-enhance/settings/ci_cd.ts
  55. if (location.pathname.endsWith("/-/settings/ci_cd")) {
  56. setTimeout(() => GM_addStyle(ci_cd_default), 25);
  57. }
  58.  
  59. // src/monkey/gitlab-enhance/dashboard/merge_requests.ts
  60. var hyperlinkResource = () => {
  61. const mergeRequests = $(".merge-request:not([hyperlinked])");
  62. mergeRequests.each((_, mergeRequestEle) => {
  63. const href = $(".js-prefetch-document", mergeRequestEle).attr("href");
  64. if (!href) return;
  65. const resourceUrl = href.replace(/\/-\/merge_requests\/\d+$/, "");
  66. const rawRefEle = $(".issuable-reference", mergeRequestEle);
  67. const rawRefName = rawRefEle.text();
  68. const [resourceName, number] = rawRefName.split("!");
  69. rawRefEle.html(`<a href="${resourceUrl}">${resourceName}</a>!${number}`);
  70. $(mergeRequestEle).attr("hyperlinked", "");
  71. });
  72. };
  73. if (location.pathname.endsWith("/dashboard/merge_requests")) {
  74. $(".issuable-list").on("mouseenter", hyperlinkResource);
  75. setTimeout(hyperlinkResource, 1e3);
  76. }
  77.  
  78. // src/monkey/gitlab-enhance/utils.ts
  79. var getButtonElement = (text) => {
  80. const classnames = "gl-font-sm! gl-ml-3 gl-button btn btn-default btn-sm";
  81. return $(`<a class="${classnames}">${text}</a>`);
  82. };
  83.  
  84. // src/monkey/gitlab-enhance/merge_requests/templateUtils.ts
  85. var INDENT_SPACE_LENGTH = 2;
  86. var templateContent = "";
  87. var indent = (level) => Array(level * INDENT_SPACE_LENGTH).fill(" ").join("");
  88. var enter = () => "\n";
  89. var emptyLine = () => templateContent += enter();
  90. var contentUtils = {
  91. listItem: (text = "", { level = 1 } = {}) => {
  92. templateContent += `${indent(level - 1)}- ${text}${enter()}`;
  93. return {
  94. ...contentUtils,
  95. end: emptyLine
  96. };
  97. },
  98. taskItem: (text = "", { selected = false, level = 1 } = {}) => {
  99. return contentUtils.listItem(`[${selected ? "x" : " "}] ${text}`, { level });
  100. }
  101. };
  102. var header = (level) => (text) => {
  103. templateContent += `${Array(level).fill("#").join("")} ${text}${enter()}${enter()}`;
  104. return contentUtils;
  105. };
  106. var h2 = header(2);
  107. var h3 = header(3);
  108. var h4 = header(4);
  109. var listItem = contentUtils.listItem;
  110. var taskItem = contentUtils.taskItem;
  111. var initTemplate = () => templateContent = "";
  112. var getTemplate = () => templateContent;
  113.  
  114. // src/monkey/gitlab-enhance/merge_requests/template.css
  115. var template_default = ".gl-display-flex:has([for=merge_request_description]){align-items:baseline}\n";
  116.  
  117. // src/monkey/gitlab-enhance/merge_requests/template.ts
  118. var getBranchType = () => {
  119. const fromBranchName = $(".align-self-center code:not([data-branch-name])").text();
  120. const prefixBranchName = fromBranchName.split("/")[0].toLowerCase();
  121. switch (prefixBranchName) {
  122. case "feature":
  123. case "feat":
  124. return 0 /* FEATURE */;
  125. case "fix":
  126. case "bugfix":
  127. return 1 /* BUGFIX */;
  128. case "hotfix":
  129. return 2 /* HOTFIX */;
  130. case "devops":
  131. case "chore":
  132. case "test":
  133. case "doc":
  134. case "docs":
  135. return 3 /* TASKS */;
  136. default:
  137. return 4 /* OTHERS */;
  138. }
  139. };
  140. var generateTemplate = () => {
  141. const branchType = getBranchType();
  142. initTemplate();
  143. h2("Type").taskItem("Feature (Story/Refactor)", { selected: branchType === 0 /* FEATURE */ }).taskItem(`Bugfix`, { selected: branchType === 1 /* BUGFIX */ }).taskItem(`Hotfix (Production Issues)`, { selected: branchType === 2 /* HOTFIX */ }).taskItem(`Tasks (DevOps / Unit Test / Document Update)`, { selected: branchType === 3 /* TASKS */ }).taskItem(`Others`, { selected: branchType === 4 /* OTHERS */ }).end();
  144. h2("Description");
  145. if (branchType !== 0 /* FEATURE */) {
  146. h3("Why (Why does this happen?)").listItem().end();
  147. h3("How (How can we avoid or solve it?)").listItem().end();
  148. }
  149. h3("What (What did you do this time?)").listItem().end();
  150. if (branchType !== 3 /* TASKS */) {
  151. h3("Results (Screenshot, etc)");
  152. h4("Before modification");
  153. h4("After modification");
  154. h2("Affected Zone").listItem("Affected Module(s):").listItem("Affected URL(s):").end();
  155. }
  156. h2("External resources (Mention, Resolves, or Closes)");
  157. return getTemplate();
  158. };
  159. var appendTemplateButton = () => {
  160. GM_addStyle(template_default);
  161. const text = "Copy Template";
  162. const btnElement = getButtonElement(text);
  163. const templateContent2 = generateTemplate();
  164. const hint = $('<span class="gl-font-sm! gl-ml-3 gl-text-secondary"></span>');
  165. let setTimeoutId;
  166. btnElement.on("click", async () => {
  167. hint.remove();
  168. setTimeoutId && clearTimeout(setTimeoutId);
  169. hint.text("Copying...");
  170. btnElement.after(hint);
  171. await GM_setClipboard(templateContent2, "text", () => {
  172. hint.text("Copied!");
  173. setTimeoutId = setTimeout(() => hint.remove(), 3e3);
  174. });
  175. });
  176. $(".gl-display-flex:has([for=merge_request_description])").append(btnElement);
  177. };
  178.  
  179. // src/monkey/gitlab-enhance/merge_requests/new.ts
  180. var appendAsTitleButton = () => {
  181. $(".commit-content").each((_, el) => {
  182. const titleElements = $(".item-title", el);
  183. const title = titleElements.text();
  184. const btnElement = getButtonElement("As title");
  185. btnElement.on("click", () => {
  186. $("input[data-testid=issuable-form-title-field]").val(title);
  187. $("input[data-testid=issuable-form-title-field]").focus();
  188. });
  189. $(".committer", el).before(btnElement);
  190. });
  191. };
  192. if (location.pathname.endsWith("/-/merge_requests/new")) {
  193. setTimeout(() => {
  194. appendTemplateButton();
  195. appendAsTitleButton();
  196. }, 1e3);
  197. }
  198.  
  199. // src/monkey/gitlab-enhance/merge_requests/edit.ts
  200. if (/\/-\/merge_requests\/\d+\/edit$/.test(location.pathname)) {
  201. setTimeout(() => {
  202. appendTemplateButton();
  203. }, 1e3);
  204. }
  205. })();