Github Actions: Dispatch Workflow with Active Branch

Userscript allowing you to select active branches when dispatching a workflow

  1. // ==UserScript==
  2. // @name Github Actions: Dispatch Workflow with Active Branch
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.1
  5. // @description Userscript allowing you to select active branches when dispatching a workflow
  6. // @author You
  7. // @match https://github.com/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=github.com
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function () {
  13. 'use strict';
  14. console.log('github-actions-workflow-dispatch-active-branch');
  15.  
  16. const getActiveBranch = async () => (await fetch(`https://github.com/${window.location.pathname.match(/\/([^\/]*\/[^\/]*)/)[1]}/branches/active`, {
  17. "headers": {
  18. "accept": "application/json",
  19. },
  20. "method": "GET",
  21. "mode": "cors",
  22. "credentials": "include"
  23. })).json();
  24.  
  25. const getClientEnv = function () {
  26. if ("undefined" != typeof document) {
  27. let e = document.getElementById("client-env");
  28. if (e)
  29. try {
  30. return JSON.parse(e.textContent || "")
  31. } catch (e) {
  32. console.error("Error parsing client-env", e)
  33. }
  34. }
  35. }
  36. const userLogin = getClientEnv().login;
  37. const watchElement = (selector, onShow) => {
  38. let el;
  39. let onHide;
  40. const work = () => {
  41. const targetEl = document.querySelector(selector);
  42. if (!el && targetEl) {
  43. el = targetEl;
  44. onHide = onShow?.(el);
  45. } else if (el && !targetEl) {
  46. el = undefined;
  47. onHide?.();
  48. }
  49. }
  50. const it = setInterval(work, 100);
  51. return () => clearInterval(it);
  52. }
  53.  
  54.  
  55. watchElement('details.details-overlay[open] .workflow-dispatch', (wf) => {
  56. let cleanup;
  57. getActiveBranch().then(res => {
  58. let controls;
  59. cleanup = watchElement('details.details-overlay[open] .branch-selection .details-overlay[open] #ref-list-branches', (el) => {
  60. controls = document.createElement('div');
  61. controls.id = "github-actions-use-active-branch-branches";
  62. controls.innerHTML = `
  63. ${res.payload.branches.map(b => `
  64. <div class="SelectMenu-item" style="background-color:${b.author.login == userLogin ? '#8882' : 'inherit'}" data-name="${b.name}"><b class="css-truncate css-truncate-overflow" style="width:64px;">${b.author.login}</b><span class="flex-1 css-truncate css-truncate-overflow ">${b.name}</span></div>
  65. `).join('')}
  66. `;
  67.  
  68. el.append(controls);
  69.  
  70. const filterInput = el.closest('.workflow-dispatch').querySelector('.SelectMenu-filter input');
  71. const handler = () => {
  72. if (filterInput.value.length > 0) {
  73. controls.style.display = 'none';
  74. el.querySelector('.SelectMenu-list').style.display = 'block';
  75. } else {
  76. controls.style.display = 'block';
  77. el.querySelector('.SelectMenu-list').style.display = 'none';
  78. }
  79. }
  80. filterInput.addEventListener('input', handler);
  81. handler();
  82.  
  83. controls.querySelectorAll('.SelectMenu-item').forEach(item => {
  84. item.addEventListener('click', () => {
  85. filterInput.value = item.dataset.name;
  86. filterInput.dispatchEvent(new Event('input', { bubbles: true }));
  87. el.querySelector('.SelectMenu-item[value="' + item.dataset.name + '"]').click();
  88. });
  89. });
  90.  
  91. return () => {
  92. if (controls) {
  93. controls.remove();
  94. }
  95. }
  96. });
  97. }).catch(console.error);
  98. return () => {
  99. cleanup?.();
  100. }
  101. });
  102.  
  103. // Your code here...
  104. })();