-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Expand file tree
/
Copy pathcheckout-pr.js
More file actions
executable file
·150 lines (134 loc) · 4.25 KB
/
Copy pathcheckout-pr.js
File metadata and controls
executable file
·150 lines (134 loc) · 4.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#!/usr/bin/env node
// Copyright IBM Corp. 2020. All Rights Reserved.
// Node module: loopback-next
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
/*
* This is an internal script for LoopBack maintainers to check out a forked
* repo/branch for a given pull request
*
* Sometimes a LoopBack maintainer needs to help improve/fix a pull request.
* This script allows us to set up the PR code base as follows:
*
* 1. Read the url and branch for base and head for the PR
* 2. Set up remote stream <pr-#> to <pr-repo>
* 3. Fetch changes from <pr-repo>/<pr-branch>
* 4. Fetch changes from origin/<base-branch>
* 5. Check out <pr-branch> to track <pr-repo>/<pr-branch>
* 6. Rebase the PR branch to the origin/<base-branch>
*/
const path = require('node:path');
const https = require('node:https');
const {parse: parseURL} = require('node:url');
const build = require('../packages/build');
const {runMain} = require('./script-util');
const ROOT_DIR = path.join(__dirname, '..');
async function checkoutPR() {
const prUrlOrNum = process.argv[2];
if (!prUrlOrNum) {
console.error(
'Usage: node %s <PR-number-or-url>',
path.relative(process.cwd(), process.argv[1]),
);
process.exit(1);
}
const parts = prUrlOrNum.split('/').filter(Boolean);
const prNum = parts[parts.length - 1] || parts[0];
console.log(`Checking out pull request #${prNum}...`);
const url = `https://api.github.com/repos/loopbackio/loopback-next/pulls/${prNum}`;
const result = await getPRInfo(url);
const headUrl = result.head.repo.ssh_url;
const headBranch = result.head.ref;
const baseBranch = result.base.ref;
const prStream = `pr-${prNum}`;
await git('remote', 'add', prStream, headUrl);
await git('fetch', prStream, headBranch);
await git('fetch', 'origin', baseBranch);
await git('checkout', '--track', `${prStream}/${headBranch}`);
await git('rebase', `origin/${baseBranch}`);
console.log(`PR ${prNum} is now checked out.`);
}
/**
* Fetch PR information
* @param {string} prUrl - PR url
*/
function getPRInfo(prUrl) {
const options = {
...parseURL(prUrl),
headers: {
'User-Agent': 'Node.js https client',
},
};
return new Promise((resolve, reject) => {
https
.get(options, res => {
const {statusCode} = res;
const contentType = res.headers['content-type'];
let error;
if (statusCode !== 200) {
error = new Error('Request Failed.\n' + `Status Code: ${statusCode}`);
} else if (!/^application\/json/.test(contentType)) {
error = new Error(
'Invalid content-type.\n' +
`Expected application/json but received ${contentType}`,
);
}
if (error) {
console.error(error.message);
// Consume response data to free up memory
res.resume();
return reject(error);
}
res.setEncoding('utf8');
let rawData = '';
res.on('data', chunk => {
rawData += chunk;
});
res.on('end', () => {
try {
const parsedData = JSON.parse(rawData);
resolve(parsedData);
} catch (e) {
reject(e);
}
});
})
.on('error', e => {
reject(e);
});
});
}
/**
* Run `git` command with the arguments
* @param {...string[]} args - Git args
*/
async function git(...args) {
console.log('> git', ...args);
const shell = build.runShell('git', args, {
cwd: ROOT_DIR,
});
await waitForProcessExit(shell);
}
/**
* Return a promise to be resolved by the child process exit event
* @param {ChildProcess} child - Child process
*/
function waitForProcessExit(child) {
return new Promise((resolve, reject) => {
child.on('exit', (code, signal) => {
if (code === 0 || code === 128) resolve(code);
else {
reject(
new Error(
`Process ${child.pid} exits with code ${code} signal ${signal}`,
),
);
}
});
});
}
console.log('+-----------------------------------------------+');
console.log('| Check out GitHub CLI - https://cli.github.com |');
console.log('+-----------------------------------------------+');
console.log();
runMain(module, checkoutPR);