diff --git a/bin/ncu-ci b/bin/ncu-ci index 8037a26d..0b2a57d7 100755 --- a/bin/ncu-ci +++ b/bin/ncu-ci @@ -14,8 +14,12 @@ const { PRBuild, BenchmarkRun, CommitBuild, HealthBuild, listBuilds, FailureAggregator, jobCache } = require('../lib/ci/ci_result_parser'); +const { + RunPRJob +} = require('../lib/ci/run_ci'); const clipboardy = require('clipboardy'); const { writeJson, writeFile } = require('../lib/file'); +const { getMergedConfig } = require('../lib/config'); const { runPromise } = require('../lib/run'); const auth = require('../lib/auth'); @@ -70,6 +74,26 @@ const argv = yargs }, handler }) + .command({ + command: 'run ', + desc: 'Run 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 ', desc: 'Automatically detect CI type and show results', @@ -147,6 +171,54 @@ const commandToType = { benchmark: BENCHMARK }; +class RunPRJobCommand { + constructor(cli, request, argv) { + this.cli = cli; + this.request = request; + this.dir = process.cwd(); + this.argv = argv; + this.config = getMergedConfig(this.dir); + } + + get owner() { + return this.argv.owner || this.config.owner; + } + + get repo() { + return this.argv.repo || this.config.repo; + } + + get prid() { + return this.argv.prid; + } + + async start() { + const { + cli, request, prid, repo, owner + } = this; + let validArgs = true; + if (!repo) { + validArgs = false; + cli.error('GitHub repository is missing, please set it via ncu-config ' + + 'or pass it via the --repo option'); + } + if (!owner) { + cli.error('GitHub owner is missing, please set it via ncu-config ' + + 'or pass it via the --owner option'); + validArgs = false; + } + if (!validArgs) { + this.cli.setExitCode(1); + return; + } + const jobRunner = new RunPRJob(cli, request, owner, repo, prid); + if (!jobRunner.start()) { + this.cli.setExitCode(1); + process.exitCode = 1; + } + } +} + class CICommand { constructor(cli, request, argv) { this.cli = cli; @@ -343,6 +415,10 @@ async function main(command, argv) { let commandHandler; // Prepare queue. switch (command) { + case 'run': { + const jobRunner = new RunPRJobCommand(cli, request, argv); + return jobRunner.start(); + } case 'rate': { commandHandler = new RateCommand(cli, request, argv); break; diff --git a/lib/ci/run_ci.js b/lib/ci/run_ci.js new file mode 100644 index 00000000..0def2146 --- /dev/null +++ b/lib/ci/run_ci.js @@ -0,0 +1,78 @@ +'use strict'; + +const FormData = require('form-data'); + +const { + CI_DOMAIN, + CI_TYPES, + CI_TYPES_KEYS +} = require('./ci_type_parser'); + +const CI_CRUMB_URL = `https://${CI_DOMAIN}/crumbIssuer/api/json`; +const CI_PR_NAME = CI_TYPES.get(CI_TYPES_KEYS.PR).jobName; +const CI_PR_URL = `https://${CI_DOMAIN}/job/${CI_PR_NAME}/build`; + +class RunPRJob { + constructor(cli, request, owner, repo, prid) { + this.cli = cli; + this.request = request; + this.owner = owner; + this.repo = repo; + this.prid = prid; + } + + async getCrumb() { + try { + const { crumb } = await this.request.json(CI_CRUMB_URL); + return crumb; + } catch (e) { + return false; + } + } + + get payload() { + const payload = new FormData(); + payload.append('json', JSON.stringify({ + parameter: [ + { name: 'CERTIFY_SAFE', value: 'on' }, + { name: 'TARGET_GITHUB_ORG', value: this.owner }, + { name: 'TARGET_REPO_NAME', value: this.repo }, + { name: 'PR_ID', value: this.prid }, + { name: 'REBASE_ONTO', value: '' }, + { name: 'DESCRIPTION_SETTER_DESCRIPTION', value: '' } + ] + })); + return payload; + } + + async start() { + 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; + } + cli.stopSpinner('Jenkins credentials valid'); + + try { + cli.startSpinner('Starting PR CI job'); + await this.request.text(CI_PR_URL, { + method: 'POST', + headers: { + 'Jenkins-Crumb': crumb + }, + body: this.payload + }); + cli.stopSpinner('PR CI job successfully started'); + } catch (err) { + cli.stopSpinner('Failed to start CI', this.cli.SPINNER_STATUS.FAILED); + return false; + } + return true; + } +} + +module.exports = { RunPRJob }; diff --git a/package.json b/package.json index e2d2cc82..96a7fe7d 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "core-validate-commit": "^3.13.1", "execa": "^4.0.1", "figures": "^3.2.0", + "form-data": "^3.0.0", "fs-extra": "^9.0.0", "ghauth": "^4.0.0", "inquirer": "^7.1.0",