507 lines
27 KiB
JavaScript
507 lines
27 KiB
JavaScript
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 CL.projects.foundations.utilities.webAccess.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
|
|
}
|
|
/*
|
|
Validation #5: The name of the User Profile node is not the Github Username.
|
|
*/
|
|
if (userProfile.name !== githubUsername) {
|
|
console.log('[INFO] Github Server -> mergeGithubPullRequests -> Validation #5 Failed -> Pull Request "' + pullRequest.title + '" not merged because the Github Username is not equal to the User Profile node\'s name. -> Github Username = ' + githubUsername + '-> userProfile.name = ' + userProfile.name)
|
|
|
|
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 User Profile node\'s name.\n\n User Profile Name = "' + userProfile.name + '"'
|
|
});
|
|
|
|
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
|
|
}
|
|
} |