155 lines
5.8 KiB
JavaScript
155 lines
5.8 KiB
JavaScript
// ==UserScript==
|
|
// @name nixpkgs-review-gha
|
|
// @match https://github.com/*
|
|
// @run-at document-idle
|
|
// ==/UserScript==
|
|
|
|
const repo = "Defelo/nixpkgs-review-gha";
|
|
|
|
const reviewDefaults = ({ title, commits, labels, author, authoredByMe, hasLinuxRebuilds, hasDarwinRebuilds }) => {
|
|
const darwinSandbox = "relaxed";
|
|
|
|
const hasRebuilds = hasLinuxRebuilds || hasDarwinRebuilds;
|
|
|
|
return {
|
|
// "branch": "main",
|
|
"x86_64-linux": !hasRebuilds || hasLinuxRebuilds,
|
|
"aarch64-linux": !hasRebuilds || hasLinuxRebuilds,
|
|
"x86_64-darwin": !hasRebuilds || hasDarwinRebuilds ? `yes_sandbox_${darwinSandbox}` : "no",
|
|
"aarch64-darwin": !hasRebuilds || hasDarwinRebuilds ? `yes_sandbox_${darwinSandbox}` : "no",
|
|
// "extra-args": "",
|
|
// "push-to-cache": true,
|
|
// "upterm": false,
|
|
// "post-result": true,
|
|
// "on-success": "nothing",
|
|
};
|
|
};
|
|
|
|
const prTrackers = [
|
|
{ name: "nixpk.gs", toUrl: pr => `https://nixpk.gs/pr-tracker.html?pr=${pr}` },
|
|
{ name: "ocfox.me", toUrl: pr => `https://nixpkgs-tracker.ocfox.me/?pr=${pr}` },
|
|
];
|
|
|
|
const sleep = duration => new Promise(resolve => setTimeout(resolve, duration));
|
|
const query = async (doc, sel) => {
|
|
await sleep(0);
|
|
while (true) {
|
|
const elem = doc.querySelector(sel);
|
|
if (elem !== null) return elem;
|
|
await sleep(100);
|
|
}
|
|
};
|
|
|
|
const getPrDetails = pr => {
|
|
const title = document.querySelector("bdi.js-issue-title.markdown-title").innerText;
|
|
const commits = [...document.querySelectorAll(".TimelineItem-body a.markdown-title[href*='/commits/']")].flatMap(
|
|
({ title, href }) => {
|
|
const match = /\/NixOS\/nixpkgs\/pull\/(\d+)\/commits\/([0-9a-f]+)$/i.exec(href);
|
|
return match === null || match[1] !== pr
|
|
? []
|
|
: [
|
|
{
|
|
commit_id: match[2],
|
|
subject: title.split("\n")[0],
|
|
description: title,
|
|
},
|
|
];
|
|
},
|
|
);
|
|
const labels = [...document.querySelectorAll("div.js-issue-labels > a")].map(x => x.innerText);
|
|
const author = document.querySelector(".js-discussion > :first-child a.author").href.split("/").at(-1);
|
|
const self = document.querySelector("div.AppHeader-user button[data-login]").getAttribute("data-login");
|
|
const authoredByMe = author === self;
|
|
const hasLinuxRebuilds = !labels.some(l => /rebuild-linux: 0$/.test(l));
|
|
const hasDarwinRebuilds = !labels.some(l => /rebuild-darwin: 0$/.test(l));
|
|
const state = document.querySelector("span.State").innerText.trim().toUpperCase();
|
|
|
|
return { title, commits, labels, author, authoredByMe, hasLinuxRebuilds, hasDarwinRebuilds, state };
|
|
};
|
|
|
|
const setupActionsPage = async () => {
|
|
const match = /^https:\/\/github.com\/([^/]+\/[^/]+)\/actions\/workflows\/review.yml#dispatch:(.*)$/.exec(
|
|
location.href,
|
|
);
|
|
if (match === null || match[1] !== repo) return;
|
|
|
|
const inputs = new URLSearchParams(match[2]);
|
|
|
|
(await query(document, "details > summary.btn")).click();
|
|
await query(document, "details .workflow-dispatch");
|
|
|
|
const setBranch = async branch => {
|
|
(await query(document, "details .workflow-dispatch")).classList.add("old-branch");
|
|
document.querySelector("details .workflow-dispatch .branch-selection > details > summary").click();
|
|
(await query(document, `ref-selector[type=branch] button[value="${branch}"]`)).click();
|
|
while ((await query(document, "details .workflow-dispatch")).classList.contains("old-branch")) await sleep(100);
|
|
};
|
|
|
|
const setInput = (name, value) => {
|
|
const selector = `details .workflow-dispatch [name='inputs[${name}]']`;
|
|
const input = document.querySelector(`${selector}:not([type=hidden])`);
|
|
|
|
if (!input) {
|
|
alert(`workflow_dispatch input '${name}' does not exist`);
|
|
return;
|
|
}
|
|
|
|
if (input.type === "checkbox") {
|
|
if (!["true", "false"].includes(value)) {
|
|
alert(`workflow_dispatch input '${name}' expects a boolean ('true' or 'false) but is set to '${value}'`);
|
|
return;
|
|
}
|
|
input.checked = value === "true";
|
|
} else {
|
|
input.value = value;
|
|
}
|
|
};
|
|
|
|
const branch = inputs.get("branch");
|
|
if (branch) await setBranch(branch);
|
|
|
|
[...inputs].filter(([name]) => name !== "branch").forEach(([name, value]) => setInput(name, value));
|
|
|
|
document.querySelector("details .workflow-dispatch button[type=submit]").focus();
|
|
};
|
|
|
|
const setupPrPage = async () => {
|
|
const match = /^https:\/\/github.com\/NixOS\/nixpkgs\/pull\/(\d+)([?#].*)?$/i.exec(location.href);
|
|
if (match === null) return;
|
|
|
|
const pr = match[1];
|
|
const actions = await query(document, ".gh-header-show .gh-header-actions");
|
|
|
|
if (actions.querySelector(".run-nixpkgs-review") === null) {
|
|
const btn = document.createElement("button");
|
|
btn.classList.add("Button", "Button--secondary", "Button--small", "run-nixpkgs-review");
|
|
btn.innerText = "Run nixpkgs-review";
|
|
actions.prepend(btn);
|
|
btn.onclick = () => {
|
|
const params = new URLSearchParams({ ...reviewDefaults(getPrDetails(pr)), pr });
|
|
window.open(`https://github.com/${repo}/actions/workflows/review.yml#dispatch:${params}`);
|
|
};
|
|
}
|
|
|
|
const { hasLinuxRebuilds, hasDarwinRebuilds, state } = getPrDetails(pr);
|
|
if ((!hasLinuxRebuilds && !hasDarwinRebuilds) || state == "MERGED") {
|
|
actions.querySelector(".run-nixpkgs-review").setAttribute("aria-disabled", true);
|
|
}
|
|
|
|
if (actions.querySelector(".goto-pr-tracker") === null) {
|
|
for (const { name, toUrl } of prTrackers) {
|
|
const btn = document.createElement("button");
|
|
btn.classList.add("Button", "Button--secondary", "Button--small", "goto-pr-tracker");
|
|
btn.innerText = prTrackers.length === 1 ? "PR Tracker" : `PR Tracker (${name})`;
|
|
actions.prepend(btn);
|
|
btn.onclick = () => {
|
|
window.open(toUrl(pr));
|
|
};
|
|
}
|
|
}
|
|
};
|
|
|
|
new MutationObserver(setupPrPage).observe(document, { subtree: true, childList: true });
|
|
|
|
setupActionsPage();
|
|
setupPrPage();
|