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

git-node: add --backport flag to land #383

Merged
merged 1 commit into from
Feb 14, 2020
Merged
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
13 changes: 12 additions & 1 deletion components/git/land.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ const landOptions = {
describe: 'Assume "yes" as answer to all prompts and run ' +
'non-interactively. If an undesirable situation occurs, such as a pull ' +
'request or commit check fails, then git node land will abort.'
},
backport: {
describe: 'Land a backport PR onto a staging branch',
default: false,
type: 'boolean'
}
};

Expand Down Expand Up @@ -152,8 +157,14 @@ async function main(state, argv, cli, req, dir) {
cli.log('run `git node land --abort` before starting a new session');
return;
}
session = new LandingSession(cli, req, dir, argv.prid);
session = new LandingSession(cli, req, dir, argv.prid, argv.backport);
const metadata = await getMetadata(session.argv, cli);
if (argv.backport) {
const split = metadata.metadata.split('\n')[0];
if (split === 'PR-URL: ') {
cli.error('Commit message is missing PR-URL');
}
}
return session.start(metadata);
} else if (state === APPLY) {
return session.apply();
Expand Down
15 changes: 9 additions & 6 deletions docs/git-node.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,22 @@ Options:
--continue, -c Continue the landing session [boolean]
--final Verify the landed PR and clean up [boolean]
--abort Abort the current landing session [boolean]
--backport Land a backport PR on a staging branch [boolean]
--yes Assume "yes" as answer to all prompts and run
non-interactively. If an undesirable situation occurs, such as
a pull request or commit check fails, then git node land will
abort. [boolean] [default: false]


Examples:
git node land 12344 Land https://github.com/nodejs/node/pull/12344 in
the current directory
git node land --abort Abort the current session
git node land --amend Append metadata to the current commit message
git node land --final Verify the landed PR and clean up
git node land --continue Continue the current landing session
git node land 12344 Land https://github.com/nodejs/node/pull/12344
in the current directory
git node land --abort Abort the current session
git node land --amend Append metadata to the current commit message
git node land --final Verify the landed PR and clean up
git node land --continue Continue the current landing session
git node land --backport 30072 Land https://github.com/nodejs/node/pull/30072
as a backport in the current directory
```

<a id="git-node-land-prerequisites"></a>
Expand Down
23 changes: 21 additions & 2 deletions lib/landing_session.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,16 @@ const {
const isWindows = process.platform === 'win32';

class LandingSession extends Session {
constructor(cli, req, dir, prid) {
constructor(cli, req, dir, prid, backport) {
super(cli, dir, prid);
this.req = req;
this.backport = backport;
}

get argv() {
const args = super.argv;
args.backport = this.backport;
return args;
}

async start(metadata) {
Expand Down Expand Up @@ -163,13 +170,25 @@ class LandingSession extends Session {
amended.push('');
}

const BACKPORT_RE = /BACKPORT-PR-URL\s*:\s*(\S+)/i;
const PR_RE = /PR-URL\s*:\s*(\S+)/i;
const REVIEW_RE = /Reviewed-By\s*:\s*(\S+)/i;

for (const line of metadata) {
if (original.includes(line)) {
if (line) {
cli.warn(`Found ${line}, skipping..`);
}
} else {
amended.push(line);
if (line.match(BACKPORT_RE)) {
let prIndex = amended.findIndex(datum => datum.match(PR_RE));
if (prIndex === -1) {
prIndex = amended.findIndex(datum => datum.match(REVIEW_RE)) - 1;
}
amended.splice(prIndex + 1, 0, line);
} else {
amended.push(line);
}
}
}

Expand Down
39 changes: 26 additions & 13 deletions lib/links.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const FIXES_RE = /(Close[ds]?|Fix(e[ds])?|Resolve[sd]?)\s*:\s*(\S+)/mgi;
const FIX_RE = /(Close[ds]?|Fix(e[ds])?|Resolve[sd]?)\s*:\s*(\S+)/i;
const REFS_RE = /Refs?\s*:\s*(\S+)/mgi;
const REF_RE = /Refs?\s*:\s*(\S+)/i;
const PR_RE = /PR-URL\s*:\s*(\S+)/i;
const cheerio = require('cheerio');

/**
Expand Down Expand Up @@ -36,15 +37,27 @@ class LinkParser {
const m = item.match(REF_RE);
if (!m) continue;
const ref = m[1];
const url = this.getRefUrlFromOP(ref);
const url = this.getUrlFromOP(ref);
if (url) result.push(url);
}
return result;
}

getPRUrlsFromArray(arr) {
const result = [];
for (const item of arr) {
const m = item.match(PR_RE);
if (!m) continue;
const prUrl = m[1];
const url = this.getUrlFromOP(prUrl);
if (url) result.push(url);
}
return result;
}

// Do this so we can reliably get the correct url.
// Otherwise, the number could reference a PR or an issue.
getRefUrlFromOP(ref) {
getUrlFromOP(ref) {
const as = this.$('a');
const links = as.map((i, el) => this.$(el)).get();
for (const link of links) {
Expand All @@ -58,22 +71,22 @@ class LinkParser {

getFixes() {
const text = this.$.text();
const fixes = text.match(FIXES_RE);
if (fixes) {
return this.getFixesUrlsFromArray(fixes);
}
return [];
const fixes = text.match(FIXES_RE) || [];
return this.getFixesUrlsFromArray(fixes);
}

getRefs() {
const text = this.$.text();
const refs = text.match(REFS_RE);
if (refs) {
return this.getRefsUrlsFromArray(refs);
}
return [];
const refs = text.match(REFS_RE) || [];
return this.getRefsUrlsFromArray(refs);
}
};

getAltPrUrl() {
const text = this.$.text();
const refs = text.match(PR_RE) || [];
return this.getPRUrlsFromArray(refs);
}
}

const GITHUB_PULL_REQUEST_URL = /github.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/;

Expand Down
24 changes: 17 additions & 7 deletions lib/metadata_gen.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ class MetadataGenerator {
* @param {PRData} data
*/
constructor(data) {
const { owner, repo, pr, reviewers } = data;
const { owner, repo, pr, reviewers, argv } = data;
this.owner = owner;
this.repo = repo;
this.pr = pr;
this.reviewers = reviewers;
this.argv = argv;
}

/**
Expand All @@ -31,15 +32,24 @@ class MetadataGenerator {
const parser = new LinkParser(owner, repo, op);
const fixes = parser.getFixes();
const refs = parser.getRefs();

const altPrUrl = parser.getAltPrUrl();
const meta = [
`PR-URL: ${prUrl}`,
...fixes.map((fix) => `Fixes: ${fix}`),
...refs.map((ref) => `Refs: ${ref}`),
...reviewedBy.map((r) => `Reviewed-By: ${r.reviewer.getContact()}`),
'' // creates final EOL
...refs.map((ref) => `Refs: ${ref}`)
];

const backport = this.argv ? this.argv.backport : undefined;
if (backport) {
meta.unshift(`Backport-PR-URL: ${prUrl}`);
meta.unshift(`PR-URL: ${altPrUrl}`);
} else {
// Reviews are only added here as backports should not contain reviews
// for the backport itself in the metadata
meta.unshift(`PR-URL: ${prUrl}`);
meta.push(
...reviewedBy.map((r) => `Reviewed-By: ${r.reviewer.getContact()}`)
);
}
meta.push(''); // creates final EOL
return meta.join('\n');
}
}
Expand Down
1 change: 1 addition & 0 deletions lib/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@ class Session {
` $ ncu-config set branch ${rev}`);
cli.separator();
return true;
// TODO warn if backporting onto master branch
}
}

Expand Down
2 changes: 2 additions & 0 deletions test/fixtures/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ const firstTimerPrivatePR = readJSON('first_timer_pr_with_private_email.json');
const semverMajorPR = readJSON('semver_major_pr.json');
const fixAndRefPR = readJSON('pr_with_fixes_and_refs.json');
const fixCrossPR = readJSON('pr_with_fixes_cross.json');
const backportPR = readJSON('pr_with_backport.json');
const conflictingPR = readJSON('conflicting_pr.json');
const emptyProfilePR = readJSON('empty_profile_pr.json');
const closedPR = readJSON('./closed_pr.json');
Expand Down Expand Up @@ -114,6 +115,7 @@ module.exports = {
semverMajorPR,
fixAndRefPR,
fixCrossPR,
backportPR,
conflictingPR,
emptyProfilePR,
readme,
Expand Down
22 changes: 22 additions & 0 deletions test/fixtures/pr_with_backport.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"createdAt": "2019-10-22T22:42:25Z",
"authorAssociation": "CONTRIBUTOR",
"author": {
"login": "gabrielschulhof",
"email": "[email protected]",
"name": "Gabriel Schulhof"
},
"url": "https://github.com/nodejs/node/pull/30072",
"bodyHTML": "<p>Build the addons for benchmarks in the same way that the addons for<br>\ntests are built.</p>\n<p>PR-URL: <a class='issue-link js-issue-link' data-error-text='Failed to load issue title' data-id='508006081' data-permission-text='Issue title is private' data-url='https://github.com/nodejs/node/issues/29995' data-hovercard-type='pull_request' data-hovercard-url='/nodejs/node/pull/29995/hovercard' href='https://github.com/nodejs/node/pull/29995'>#29995</a><br>\n<span class='issue-keyword tooltipped tooltipped-se' aria-label='This pull request closes issue #1961.'>Fixes</span>: <a class='ssue-link js-issue-link' data-error-text='Failed to load issue title' data-id='507966018' data-permission-text='Issue title is private' data-url='https://github.com/nodejs/build/issues/1961' data-hovercard-type='issue' data-hovercard-url='/nodejs/build/issues/1961/hovercard' href='https://github.com/nodejs/build/issues/1961'>nodejs/build#1961</a><br>\nRefs: <a class='commit-link' href='https://github.com/nodejs/node/commit/53ca0b9ae145c430842bf78e553e3b6cbd2823aa#commitcomment-35494896'><tt>53ca0b9</tt>#commitcomment-35494896</a><br>\nReviewed-By: <a class='user-mention' data-hovercard-type='user' data-hovercard-url='/users/addaleax/hovercard' data-octo-click='hovercard-link-click' data-octo-dimensions='link_type:self' href='https://github.com/addaleax'>@addaleax</a><br>\nReviewed-By: <a class='user-mention' data-hovercard-type='user' data-hovercard-url='/users/Trott/hovercard' data-octo-click='hovercard-link-click' data-octo-dimensions='link_type:self' href='https://github.com/Trott'>@Trott</a><br>\nReviewed-By: <a class='user-mention' data-hovercard-type='user' data-hovercard-url='/users/BethGriggs/hovercard' data-octo-click='hovercard-link-click' data-octo-dimensions='link_type:self' href='https://github.com/BethGriggs'>@BethGriggs</a><br>\nReviewed-By: <a class='user-mention' data-hovercard-type='user' data-hovercard-url='/users/gengjiawen/hovercard' data-octo-click='hovercard-link-click' data-octo-dimensions='link_type:self' href='https://github.com/gengjiawen'>@gengjiawen</a></p>\n\n<h5>Checklist</h5>\n\n<ul class='contains-task-list'>\n<li class='task-list-item'><input type='checkbox' id='' disabled='' class='task-list-item-checkbox' checked=''> <code>make -j4 test</code> (UNIX), or <code>vcbuild test</code> (Windows) passes</li>\n<li class='task-list-item'><input type='checkbox' id='' disabled='' class='task-list-item-checkbox' checked=''> commit message follows <a href='https://github.com/nodejs/node/blob/master/doc/guides/contributing/pull-requests.md#commit-message-guidelines'>commit guidelines</a></li>\n</ul>\n\n<p>We need this PR along with <a class='issue-link js-issue-link' data-error-text='Failed to load issue title' data-id='510938668' data-permission-text='Issue title is private' data-url='https://github.com/nodejs/node/issues/30070' data-hovercard-type='pull_request' data-hovercard-url='/nodejs/node/pull/30070/hovercard' href='https://github.com/nodejs/node/pull/30070'>#30070</a> because <a class='commit-link' href='https://github.com/nodejs/node/commit/53ca0b9ae145c430842bf78e553e3b6cbd2823aa#commitcomment-35494896'><tt>53ca0b9</tt>#commitcomment-35494896</a></p>",
"bodyText": "Build the addons for benchmarks in the same way that the addons for\ntests are built.\nPR-URL: #29995\nFixes: nodejs/build#1961\nRefs: 53ca0b9#commitcomment-35494896\nReviewed-By: @addaleax\nReviewed-By: @Trott\nReviewed-By: @BethGriggs\nReviewed-By: @gengjiawen\n\nChecklist\n\n\n make -j4 test (UNIX), or vcbuild test (Windows) passes\n commit message follows commit guidelines\n\n\nWe need this PR along with #30070 because 53ca0b9#commitcomment-35494896",
"labels": {
"nodes": [
{
"name": "build"
},
{
"name": "v12.x"
}
]
}
}
26 changes: 26 additions & 0 deletions test/unit/metadata_gen.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const MetadataGenerator = require('../../lib/metadata_gen');
const {
fixAndRefPR,
fixCrossPR,
backportPR,
allGreenReviewers
} = require('../fixtures/data');

Expand All @@ -15,6 +16,21 @@ const data = {
reviewers: allGreenReviewers
};
const crossData = Object.assign({}, data, { pr: fixCrossPR });
const backportArgv = {
argv: {
owner: 'nodejs',
repo: 'node',
upstream: 'upstream',
branch: 'v12.x-staging',
readme: undefined,
waitTimeSingleApproval: undefined,
waitTimeMultiApproval: undefined,
prid: 30072,
backport: true
}
};

const backportData = Object.assign({}, data, { pr: backportPR }, backportArgv);

const expected = `PR-URL: https://github.com/nodejs/node/pull/16438
Fixes: https://github.com/nodejs/node/issues/16437
Expand All @@ -31,6 +47,11 @@ Reviewed-By: Quux User <[email protected]>
Reviewed-By: Baz User <[email protected]>
Reviewed-By: Bar User <[email protected]>
`;
const backportExpected = `PR-URL: https://github.com/nodejs/node/pull/29995
Backport-PR-URL: https://github.com/nodejs/node/pull/30072
Fixes: https://github.com/nodejs/build/issues/1961
Refs: https://github.com/nodejs/node/commit/53ca0b9ae145c430842bf78e553e3b6cbd2823aa#commitcomment-35494896
`;

describe('MetadataGenerator', () => {
it('should generate metadata properly', () => {
Expand All @@ -42,4 +63,9 @@ describe('MetadataGenerator', () => {
const results = new MetadataGenerator(crossData).getMetadata();
assert.strictEqual(crossExpected, results);
});

it('should generate correct metadata for a backport', () => {
const backportResults = new MetadataGenerator(backportData).getMetadata();
assert.strictEqual(backportExpected, backportResults);
});
});