Skip to content

Commit

Permalink
feat: add ncu-cu resume <prid> command
Browse files Browse the repository at this point in the history
  • Loading branch information
MoLow committed Jul 19, 2022
1 parent bbad9a0 commit 547c5a8
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 9 deletions.
66 changes: 61 additions & 5 deletions bin/ncu-ci.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,26 @@ const args = yargs(hideBin(process.argv))
},
handler
})
.command({
command: 'resume <prid>',
desc: 'Resume CI for given PR',
builder: (yargs) => {
yargs
.positional('prid', {
describe: 'ID of the PR',
type: 'number'
})
.option('owner', {
default: '',
describe: 'GitHub repository owner'
})
.option('repo', {
default: '',
describe: 'GitHub repository name'
});
},
handler
})
.command({
command: 'url <url>',
desc: 'Automatically detect CI type and show results',
Expand Down Expand Up @@ -253,10 +273,8 @@ class RunPRJobCommand {
return this.argv.prid;
}

async start() {
const {
cli, request, prid, repo, owner
} = this;
validate() {
const { cli, repo, owner } = this;
let validArgs = true;
if (!repo) {
validArgs = false;
Expand All @@ -270,10 +288,44 @@ class RunPRJobCommand {
}
if (!validArgs) {
this.cli.setExitCode(1);
}
return validArgs;
}

async start() {
const {
cli, request, prid, repo, owner
} = this;
if (!this.validate()) {
return;
}
const jobRunner = new RunPRJob(cli, request, owner, repo, prid);
if (!jobRunner.start()) {
if (!await jobRunner.start()) {
this.cli.setExitCode(1);
process.exitCode = 1;
}
}
}

class ResumePRJobCommand extends RunPRJobCommand {
async start() {
const {
cli, request, prid, repo, owner
} = this;
if (!this.validate()) {
return;
}
// Parse CI links from PR.
const parser = await JobParser.fromPRId(this, cli, request);
const ciMap = parser.parse();

if (!ciMap.has(PR)) {
cli.info(`No CI run detected from pull request ${prid}`);
}

const { jobid } = ciMap.get(PR);
const jobRunner = new RunPRJob(cli, request, owner, repo, prid, jobid);
if (!await jobRunner.resume()) {
this.cli.setExitCode(1);
process.exitCode = 1;
}
Expand Down Expand Up @@ -539,6 +591,10 @@ async function main(command, argv) {
const jobRunner = new RunPRJobCommand(cli, request, argv);
return jobRunner.start();
}
case 'resume': {
const jobResumer = new ResumePRJobCommand(cli, request, argv);
return jobResumer.start();
}
case 'rate': {
commandHandler = new RateCommand(cli, request, argv);
break;
Expand Down
7 changes: 7 additions & 0 deletions lib/ci/ci_type_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,13 @@ JobParser.fromPR = async function(url, cli, request) {
return new JobParser(thread);
};

JobParser.fromPRId = async function({ owner, repo, prid }, cli, request) {
const data = new PRData({ owner, repo, prid }, cli, request);
await data.getThreadData();
const thread = data.getThread();
return new JobParser(thread);
};

export const CI_TYPES_KEYS = {
CITGM,
CITGM_NOBUILD,
Expand Down
1 change: 1 addition & 0 deletions lib/ci/jenkins_constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const ACTION_TREE = 'actions[parameters[name,value]]';
const CHANGE_FIELDS = 'commitId,author[absoluteUrl,fullName],authorEmail,' +
'msg,date';
const CHANGE_TREE = `changeSet[items[${CHANGE_FIELDS}]]`;
export const BASIC_TREE = 'result,url,number';
export const PR_TREE =
`result,url,number,${ACTION_TREE},${CHANGE_TREE},builtOn,` +
`subBuilds[${BUILD_FIELDS},build[subBuilds[${BUILD_FIELDS}]]]`;
Expand Down
64 changes: 60 additions & 4 deletions lib/ci/run_ci.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import FormData from 'form-data';

import { BASIC_TREE } from './jenkins_constants.js';
import { TestBuild } from './build-types/test_build.js';
import {
CI_DOMAIN,
CI_TYPES,
Expand All @@ -9,14 +11,16 @@ import {
export const CI_CRUMB_URL = `https://${CI_DOMAIN}/crumbIssuer/api/json`;
const CI_PR_NAME = CI_TYPES.get(CI_TYPES_KEYS.PR).jobName;
export const CI_PR_URL = `https://${CI_DOMAIN}/job/${CI_PR_NAME}/build`;
export const CI_PR_RESUME_URL = `https://${CI_DOMAIN}/job/${CI_PR_NAME}/`;

export class RunPRJob {
constructor(cli, request, owner, repo, prid) {
constructor(cli, request, owner, repo, prid, jobid) {
this.cli = cli;
this.request = request;
this.owner = owner;
this.repo = repo;
this.prid = prid;
this.jobid = jobid;
}

async getCrumb() {
Expand All @@ -43,18 +47,28 @@ export class RunPRJob {
return payload;
}

async start() {
async #validateJenkinsCredentials() {
const { cli } = this;
cli.startSpinner('Validating Jenkins credentials');
const crumb = await this.getCrumb();

if (crumb === false) {
cli.stopSpinner('Jenkins credentials invalid',
this.cli.SPINNER_STATUS.FAILED);
return false;
return { crumb, success: false };
}
cli.stopSpinner('Jenkins credentials valid');

return { crumb, success: true };
}

async start() {
const { cli } = this;
const { crumb, success } = await this.#validateJenkinsCredentials();
if (success === false) {
return false;
}

try {
cli.startSpinner('Starting PR CI job');
const response = await this.request.fetch(CI_PR_URL, {
Expand All @@ -64,7 +78,7 @@ export class RunPRJob {
},
body: this.payload
});
if (response.status !== 201) {
if (response.status !== 200) {
cli.stopSpinner(
`Failed to start PR CI: ${response.status} ${response.statusText}`,
this.cli.SPINNER_STATUS.FAILED);
Expand All @@ -77,4 +91,46 @@ export class RunPRJob {
}
return true;
}

async resume() {
const { cli, request, jobid } = this;
const { crumb, success } = await this.#validateJenkinsCredentials();
if (success === false) {
return false;
}

try {
cli.startSpinner('Resuming PR CI job');
const path = `job/${CI_PR_NAME}/${jobid}/`;
const testBuild = new TestBuild(cli, request, path, BASIC_TREE);
const { result } = await testBuild.getBuildData();

if (result !== 'FAILURE') {
cli.stopSpinner(
`CI Job is in status ${result ?? 'RUNNING'}, skipping resume`,
this.cli.SPINNER_STATUS.FAILED);
return false;
}

const resume_url = `${CI_PR_RESUME_URL}${jobid}/resume`;
const response = await this.request.fetch(resume_url, {
method: 'POST',
headers: {
'Jenkins-Crumb': crumb
}
});
if (response.status !== 201) {
cli.stopSpinner(
`Failed to resume PR CI: ${response.status} ${response.statusText}`,
this.cli.SPINNER_STATUS.FAILED);
return false;
}

cli.stopSpinner('PR CI job successfully resumed');
} catch (err) {
cli.stopSpinner('Failed to resume CI', this.cli.SPINNER_STATUS.FAILED);
return false;
}
return true;
}
}

0 comments on commit 547c5a8

Please sign in to comment.