Skip to content

Instantly share code, notes, and snippets.

@tdbe
Last active August 29, 2023 20:27
Show Gist options
  • Save tdbe/106a612e5a06d4b53e2523be9f8fac90 to your computer and use it in GitHub Desktop.
Save tdbe/106a612e5a06d4b53e2523be9f8fac90 to your computer and use it in GitHub Desktop.
Quick Greasemonkey/Tampermonkey script for GitHub PRs: disable merge button until certain checks are met. Looks at branch naming pattern, tags, automatic tests. Highlights what it found.
// ==UserScript==
// @name GitHub disable merge button until certain checks are met
// @description Looks at branch naming pattern, tags, automatic tests. Highlights what it found.
// @author github/tdbe
// @version 1
// @match *github.com/*/pull/*
// @run-at document-idle
// @connect github.com
// @connect githubusercontent.com
// @grant GM_notification
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addStyle
// ==/UserScript==
(function() {
'use strict';
function doChecks(mergeButtons){
for (let i = 0; i < mergeButtons.length; i++) {
mergeButtons[i].style.backgroundColor = "yellow";// set all merge buttons to magenta so we know the greasemonkey is active on this page
mergeButtons[i].disabled = true;// disable just for safety
}
//checks:
var passed = true;
// check title:
// <bdi class="js-issue-title markdown-title">
var elems = document.getElementsByClassName("js-issue-title");
for (var i=0; i<elems.length; ++i) {
if(elems[i].classList.contains("markdown-title")){
// check if title starts with correct prefix:
if(!elems[i].innerHTML.includes("[")){
passed = false;
elems[i].style.outline = "red dashed 3px";
}
else{
elems[i].style.outline = "#d35ed3 dashed 3px";
}
}
}
// check tags:
// <a id="label-209d8e" href="..../QA%20OK" data-name="QA OK"
// <a id="label-f89fb1" href="..../Review%20OK" data-name="Review OK"
//elems = document.getElementsByTagName("a");
elems = document.getElementsByClassName("IssueLabel");
for (i=0; i<elems.length; ++i) {
// check for blocking tags:
var attr = elems[i].getAttribute("data-name");
if(attr == "Needs QA" || attr == "QA WIP" || attr == "Needs Review"){
passed = false;
elems[i].style.fontSize = "2em";
elems[i].style.outline = "red dashed 3px";
}
}
// check if status says "All checks have pased"
elems = document.getElementsByClassName("status-heading");
var found = false;
for (i=0; i<elems.length; ++i) {
if(elems[i].innerHTML.startsWith("All checks have passed")){
found = true;
}
}
if(found == false){
passed = false;
for (i=0; i<elems.length; ++i) {
elems[i].style.fontSize = "3em";
elems[i].style.outline = "red dashed 3px";
}
}
else{
for (i=0; i<elems.length; ++i) {
elems[i].style.fontSize = "3em";
elems[i].style.outline = "#d35ed3 dashed 3px";
}
}
if(!passed){
for (let i = 0; i < mergeButtons.length; i++) {
mergeButtons[i].style.backgroundColor = "#b53131";// set all merge buttons to red so we know the greasemonkey is active on this page
mergeButtons[i].style.outline = "red dashed 3px";
mergeButtons[i].disabled = true;
}
}
else{// PASSED, or, error conditions not found, so set it to magenta.
for (let i = 0; i < mergeButtons.length; i++) {
mergeButtons[i].style.backgroundColor = "#d35ed3";// set all merge buttons to magenta so we know the greasemonkey is active on this page
mergeButtons[i].style.outline = "#d35ed3 dashed 3px";
mergeButtons[i].disabled = false;
}
}
}
function run(withRetries){
var states = document.getElementsByClassName("State--open");
if(states.length == 0){
return;
}
// also mark the "Wants to merge" line to check source branch and dest branch
var srcDest = document.getElementsByClassName("commit-ref-dropdown")[0].parentNode;
srcDest.style.outline = "yellow dashed 8px";
var mergeButtons = document.getElementsByClassName("merge-box-button");
if(withRetries){
var i = 0;
var intr = setInterval(function() {
mergeButtons = document.getElementsByClassName("merge-box-button");
if (++i == 10 || mergeButtons.length!= 0) clearInterval(intr);
//if(mergeButtons.length == 0){
// alert("Could not find mergeButtons: "+mergeButtons.length);
//}
doChecks(mergeButtons);
}, 1000)
}
else{
doChecks(mergeButtons);
}
}
run(true);
let prevScrollPos = 0;
let scrollPos = 0;
let delta = 100;
let ticking = false;
function doSomething(scrollPos) {
if(scrollPos-prevScrollPos>delta){
run(false);
prevScrollPos = scrollPos;
}
}
document.addEventListener("scroll", (event) => {
scrollPos = window.scrollY;
if (!ticking) {
window.requestAnimationFrame(() => {
doSomething(scrollPos);
ticking = false;
});
ticking = true;
}
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment