Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create 'Bors try' monitor #20

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/bors-retry/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM node:12-alpine

WORKDIR /usr/src/app
COPY package.json .

RUN npm install
COPY . .

EXPOSE 8080
CMD [ "npm", "start" ]
111 changes: 111 additions & 0 deletions src/bors-retry/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
Bors Retry Monitor
=================

Instructions
------------

Monitors GitHub pull requests, initiating Bors builds until the desired number of consecutive successful runs (TARGET_RUNS) has been met.

1. Build the docker container:
```
docker build -t bors-retry
```

2. Run the docker container with the appropriate environment variables:
```
docker run \
-e TARGET_RUNS <number, default: 5> \
-e SHOULD_START <boolean, default: false> \
-e GITHUB_AUTH <string, *required*> \
-e GITHUB_ISSUE <number, *required*> \
bors-retry
```

The monitor could also be ran as a NodeJS app by setting the appropriate environment variables, and the command `npm start`.

Required Arguments
-----------------

GITHUB_AUTH - Personal GitHub access token [(tutorial)](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line)

GITHUB_ISSUE - Issue number to monitor

Optional Arguments
-----------------
TARGET_RUNS - Target # of consecutive successful runs (default: 5)

SHOULD_START - Start a new monitoring cycle, if previous one is already finished (default: false)

Monitoring Output
----------------
```
{
date: <[string] timestamp of the monitoring sample>,
issue: <[number] GitHub PR being monitored>,
target: <[number] corresponds with the TARGET_RUNS env variable>,
streak: <[number] number of consecutive success (streak > 0) or failures (streak < 0)>
pending: <[array - string] date of the latest `bors try` request>,
success: <[array - run information] all successful Bors runs>,
failed: <array - run information] all failed Bors runs>,
runs: <[array - run information] all Bors runs>,
completedCycles: <[number] number of completed Bors cycles (a cycle is reaching the targetted number of runs)>,
actions: <[array - string] pending actions for a given monitoring sample>,
monitoringComplete: <[boolean] end of a cycle detected>
}
```

Run Information:
```
{
start: <[string] timestamp of Bors try request>,
end: <[string] timestamp of completion of Bors try>,
duration: <[number] duration of Bors build in minutes>,
result: <[string] result of the build: 'success' or 'failed'>
}
```

Actions:
```
monitorStart - initiating a cycle
borsTry - initiating a Bors run
```

Sample monitoring output:
```
{
date: '2020/05/31 06:40:35',
issue: '3083',
target: '5',
pending: [ '2020/05/31 06:11:39' ],
sucess: [
{
start: '2020/05/30 05:41:09',
end: '2020/05/30 06:18:08',
duration: 36,
result: 'success'
},
...
],
failed: [
{
start: '2020/05/30 02:21:29',
end: '2020/05/30 03:41:39',
duration: 80,
result: 'failed'
},
...
],
runs: [
{
start: '2020/05/30 02:21:29',
end: '2020/05/30 03:41:39',
duration: 80,
result: 'failed'
},
...
],
completedCycles: 2,
actions: [],
monitoringComplete: false
}
```
196 changes: 196 additions & 0 deletions src/bors-retry/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
if (!process.env.GITHUB_AUTH || !process.env.GITHUB_ISSUE) {
throw "REQUIRED ENV: GITHUB_AUTH, GITHUB_ISSUE"
}

const target = process.env.TARGET_RUNS || 5
shouldStart = process.env.SHOULD_START || false,
githubAuth = process.env.GITHUB_AUTH,
githubIssue = process.env.GITHUB_ISSUE,
pollInterval = 60*1000,
staleBorsRequest = 4,
dateFormat = "YYYY/MM/DD HH:mm:ss";

const { Octokit } = require("@octokit/rest"),
moment = require("moment"),
github = new Octokit({
auth: githubAuth
});

const githubActions = {
monitorStart: "===== Bors Monitoring Start =====",
monitorEnd: "===== Bors Monitoring End =====",
borsTry: "bors try"
}

const writeComment = (comment) => {
const body = githubActions[comment]
console.log(`[Bors Monitor] Commenting: ${body}`);
github.issues.createComment({
owner: "openenclave",
repo: "openenclave",
issue_number: githubIssue,
body
});
}

const monitor = async (targetRuns = 5, createNew = false) => {

try {

const data = [],
actions = [];
let page = 1;
while(1) {
const res = await github.issues.listComments({
owner: "openenclave",
repo: "openenclave",
issue_number: githubIssue,
page
});
if (res.data.length > 0) {
data.push(...res.data);
} else {
break;
}
page++;
}

let start = 0,
streak = 0,
end = 0,
allRuns = [],
previousRequests = [],
successfulRuns = [],
failedRuns = [];

const reset = () => {
streak = 0;
allRuns = [];
previousRequests = [];
successfulRuns = [];
failedRuns = [];
}
const comments = data.map((comment) => {

const { body, user, created_at } = comment,
{ monitorStart, monitorEnd, borsTry } = githubActions,
text = body.trim(),
commentDate = moment(created_at);

if (text === monitorStart) {
start++;
} else if (text === monitorEnd) {
end++;
if (start === end) {
reset();
}
}

if (start > end) {
if (text === borsTry) {
previousRequests.push(commentDate);
} else if (text === "bors try-" && previousRequests.length > 0) {
previousRequests.shift();
} else if (user.login === "bors[bot]") {

if (text.includes("Already running a review")) {
previousRequests.shift();
} else if (previousRequests.length > 0) {

const prev = previousRequests.shift(),
duration = commentDate.diff(prev,"minutes"),
runDetails = {
start: prev.format(dateFormat),
end: commentDate.format(dateFormat),
duration
};

if (text.includes("Build succeeded")) {
runDetails.result = "success";
successfulRuns.push(runDetails);
allRuns.push(runDetails);
streak = streak > 0 ? streak+1 : 1;
} else if (text.includes("Build failed")) {
runDetails.result = "failed";
failedRuns.push(runDetails);
allRuns.push(runDetails);
streak = streak < 0 ? streak-1 : -1;
} else {
previousRequests.unshift(prev);
}
}
}
}

return comment;
})

if (start === end) {
if (start === 0 || createNew) {
actions.push("monitorStart");
start++;
successfulRuns = [];
} else {
console.log("\tMonitoring exiting - SHOULD_START=false");
return;
}
}

const pending = previousRequests.length,
totalRuns = allRuns.length,
success = successfulRuns.length,
failed = failedRuns.length,
isCycleRunning = start - end > 0,
noCurrentRuns = pending === 0;

//console.log(isCycleRunning, noCurrentRuns, success, failed, targetRuns, pending, totalRuns)
if (isCycleRunning && noCurrentRuns && streak < targetRuns) {
actions.push("borsTry");
}

const monitoringComplete = streak === targetRuns,
requestDate = moment(),
monitorStatus = {
date: requestDate.format(dateFormat),
issue: githubIssue,
target: targetRuns,
streak,
pending: previousRequests.map((prev) => {return prev.format(dateFormat)}),
sucess: successfulRuns,
failed: failedRuns,
runs: allRuns,
completedCycles: end,
actions,
monitoringComplete
}

if (pending - totalRuns > 0 && previousRequests.length > 0) {
const lastRequestDate = new moment(previousRequests[0])

monitorStatus["lastRequestDate"] = lastRequestDate.format(dateFormat);
monitorStatus["sinceLastRequest"] = requestDate.diff(lastRequestDate,"minutes");
}

console.log(monitorStatus)

if (actions.length > 0) {
actions.forEach((action) => {
writeComment(action);
});
}

if (monitoringComplete) {
console.log(`\tMonitoring complete - ${success}/${totalRuns} runs succeeded`);
writeComment(monitorEnd);
return;
}

setTimeout(() => {monitor(targetRuns)}, pollInterval);

} catch(e) {
console.log("error:",e);
}

}

monitor(target,shouldStart);
Loading