Client Github Server Implementation
This commit is contained in:
1 parent
6d67490942
commit
37d70849ec
13 files changed
+843
-535
No files matched your search
@@ -0,0 +1,484 @@
|
||||
exports.newGithubServer = function newGithubServer() {
|
||||
|
||||
const MODULE = "Github Server"
|
||||
|
||||
let thisObject = {
|
||||
getGithubStars: getGithubStars,
|
||||
getGithubWatchers: getGithubWatchers,
|
||||
getGithubForks: getGithubForks,
|
||||
mergePullRequests: mergePullRequests,
|
||||
initialize: initialize,
|
||||
finalize: finalize,
|
||||
run: run
|
||||
}
|
||||
|
||||
const GITHUB_API_WAITING_TIME = 250
|
||||
return thisObject
|
||||
|
||||
function finalize() {
|
||||
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
|
||||
}
|
||||
|
||||
function run() {
|
||||
|
||||
}
|
||||
|
||||
async function getGithubStars(repository, username, token) {
|
||||
return await getGithubUsernames('activity', 'listStargazersForRepo', repository, username, token)
|
||||
}
|
||||
|
||||
async function getGithubWatchers(repository, username, token) {
|
||||
return await getGithubUsernames('activity', 'listWatchersForRepo', repository, username, token)
|
||||
}
|
||||
|
||||
async function getGithubForks(repository, username, token) {
|
||||
return await getGithubUsernames('repos', 'listForks', repository, username, token)
|
||||
}
|
||||
|
||||
async function mergePullRequests(commitMessage, username, token) {
|
||||
return await mergeGithubPullRequests(commitMessage, username, token)
|
||||
}
|
||||
|
||||
async function getGithubUsernames(endpoint, method, repository, username, token) {
|
||||
try {
|
||||
|
||||
repository = unescape(repository)
|
||||
username = unescape(username)
|
||||
token = unescape(token)
|
||||
let error
|
||||
let githubListArray = []
|
||||
|
||||
return await getRepoInfo()
|
||||
|
||||
async function getRepoInfo() {
|
||||
await doGithub()
|
||||
if (error !== undefined) {
|
||||
|
||||
let docs = {
|
||||
project: 'Governance',
|
||||
category: 'Topic',
|
||||
type: 'Gov Error - Get Repository Information',
|
||||
anchor: undefined,
|
||||
placeholder: {}
|
||||
}
|
||||
|
||||
return respondWithDocsObject(docs, error)
|
||||
}
|
||||
|
||||
return {
|
||||
result: global.DEFAULT_OK_RESPONSE.result,
|
||||
githubListArray: githubListArray
|
||||
}
|
||||
}
|
||||
|
||||
async function doGithub() {
|
||||
|
||||
const { Octokit } = require("@octokit/rest")
|
||||
|
||||
const octokit = new Octokit({
|
||||
auth: token,
|
||||
userAgent: 'Superalgos Beta 11'
|
||||
})
|
||||
await getList()
|
||||
|
||||
async function getList() {
|
||||
const repo = repository
|
||||
const owner = 'Superalgos'
|
||||
const per_page = 100 // Max
|
||||
let page = 0
|
||||
let lastPage = false
|
||||
|
||||
while (lastPage === false) {
|
||||
try {
|
||||
page++
|
||||
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
|
||||
let listResponse = await octokit[endpoint][method]({
|
||||
owner,
|
||||
repo,
|
||||
per_page,
|
||||
page
|
||||
});
|
||||
|
||||
if (listResponse.data.length < 100) {
|
||||
lastPage = true
|
||||
}
|
||||
|
||||
for (let i = 0; i < listResponse.data.length; i++) {
|
||||
let listItem = listResponse.data[i]
|
||||
let githubUsername
|
||||
switch (endpoint) {
|
||||
case 'activity': {
|
||||
githubUsername = listItem.login
|
||||
break
|
||||
}
|
||||
case 'repos': {
|
||||
githubUsername = listItem.owner.login
|
||||
break
|
||||
}
|
||||
}
|
||||
//console.log(listItem)
|
||||
|
||||
githubListArray.push(githubUsername)
|
||||
}
|
||||
console.log('[INFO] Github Server -> getRepoInfo -> doGithub -> getList -> ' + method + ' Page = ' + page)
|
||||
console.log('[INFO] Github Server -> getRepoInfo -> doGithub -> getList -> ' + method + ' Received = ' + listResponse.data.length)
|
||||
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
|
||||
if (err.stack.indexOf('last page') >= 0) {
|
||||
return
|
||||
} else {
|
||||
console.log('[ERROR] Github Server -> getRepoInfo -> doGithub -> getList ->Method call produced an error.')
|
||||
console.log('[ERROR] Github Server -> getRepoInfo -> doGithub -> getList ->err.stack = ' + err.stack)
|
||||
console.log('[ERROR] Github Server -> getRepoInfo -> doGithub -> getList ->repository = ' + repository)
|
||||
console.log('[ERROR] Github Server -> getRepoInfo -> doGithub -> getList ->username = ' + username)
|
||||
console.log('[ERROR] Github Server -> getRepoInfo -> doGithub -> getList ->token starts with = ' + token.substring(0, 10) + '...')
|
||||
console.log('[ERROR] Github Server -> getRepoInfo -> doGithub -> getList ->token ends with = ' + '...' + token.substring(token.length - 10))
|
||||
error = err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.log('[ERROR] Github Server -> getRepoInfo -> Method call produced an error.')
|
||||
console.log('[ERROR] Github Server -> getRepoInfo -> err.stack = ' + err.stack)
|
||||
console.log('[ERROR] Github Server -> getRepoInfo -> repository = ' + repository)
|
||||
console.log('[ERROR] Github Server -> getRepoInfo -> username = ' + username)
|
||||
console.log('[ERROR] Github Server -> getRepoInfo -> token starts with = ' + token.substring(0, 10) + '...')
|
||||
console.log('[ERROR] Github Server -> getRepoInfo -> token ends with = ' + '...' + token.substring(token.length - 10))
|
||||
|
||||
let error = {
|
||||
result: 'Fail Because',
|
||||
message: err.message,
|
||||
stack: err.stack
|
||||
}
|
||||
return error
|
||||
}
|
||||
}
|
||||
|
||||
async function mergeGithubPullRequests(commitMessage, username, token) {
|
||||
try {
|
||||
commitMessage = unescape(commitMessage)
|
||||
username = unescape(username)
|
||||
token = unescape(token)
|
||||
const repository = 'Superalgos'
|
||||
|
||||
/* Unsavping # */
|
||||
for (let i = 0; i < 10; i++) {
|
||||
commitMessage = commitMessage.replace('_SLASH_', '/')
|
||||
commitMessage = commitMessage.replace('_HASHTAG_', '#')
|
||||
}
|
||||
|
||||
let error
|
||||
let githubPrListArray = []
|
||||
|
||||
return await processOpenPullRequests()
|
||||
|
||||
async function processOpenPullRequests() {
|
||||
await doGithub()
|
||||
if (error !== undefined) {
|
||||
|
||||
let docs = {
|
||||
project: 'Governance',
|
||||
category: 'Topic',
|
||||
type: 'Gov Error - Merge Pull Requests',
|
||||
anchor: undefined,
|
||||
placeholder: {}
|
||||
}
|
||||
|
||||
return respondWithDocsObject(docs, error)
|
||||
}
|
||||
|
||||
return {
|
||||
result: global.DEFAULT_OK_RESPONSE.result
|
||||
}
|
||||
}
|
||||
|
||||
async function doGithub() {
|
||||
try {
|
||||
const repo = repository
|
||||
const owner = 'Superalgos'
|
||||
const { Octokit } = require("@octokit/rest")
|
||||
const octokit = new Octokit({
|
||||
auth: token,
|
||||
userAgent: 'Superalgos Beta 11'
|
||||
})
|
||||
await getPrList()
|
||||
await mergePrs()
|
||||
|
||||
async function getPrList() {
|
||||
|
||||
const per_page = 100 // Max
|
||||
let page = 0
|
||||
let lastPage = false
|
||||
|
||||
while (lastPage === false) {
|
||||
try {
|
||||
page++
|
||||
|
||||
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
|
||||
|
||||
let listResponse = await octokit.rest.pulls.list({
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
state: 'open',
|
||||
head: 'develop',
|
||||
base: 'develop',
|
||||
sort: undefined,
|
||||
direction: undefined,
|
||||
per_page: per_page,
|
||||
page: page
|
||||
});
|
||||
|
||||
if (listResponse.data.length < 100) {
|
||||
lastPage = true
|
||||
}
|
||||
console.log('[INFO] Github Server -> mergeGithubPullRequests -> doGithub -> getPrList -> Receiving Page = ' + page)
|
||||
for (let i = 0; i < listResponse.data.length; i++) {
|
||||
let pullRequest = listResponse.data[i]
|
||||
console.log('[INFO] Github Server -> mergeGithubPullRequests -> doGithub -> getPrList -> Pull Request "' + pullRequest.title + '" found and added to the list to validate. ')
|
||||
githubPrListArray.push(pullRequest)
|
||||
}
|
||||
console.log('[INFO] Github Server -> mergeGithubPullRequests -> doGithub -> getPrList -> Received = ' + listResponse.data.length)
|
||||
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
|
||||
if (err.stack.indexOf('last page') >= 0) {
|
||||
return
|
||||
} else {
|
||||
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> doGithub -> getPrList ->Method call produced an error.')
|
||||
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> doGithub -> getPrList ->err.stack = ' + err.stack)
|
||||
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> doGithub -> getPrList ->repository = ' + repository)
|
||||
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> doGithub -> getPrList ->username = ' + username)
|
||||
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> doGithub -> getPrList ->token starts with = ' + token.substring(0, 10) + '...')
|
||||
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> doGithub -> getPrList ->token ends with = ' + '...' + token.substring(token.length - 10))
|
||||
error = err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function mergePrs() {
|
||||
/*
|
||||
In order to automatically merge a PRs we will run several validations first.
|
||||
We want to automate specifically the merging of Profile File changes at the
|
||||
Governance System done by the same Github user name that matches the profile
|
||||
changed. We will also not do the merge if there are more than one file at the
|
||||
PR.
|
||||
|
||||
We will go through the list of open PRs and run the validations at each one of them.
|
||||
*/
|
||||
console.log('[INFO] Github Server -> mergeGithubPullRequests -> Ready to Validate ' + githubPrListArray.length + ' pull requests. ')
|
||||
|
||||
for (let i = 0; i < githubPrListArray.length; i++) {
|
||||
let pullRequest = githubPrListArray[i]
|
||||
/*
|
||||
Lets get the files changed at this Pull Request.
|
||||
*/
|
||||
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
|
||||
let listResponse = await octokit.rest.pulls.listFiles({
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
pull_number: pullRequest.number
|
||||
});
|
||||
/*
|
||||
Validation #1: If the PR has more than one file, then we will not automatically
|
||||
merge it.
|
||||
*/
|
||||
if (listResponse.data.length !== 1) {
|
||||
console.log('[INFO] Github Server -> mergeGithubPullRequests -> Validation #1 Failed -> Pull Request "' + pullRequest.title + '" not merged because it contains more than 1 file. -> fileCount = ' + listResponse.data.length)
|
||||
/*
|
||||
We will close PRs that contains any User Profile file together with other files in the same Pull Request.
|
||||
This will avoid manual merges to include User Profile files.
|
||||
*/
|
||||
for (j = 0; j < listResponse.data.length; j++) {
|
||||
let pullRequestFile = listResponse.data[j]
|
||||
let fileContentUrl = pullRequestFile.raw_url
|
||||
if (fileContentUrl.indexOf('Governance/Plugins/User-Profiles') >= 0) {
|
||||
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
|
||||
await octokit.rest.issues.createComment({
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
issue_number: pullRequest.number,
|
||||
body: 'This Pull Request could not be automatically merged and was closed by the Superalgos Governance System because it was detected that a User Profile file... \n\n' + fileContentUrl + '\n\n...was submitted together with ' + (listResponse.data.length - 1) + ' other file/s. User Profiles files as per the Governance System rules, must be the only file present at a Pull Request in order to pass all the validations and be automatically merged.'
|
||||
});
|
||||
|
||||
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
|
||||
await octokit.rest.pulls.update({
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
pull_number: pullRequest.number,
|
||||
state: 'closed'
|
||||
});
|
||||
break
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
let pullRequestFile = listResponse.data[0]
|
||||
/*
|
||||
Validation #2: File Name must be the same to the Github Username of the PR owner.
|
||||
*/
|
||||
let fileContentUrl = pullRequestFile.raw_url
|
||||
|
||||
if (fileContentUrl.indexOf('Governance/Plugins/User-Profiles') < 0) {
|
||||
/*
|
||||
If it is not a user profile then there is no need to auto merge.
|
||||
*/
|
||||
console.log('[INFO] Github Server -> mergeGithubPullRequests -> Validation #2 Failed -> Pull Request "' + pullRequest.title + '" not merged because the file modified at the Pull Request is not a User Profile file. -> fileContentUrl = ' + fileContentUrl)
|
||||
continue
|
||||
}
|
||||
|
||||
let splittedURL = fileContentUrl.split('/')
|
||||
let fileName = splittedURL[splittedURL.length - 1]
|
||||
let splittedFileName = fileName.split('.')
|
||||
fileName = splittedFileName[0]
|
||||
let githubUsername = pullRequest.user.login
|
||||
|
||||
if (githubUsername !== fileName) {
|
||||
console.log('[INFO] Github Server -> mergeGithubPullRequests -> Validation #2 Failed -> Pull Request "' + pullRequest.title + '" not merged because the Github Username is not equal to the File Name. -> Github Username = ' + githubUsername + '-> fileName = ' + fileName)
|
||||
|
||||
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
|
||||
await octokit.rest.issues.createComment({
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
issue_number: pullRequest.number,
|
||||
body: 'This Pull Request could not be automatically merged and was closed by the Superalgos Governance System because it was detected that the Github User "' + githubUsername + '" who submitted it, is not equal to the name of the User Profile Plugin File.\n\n File Name = "' + fileName + '"'
|
||||
});
|
||||
|
||||
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
|
||||
await octokit.rest.pulls.update({
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
pull_number: pullRequest.number,
|
||||
state: 'closed'
|
||||
});
|
||||
continue
|
||||
}
|
||||
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
|
||||
let fileContent = await fetchAPIDataFile(fileContentUrl)
|
||||
|
||||
let userProfile = JSON.parse(fileContent)
|
||||
/*
|
||||
Validation #3: The file changed at the PR is a User Profile.
|
||||
*/
|
||||
if (userProfile.type !== 'User Profile') {
|
||||
console.log('[INFO] Github Server -> mergeGithubPullRequests -> Validation #3 Failed -> Pull Request "' + pullRequest.title + '" not merged because the file modified is not a User Profile. -> Type = ' + userProfile.type)
|
||||
continue
|
||||
}
|
||||
/*
|
||||
Validation #4: The message signed at the config is not the Github Username.
|
||||
*/
|
||||
let config = JSON.parse(userProfile.config)
|
||||
let messageSigned = config.signature.message
|
||||
if (messageSigned !== githubUsername) {
|
||||
console.log('[INFO] Github Server -> mergeGithubPullRequests -> Validation #4 Failed -> Pull Request "' + pullRequest.title + '" not merged because the Github Username is not equal to the Message Signed at the User Profile. -> Github Username = ' + githubUsername + '-> messageSigned = ' + messageSigned)
|
||||
|
||||
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
|
||||
await octokit.rest.issues.createComment({
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
issue_number: pullRequest.number,
|
||||
body: 'This Pull Request could not be automatically merged and was closed by the Superalgos Governance System because it was detected that the Github User "' + githubUsername + '" who submitted it, is not equal to the Message Signed at the User Profile.\n\n Message Signed = "' + messageSigned + '"'
|
||||
});
|
||||
|
||||
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
|
||||
await octokit.rest.pulls.update({
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
pull_number: pullRequest.number,
|
||||
state: 'closed'
|
||||
});
|
||||
continue
|
||||
}
|
||||
/*
|
||||
All validations passed, we will proceed an merge this Pull Request.
|
||||
*/
|
||||
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
|
||||
let mergeResponse = await octokit.rest.pulls.merge({
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
pull_number: pullRequest.number,
|
||||
commit_title: 'Changes in User Profile done by Profile Owner ' + githubUsername + ' automatically merged by Superalgos.'
|
||||
});
|
||||
|
||||
if (mergeResponse.data.merged !== true) {
|
||||
console.log('[WARN] Github Server -> mergeGithubPullRequests -> Merge Failed -> Pull Request "' + pullRequest.title + '" not merged because Github could not merge it. -> mergeResponse.message = ' + mergeResponse.data.message)
|
||||
continue
|
||||
} else {
|
||||
console.log('[INFO] Github Server -> mergeGithubPullRequests -> Merge Succed -> Pull Request "' + pullRequest.title + '" successfully merged. -> mergeResponse.message = ' + mergeResponse.data.message)
|
||||
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
|
||||
await octokit.rest.issues.createComment({
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
issue_number: pullRequest.number,
|
||||
body: 'This Pull Request was automatically merged by the Superalgos Governance System because it was detected that the Github User " ' + githubUsername + '" who submitted it, modified its own User Profile Plugin File and nothning else but that file. All validations were successfull.'
|
||||
});
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> doGithub -> err.stack = ' + err.stack)
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> Method call produced an error.')
|
||||
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> err.stack = ' + err.stack)
|
||||
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> repository = ' + repository)
|
||||
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> username = ' + username)
|
||||
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> token starts with = ' + token.substring(0, 10) + '...')
|
||||
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> token ends with = ' + '...' + token.substring(token.length - 10))
|
||||
|
||||
let error = {
|
||||
result: 'Fail Because',
|
||||
message: err.message,
|
||||
stack: err.stack
|
||||
}
|
||||
return error
|
||||
}
|
||||
}
|
||||
|
||||
function respondWithDocsObject(docs, error) {
|
||||
|
||||
if (error.message !== undefined) {
|
||||
docs.placeholder.errorMessage = {
|
||||
style: 'Error',
|
||||
text: error.message
|
||||
}
|
||||
}
|
||||
if (error.stack !== undefined) {
|
||||
docs.placeholder.errorStack = {
|
||||
style: 'Javascript',
|
||||
text: error.stack
|
||||
}
|
||||
}
|
||||
if (error.code !== undefined) {
|
||||
docs.placeholder.errorCode = {
|
||||
style: 'Json',
|
||||
text: error.code
|
||||
}
|
||||
}
|
||||
|
||||
docs.placeholder.errorDetails = {
|
||||
style: 'Json',
|
||||
text: JSON.stringify(error, undefined, 4)
|
||||
}
|
||||
|
||||
let customResponse = {
|
||||
result: global.CUSTOM_FAIL_RESPONSE.result,
|
||||
docs: docs
|
||||
}
|
||||
|
||||
return customResponse
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user