зеркало из https://github.com/microsoft/rnx-kit.git
feat(rn-changelog-generator): add `single` command to RN changelog generator script and refactor (#955)
This commit is contained in:
Родитель
e90f6fd854
Коммит
e136a309dd
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"@rnx-kit/rn-changelog-generator": minor
|
||||
---
|
||||
|
||||
Add `single` command to see changelog message and dimensions for a single commit
|
|
@ -61,22 +61,28 @@ git pull
|
|||
popd
|
||||
```
|
||||
|
||||
Then create a GitHub ‘personal access token’ that can be used by the script to
|
||||
fetch commit metadata from the GitHub API.
|
||||
|
||||
1. Visit the [token settings page](https://github.com/settings/tokens).
|
||||
1. Generate a new token and **only** give it the `public_repo` scope.
|
||||
1. Store this token somewhere secure for future usage.
|
||||
|
||||
## Usage
|
||||
|
||||
Generate a changelog for `react-native` commits between versions 0.65.0 and
|
||||
0.66.0:
|
||||
|
||||
```sh
|
||||
npx rn-changelog-generator --base v0.65.0 --compare v0.66.0 --repo ../../../react-native --changelog ../../../react-native/CHANGELOG.md --token [GH_TOKEN] > NEW_CHANGES.md
|
||||
npx rn-changelog-generator --base v0.65.0 --compare v0.66.0 --repo ../../../react-native --changelog ../../../react-native/CHANGELOG.md > NEW_CHANGES.md
|
||||
```
|
||||
|
||||
As explained above, you will need to have a local clone of `react-native`, which
|
||||
is referenced by the `--repo` parameter. You'll also need to provide a GitHub
|
||||
personal access token for the `--token` parameter.
|
||||
is referenced by the `--repo` parameter.
|
||||
|
||||
### [Optional] Get a Github 'personal acccess token'
|
||||
|
||||
This script uses the Github API to fetch commit metadata. This data is
|
||||
accessable without authentication as its public.
|
||||
|
||||
You can optionally provide a GitHub personal access token for the `--token`
|
||||
parameter which may be necessary in case Github API rate-limits your requests.
|
||||
|
||||
Instructions to create a GitHub ‘personal access token’:
|
||||
|
||||
1. Visit the [token settings page](https://github.com/settings/tokens).
|
||||
1. Generate a new token and **only** give it the `public_repo` scope.
|
||||
1. Store this token somewhere secure for future usage.
|
||||
|
|
|
@ -7,26 +7,19 @@ import fs from "fs";
|
|||
import chalk from "chalk";
|
||||
import pLimit from "p-limit";
|
||||
import deepmerge from "deepmerge";
|
||||
import https from "https";
|
||||
import child_process from "child_process";
|
||||
import { IncomingHttpHeaders } from "http";
|
||||
import { fetchCommits, Commit } from "./utils/commits";
|
||||
import getChangeMessage from "./utils/getChangeMessage";
|
||||
import formatCommitLink from "./utils/formatCommitLink";
|
||||
import getChangeDimensions, {
|
||||
CHANGE_TYPE,
|
||||
CHANGE_CATEGORY,
|
||||
ChangeType,
|
||||
ChangeCategory,
|
||||
} from "./utils/getChangeDimensions";
|
||||
|
||||
const execFile = util.promisify(child_process.execFile);
|
||||
|
||||
const CHANGE_TYPE = [
|
||||
"breaking",
|
||||
"added",
|
||||
"changed",
|
||||
"deprecated",
|
||||
"removed",
|
||||
"fixed",
|
||||
"security",
|
||||
"unknown",
|
||||
"failed",
|
||||
] as const;
|
||||
|
||||
const CHANGE_CATEGORY = ["android", "ios", "general", "internal"] as const;
|
||||
|
||||
export const CHANGES_TEMPLATE: Changes = Object.freeze(
|
||||
CHANGE_TYPE.reduce(
|
||||
(acc, key) => ({
|
||||
|
@ -39,105 +32,9 @@ export const CHANGES_TEMPLATE: Changes = Object.freeze(
|
|||
)
|
||||
) as Changes;
|
||||
|
||||
const CHANGELOG_LINE_REGEXP = new RegExp(
|
||||
`(\\[(${[...CHANGE_TYPE, ...CHANGE_CATEGORY].join("|")})\\]s*)+`,
|
||||
"i"
|
||||
);
|
||||
export type PlatformChanges = Record<ChangeCategory, string[]>;
|
||||
|
||||
export interface Commit {
|
||||
sha: string;
|
||||
commit: { message: string };
|
||||
author?: { login: string };
|
||||
}
|
||||
|
||||
export type PlatformChanges = Record<typeof CHANGE_CATEGORY[number], string[]>;
|
||||
|
||||
export type Changes = Record<typeof CHANGE_TYPE[number], PlatformChanges>;
|
||||
|
||||
//#region NETWORK
|
||||
//*****************************************************************************
|
||||
|
||||
function fetchJSON<T>(token: string, path: string) {
|
||||
const host = "api.github.com";
|
||||
console.warn(chalk.yellow(`https://${host}${path}`));
|
||||
return new Promise<{ json: T; headers: IncomingHttpHeaders }>(
|
||||
(resolve, reject) => {
|
||||
let data = "";
|
||||
|
||||
https
|
||||
.get({
|
||||
host,
|
||||
path,
|
||||
headers: {
|
||||
Authorization: `token ${token}`,
|
||||
"User-Agent":
|
||||
"https://github.com/react-native-community/releases/blob/master/scripts/changelog-generator.js",
|
||||
},
|
||||
})
|
||||
.on("response", (response) => {
|
||||
if (response.statusCode !== 200) {
|
||||
return reject(
|
||||
new Error(`[!] Got HTTP status: ${response.statusCode}`)
|
||||
);
|
||||
}
|
||||
|
||||
response.on("data", (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
response.on("end", () => {
|
||||
try {
|
||||
resolve({ json: JSON.parse(data), headers: response.headers });
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
|
||||
response.on("error", (error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function fetchCommits(token: string, base: string, compare: string) {
|
||||
console.warn(chalk.green("Fetch commit data"));
|
||||
console.group();
|
||||
const commits: Commit[] = [];
|
||||
let page = 1;
|
||||
return new Promise<Commit[]>((resolve, reject) => {
|
||||
const fetchPage = () => {
|
||||
fetchJSON<Commit[]>(
|
||||
token,
|
||||
`/repos/facebook/react-native/commits?sha=${compare}&page=${page++}`
|
||||
)
|
||||
.then(({ json, headers }) => {
|
||||
for (const commit of json) {
|
||||
commits.push(commit);
|
||||
if (commit.sha === base) {
|
||||
console.groupEnd();
|
||||
return resolve(commits);
|
||||
}
|
||||
}
|
||||
if (!(headers["link"] as string).includes("next")) {
|
||||
throw new Error(
|
||||
"Did not find commit after paging through all commits"
|
||||
);
|
||||
}
|
||||
setImmediate(fetchPage);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.groupEnd();
|
||||
reject(e);
|
||||
});
|
||||
};
|
||||
fetchPage();
|
||||
});
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
//#endregion
|
||||
export type Changes = Record<ChangeType, PlatformChanges>;
|
||||
|
||||
//#region FILTER COMMITS
|
||||
//*****************************************************************************
|
||||
|
@ -385,108 +282,9 @@ export async function getOffsetBaseCommit(
|
|||
//*****************************************************************************
|
||||
//#endregion
|
||||
|
||||
//#region UTILITIES
|
||||
//*****************************************************************************
|
||||
|
||||
function isAndroidCommit(change: string) {
|
||||
return (
|
||||
!/(\[ios\]|\[general\])/i.test(change) &&
|
||||
(/\b(android|java)\b/i.test(change) || /android/i.test(change))
|
||||
);
|
||||
}
|
||||
|
||||
function isIOSCommit(change: string) {
|
||||
return (
|
||||
!/(\[android\]|\[general\])/i.test(change) &&
|
||||
(/\b(ios|xcode|swift|objective-c|iphone|ipad)\b/i.test(change) ||
|
||||
/ios\b/i.test(change) ||
|
||||
/\brct/i.test(change))
|
||||
);
|
||||
}
|
||||
|
||||
function isBreaking(change: string) {
|
||||
return /\b(breaking)\b/i.test(change);
|
||||
}
|
||||
|
||||
function isAdded(change: string) {
|
||||
return /\b(added)\b/i.test(change);
|
||||
}
|
||||
|
||||
function isChanged(change: string) {
|
||||
return /\b(changed)\b/i.test(change);
|
||||
}
|
||||
|
||||
function isDeprecated(change: string) {
|
||||
return /\b(deprecated)\b/i.test(change);
|
||||
}
|
||||
|
||||
function isRemoved(change: string) {
|
||||
return /\b(removed)\b/i.test(change);
|
||||
}
|
||||
|
||||
function isFixed(change: string) {
|
||||
return /\b(fixed)\b/i.test(change);
|
||||
}
|
||||
|
||||
function isSecurity(change: string) {
|
||||
return /\b(security)\b/i.test(change);
|
||||
}
|
||||
|
||||
function isFabric(change: string) {
|
||||
return /\b(fabric)\b/i.test(change);
|
||||
}
|
||||
|
||||
function isTurboModules(change: string) {
|
||||
return /\b(tm)\b/i.test(change);
|
||||
}
|
||||
|
||||
function isInternal(change: string) {
|
||||
return /\[internal\]/i.test(change);
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
//#endregion
|
||||
|
||||
//#region FORMATTING
|
||||
//*****************************************************************************
|
||||
|
||||
function formatCommitLink(sha: string) {
|
||||
return `https://github.com/facebook/react-native/commit/${sha}`;
|
||||
}
|
||||
|
||||
export function getChangeMessage(item: Commit, onlyMessage = false) {
|
||||
const commitMessage = item.commit.message.split("\n");
|
||||
let entry =
|
||||
commitMessage
|
||||
.reverse()
|
||||
.find((a) => /\[ios\]|\[android\]|\[general\]/i.test(a)) ||
|
||||
commitMessage.reverse()[0];
|
||||
entry = entry.replace(/^((changelog:\s*)?(\[\w+\]\s?)+[\s-]*)/i, ""); //Remove the [General] [whatever]
|
||||
entry = entry.replace(/ \(#\d*\)$/i, ""); //Remove the PR number if it's on the end
|
||||
|
||||
// Capitalize
|
||||
if (/^[a-z]/.test(entry)) {
|
||||
entry = entry.slice(0, 1).toUpperCase() + entry.slice(1);
|
||||
}
|
||||
|
||||
if (onlyMessage) {
|
||||
return entry;
|
||||
}
|
||||
|
||||
const authorSection = `([${item.sha.slice(0, 10)}](${formatCommitLink(
|
||||
item.sha
|
||||
)})${
|
||||
item.author
|
||||
? " by [@" +
|
||||
item.author.login +
|
||||
"](https://github.com/" +
|
||||
item.author.login +
|
||||
")"
|
||||
: ""
|
||||
})`;
|
||||
return `- ${entry} ${authorSection}`;
|
||||
}
|
||||
|
||||
export function getChangelogDesc(
|
||||
commits: Commit[],
|
||||
verbose: boolean,
|
||||
|
@ -496,88 +294,28 @@ export function getChangelogDesc(
|
|||
const commitsWithoutExactChangelogTemplate: string[] = [];
|
||||
|
||||
commits.forEach((item) => {
|
||||
let change = item.commit.message.split("\n").find((line) => {
|
||||
return CHANGELOG_LINE_REGEXP.test(line);
|
||||
});
|
||||
if (!change) {
|
||||
const {
|
||||
changeCategory,
|
||||
changeType,
|
||||
doesNotFollowTemplate,
|
||||
fabric,
|
||||
internal,
|
||||
turboModules,
|
||||
} = getChangeDimensions(item);
|
||||
|
||||
if (doesNotFollowTemplate) {
|
||||
commitsWithoutExactChangelogTemplate.push(item.sha);
|
||||
change = item.commit.message;
|
||||
}
|
||||
|
||||
if (!verbose && (fabric || turboModules || internal)) {
|
||||
return;
|
||||
}
|
||||
const message = getChangeMessage(item, onlyMessage);
|
||||
|
||||
if (!verbose) {
|
||||
if (isFabric(change.split("\n")[0])) return;
|
||||
if (isTurboModules(change.split("\n")[0])) return;
|
||||
if (isInternal(change)) return;
|
||||
}
|
||||
|
||||
if (isBreaking(change)) {
|
||||
if (isAndroidCommit(change)) {
|
||||
acc.breaking.android.push(message);
|
||||
} else if (isIOSCommit(change)) {
|
||||
acc.breaking.ios.push(message);
|
||||
} else {
|
||||
acc.breaking.general.push(message);
|
||||
}
|
||||
} else if (isAdded(change)) {
|
||||
if (isAndroidCommit(change)) {
|
||||
acc.added.android.push(message);
|
||||
} else if (isIOSCommit(change)) {
|
||||
acc.added.ios.push(message);
|
||||
} else {
|
||||
acc.added.general.push(message);
|
||||
}
|
||||
} else if (isChanged(change)) {
|
||||
if (isAndroidCommit(change)) {
|
||||
acc.changed.android.push(message);
|
||||
} else if (isIOSCommit(change)) {
|
||||
acc.changed.ios.push(message);
|
||||
} else {
|
||||
acc.changed.general.push(message);
|
||||
}
|
||||
} else if (isFixed(change)) {
|
||||
if (isAndroidCommit(change)) {
|
||||
acc.fixed.android.push(message);
|
||||
} else if (isIOSCommit(change)) {
|
||||
acc.fixed.ios.push(message);
|
||||
} else {
|
||||
acc.fixed.general.push(message);
|
||||
}
|
||||
} else if (isRemoved(change)) {
|
||||
if (isAndroidCommit(change)) {
|
||||
acc.removed.android.push(message);
|
||||
} else if (isIOSCommit(change)) {
|
||||
acc.removed.ios.push(message);
|
||||
} else {
|
||||
acc.removed.general.push(message);
|
||||
}
|
||||
} else if (isDeprecated(change)) {
|
||||
if (isAndroidCommit(change)) {
|
||||
acc.deprecated.android.push(message);
|
||||
} else if (isIOSCommit(change)) {
|
||||
acc.deprecated.ios.push(message);
|
||||
} else {
|
||||
acc.deprecated.general.push(message);
|
||||
}
|
||||
} else if (isSecurity(change)) {
|
||||
if (isAndroidCommit(change)) {
|
||||
acc.security.android.push(message);
|
||||
} else if (isIOSCommit(change)) {
|
||||
acc.security.ios.push(message);
|
||||
} else {
|
||||
acc.security.general.push(message);
|
||||
}
|
||||
} else if (item.commit.message.match(/changelog/i)) {
|
||||
acc.failed.general.push(message);
|
||||
if (changeType === "failed") {
|
||||
acc[changeType].general.push(message);
|
||||
} else {
|
||||
if (isAndroidCommit(change)) {
|
||||
acc.unknown.android.push(message);
|
||||
} else if (isIOSCommit(change)) {
|
||||
acc.unknown.ios.push(message);
|
||||
} else {
|
||||
acc.unknown.general.push(message);
|
||||
}
|
||||
acc[changeType][changeCategory].push(message);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -741,7 +479,7 @@ export async function getAllChangelogDescriptions(
|
|||
|
||||
export async function run(
|
||||
options: Parameters<typeof getAllChangelogDescriptions>[1] & {
|
||||
token: string;
|
||||
token: string | null;
|
||||
base: string;
|
||||
compare: string;
|
||||
}
|
||||
|
@ -755,66 +493,17 @@ export async function run(
|
|||
return buildMarkDown(options.compare, changes);
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
const argv = require("yargs")
|
||||
.usage(
|
||||
"$0 [args]",
|
||||
"Generate a React Native changelog from the commits and PRs"
|
||||
)
|
||||
.options({
|
||||
base: {
|
||||
alias: "b",
|
||||
string: true,
|
||||
describe:
|
||||
"The base branch/tag/commit to compare against (most likely the previous stable version)",
|
||||
demandOption: true,
|
||||
},
|
||||
compare: {
|
||||
alias: "c",
|
||||
string: true,
|
||||
describe:
|
||||
"The new version branch/tag/commit (most likely the latest release candidate)",
|
||||
demandOption: true,
|
||||
},
|
||||
repo: {
|
||||
alias: "r",
|
||||
string: true,
|
||||
describe: "The path to an up-to-date clone of the react-native repo",
|
||||
demandOption: true,
|
||||
},
|
||||
changelog: {
|
||||
alias: "f",
|
||||
string: true,
|
||||
describe: "The path to the existing CHANGELOG.md file",
|
||||
demandOption: true,
|
||||
default: path.resolve(__dirname, "../CHANGELOG.md"),
|
||||
},
|
||||
token: {
|
||||
alias: "t",
|
||||
string: true,
|
||||
describe:
|
||||
"A GitHub token that has `public_repo` access (generate at https://github.com/settings/tokens)",
|
||||
demandOption: true,
|
||||
},
|
||||
maxWorkers: {
|
||||
alias: "w",
|
||||
number: true,
|
||||
describe:
|
||||
"Specifies the maximum number of concurrent sub-processes that will be spawned",
|
||||
default: 10,
|
||||
},
|
||||
verbose: {
|
||||
alias: "v",
|
||||
boolean: true,
|
||||
describe:
|
||||
"Verbose listing, includes internal changes as well as public-facing changes",
|
||||
demandOption: false,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
.version(false)
|
||||
.help("help").argv;
|
||||
interface GenerateArgs {
|
||||
base: string;
|
||||
compare: string;
|
||||
repo: string;
|
||||
changelog: string;
|
||||
token: string | null;
|
||||
maxWorkers: number;
|
||||
verbose: boolean;
|
||||
}
|
||||
|
||||
function handler(argv: GenerateArgs) {
|
||||
const gitDir = path.join(argv.repo, ".git");
|
||||
git(gitDir, "rev-parse")
|
||||
.catch(() => {
|
||||
|
@ -834,5 +523,61 @@ if (require.main === module) {
|
|||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
handler,
|
||||
args: {
|
||||
base: {
|
||||
alias: "b",
|
||||
string: true,
|
||||
describe:
|
||||
"The base branch/tag/commit to compare against (most likely the previous stable version)",
|
||||
demandOption: true,
|
||||
},
|
||||
compare: {
|
||||
alias: "c",
|
||||
string: true,
|
||||
describe:
|
||||
"The new version branch/tag/commit (most likely the latest release candidate)",
|
||||
demandOption: true,
|
||||
},
|
||||
repo: {
|
||||
alias: "r",
|
||||
string: true,
|
||||
describe: "The path to an up-to-date clone of the react-native repo",
|
||||
demandOption: true,
|
||||
},
|
||||
changelog: {
|
||||
alias: "f",
|
||||
string: true,
|
||||
describe: "The path to the existing CHANGELOG.md file",
|
||||
demandOption: true,
|
||||
default: path.resolve(__dirname, "../CHANGELOG.md"),
|
||||
},
|
||||
token: {
|
||||
alias: "t",
|
||||
string: true,
|
||||
describe:
|
||||
"A GitHub token that has `public_repo` access (generate at https://github.com/settings/tokens)",
|
||||
demandOption: false,
|
||||
default: null,
|
||||
},
|
||||
maxWorkers: {
|
||||
alias: "w",
|
||||
number: true,
|
||||
describe:
|
||||
"Specifies the maximum number of concurrent sub-processes that will be spawned",
|
||||
default: 10,
|
||||
},
|
||||
verbose: {
|
||||
alias: "v",
|
||||
boolean: true,
|
||||
describe:
|
||||
"Verbose listing, includes internal changes as well as public-facing changes",
|
||||
demandOption: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
//*****************************************************************************
|
||||
//#endregion
|
|
@ -1 +1,19 @@
|
|||
export { run } from "./changelog-generator";
|
||||
import generator from "./generator";
|
||||
import single from "./single";
|
||||
|
||||
if (require.main === module) {
|
||||
require("yargs")
|
||||
.command(
|
||||
"$0",
|
||||
"Generate a React Native changelog from the commits and PRs",
|
||||
generator.args,
|
||||
generator.handler
|
||||
)
|
||||
.command(
|
||||
"single",
|
||||
"Generate changelog message and classification for single commit",
|
||||
single.args,
|
||||
single.handler
|
||||
)
|
||||
.help("help").argv;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
import { fetchCommit } from "./utils/commits";
|
||||
import getChangeDimensions from "./utils/getChangeDimensions";
|
||||
import getChangeMessage from "./utils/getChangeMessage";
|
||||
|
||||
interface SingleArgs {
|
||||
commit: string;
|
||||
token: string | null;
|
||||
}
|
||||
|
||||
async function handler(argv: SingleArgs) {
|
||||
const commitData = await fetchCommit(argv.token, argv.commit);
|
||||
console.log(getChangeMessage(commitData));
|
||||
console.log(getChangeDimensions(commitData));
|
||||
}
|
||||
|
||||
export default {
|
||||
handler,
|
||||
args: {
|
||||
token: {
|
||||
alias: "t",
|
||||
string: true,
|
||||
describe:
|
||||
"A GitHub token that has `public_repo` access (generate at https://github.com/settings/tokens)",
|
||||
demandOption: false,
|
||||
default: null,
|
||||
},
|
||||
commit: {
|
||||
alias: "c",
|
||||
string: true,
|
||||
describe: "Commit sha to generate changelog message for",
|
||||
},
|
||||
},
|
||||
};
|
|
@ -0,0 +1,105 @@
|
|||
import https from "https";
|
||||
import chalk from "chalk";
|
||||
import { IncomingHttpHeaders } from "http";
|
||||
|
||||
export interface Commit {
|
||||
sha: string;
|
||||
commit: { message: string };
|
||||
author?: { login: string };
|
||||
}
|
||||
|
||||
function fetchJSON<T>(token: string | null, path: string) {
|
||||
const host = "api.github.com";
|
||||
console.warn(chalk.yellow(`https://${host}${path}`));
|
||||
return new Promise<{ json: T; headers: IncomingHttpHeaders }>(
|
||||
(resolve, reject) => {
|
||||
let data = "";
|
||||
|
||||
https
|
||||
.get({
|
||||
host,
|
||||
path,
|
||||
headers: {
|
||||
...(token != null ? { Authorization: `token ${token}` } : null),
|
||||
"User-Agent":
|
||||
"https://github.com/react-native-community/releases/blob/master/scripts/changelog-generator.js",
|
||||
},
|
||||
})
|
||||
.on("response", (response) => {
|
||||
if (response.statusCode !== 200) {
|
||||
return reject(
|
||||
new Error(`[!] Got HTTP status: ${response.statusCode}`)
|
||||
);
|
||||
}
|
||||
|
||||
response.on("data", (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
response.on("end", () => {
|
||||
try {
|
||||
resolve({ json: JSON.parse(data), headers: response.headers });
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
|
||||
response.on("error", (error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function fetchCommits(
|
||||
token: string | null,
|
||||
base: string,
|
||||
compare: string
|
||||
) {
|
||||
console.warn(chalk.green("Fetch commit data"));
|
||||
console.group();
|
||||
const commits: Commit[] = [];
|
||||
let page = 1;
|
||||
return new Promise<Commit[]>((resolve, reject) => {
|
||||
const fetchPage = () => {
|
||||
fetchJSON<Commit[]>(
|
||||
token,
|
||||
`/repos/facebook/react-native/commits?sha=${compare}&page=${page++}`
|
||||
)
|
||||
.then(({ json, headers }) => {
|
||||
for (const commit of json) {
|
||||
commits.push(commit);
|
||||
if (commit.sha === base) {
|
||||
console.groupEnd();
|
||||
return resolve(commits);
|
||||
}
|
||||
}
|
||||
if (!(headers["link"] as string).includes("next")) {
|
||||
throw new Error(
|
||||
"Did not find commit after paging through all commits"
|
||||
);
|
||||
}
|
||||
setImmediate(fetchPage);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.groupEnd();
|
||||
reject(e);
|
||||
});
|
||||
};
|
||||
fetchPage();
|
||||
});
|
||||
}
|
||||
|
||||
export function fetchCommit(token: string | null, sha: string) {
|
||||
return new Promise<Commit>((resolve, reject) => {
|
||||
fetchJSON<Commit>(token, `/repos/facebook/react-native/commits/${sha}`)
|
||||
.then(({ json }) => {
|
||||
resolve(json);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export default function formatCommitLink(sha: string) {
|
||||
return `https://github.com/facebook/react-native/commit/${sha}`;
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
import { Commit } from "./commits";
|
||||
|
||||
export const CHANGE_TYPE = [
|
||||
"breaking",
|
||||
"added",
|
||||
"changed",
|
||||
"deprecated",
|
||||
"removed",
|
||||
"fixed",
|
||||
"security",
|
||||
"unknown",
|
||||
"failed",
|
||||
] as const;
|
||||
export type ChangeType = typeof CHANGE_TYPE[number];
|
||||
|
||||
export const CHANGE_CATEGORY = [
|
||||
"android",
|
||||
"ios",
|
||||
"general",
|
||||
"internal",
|
||||
] as const;
|
||||
export type ChangeCategory = typeof CHANGE_CATEGORY[number];
|
||||
|
||||
const CHANGELOG_LINE_REGEXP = new RegExp(
|
||||
`(\\[(${[...CHANGE_TYPE, ...CHANGE_CATEGORY].join("|")})\\]s*)+`,
|
||||
"i"
|
||||
);
|
||||
function isAndroidCommit(change: string) {
|
||||
return (
|
||||
!/(\[ios\]|\[general\])/i.test(change) &&
|
||||
(/\b(android|java)\b/i.test(change) || /android/i.test(change))
|
||||
);
|
||||
}
|
||||
|
||||
function isIOSCommit(change: string) {
|
||||
return (
|
||||
!/(\[android\]|\[general\])/i.test(change) &&
|
||||
(/\b(ios|xcode|swift|objective-c|iphone|ipad)\b/i.test(change) ||
|
||||
/ios\b/i.test(change) ||
|
||||
/\brct/i.test(change))
|
||||
);
|
||||
}
|
||||
|
||||
function isBreaking(change: string) {
|
||||
return /\b(breaking)\b/i.test(change);
|
||||
}
|
||||
|
||||
function isAdded(change: string) {
|
||||
return /\b(added)\b/i.test(change);
|
||||
}
|
||||
|
||||
function isChanged(change: string) {
|
||||
return /\b(changed)\b/i.test(change);
|
||||
}
|
||||
|
||||
function isDeprecated(change: string) {
|
||||
return /\b(deprecated)\b/i.test(change);
|
||||
}
|
||||
|
||||
function isRemoved(change: string) {
|
||||
return /\b(removed)\b/i.test(change);
|
||||
}
|
||||
|
||||
function isFixed(change: string) {
|
||||
return /\b(fixed)\b/i.test(change);
|
||||
}
|
||||
|
||||
function isSecurity(change: string) {
|
||||
return /\b(security)\b/i.test(change);
|
||||
}
|
||||
|
||||
function isFabric(change: string) {
|
||||
return /\b(fabric)\b/i.test(change);
|
||||
}
|
||||
|
||||
function isTurboModules(change: string) {
|
||||
return /\b(tm)\b/i.test(change);
|
||||
}
|
||||
|
||||
function isInternal(change: string) {
|
||||
return /\[internal\]/i.test(change);
|
||||
}
|
||||
|
||||
function getChangeType(changelogMsg: string, commitMsg: string): ChangeType {
|
||||
if (isBreaking(changelogMsg)) {
|
||||
return "breaking";
|
||||
} else if (isAdded(changelogMsg)) {
|
||||
return "added";
|
||||
} else if (isChanged(changelogMsg)) {
|
||||
return "changed";
|
||||
} else if (isFixed(changelogMsg)) {
|
||||
return "fixed";
|
||||
} else if (isRemoved(changelogMsg)) {
|
||||
return "removed";
|
||||
} else if (isDeprecated(changelogMsg)) {
|
||||
return "deprecated";
|
||||
} else if (isSecurity(commitMsg)) {
|
||||
return "security";
|
||||
} else if (commitMsg.match(/changelog/i)) {
|
||||
return "failed";
|
||||
} else {
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
function getChangeCategory(commitMsg: string): ChangeCategory {
|
||||
if (isAndroidCommit(commitMsg)) {
|
||||
return "android";
|
||||
} else if (isIOSCommit(commitMsg)) {
|
||||
return "ios";
|
||||
} else {
|
||||
return "general";
|
||||
}
|
||||
}
|
||||
export default function getChangeDimensions(item: Commit) {
|
||||
const commitMsg = item.commit.message;
|
||||
let changelogMsg = commitMsg.split("\n").find((line) => {
|
||||
return CHANGELOG_LINE_REGEXP.test(line);
|
||||
});
|
||||
const doesNotFollowTemplate = !changelogMsg;
|
||||
if (!changelogMsg) {
|
||||
changelogMsg = commitMsg;
|
||||
}
|
||||
|
||||
return {
|
||||
doesNotFollowTemplate,
|
||||
changeCategory: getChangeCategory(changelogMsg),
|
||||
changeType: getChangeType(changelogMsg, commitMsg),
|
||||
fabric: isFabric(changelogMsg.split("\n")[0]),
|
||||
internal: isInternal(changelogMsg),
|
||||
turboModules: isTurboModules(changelogMsg.split("\n")[0]),
|
||||
};
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
import { Commit } from "./commits";
|
||||
import formatCommitLink from "./formatCommitLink";
|
||||
|
||||
export default function getChangeMessage(item: Commit, onlyMessage = false) {
|
||||
const commitMessage = item.commit.message.split("\n");
|
||||
let entry =
|
||||
commitMessage
|
||||
.reverse()
|
||||
.find((a) => /\[ios\]|\[android\]|\[general\]/i.test(a)) ||
|
||||
commitMessage.reverse()[0];
|
||||
entry = entry.replace(/^((changelog:\s*)?(\[\w+\]\s?)+[\s-]*)/i, ""); //Remove the [General] [whatever]
|
||||
entry = entry.replace(/ \(#\d*\)$/i, ""); //Remove the PR number if it's on the end
|
||||
|
||||
// Capitalize
|
||||
if (/^[a-z]/.test(entry)) {
|
||||
entry = entry.slice(0, 1).toUpperCase() + entry.slice(1);
|
||||
}
|
||||
|
||||
if (onlyMessage) {
|
||||
return entry;
|
||||
}
|
||||
|
||||
const authorSection = `([${item.sha.slice(0, 10)}](${formatCommitLink(
|
||||
item.sha
|
||||
)})${
|
||||
item.author
|
||||
? " by [@" +
|
||||
item.author.login +
|
||||
"](https://github.com/" +
|
||||
item.author.login +
|
||||
")"
|
||||
: ""
|
||||
})`;
|
||||
return `- ${entry} ${authorSection}`;
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
{
|
||||
"sha": "50e109c78d3ae9c10d8472d2f4888d7f59214fdd",
|
||||
"node_id": "C_kwDOAbrxp9oAKDUwZTEwOWM3OGQzYWU5YzEwZDg0NzJkMmY0ODg4ZDdmNTkyMTRmZGQ",
|
||||
"commit": {
|
||||
"author": {
|
||||
"name": "Luna Wei",
|
||||
"email": "luwe@fb.com",
|
||||
"date": "2021-12-18T02:35:48Z"
|
||||
},
|
||||
"committer": {
|
||||
"name": "Facebook GitHub Bot",
|
||||
"email": "facebook-github-bot@users.noreply.github.com",
|
||||
"date": "2021-12-18T02:37:37Z"
|
||||
},
|
||||
"message": "Fix test_js/test_js_prev_lts\n\nSummary:\nChangelog: [Internal] Remove un-necessary package installs which was using `npm install flow-bin --save-dev` which was wiping out our `node_modules` from the circleCI yarn install.\n\nIt was un-necessary as we already have `flow-bin` as a dependency in our current set-up.\n\nIn addtion, we were running `npm pack` without properly copying over our package.json dependencies (which occurs in `prepare-package-for-release`) for a consumable react-native package.\n\nThis may not have caused issue but technically we were creating an \"incomplete\" package to do our e2e testing on.\n\nReviewed By: charlesbdudley\n\nDifferential Revision: D33197965\n\nfbshipit-source-id: 6583ef1f8e17333c0f27ecc37635c36ae5a0bb62",
|
||||
"tree": {
|
||||
"sha": "bc76306afa2dda29080c242f8fd999cf47499b1f",
|
||||
"url": "https://api.github.com/repos/facebook/react-native/git/trees/bc76306afa2dda29080c242f8fd999cf47499b1f"
|
||||
},
|
||||
"url": "https://api.github.com/repos/facebook/react-native/git/commits/50e109c78d3ae9c10d8472d2f4888d7f59214fdd",
|
||||
"comment_count": 0,
|
||||
"verification": {
|
||||
"verified": false,
|
||||
"reason": "unsigned",
|
||||
"signature": null,
|
||||
"payload": null
|
||||
}
|
||||
},
|
||||
"url": "https://api.github.com/repos/facebook/react-native/commits/50e109c78d3ae9c10d8472d2f4888d7f59214fdd",
|
||||
"html_url": "https://github.com/facebook/react-native/commit/50e109c78d3ae9c10d8472d2f4888d7f59214fdd",
|
||||
"comments_url": "https://api.github.com/repos/facebook/react-native/commits/50e109c78d3ae9c10d8472d2f4888d7f59214fdd/comments",
|
||||
"author": {
|
||||
"login": "lunaleaps",
|
||||
"id": 1309636,
|
||||
"node_id": "MDQ6VXNlcjEzMDk2MzY=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1309636?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/lunaleaps",
|
||||
"html_url": "https://github.com/lunaleaps",
|
||||
"followers_url": "https://api.github.com/users/lunaleaps/followers",
|
||||
"following_url": "https://api.github.com/users/lunaleaps/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/lunaleaps/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/lunaleaps/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/lunaleaps/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/lunaleaps/orgs",
|
||||
"repos_url": "https://api.github.com/users/lunaleaps/repos",
|
||||
"events_url": "https://api.github.com/users/lunaleaps/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/lunaleaps/received_events",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"committer": {
|
||||
"login": "facebook-github-bot",
|
||||
"id": 6422482,
|
||||
"node_id": "MDQ6VXNlcjY0MjI0ODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/6422482?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/facebook-github-bot",
|
||||
"html_url": "https://github.com/facebook-github-bot",
|
||||
"followers_url": "https://api.github.com/users/facebook-github-bot/followers",
|
||||
"following_url": "https://api.github.com/users/facebook-github-bot/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/facebook-github-bot/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/facebook-github-bot/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/facebook-github-bot/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/facebook-github-bot/orgs",
|
||||
"repos_url": "https://api.github.com/users/facebook-github-bot/repos",
|
||||
"events_url": "https://api.github.com/users/facebook-github-bot/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/facebook-github-bot/received_events",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"parents": [
|
||||
{
|
||||
"sha": "d1e359a15a9398ad64335b7e135c187bb7442373",
|
||||
"url": "https://api.github.com/repos/facebook/react-native/commits/d1e359a15a9398ad64335b7e135c187bb7442373",
|
||||
"html_url": "https://github.com/facebook/react-native/commit/d1e359a15a9398ad64335b7e135c187bb7442373"
|
||||
}
|
||||
],
|
||||
"stats": {
|
||||
"total": 26,
|
||||
"additions": 7,
|
||||
"deletions": 19
|
||||
},
|
||||
"files": [
|
||||
{
|
||||
"sha": "9129098dce574b342aa7d9223041f80806e51306",
|
||||
"filename": ".circleci/config.yml",
|
||||
"status": "modified",
|
||||
"additions": 2,
|
||||
"deletions": 2,
|
||||
"changes": 4,
|
||||
"blob_url": "https://github.com/facebook/react-native/blob/50e109c78d3ae9c10d8472d2f4888d7f59214fdd/.circleci/config.yml",
|
||||
"raw_url": "https://github.com/facebook/react-native/raw/50e109c78d3ae9c10d8472d2f4888d7f59214fdd/.circleci/config.yml",
|
||||
"contents_url": "https://api.github.com/repos/facebook/react-native/contents/.circleci/config.yml?ref=50e109c78d3ae9c10d8472d2f4888d7f59214fdd",
|
||||
"patch": "@@ -78,7 +78,7 @@ commands:\n steps:\n - restore_cache:\n keys:\n- - v4-yarn-cache-{{ arch }}-{{ checksum \"yarn.lock\" }}\n+ - v5-yarn-cache-{{ arch }}-{{ checksum \"yarn.lock\" }}\n - run:\n name: \"Yarn: Install Dependencies\"\n command: |\n@@ -90,7 +90,7 @@ commands:\n - save_cache:\n paths:\n - ~/.cache/yarn\n- key: v4-yarn-cache-{{ arch }}-{{ checksum \"yarn.lock\" }}\n+ key: v5-yarn-cache-{{ arch }}-{{ checksum \"yarn.lock\" }}\n \n install_buck_tooling:\n steps:"
|
||||
},
|
||||
{
|
||||
"sha": "fa7fe3f6e556ef24a986ebcaf6a4160a62761ac9",
|
||||
"filename": "scripts/run-ci-e2e-tests.js",
|
||||
"status": "modified",
|
||||
"additions": 5,
|
||||
"deletions": 17,
|
||||
"changes": 22,
|
||||
"blob_url": "https://github.com/facebook/react-native/blob/50e109c78d3ae9c10d8472d2f4888d7f59214fdd/scripts/run-ci-e2e-tests.js",
|
||||
"raw_url": "https://github.com/facebook/react-native/raw/50e109c78d3ae9c10d8472d2f4888d7f59214fdd/scripts/run-ci-e2e-tests.js",
|
||||
"contents_url": "https://api.github.com/repos/facebook/react-native/contents/scripts/run-ci-e2e-tests.js?ref=50e109c78d3ae9c10d8472d2f4888d7f59214fdd",
|
||||
"patch": "@@ -56,25 +56,13 @@ try {\n }\n }\n \n- if (argv.js) {\n- describe('Install Flow');\n- if (\n- tryExecNTimes(\n- () => {\n- return exec('npm install --save-dev flow-bin').code;\n- },\n- numberOfRetries,\n- () => exec('sleep 10s'),\n- )\n- ) {\n- echo('Failed to install Flow');\n- echo('Most common reason is npm registry connectivity, try again');\n- exitCode = 1;\n- throw Error(exitCode);\n- }\n+ describe('Create react-native package');\n+ if (exec('node ./scripts/set-rn-version.js --version 1000.0.0').code) {\n+ echo('Failed to set version and update package.json ready for release');\n+ exitCode = 1;\n+ throw Error(exitCode);\n }\n \n- describe('Create react-native package');\n if (exec('npm pack').code) {\n echo('Failed to pack react-native');\n exitCode = 1;"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,26 +1,20 @@
|
|||
import https from "https";
|
||||
import { EventEmitter } from "events";
|
||||
import fs from "fs";
|
||||
import { promises as fs } from "fs";
|
||||
import path from "path";
|
||||
import util from "util";
|
||||
import deepmerge from "deepmerge";
|
||||
|
||||
const readFile = util.promisify(fs.readFile);
|
||||
|
||||
import {
|
||||
CHANGES_TEMPLATE,
|
||||
git,
|
||||
run,
|
||||
fetchCommits,
|
||||
getAllChangelogDescriptions,
|
||||
getChangeMessage,
|
||||
getChangelogDesc,
|
||||
getOffsetBaseCommit,
|
||||
getOriginalCommit,
|
||||
getFirstCommitAfterForkingFromMain,
|
||||
Changes,
|
||||
PlatformChanges,
|
||||
} from "../src/changelog-generator";
|
||||
} from "../src/generator";
|
||||
|
||||
if (!process.env.RN_REPO) {
|
||||
throw new Error(
|
||||
|
@ -39,7 +33,7 @@ function requestWithFixtureResponse(fixture: string) {
|
|||
(responseEmitter as any).headers = { link: 'rel="next"' };
|
||||
setImmediate(() => {
|
||||
requestEmitter.emit("response", responseEmitter);
|
||||
readFile(path.join(__dirname, "__fixtures__", fixture), "utf-8").then(
|
||||
fs.readFile(path.join(__dirname, "__fixtures__", fixture), "utf-8").then(
|
||||
(data) => {
|
||||
responseEmitter.emit("data", data);
|
||||
responseEmitter.emit("end");
|
||||
|
@ -95,23 +89,6 @@ describe(getOffsetBaseCommit, () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe(getChangeMessage, () => {
|
||||
it("formats a changelog entry", () => {
|
||||
expect(
|
||||
getChangeMessage({
|
||||
sha: "abcde123456789",
|
||||
commit: {
|
||||
message:
|
||||
"Some ignored commit message\n\n[iOS] [Fixed] - Some great fixes! (#42)",
|
||||
},
|
||||
author: { login: "alloy" },
|
||||
})
|
||||
).toEqual(
|
||||
"- Some great fixes! ([abcde12345](https://github.com/facebook/react-native/commit/abcde123456789) by [@alloy](https://github.com/alloy))"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("functions that hit GitHub's commits API", () => {
|
||||
// The 2nd to last commit in commits-v0.60.5-page-2.json, which is the 59th commit.
|
||||
const base = "53e32a47e4062f428f8d714333236cedbe05b482";
|
||||
|
@ -138,20 +115,6 @@ describe("functions that hit GitHub's commits API", () => {
|
|||
Object.defineProperty(https, "get", { value: getMock });
|
||||
});
|
||||
|
||||
describe(fetchCommits, () => {
|
||||
it("paginates back from `compare` to `base`", () => {
|
||||
return fetchCommits("authn-token", base, compare).then((commits) => {
|
||||
expect(commits.length).toEqual(59);
|
||||
expect(commits[0].sha).toEqual(
|
||||
"35300147ca66677f42e8544264be72ac0e9d1b45"
|
||||
);
|
||||
expect(commits[30].sha).toEqual(
|
||||
"99bc31cfa609e838779c29343684365a2ed6169f"
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe(run, () => {
|
||||
it("fetches commits, filters them, and generates markdown", () => {
|
||||
return run({
|
|
@ -0,0 +1,81 @@
|
|||
import https from "https";
|
||||
import { EventEmitter } from "events";
|
||||
import { promises as fs } from "fs";
|
||||
import path from "path";
|
||||
|
||||
import { fetchCommits, fetchCommit } from "../../src/utils/commits";
|
||||
|
||||
console.warn = () => {};
|
||||
console.error = () => {};
|
||||
|
||||
function requestWithFixtureResponse(fixture: string) {
|
||||
const requestEmitter = new EventEmitter();
|
||||
const responseEmitter = new EventEmitter();
|
||||
(responseEmitter as any).statusCode = 200;
|
||||
(responseEmitter as any).headers = { link: 'rel="next"' };
|
||||
setImmediate(() => {
|
||||
requestEmitter.emit("response", responseEmitter);
|
||||
fs.readFile(
|
||||
path.join(__dirname, "..", "__fixtures__", fixture),
|
||||
"utf-8"
|
||||
).then((data) => {
|
||||
responseEmitter.emit("data", data);
|
||||
responseEmitter.emit("end");
|
||||
});
|
||||
});
|
||||
return requestEmitter;
|
||||
}
|
||||
|
||||
describe("functions that hit GitHub's commits API", () => {
|
||||
// The 2nd to last commit in commits-v0.60.5-page-2.json, which is the 59th commit.
|
||||
const base = "53e32a47e4062f428f8d714333236cedbe05b482";
|
||||
// The first commit in commits-v0.60.5-page-1.json, which is the last chronologically as the
|
||||
// GH API returns commits in DESC order.
|
||||
const compare = "35300147ca66677f42e8544264be72ac0e9d1b45";
|
||||
const internalCommit = "50e109c78d3ae9c10d8472d2f4888d7f59214fdd";
|
||||
|
||||
beforeAll(() => {
|
||||
const getMock = jest.fn((uri) => {
|
||||
if (
|
||||
uri.path ===
|
||||
`/repos/facebook/react-native/commits?sha=${compare}&page=1`
|
||||
) {
|
||||
return requestWithFixtureResponse("commits-v0.60.5-page-1.json");
|
||||
} else if (
|
||||
uri.path ===
|
||||
`/repos/facebook/react-native/commits?sha=${compare}&page=2`
|
||||
) {
|
||||
return requestWithFixtureResponse("commits-v0.60.5-page-2.json");
|
||||
} else if (
|
||||
uri.path === `/repos/facebook/react-native/commits/${internalCommit}`
|
||||
) {
|
||||
return requestWithFixtureResponse("commit-internal.json");
|
||||
} else {
|
||||
throw new Error(`Unexpected request: ${uri.path}`);
|
||||
}
|
||||
});
|
||||
Object.defineProperty(https, "get", { value: getMock });
|
||||
});
|
||||
|
||||
describe(fetchCommits, () => {
|
||||
it("paginates back from `compare` to `base`", () => {
|
||||
return fetchCommits("authn-token", base, compare).then((commits) => {
|
||||
expect(commits.length).toEqual(59);
|
||||
expect(commits[0].sha).toEqual(
|
||||
"35300147ca66677f42e8544264be72ac0e9d1b45"
|
||||
);
|
||||
expect(commits[30].sha).toEqual(
|
||||
"99bc31cfa609e838779c29343684365a2ed6169f"
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe(fetchCommit, () => {
|
||||
it("returns metadata for single commit", () => {
|
||||
return fetchCommit(null, internalCommit).then((commitMetadata) => {
|
||||
expect(commitMetadata.sha).toEqual(internalCommit);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,41 @@
|
|||
import getChangeDimensions from "../../src/utils/getChangeDimensions";
|
||||
|
||||
console.warn = () => {};
|
||||
console.error = () => {};
|
||||
|
||||
describe(getChangeDimensions, () => {
|
||||
it("should have fixed, ios dimensions", () => {
|
||||
const dimensions = getChangeDimensions({
|
||||
sha: "abcde123456789",
|
||||
commit: {
|
||||
message:
|
||||
"Some ignored commit message\n\n[iOS] [Fixed] - Some great fixes! (#42)",
|
||||
},
|
||||
author: { login: "alloy" },
|
||||
});
|
||||
expect(dimensions.changeCategory).toBe("ios");
|
||||
expect(dimensions.changeType).toBe("fixed");
|
||||
expect(dimensions.fabric).toBe(false);
|
||||
expect(dimensions.turboModules).toBe(false);
|
||||
expect(dimensions.doesNotFollowTemplate).toBe(false);
|
||||
});
|
||||
|
||||
it("should have failed, general dimensions", () => {
|
||||
const dimensions = getChangeDimensions({
|
||||
sha: "50e109c78d3ae9c10d8472d2f4888d7f59214fdd",
|
||||
commit: {
|
||||
message:
|
||||
'Fix test_js/test_js_prev_lts\n\nSummary:\nChangelog: [Internal] Remove un-necessary package installs which was using `npm install flow-bin --save-dev` which was wiping out our `node_modules` from the circleCI yarn install.\n\nIt was un-necessary as we already have `flow-bin` as a dependency in our current set-up.\n\nIn addtion, we were running `npm pack` without properly copying over our package.json dependencies (which occurs in `prepare-package-for-release`) for a consumable react-native package.\n\nThis may not have caused issue but technically we were creating an "incomplete" package to do our e2e testing on.\n\nReviewed By: charlesbdudley\n\nDifferential Revision: D33197965\n\nfbshipit-source-id: 6583ef1f8e17333c0f27ecc37635c36ae5a0bb62',
|
||||
},
|
||||
author: {
|
||||
login: "lunaleaps",
|
||||
},
|
||||
});
|
||||
expect(dimensions.doesNotFollowTemplate).toBe(false);
|
||||
expect(dimensions.fabric).toBe(false);
|
||||
expect(dimensions.turboModules).toBe(false);
|
||||
expect(dimensions.internal).toBe(true);
|
||||
expect(dimensions.changeCategory).toBe("general");
|
||||
expect(dimensions.changeType).toBe("failed");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
import getChangeMessage from "../../src/utils/getChangeMessage";
|
||||
|
||||
console.warn = () => {};
|
||||
console.error = () => {};
|
||||
|
||||
describe(getChangeMessage, () => {
|
||||
it("formats a changelog entry", () => {
|
||||
expect(
|
||||
getChangeMessage({
|
||||
sha: "abcde123456789",
|
||||
commit: {
|
||||
message:
|
||||
"Some ignored commit message\n\n[iOS] [Fixed] - Some great fixes! (#42)",
|
||||
},
|
||||
author: { login: "alloy" },
|
||||
})
|
||||
).toEqual(
|
||||
"- Some great fixes! ([abcde12345](https://github.com/facebook/react-native/commit/abcde123456789) by [@alloy](https://github.com/alloy))"
|
||||
);
|
||||
});
|
||||
});
|
Загрузка…
Ссылка в новой задаче