Skip to content
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
58 changes: 52 additions & 6 deletions bin/ncu-ci.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
import {
RunPRJob
} from '../lib/ci/run_ci.js';
import { ResumePRJob } from '../lib/ci/resume_ci.js';
import { writeJson, writeFile } from '../lib/file.js';
import { getMergedConfig } from '../lib/config.js';
import { runPromise } from '../lib/run.js';
Expand Down Expand Up @@ -134,6 +135,26 @@ const args = yargs(hideBin(process.argv))
},
handler
})
.command({
command: 'resume <prid>',
desc: 'Resume the latest CI run for given PR',
builder: (yargs) => {
yargs
.positional('prid', {
describe: 'ID of the PR or URL to the PR',
type: 'string'
})
.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 @@ -278,11 +299,13 @@ class RunPRJobCommand {
return this.argv.prid;
}

async start() {
const {
cli, request, prid, repo, owner
} = this;
validate() {
const { cli, prid, repo, owner } = this;
let validArgs = true;
if (!Number.isSafeInteger(prid) || prid <= 0) {
Comment thread
panva marked this conversation as resolved.
validArgs = false;
cli.error('Pull request ID must be a positive integer');
}
if (!repo) {
validArgs = false;
cli.error('GitHub repository is missing, please set it via ncu-config ' +
Expand All @@ -295,6 +318,13 @@ class RunPRJobCommand {
}
if (!validArgs) {
this.cli.setExitCode(1);
}
return validArgs;
}

async start() {
const { cli, request, prid, repo, owner } = this;
if (!this.validate()) {
return;
}
const { certifySafe, checkForDuplicates } = this.argv;
Expand All @@ -308,6 +338,20 @@ class RunPRJobCommand {
}
}

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

class CICommand {
constructor(cli, request, argv) {
this.cli = cli;
Expand Down Expand Up @@ -565,7 +609,8 @@ async function main(command, argv) {
let commandHandler;
// Prepare queue.
switch (command) {
case 'run': {
case 'run':
case 'resume': {
const maybeURL = URL.parse(argv.prid);
if (maybeURL?.host === 'github.com') {
const [, owner, repo, , prid, , commit_sha] = maybeURL.pathname.split('/');
Expand All @@ -575,7 +620,8 @@ async function main(command, argv) {
argv.prid = prid;
}
argv.prid = Number(argv.prid);
const jobRunner = new RunPRJobCommand(cli, request, argv);
const Command = command === 'run' ? RunPRJobCommand : ResumePRJobCommand;
const jobRunner = new Command(cli, request, argv);
return jobRunner.start();
}
case 'rate': {
Expand Down
33 changes: 33 additions & 0 deletions docs/ncu-ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Commands:
runs
ncu-ci walk <type> Walk the CI and display the failures
ncu-ci run <prid> Start a node-test-pull-request CI job for a PR
ncu-ci resume <prid> Resume the latest node-test-pull-request CI job for a PR
ncu-ci url <url> Automatically detect CI type and show results
ncu-ci pr <jobid> Show results of a node-test-pull-request CI job
ncu-ci commit <jobid> Show results of a node-test-commit CI job
Expand Down Expand Up @@ -172,6 +173,38 @@ ncu-ci run https://github.com/nodejs/node/pull/34127/commits/35ea6ded7315cf9d058
If the PR has the `v8 engine` label, `ncu-ci run` also triggers the `node-test-commit-v8-linux`
job after the main PR CI job is started successfully.

### `ncu-ci resume <prid>`

`ncu-ci resume <prid>` resumes the latest `node-test-pull-request` CI run linked
in the PR description, comments, or reviews. The job must have finished with
`FAILURE` or `ABORTED` and expose Jenkins' resume action. Running jobs and jobs with
other results are not resumed. If no PR CI run is found, the command reports that
and exits unsuccessfully.

The CI-approved commit (`COMMIT_SHA_CHECK`) must match the PR's current HEAD.
The command refuses to resume if they differ or the approved commit cannot be
determined.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW Jenkins already checks for that (for the HEAD, not for approvals). Just saying in case that adds too much complexity

@panva panva Sep 10, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but that check runs inside the build script, so we'd get a resume link followed by a failed build, rather than [resume-ci-failed] on the PR, which is what I was aiming for.

Before resuming, the command streams failed-job console output and compares
failure diagnostics with the PR's changed files. It refuses to resume if a failed
test or a file referenced in a failure diagnostic is changed by the PR. Logs are
scanned one at a time with bounded memory. HTTP compression is decoded as the
response arrives. A match cancels the download and skips remaining logs. Unknown
or unavailable failure details do not prevent resuming; the check uses the
available diagnostics. Failure to retrieve the PR's changed-file list prevents
resuming.

Pass a PR number with repository information from config or flags, or a PR URL:

```sh
ncu-ci resume 34127 --owner nodejs --repo node
ncu-ci resume https://github.com/nodejs/node/pull/34127
```

This uses Jenkins' **Resume build** action on the existing job. It does not start
a fresh CI run for the current PR head. Jenkins credentials with permission to
resume the job are required.

### `ncu-ci pr <jobid>`

`ncu-ci pr <jobid>` returns information about the results of a `node-test-pull-request` job.
Expand Down
Loading
Loading