JSTQL-JS-Transform/output_testing/35prepare-release.js

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();