azure-pipelines-wrapper/conflict_detect.js (205 lines of code) (raw):
const spawnSync = require('child_process').spawnSync;
const { Octokit } = require('@octokit/rest');
const util = require('util');
const { setTimeout } = require('timers/promises');
const eventhub = require('./eventhub');
const akv = require('./keyvault');
const InProgress = 'in_progress'
const MsConflict = 'ms_conflict'
const MsChecker = 'ms_checker'
const { v4: uuidv4 } = require('uuid');
const COMPLETED = 'completed'
const FAILURE = 'failure'
const SUCCESS = 'success'
async function check_create(app, context, uuid, owner, repo, url, commit, check_name, result, status, output_title, output_summary){
if (! result) {
app.log.error(`[ CONFLICT DETECT ] [${uuid}] check_create: result=BLANK`)
result = SUCCESS
}
param={
owner: owner,
repo: repo,
head_sha: commit,
name: check_name,
status: status,
conclusion: result,
output: {
title: output_title,
summary: output_summary,
},
}
app.log.info([`[ CONFLICT DETECT ] [${uuid}] check_create`, result, status, output_title, output_summary].join(" "))
let check = await context.octokit.rest.checks.create(param);
let eventDatas = [];
let dateString = new Date().toISOString()
let payload = {
"action": status,
"pr_url": url,
"output": output_summary,
"result": result,
};
let eventData = {
body: {"Timestamp": dateString, "Name": check_name, "Action": status, "Payload": payload}
};
eventDatas.push(eventData);
eventhub.sendEventBatch(eventDatas, app);
if (check.status/10 >= 30 || check.status/10 < 20){
app.log.error([`[ CONFLICT DETECT ] [${uuid}] check_create`, util.inspect(check, {depth: null})].join(" "))
} else {
app.log.info([`[ CONFLICT DETECT ] [${uuid}] check_create`, check.status].join(" "))
}
}
function init(app) {
app.log.info("[ CONFLICT DETECT ] Init conflict detect");
app.on( ["pull_request.opened", "pull_request.synchronize", "pull_request.reopened", "issue_comment.created"] , async (context) => {
var payload = context.payload;
const uuid = uuidv4()
var full_name = payload.repository.full_name
var owner = full_name.split('/')[0]
var repo = full_name.split('/')[1]
var gh_token = await akv.getGithubToken()
// comment to start PR validation.
if (payload.pull_request) {
var body = ''
var issue_number = payload.number.toString()
var pr_owner = payload.pull_request.user.login
if ("sonic-net/sonic-buildimage" != full_name) {
body='/azp run'
} else {
body='/azp run Azure.sonic-buildimage'
}
app.log.info(`[ AUTO COMMENT ] repo: ${repo}, PR: ${issue_number}, body: ${body}`)
const sonicbld_octokit = new Octokit({
auth: gh_token,
});
try {
await setTimeout(5000)
const response = await sonicbld_octokit.rest.issues.createComment({
owner,
repo,
issue_number,
body,
});
app.log.info(`[ AUTO COMMENT ] Comment created: ${response.data}`)
} catch(error) {
app.log.error(`[ AUTO COMMENT ] Comment error: ${error}`)
}
}
if ("sonic-net/sonic-buildimage" != full_name) {
app.log.info(`[ CONFLICT DETECT ] [${uuid}] repo not match!`)
return
}
var url, number, commit, base_branch, pr_owner, check_suite
var script_branch = await akv.getSecretFromCache("CONFLICT_SCRIPT_BRANCH")
var msazure_token = await akv.getSecretFromCache("MSAZURE_TOKEN")
var param = Array()
param.push(`FOLDER=conflict`)
if (payload.issue && payload.action == "created") {
// issue_comment.created
let comment_body = payload.comment.body.trim().toLowerCase()
if (!payload.issue.pull_request) {
app.log.error(`[ CONFLICT DETECT ] [${uuid}] no PR found, exit!`)
return
}
url = payload.issue.html_url
number = payload.issue.number.toString()
let pr = await context.octokit.rest.pulls.get({
owner: owner,
repo: repo,
pull_number: number,
});
commit = pr.data.head.sha
base_branch = pr.data.base.ref
pr_owner = pr.data.head.user.login
if (comment_body.startsWith(`/azpw ${MsConflict}`)) {
check_suite = MsConflict
if (comment_body.includes(" -f ") || comment_body.endsWith(" -f")){
param.push("FORCE_PUSH=true")
check_suite = "ALL"
} else {
param.push("FORCE_PUSH=false")
}
} else if (comment_body.startsWith(`/azpw ${MsChecker}`)) {
check_suite = MsChecker
} else {
app.log.info(`[ CONFLICT DETECT ] [${uuid}] comment: ${comment_body}, exit!`)
return
}
comment_body=comment_body.replace('/azpw ', '')
param.push(`ACTION="${check_suite}"`)
} else {
// pull_request.opened/synchronize/reopend
url = payload.pull_request.html_url
number = payload.number.toString()
commit = payload.pull_request.head.sha
base_branch = payload.pull_request.base.ref
pr_owner = payload.pull_request.user.login
param.push("FORCE_PUSH=true")
param.push(`ACTION=ALL`)
check_suite = "ALL"
if (payload.pull_request.title.startsWith("[submodule]") && pr_owner == "mssonicbld") {
app.log.info(`[ CONFLICT DETECT ] [${uuid}] submodule update PR, return!`)
return
}
}
app.log.info([`[ CONFLICT DETECT ] [${uuid}]`, url, number, commit, base_branch, pr_owner, check_suite].join(" "))
param.push(`UUID=${uuid}`)
param.push(`REPO=${repo}`)
param.push(`GH_TOKEN=${gh_token}`)
param.push(`MSAZURE_TOKEN=${msazure_token}`)
param.push(`SCRIPT_URL=https://mssonicbld:${gh_token}@raw.githubusercontent.com/Azure/sonic-pipelines-internal/${script_branch}/azure-pipelines/ms_conflict_detect.sh`)
param.push(`PR_NUMBER=${number}`)
param.push(`PR_URL=${url}`)
param.push(`PR_OWNER=${pr_owner}`)
param.push(`PR_BASE_BRANCH=${base_branch}`)
param.push(`PR_HEAD_COMMIT=${commit}`)
// If it belongs to ms, comment on PR.
var description = '', comment_at = '', mspr = '', tmp = '', ms_conflict_result = '', ms_checker_result = '', output = ''
var run = spawnSync('./bash_action.sh', param, { encoding: 'utf-8' })
for (const line of run.stdout.split(/\r?\n/)){
output = line
if (line.includes("pr_owner: ")){
comment_at = line.split(' ').pop()
}
if (line.includes("ms_pr: ") && mspr == ''){
mspr = line.split(' ').pop()
}
if (line.includes("ms_pr_new: ") && ! line.endsWith('null') ){
mspr = line.split(' ').pop()
}
if (line.includes("tmp dir: ")){
tmp = line.split(' ').pop()
}
if (line.includes("ms_conflict.result: ")){
ms_conflict_result = line.split(' ').pop()
}
if (line.includes("ms_checker.result: ")){
ms_checker_result = line.split(' ').pop()
}
}
app.log.info(`[ CONFLICT DETECT ] [${uuid}] ${mspr}, ${tmp}`)
if ( ['ALL',MsConflict].includes(check_suite) ) {
if (run.status == 254) {
app.log.info([`[ CONFLICT DETECT ] [${uuid}] Conflict detected!`, url].join(" "))
description = `@${comment_at} PR: ${url} is conflict with MS internal repo<br>${mspr}<br>Please push fix commit to sonicbld/precheck/head/${number}<br>Then approve PR and comment "/azpw ${MsConflict}" in github PR.`
} else if (run.status == 253){
app.log.info([`[ CONFLICT DETECT ] [${uuid}] Conflict already exists!`, url].join(" "))
description = `@${comment_at} Conflict already exists in ${base_branch}<br>Please wait a few hours to run ms_conflict again!<br>'/azpw ${MsConflict}'`
} else if (run.status == 252){
app.log.info([`[ CONFLICT DETECT ] [${uuid}] Github Branch Error!`, url].join(" "))
description = `@${comment_at} Github Branch not ready<br>Please wait a few minutes to run again!<br>'/azpw ${MsConflict}'`
} else if (run.status != 0){
app.log.info([`[ CONFLICT DETECT ] [${uuid}] Unknown error liushilongbuaa need to check! ${output}`, url].join(" "))
description = `@liushilongbuaa Please help check!<br>${mspr}<br>${tmp}`
} else {
app.log.info([`[ CONFLICT DETECT ] [${uuid}] Exit: 0`, url].join(" "))
description = `${SUCCESS}<br>${mspr}`
}
check_create(app, context, uuid, owner, repo, url, commit, MsConflict, ms_conflict_result, COMPLETED, "MS conflict detect", `${ms_conflict_result}: ${description}`)
}
if ( ['ALL',MsChecker].includes(check_suite) ) {
description = `inprogress: ${mspr}`
check_create(app, context, uuid, owner, repo, url, commit, MsChecker, SUCCESS, COMPLETED, "MS PR validation", description)
// check_create(app, context, uuid, owner, repo, url, commit, MsChecker, null, InProgress, "MS PR validation", description)
}
app.log.error(`[ CONFLICT DETECT ] [${uuid}] Exit Code: ${run.status}`)
});
};
module.exports = Object.freeze({
init: init,
});