Last active
February 11, 2020 17:55
-
-
Save joshcanhelp/54607ffca99a7ab7c5f3851bacaad1b0 to your computer and use it in GitHub Desktop.
Google Script Editor functions for getting SDK quality metrics
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* globals SpreadsheetApp, Browser, CacheService, UrlFetchApp, PropertiesService */ | |
/* | |
* | |
* Macro functions | |
* | |
*/ | |
/** | |
* Prompt the user for a GitHub API token. | |
*/ | |
function saveGitHubApiToken() { | |
'use strict'; | |
var token = SpreadsheetApp.getUi().prompt('Enter your GitHub API token').getResponseText(); | |
PropertiesService.getUserProperties().setProperty('GITHUB_API_TOKEN', token); | |
} | |
/** | |
* Prompt the user for a Codecov API token. | |
*/ | |
function saveCodecovApiToken() { | |
'use strict'; | |
var token = SpreadsheetApp.getUi().prompt('Enter your Codecov API token').getResponseText(); | |
PropertiesService.getUserProperties().setProperty('CODECOV_API_TOKEN', token); | |
} | |
/** | |
* Updates selected row with new data. | |
*/ | |
function updateRow() { | |
'use strict'; | |
if (!GitHub.prototype.tokenCheck() || !Codecov.prototype.tokenCheck()) { | |
return; | |
} | |
var sheet = SpreadsheetApp.getActiveSheet(); | |
var activeRange = SpreadsheetApp.getActiveRange(); | |
var dataPoints = sheet.getRange("2:2").getValues()[0].filter(function(el) { return el.length; }); | |
var currRow = activeRange.getRowIndex(); | |
var bottomRow = activeRange.getLastRow(); | |
if (!currRow || !bottomRow) { | |
Browser.msgBox('Select at least one cell in a repo row to update.'); | |
return; | |
} | |
for (currRow; currRow <= bottomRow; currRow++) { | |
var repoName = sheet.getRange(currRow, 1).getValue(); | |
if (currRow > 2 && repoName) { | |
var github = new GitHub(repoName); | |
var codecov = new Codecov(repoName); | |
var otherData = { | |
readme: github.getReadme(), | |
ci: github.getCi(), | |
topics: github.getTopics(), | |
coverage: codecov.getCoverage() | |
}; | |
var currCol = 3; | |
dataPoints.forEach(function(el) { | |
var dataPoint = el.split('|'); | |
var value; | |
if ('comm' === dataPoint[0]) { | |
dataPoint = dataPoint[1]; | |
value = github.getCommunity(dataPoint); | |
} else if ('repo' === dataPoint[0]) { | |
dataPoint = dataPoint[1]; | |
value = github.getRepo(dataPoint); | |
} else if ('release' === dataPoint[0]) { | |
dataPoint = dataPoint[1]; | |
value = github.getLatestRelease(dataPoint); | |
} else if ('traffic' === dataPoint[0]) { | |
dataPoint = dataPoint[1]; | |
value = github.getTraffic(dataPoint); | |
} else { | |
dataPoint = dataPoint[0]; | |
value = otherData[dataPoint]; | |
} | |
switch (dataPoint) { | |
case 'issue_template': | |
case 'pull_request_template': | |
case 'contributing': | |
case 'code_of_conduct': | |
value = value ? 'Yes' : 'No'; | |
break; | |
case 'pushed_at': | |
value = value.split('T')[0]; | |
break; | |
case 'readme': | |
value = processReadme(value); | |
break; | |
case 'coverage': | |
value = value / 100; | |
break; | |
case 'language': | |
case 'tag_name': | |
value = value || 'None'; | |
break; | |
case 'published_at': | |
value = ( value && value.split('T')[0] ) || 'None'; | |
break; | |
case 'topics': | |
value = value.length ? value.join(', ') : 'None'; | |
break; | |
} | |
sheet.getRange(currRow, currCol).setValue(value); | |
currCol++; | |
}); | |
} else if (!repoName) { | |
Browser.msgBox("Update complete!"); | |
return; | |
} | |
} | |
Browser.msgBox("Update complete!"); | |
} | |
/** | |
* Deletes the script cache. | |
*/ | |
function deleteCache() { | |
'use strict'; | |
var cache = CacheService.getScriptCache(); | |
getRepoNames().forEach(function(repoName) { | |
cache.remove(repoName, '--codecov'); | |
cache.remove(repoName, '--gh-comm'); | |
cache.remove(repoName, '--gh-readme'); | |
cache.remove(repoName, '--gh-repo'); | |
cache.remove(repoName, '--gh-release'); | |
}); | |
} | |
/* | |
* | |
* Helper functions | |
* | |
*/ | |
/** | |
* Get all repo names. | |
*/ | |
function getRepoNames() { | |
'use strict'; | |
var ss = SpreadsheetApp.getActiveSpreadsheet(); | |
var sheet = ss.getSheets()[0]; | |
var range = sheet.getRange("A:A"); | |
return range.getValues().filter(function(el) { return el && el !== 'Name'; }); | |
} | |
/** | |
* Process README contents to look for sections and formatting. | |
* | |
* @param readme string - README contents in Markdown format. | |
* | |
* @returns {number} | |
*/ | |
function processReadme(readme) { | |
'use strict'; | |
var sections = [ | |
'Table of Contents', 'Documentation', 'Installation', 'Getting Started', 'Contribution', 'Support + Feedback', | |
'Vulnerability Reporting', 'What is Auth0?', 'License' | |
]; | |
var sectionPoints = '#' === readme[0] ? 1 : 0; | |
sections.forEach(function(section) { | |
sectionPoints += ( readme.indexOf('## ' + section) >= 0 ? 1: 0 ); | |
}); | |
return sectionPoints / (sections.length + 1); | |
} | |
/** | |
* GitHub processor constructor. | |
* | |
* @param repoName string - Repo name for coverage. | |
* @param skipCache boolean - Should we skip the script cache for this app? | |
* | |
* @constructor | |
*/ | |
function GitHub(repoName, skipCache) { | |
'use strict'; | |
this.apiToken = PropertiesService.getUserProperties().getProperty('GITHUB_API_TOKEN'); | |
this.httpParams = { headers : { Authorization : 'token ' + this.apiToken } }; | |
this.repoName = repoName || 'wp-auth0'; | |
this.apiBaseUrl = 'https://api.github.com/repos/auth0/' + this.repoName; | |
this.skipCache = skipCache || false; | |
this.cacheExpires = 60; // One minute | |
} | |
/** | |
* Create the cache key to use. | |
* | |
* @param key string - Specific key being cached. | |
* | |
* @returns {string} | |
*/ | |
GitHub.prototype.cacheKey = function (key) { | |
'use strict'; | |
return this.repoName + '--' + key; | |
}; | |
/** | |
* Prompt the user for a GitHub token. | |
*/ | |
GitHub.prototype.tokenCheck = function() { | |
'use strict'; | |
if (!this.apiToken && !PropertiesService.getUserProperties().getProperty('GITHUB_API_TOKEN')) { | |
Browser.msgBox('Select Tools > Macros > saveGitHubApiToken and enter your GitHub API token.'); | |
return false; | |
} | |
return true; | |
} | |
/** | |
* Get the community profile. | |
* https://developer.github.com/v3/repos/community/ | |
* | |
* @param key string - Specific key to get, null/false to return complete object. | |
* | |
* @returns {*} | |
*/ | |
GitHub.prototype.getCommunity = function (key) { | |
'use strict'; | |
var cache = CacheService.getScriptCache(); | |
var cacheKey = this.cacheKey('gh-comm'); | |
var commJson = cache.get(cacheKey); | |
if (!commJson || this.skipCache ) { | |
if (!this.tokenCheck()) { | |
return ''; | |
} | |
this.httpParams.headers.Accept = 'application/vnd.github.black-panther-preview+json'; | |
commJson = UrlFetchApp.fetch( this.apiBaseUrl + '/community/profile', this.httpParams).getContentText(); | |
if (commJson) { | |
cache.put(cacheKey, commJson, this.cacheExpires); | |
} | |
} | |
var commData = {}; | |
try { | |
commData = JSON.parse(commJson); | |
} catch (e) { | |
return 'JSON parse error'; | |
} | |
if (!key) { | |
return commData; | |
} else if ('license' === key) { | |
return commData.files.license && commData.files.license.name; | |
} else if (['health_percentage', 'description', 'documentation'].indexOf(key) >= 0) { | |
return commData[key]; | |
} else { | |
return commData.files[key] && commData.files[key].html_url; | |
} | |
}; | |
/** | |
* Get a single repo. | |
* https://developer.github.com/v3/repos/#get | |
* | |
* @param key string - Specific key to get, null/false to return complete object. | |
* | |
* @returns {*} | |
*/ | |
GitHub.prototype.getRepo = function (key) { | |
'use strict'; | |
var cache = CacheService.getScriptCache(); | |
var cacheKey = this.cacheKey('gh-repo'); | |
var githubJson = cache.get(cacheKey); | |
if (!githubJson || this.skipCache ) { | |
if (!this.tokenCheck()) { | |
return ''; | |
} | |
githubJson = UrlFetchApp.fetch( this.apiBaseUrl, this.httpParams ).getContentText(); | |
if (githubJson) { | |
cache.put(cacheKey, githubJson, this.cacheExpires); | |
} | |
} | |
var githubData = {}; | |
try { | |
githubData = JSON.parse(githubJson); | |
} catch (e) { | |
return 'JSON parse error'; | |
} | |
return key ? githubData[key] : githubData; | |
}; | |
/** | |
* Get the latest release from a repo. | |
* https://developer.github.com/v3/repos/releases/#get-the-latest-release | |
* | |
* @returns {*} | |
*/ | |
GitHub.prototype.getLatestRelease = function (key) { | |
'use strict'; | |
var cache = CacheService.getScriptCache(); | |
var cacheKey = this.cacheKey('gh-release'); | |
var releaseJson = cache.get(cacheKey); | |
if (!releaseJson || this.skipCache ) { | |
if (!this.tokenCheck()) { | |
return ''; | |
} | |
var params = this.httpParams; | |
params.muteHttpExceptions = true; | |
releaseJson = UrlFetchApp.fetch( this.apiBaseUrl + '/releases/latest', params ).getContentText(); | |
if (releaseJson) { | |
cache.put(cacheKey, releaseJson, this.cacheExpires); | |
} | |
} | |
var releaseData = {}; | |
try { | |
releaseData = JSON.parse(releaseJson); | |
} catch (e) { | |
return 'JSON parse error'; | |
} | |
return key ? releaseData[key] : releaseData; | |
}; | |
/** | |
* Get the repo's view count. | |
* https://developer.github.com/v3/repos/traffic/#views | |
* | |
* @returns {*} | |
*/ | |
GitHub.prototype.getTraffic = function (key) { | |
'use strict'; | |
var cache = CacheService.getScriptCache(); | |
var cacheKey = this.cacheKey('gh-traffic'); | |
var trafficJson = cache.get(cacheKey); | |
if (!trafficJson || this.skipCache ) { | |
if (!this.tokenCheck()) { | |
return ''; | |
} | |
var params = this.httpParams; | |
params.muteHttpExceptions = true; | |
trafficJson = UrlFetchApp.fetch( this.apiBaseUrl + '/traffic/views', params ).getContentText(); | |
if (trafficJson) { | |
cache.put(cacheKey, trafficJson, this.cacheExpires); | |
} | |
} | |
var trafficData = {}; | |
try { | |
trafficData = JSON.parse(trafficJson); | |
} catch (e) { | |
return 'JSON parse error'; | |
} | |
return key ? trafficData[key] : trafficData; | |
}; | |
/** | |
* Get topics for a repo. | |
* https://developer.github.com/v3/repos/#list-all-topics-for-a-repository | |
*/ | |
GitHub.prototype.getTopics = function () { | |
'use strict'; | |
var cache = CacheService.getScriptCache(); | |
var cacheKey = this.cacheKey('gh-topics'); | |
var topicsJson = cache.get(cacheKey); | |
if (!topicsJson || this.skipCache ) { | |
if (!this.tokenCheck()) { | |
return ''; | |
} | |
var params = this.httpParams; | |
params.headers.Accept = 'application/vnd.github.mercy-preview+json'; | |
var topicsJson = UrlFetchApp.fetch( this.apiBaseUrl + '/topics', params ).getContentText(); | |
if (topicsJson) { | |
cache.put(cacheKey, topicsJson, this.cacheExpires); | |
} | |
} | |
var topicsData = {}; | |
try { | |
topicsData = JSON.parse(topicsJson); | |
} catch (e) { | |
return 'JSON parse error'; | |
} | |
return topicsData && topicsData.names; | |
}; | |
/** | |
* Get the README file contents. | |
*/ | |
GitHub.prototype.getReadme = function() { | |
'use strict'; | |
var cache = CacheService.getScriptCache(); | |
var cacheKey = this.cacheKey('gh-readme'); | |
var readmeContent = cache.get(cacheKey); | |
if (!readmeContent || this.skipCache) { | |
var readmeUrl = this.getCommunity('readme'); | |
readmeUrl = readmeUrl.replace('github.com', 'raw.githubusercontent.com'); | |
readmeUrl = readmeUrl.replace('blob/master', 'master'); | |
readmeContent = UrlFetchApp.fetch( readmeUrl ).getContentText(); | |
if (readmeContent) { | |
cache.put(cacheKey, readmeContent, this.cacheExpires); | |
} | |
} | |
return readmeContent; | |
}; | |
/** | |
* Check for a Circle or Travis config file. | |
*/ | |
GitHub.prototype.getCi = function () { | |
'use strict'; | |
var cache = CacheService.getScriptCache(); | |
var cacheKey = this.cacheKey('gh-ci'); | |
var ciApp = cache.get(cacheKey); | |
if (!ciApp || this.skipCache ) { | |
ciApp = 'None'; | |
var params = { muteHttpExceptions: true }; | |
var circleResponse = UrlFetchApp.fetch( | |
'https://github.com/auth0/' + this.repoName + '/tree/master/.circleci/config.yml', | |
params | |
); | |
if ( circleResponse.getResponseCode() < 300 ) { | |
ciApp = 'Circle'; | |
} else { | |
var travisResponse = UrlFetchApp.fetch( | |
'https://github.com/auth0/' + this.repoName + '/tree/master/.travis.yml', | |
params | |
); | |
if ( travisResponse.getResponseCode() < 300 ) { | |
ciApp = 'Travis'; | |
} | |
} | |
cache.put(cacheKey, ciApp, this.cacheExpires); | |
} | |
return ciApp; | |
}; | |
/** | |
* Codecov processor constructor. | |
* | |
* @param repoName string - Repo name for coverage. | |
* @param skipCache boolean - Should we skip the script cache for this app? | |
* | |
* @constructor | |
*/ | |
function Codecov(repoName, skipCache) { | |
'use strict'; | |
this.apiToken = PropertiesService.getUserProperties().getProperty('CODECOV_API_TOKEN'); | |
this.repoName = repoName || 'wp-auth0'; | |
this.skipCache = skipCache || false; | |
this.cacheExpires = 60; // One minute | |
} | |
/** | |
* Create the cache key to use. | |
* | |
* @param key string - Specific key being cached. | |
* | |
* @returns {string} | |
*/ | |
Codecov.prototype.cacheKey = function (key) { | |
'use strict'; | |
return this.repoName + '--' + key; | |
}; | |
/** | |
* Prompt the user for a Codecov token. | |
*/ | |
Codecov.prototype.tokenCheck = function() { | |
'use strict'; | |
if (!this.apiToken && !PropertiesService.getUserProperties().getProperty('CODECOV_API_TOKEN')) { | |
Browser.msgBox('Select Tools > Macros > saveCodecovApiToken and enter your Codecov API token.'); | |
return false; | |
} | |
return true; | |
} | |
/** | |
* Get coverage percent of last commit in the master branch. | |
* | |
* @returns {string} | |
*/ | |
Codecov.prototype.getCoverage = function () { | |
'use strict'; | |
var cache = CacheService.getScriptCache(); | |
var cacheKey = this.cacheKey('codecov'); | |
var coverage = cache.get(cacheKey); | |
if (null === coverage || this.skipCache) { | |
if (!this.apiToken) { | |
Browser.msgBox('Add CODECOV_API_TOKEN in Script Editor > File > Project Properties > User Properties tab'); | |
return ''; | |
} | |
var response = UrlFetchApp.fetch( | |
'https://codecov.io/api/gh/auth0/' + this.repoName + '?access_token=' + this.apiToken | |
).getContentText(); | |
var responseData = {}; | |
try { | |
responseData = JSON.parse(response); | |
} catch (e) { | |
return 'JSON parse error'; | |
} | |
coverage = responseData.commit && responseData.commit.totals && responseData.commit.totals.c; | |
coverage = coverage || 0; | |
cache.put(cacheKey, coverage, this.cacheExpires); | |
} | |
return coverage; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment