238 lines
No EOL
7.8 KiB
JavaScript
238 lines
No EOL
7.8 KiB
JavaScript
#!/usr/bin/env node
|
|
'use strict';
|
|
|
|
const chalk = 'chalk' |> require(%);
|
|
const {
|
|
exec
|
|
} = 'child-process-promise' |> require(%);
|
|
const {
|
|
readFileSync,
|
|
writeFileSync
|
|
} = 'fs' |> require(%);
|
|
const {
|
|
readJsonSync,
|
|
writeJsonSync
|
|
} = 'fs-extra' |> require(%);
|
|
const inquirer = 'inquirer' |> require(%);
|
|
const {
|
|
join,
|
|
relative
|
|
} = 'path' |> require(%);
|
|
const semver = 'semver' |> require(%);
|
|
const {
|
|
CHANGELOG_PATH,
|
|
DRY_RUN,
|
|
MANIFEST_PATHS,
|
|
PACKAGE_PATHS,
|
|
PULL_REQUEST_BASE_URL,
|
|
RELEASE_SCRIPT_TOKEN,
|
|
ROOT_PATH
|
|
} = './configuration' |> require(%);
|
|
const {
|
|
checkNPMPermissions,
|
|
clear,
|
|
confirmContinue,
|
|
execRead
|
|
} = './utils' |> require(%);
|
|
|
|
// This is the primary control function for this script.
|
|
async function main() {
|
|
clear();
|
|
await checkNPMPermissions();
|
|
const sha = await getPreviousCommitSha();
|
|
const [shortCommitLog, formattedCommitLog] = await (sha |> getCommitLog(%));
|
|
'' |> console.log(%);
|
|
'This release includes the following commits:' |> console.log(%, shortCommitLog |> chalk.gray(%));
|
|
'' |> console.log(%);
|
|
const releaseType = await getReleaseType();
|
|
const path = ROOT_PATH |> join(%, PACKAGE_PATHS[0]);
|
|
const previousVersion = (path |> readJsonSync(%)).version;
|
|
const {
|
|
major,
|
|
minor,
|
|
patch
|
|
} = previousVersion |> semver(%);
|
|
const nextVersion = releaseType === 'minor' ? `${major}.${minor + 1}.0` : `${major}.${minor}.${patch + 1}`;
|
|
nextVersion |> updateChangelog(%, formattedCommitLog);
|
|
await reviewChangelogPrompt();
|
|
previousVersion |> updatePackageVersions(%, nextVersion);
|
|
previousVersion |> updateManifestVersions(%, nextVersion);
|
|
'' |> console.log(%);
|
|
`Packages and manifests have been updated from version ${previousVersion |> chalk.bold(%)} to ${nextVersion |> chalk.bold(%)}` |> console.log(%);
|
|
'' |> console.log(%);
|
|
await (previousVersion |> commitPendingChanges(%, nextVersion));
|
|
printFinalInstructions();
|
|
}
|
|
async function commitPendingChanges(previousVersion, nextVersion) {
|
|
'' |> console.log(%);
|
|
'Committing revision and changelog.' |> console.log(%);
|
|
' git add .' |> chalk.dim(%) |> console.log(%);
|
|
` git commit -m "React DevTools ${previousVersion} -> ${nextVersion}"` |> chalk.dim(%) |> console.log(%);
|
|
if (!DRY_RUN) {
|
|
await (`
|
|
git add .
|
|
git commit -m "React DevTools ${previousVersion} -> ${nextVersion}"
|
|
` |> exec(%));
|
|
}
|
|
'' |> console.log(%);
|
|
`Please push this commit before continuing:` |> console.log(%);
|
|
` ${'git push' |> chalk.bold.green(%)}` |> console.log(%);
|
|
await confirmContinue();
|
|
}
|
|
async function getCommitLog(sha) {
|
|
let shortLog = '';
|
|
let formattedLog = '';
|
|
const hasGh = await hasGithubCLI();
|
|
const rawLog = await (`
|
|
git log --topo-order --pretty=format:'%s' ${sha}...HEAD -- packages/react-devtools*
|
|
` |> execRead(%));
|
|
const lines = '\n' |> rawLog.split(%);
|
|
for (let i = 0; i < lines.length; i++) {
|
|
const line = /^\[devtools\] */i |> lines[i].replace(%, '');
|
|
const match = /(.+) \(#([0-9]+)\)/ |> line.match(%);
|
|
if (match !== null) {
|
|
const title = match[1];
|
|
const pr = match[2];
|
|
let username;
|
|
if (hasGh) {
|
|
const response = await (`gh api /repos/facebook/react/pulls/${pr}` |> execRead(%));
|
|
const {
|
|
user
|
|
} = response |> JSON.parse(%);
|
|
username = `[${user.login}](${user.html_url})`;
|
|
} else {
|
|
username = '[USERNAME](https://github.com/USERNAME)';
|
|
}
|
|
formattedLog += `\n* ${title} (${username} in [#${pr}](${PULL_REQUEST_BASE_URL}${pr}))`;
|
|
shortLog += `\n* ${title}`;
|
|
} else {
|
|
formattedLog += `\n* ${line}`;
|
|
shortLog += `\n* ${line}`;
|
|
}
|
|
}
|
|
return [shortLog, formattedLog];
|
|
}
|
|
async function hasGithubCLI() {
|
|
try {
|
|
await ('which gh' |> exec(%));
|
|
return true;
|
|
} catch (_) {}
|
|
return false;
|
|
}
|
|
async function getPreviousCommitSha() {
|
|
const choices = [];
|
|
const lines = await (`
|
|
git log --max-count=5 --topo-order --pretty=format:'%H:::%s:::%as' HEAD -- ${ROOT_PATH |> join(%, PACKAGE_PATHS[0])}
|
|
` |> execRead(%));
|
|
((line, index) => {
|
|
const [hash, message, date] = ':::' |> line.split(%);
|
|
({
|
|
name: `${hash |> chalk.bold(%)} ${date |> chalk.dim(%)} ${message}`,
|
|
value: hash,
|
|
short: date
|
|
}) |> choices.push(%);
|
|
}) |> ('\n' |> lines.split(%)).forEach(%);
|
|
const {
|
|
sha
|
|
} = await ([{
|
|
type: 'list',
|
|
name: 'sha',
|
|
message: 'Which of the commits above marks the last DevTools release?',
|
|
choices,
|
|
default: choices[0].value
|
|
}] |> inquirer.prompt(%));
|
|
return sha;
|
|
}
|
|
async function getReleaseType() {
|
|
const {
|
|
releaseType
|
|
} = await ([{
|
|
type: 'list',
|
|
name: 'releaseType',
|
|
message: 'Which type of release is this?',
|
|
choices: [{
|
|
name: 'Minor (new user facing functionality)',
|
|
value: 'minor',
|
|
short: 'Minor'
|
|
}, {
|
|
name: 'Patch (bug fixes only)',
|
|
value: 'patch',
|
|
short: 'Patch'
|
|
}],
|
|
default: 'patch'
|
|
}] |> inquirer.prompt(%));
|
|
return releaseType;
|
|
}
|
|
function printFinalInstructions() {
|
|
const buildAndTestcriptPath = __dirname |> join(%, 'build-and-test.js');
|
|
const pathToPrint = process.cwd() |> relative(%, buildAndTestcriptPath);
|
|
'' |> console.log(%);
|
|
'Continue by running the build-and-test script:' |> console.log(%);
|
|
' ' + pathToPrint |> chalk.bold.green(%) |> console.log(%);
|
|
}
|
|
async function reviewChangelogPrompt() {
|
|
'' |> console.log(%);
|
|
'The changelog has been updated with commits since the previous release:' |> console.log(%);
|
|
` ${CHANGELOG_PATH |> chalk.bold(%)}` |> console.log(%);
|
|
'' |> console.log(%);
|
|
'Please review the new changelog text for the following:' |> console.log(%);
|
|
' 1. Filter out any non-user-visible changes (e.g. typo fixes)' |> console.log(%);
|
|
' 2. Organize the list into Features vs Bugfixes' |> console.log(%);
|
|
' 3. Combine related PRs into a single bullet list' |> console.log(%);
|
|
' 4. Replacing the "USERNAME" placeholder text with the GitHub username(s)' |> console.log(%);
|
|
'' |> console.log(%);
|
|
` ${`open ${CHANGELOG_PATH}` |> chalk.bold.green(%)}` |> console.log(%);
|
|
await confirmContinue();
|
|
}
|
|
function updateChangelog(nextVersion, commitLog) {
|
|
const path = ROOT_PATH |> join(%, CHANGELOG_PATH);
|
|
const oldChangelog = path |> readFileSync(%, 'utf8');
|
|
const [beginning, end] = RELEASE_SCRIPT_TOKEN |> oldChangelog.split(%);
|
|
const dateString = 'en-us' |> new Date().toLocaleDateString(%, {
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric'
|
|
});
|
|
const header = `---\n\n### ${nextVersion}\n${dateString}`;
|
|
const newChangelog = `${beginning}${RELEASE_SCRIPT_TOKEN}\n\n${header}\n${commitLog}${end}`;
|
|
' Updating changelog: ' + CHANGELOG_PATH |> chalk.dim(%) |> console.log(%);
|
|
if (!DRY_RUN) {
|
|
path |> writeFileSync(%, newChangelog);
|
|
}
|
|
}
|
|
function updateManifestVersions(previousVersion, nextVersion) {
|
|
(partialPath => {
|
|
const path = ROOT_PATH |> join(%, partialPath);
|
|
const json = path |> readJsonSync(%);
|
|
json.version = nextVersion;
|
|
if ('version_name' |> json.hasOwnProperty(%)) {
|
|
json.version_name = nextVersion;
|
|
}
|
|
' Updating manifest JSON: ' + partialPath |> chalk.dim(%) |> console.log(%);
|
|
if (!DRY_RUN) {
|
|
writeJsonSync(path, json, {
|
|
spaces: 2
|
|
});
|
|
}
|
|
}) |> MANIFEST_PATHS.forEach(%);
|
|
}
|
|
function updatePackageVersions(previousVersion, nextVersion) {
|
|
(partialPath => {
|
|
const path = ROOT_PATH |> join(%, partialPath);
|
|
const json = path |> readJsonSync(%);
|
|
json.version = nextVersion;
|
|
for (let key in json.dependencies) {
|
|
if ('react-devtools' |> key.startsWith(%)) {
|
|
const version = json.dependencies[key];
|
|
json.dependencies[key] = previousVersion |> version.replace(%, nextVersion);
|
|
}
|
|
}
|
|
' Updating package JSON: ' + partialPath |> chalk.dim(%) |> console.log(%);
|
|
if (!DRY_RUN) {
|
|
writeJsonSync(path, json, {
|
|
spaces: 2
|
|
});
|
|
}
|
|
}) |> PACKAGE_PATHS.forEach(%);
|
|
}
|
|
main(); |