您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Fetch package diff from pipeline heat-map and populate mcm.
// ==UserScript== // @name Automated MCM Population // @namespace http://tampermonkey.net/ // @version 1.0 // @description Fetch package diff from pipeline heat-map and populate mcm. // @author adityamz@ // @match https://code.amazon.com/version-sets/*/revisions/* // @match https://pipelines.amazon.com/pipelines/*/heat_map // @match https://pipelines.amazon.com/pipelines/* // @match https://mcm.amazon.com/cms/new* // @match https://mcm.amazon.com/cms/*/edit* // @require https://code.jquery.com/jquery-3.4.1.min.js // @grant GM.xmlHttpRequest // ==/UserScript== (function() { //========== This part is run in code.amazon.com ========== function addNewPackages(packageName, packageUrl) { unsafeWindow.packageDetails.push({ "packageName": packageName, "type": "Added", "diffUrl": packageUrl, "owners": [], "commits": [] }); } function addRemovedPackages(packagename, packageUrl) { unsafeWindow.packageDetails.push({ "packageName": packagename, "type": "Removed", "diffUrl": packageUrl, "owners": [], "commits": [] }); } function addBranchChangePackages(packageName, branchName, oldBranchName, startHash, endHash, diffUrl) { const asciURL = "https://pipelines.amazon.com/diff/pkg_changes?package_name=" + packageName + "&branch_name=" + branchName + "&old_commit_id=" + startHash + "&new_commit_id=" + endHash; GM.xmlHttpRequest({ method: "GET", url: asciURL, onload: function() { if (this.readyState === 4 && this.status === 200) { const changes = []; let resElem = document.createElement("div"); resElem.innerHTML = this.responseText; const commits = resElem.querySelectorAll('li'); commits.forEach(function(commit){ let author = commit.querySelector('.author>a').textContent; let description = commit.querySelector('.description').textContent; let repoChangeId = commit.querySelector('.change-link>a').href.split('/').reverse()[0]; changes.push({author,description,repoChangeId}); }); let ownerList = []; const commitList = []; changes.forEach(function(change) { const author = change.author; const commit = author + '@: [' + change.description.split('\n')[0] + '](https://code.amazon.com/packages/' + packageName + '/commits/' + change.repoChangeId + ')'; ownerList.push(author); commitList.push(commit); }); ownerList = ownerList.filter((v, i, a) => a.indexOf(v) === i); const changeType = "Branch Changed (" + oldBranchName + " => " + branchName + ")"; unsafeWindow.packageDetails.push({ "packageName": packageName, "type": changeType, "diffUrl": diffUrl, "owners": ownerList, "commits": commitList }); } } }); } function addModifiedPackages(packageName, branchName, startHash, endHash, diffUrl) { const asciURL = "https://pipelines.amazon.com/diff/pkg_changes?package_name=" + packageName + "&branch_name=" + branchName + "&old_commit_id=" + startHash + "&new_commit_id=" + endHash; GM.xmlHttpRequest({ method: "GET", url: asciURL, onload: function(response) { //console.log(this.responseText); if(this.status === 400){ console.log(this.responseJSON); $("#toastDiff").text("Diff API Throttled."); $("#toastDiff").fadeIn("slow", function() { setTimeout(function() { $("#toastDiff").fadeOut("slow") }, 1500) }); } if (this.readyState === 4 && this.status === 200) { const changes = []; let resElem = document.createElement("div"); resElem.innerHTML = this.responseText; const commits = resElem.querySelectorAll('li'); commits.forEach(function(commit){ let author = commit.querySelector('.author>a').textContent; let description = commit.querySelector('.description').textContent; let repoChangeId = commit.querySelector('.change-link>a').href.split('/').reverse()[0]; changes.push({author,description,repoChangeId}); }); console.log("fetched details for :"+ packageName); let ownerList = []; const commitList = []; changes.forEach(function(change) { const author = change.author; const commit = author + '@: [' + change.description.split('\n')[0] + '](https://code.amazon.com/packages/' + packageName + '/commits/' + change.repoChangeId + ')'; ownerList.push(author); commitList.push(commit); }); ownerList = ownerList.filter((v, i, a) => a.indexOf(v) === i); unsafeWindow.packageDetails.push({ "packageName": packageName, "type": "Source Changed", "diffUrl": diffUrl, "owners": ownerList, "commits": commitList }); } } }); } function getPackageDiffOwners() { let allOwners = []; let diffOwnersString = ""; let isPackageChecked = false; unsafeWindow.packageDetails.forEach(pkgDetail => { isPackageChecked = $("#" + pkgDetail.packageName + "Check").is(":checked"); if (isPackageChecked) { diffOwnersString = diffOwnersString + "* [" + pkgDetail.packageName + "](https://code.amazon.com" + pkgDetail.diffUrl + ") [" + pkgDetail.type + "]\n"; pkgDetail.owners.forEach(owner => { diffOwnersString = diffOwnersString + " * " + owner + "\n"; allOwners.push(owner); }) } }) if (allOwners.length > 0) { allOwners = allOwners.filter((v, i, a) => a.indexOf(v) === i); diffOwnersString = diffOwnersString + "* All Owners\n"; allOwners.forEach(owner => { diffOwnersString = diffOwnersString + " * " + owner + "\n"; }) } unsafeWindow.commitOwners=JSON.stringify(allOwners) return copyToClipboard(diffOwnersString); } function getPackageDiffCommits() { let diffCommitsString = ""; let isPackageChecked = false; unsafeWindow.packageDetails.forEach(pkgDetail => { isPackageChecked = $("#" + pkgDetail.packageName + "Check").is(":checked"); if (isPackageChecked) { diffCommitsString = diffCommitsString + "* [" + pkgDetail.packageName + "](https://code.amazon.com" + pkgDetail.diffUrl + ") [" + pkgDetail.type + "]\n"; pkgDetail.commits.forEach(commit => { diffCommitsString = diffCommitsString + " * " + commit + "\n"; }) } }) unsafeWindow.commitChanges = diffCommitsString; let pipelineDescription = getPipelineDescription(unsafeWindow.pipelineDetails, unsafeWindow.vsDetails) let commitDetailsDescription = getCommitDetailsDescription(diffCommitsString) return copyToClipboard(pipelineDescription + '\n'+commitDetailsDescription); } function copyToClipboard(text) { if (text !== "") { $("#toastDiff").text("Copied to clipboard!"); $("#toastDiff").fadeIn("slow", function() { setTimeout(function() { $("#toastDiff").fadeOut("slow") }, 1500) }); const textarea = document.createElement("textarea"); textarea.textContent = text; textarea.style.position = "fixed"; document.body.appendChild(textarea); textarea.select(); try { document.execCommand("copy"); } catch (ex) { console.warn("Copy to clipboard failed.", ex); } finally { document.body.removeChild(textarea); return true; } } else { $("#toastDiff").text("No diff found! Try again."); $("#toastDiff").fadeIn("slow", function() { setTimeout(function() { $("#toastDiff").fadeOut("slow") }, 1500) }); return false; } } unsafeWindow.packageDetails = []; unsafeWindow.pipelineDetails = {pipelineName:'',packages:[]}; $('#packages tr.added-pkg').each(function(i, el) { const $tds = $(this).find("td"); const packageUrl = $tds.eq(0).find("a").attr("href"); const packageName = $tds.eq(0).text(); const checkbox = document.createElement("input"); checkbox.setAttribute("type", "checkbox"); checkbox.setAttribute("id", (packageName + "Check")); checkbox.setAttribute("style", "margin-right:5px"); $tds.eq(0).prepend(checkbox); addNewPackages(packageName, packageUrl); }) $('#packages tr.error').each(function(i, el) { const $tds = $(this).find('td'); const packageUrl = $tds.eq(0).find('a').attr('href'); const packageName = $tds.eq(0).text(); const checkbox = document.createElement("input"); checkbox.setAttribute("type", "checkbox"); checkbox.setAttribute("id", (packageName + "Check")); checkbox.setAttribute("style", "margin-right:5px"); $tds.eq(0).prepend(checkbox); addRemovedPackages(packageName, packageUrl); }) $('#packages tr.branch-change').each(function(i, el) { const $tds = $(this).find('td'); const diffUrl = $tds.eq(5).find('a').attr('href').trim(); const startHash = $tds.eq(1).find('a').attr('href').split('/')[4].trim(); const endHash = $tds.eq(3).find('a').attr('href').split('/')[4].trim(); const packageName = $tds.eq(0).text().trim(); const oldBranchName = $tds.eq(6).text().split("->")[0].trim(); const branchName = $tds.eq(6).text().split("->")[1].trim(); const checkbox = document.createElement("input"); checkbox.setAttribute("type", "checkbox"); checkbox.setAttribute("id", (packageName + "Check")); checkbox.setAttribute("style", "margin-right:5px"); $tds.eq(0).prepend(checkbox); $(checkbox).change(function(){ if(this.checked){ addBranchChangePackages(packageName, branchName, oldBranchName, startHash, endHash, diffUrl); } }) }) $('#packages tr.source-change').each(function(i, el) { const $tds = $(this).find('td'); const diffUrl = $tds.eq(5).find('a').attr('href').trim(); const startHash = $tds.eq(1).find('a').attr('href').split('/')[4].trim(); const endHash = $tds.eq(3).find('a').attr('href').split('/')[4].trim(); const packageName = $tds.eq(0).text().trim(); const branchName = $tds.eq(6).text().trim(); const checkbox = document.createElement("input"); checkbox.setAttribute("type", "checkbox"); checkbox.setAttribute("id", (packageName + "Check")); checkbox.setAttribute("style", "margin-right:5px"); $tds.eq(0).prepend(checkbox); $(checkbox).change(function(){ if(this.checked){ addModifiedPackages(packageName, branchName, startHash, endHash, diffUrl); } }) }) function createMCMWithTemplate(mcm_id){ let diffcommit = getPackageDiffCommits(); let diffpackage = getPackageDiffOwners(); window.location.href = "https://mcm.amazon.com/cms/new?from_template="+mcm_id+"&commitChanges="+encodeURIComponent(unsafeWindow.commitChanges)+"&commitOwners="+unsafeWindow.commitOwners+ "&vsDetails=" + encodeURIComponent(JSON.stringify(unsafeWindow.vsDetails)) + "&pipelineDetails=" + encodeURIComponent(JSON.stringify(unsafeWindow.pipelineDetails)); } function updateMCMWithExistingDraft(mcm_id){ let diffcommit = getPackageDiffCommits(); let diffpackage = getPackageDiffOwners(); window.location.href = "https://mcm.amazon.com/cms/"+mcm_id+"/edit?commitChanges="+encodeURIComponent(unsafeWindow.commitChanges)+"&commitOwners="+unsafeWindow.commitOwners+ "&vsDetails=" + encodeURIComponent(JSON.stringify(unsafeWindow.vsDetails)) + "&pipelineDetails=" + encodeURIComponent(JSON.stringify(unsafeWindow.pipelineDetails)); } const vsRangeDiff = document.getElementById('vs_range_diff'); if (vsRangeDiff) { const mainDiv = document.createElement('div'); mainDiv.innerHTML = '<div><div style="overflow: visible;"><div class="text-right top-buffer-small" style="background: aliceblue;padding: 8px;filter: drop-shadow(2px 2px 2px black);"><div> <button type="button" class="btn btn-default" id="diffCommits" style="width: 48%;float: left;">Copy Diff Commits</button> <button type="button" class="btn btn-default" id="diffOwners" style=" width: 48%;">Copy Diff Owners</button><input id="mcm_id" type="input" style="width: 100%;margin-top: 10px" placeholder="MCM template ID eg: MCM-78603254 or leave empty to use default template"/><button type="button" disabled class="btn btn-default" id="createMCM" style="width: 100%;margin-top: 10px">Loading ...</button></div></div><div id="toastDiff" style="width: 180px; background-color: rgba(0, 0, 0, 0.8); height: 30px; position: absolute; margin: 20px 20px 20px 6%; border-radius: 50px; color: rgba(255, 255, 255, 0.8);text-align: center;padding-top: 6px; display: none;"></div></div></div>'; vsRangeDiff.appendChild(mainDiv); unsafeWindow.pipelineDetails = JSON.parse(new URLSearchParams(window.location.search).get('pipelineDetails')); unsafeWindow.vsDetails = JSON.parse(new URLSearchParams(window.location.search).get('vsDetails')); //mark all the packages autobuilt in the pipeline as checked for diff creation window.addEventListener("load", ()=>{ unsafeWindow.pipelineDetails.packages.forEach((pkg)=>{ let pkgElements = document.querySelectorAll('#'+pkg+'Check'); pkgElements.forEach((pkgElem)=>{ $(pkgElem).click(); }) }); $('#createMCM').text('Create MCM'); $('#createMCM').prop('disabled',false); }); // adding click listener for diff commits $( "#diffCommits" ).click(function() { getPackageDiffCommits(); }); $( "#diffOwners" ).click(function() { getPackageDiffOwners(); }); $( "#createMCM" ).click(function() { const mcm_id = $('#mcm_id').val(); if(mcm_id){ updateMCMWithExistingDraft(mcm_id); } else { createMCMWithTemplate('7e9a85a5-bd0a-4560-8379-00a73ef82f1b'); } }); } //========== This part is run in pipelines.amazon.com ========== function getVSDiffLink(href) { const splittedHref = href.split("&"); const currVS = splittedHref[0].split("=")[1]; const currRev = splittedHref[1].split("=")[1]; const prevVS = splittedHref[3].split("=")[1]; const prevRev = splittedHref[4].split("=")[1]; const vsDetails = {}; vsDetails.cur = currRev; vsDetails.prev = prevRev; vsDetails.name = prevVS; return "https://code.amazon.com/version-sets/" + currVS + "/revisions/" + currRev + "?previous=" + prevRev + "&previous_vs=" + prevVS + "&pipelineDetails=" + sessionStorage.getItem('pipelineDetails') + "&vsDetails=" + JSON.stringify(vsDetails); } const pipelineName = document.querySelector('.pipeline-name-header'); if(pipelineName){ unsafeWindow.pipelineDetails.pipelineName = pipelineName.innerText; } const packages = document.querySelector('#stage-name-Packages > div > h3 > span'); if(packages){ document.querySelectorAll('.autobuild .target-details > div.name').forEach((pkg)=>{ let pkg_name = pkg.getAttribute('data-targetname').split('/')[0] unsafeWindow.pipelineDetails.packages.push(pkg_name) }) sessionStorage.setItem("pipelineDetails",JSON.stringify(unsafeWindow.pipelineDetails)) } const diffLinkElements = document.getElementsByClassName("diff-link"); if (diffLinkElements && !packages) { for(let i=0; i < diffLinkElements.length; i++) { const element = diffLinkElements[i]; const href = element.getElementsByTagName("a")[0].getAttribute("href"); const vsDiffLink = getVSDiffLink(href); const aTag = document.createElement("a"); aTag.setAttribute("href", vsDiffLink); aTag.innerText = "VS"; element.appendChild(aTag); } } //this code part will run in mcm const insideMcm = $('.mcm-logo').length function getPipelineDescription(pipelineDetails, vsDetails){ let pipelineDescription = "" pipelineDescription+= `## ${pipelineDetails.pipelineName}\n` pipelineDescription+= `- Pipeline Link - https://pipelines.amazon.com/pipelines/${pipelineDetails.pipelineName}\n` pipelineDescription+= `- Pipeline Diff - [Link](https://pipelines.amazon.com/diff?new_name=${vsDetails.name}&new_revision=${vsDetails.cur}&new_type=VS&old_name=${vsDetails.name}&old_revision=${vsDetails.prev}&old_type=VS)\n` pipelineDescription+= `- Current VFI - ${vsDetails.name}@B${vsDetails.prev}\n` pipelineDescription+= `- VFI to be Deployed - ${vsDetails.name}@B${vsDetails.cur}\n` return pipelineDescription; } function getCommitDetailsDescription(commitDetails){ let commitChanges = ""; if(commitDetails){ commitChanges+="#### Changes to be promoted in prod:\n" commitChanges+=commitDetails; } return commitChanges; } function updateDescription(commitDetails, vsDetails, pipelineDetails){ const curr = $('#description').val() const rollbackId = "## Rollback Procedure"; const pipelineDescription=getPipelineDescription(pipelineDetails, vsDetails); console.log(pipelineDescription); const pos = curr.indexOf('#### What is the purpose of this activity or change?')<0?curr.indexOf('Activity Details'):curr.indexOf('#### What is the purpose of this activity or change?'); let commitChanges = getCommitDetailsDescription(commitDetails); let data = curr.substr(0, pos) + pipelineDescription+'\n'+ commitChanges+'\n' + curr.substr(pos); $('#description').val(data) } function updateMCMTitle(pipelineDetails){ const title = $('#title') title.val(title.val()+pipelineDetails.pipelineName) } function addcommitOwners(commitOwners) { commitOwners.forEach((owner)=>{ $('#add_approver_login').val(owner) $('#add_approver').click() }) } if(insideMcm>0){ const data = new URLSearchParams(window.location.search); const commitDetails = decodeURIComponent(data.get('commitChanges')); const versionSetDetails = JSON.parse(decodeURIComponent(data.get('vsDetails'))); const pipelineDetails = JSON.parse(decodeURIComponent(data.get('pipelineDetails'))); console.log(pipelineDetails); console.log(versionSetDetails); console.log(commitDetails); const commitOwners = JSON.parse(data.get('commitOwners')); updateMCMTitle(pipelineDetails); if(versionSetDetails){ updateDescription(commitDetails, versionSetDetails, pipelineDetails) } if(commitOwners && commitOwners.length>0){ addcommitOwners(commitOwners); } } })();