Skip to content
Snippets Groups Projects
Unverified Commit d549cd64 authored by Ryan Laurie's avatar Ryan Laurie Committed by GitHub
Browse files

Create new release note sections (#44967)

* Create new release note sections

* add explanatory text
parent 282fb413
No related branches found
No related tags found
Loading
export const nonUserFacingLabels = [
'.CI & Tests',
'.Building & Releasing',
'Type:Documentation',
];
\ No newline at end of file
import { match } from 'ts-pattern';
import { nonUserFacingLabels } from "./constants";
import { getMilestoneIssues, isLatestRelease } from "./github";
import type { ReleaseProps, Issue } from "./types";
import {
......@@ -34,15 +37,37 @@ SHA-256 checksum for the {{version}} JAR:
{{bug-fixes}}
**Already Fixed**
Issues that we have recently confirmed to have been fixed at some point in the past.
{{already-fixed}}
**Under the Hood**
{{under-the-hood}}
</details>
`;
const isBugIssue = (issue: Issue) => {
const hasLabel = (issue: Issue, label: string) => {
if (typeof issue.labels === 'string') {
return issue.labels.includes("Type:Bug");
return issue.labels.includes(label);
}
return issue.labels.some(tag => tag.name === "Type:Bug");
return issue.labels.some(tag => tag.name === label);
}
const isBugIssue = (issue: Issue) => {
return hasLabel(issue, "Type:Bug");
}
const isAlreadyFixedIssue = (issue: Issue) => {
return hasLabel(issue, ".Already Fixed");
};
const isNonUserFacingIssue = (issue: Issue) => {
return nonUserFacingLabels.some(label => hasLabel(issue, label));
}
const formatIssue = (issue: Issue) => `- ${issue.title} (#${issue.number})`;
......@@ -69,6 +94,35 @@ export const getReleaseTitle = (version: string) => {
return `Metabase ${version}`;
};
enum IssueType {
bugFixes = 'bugFixes',
enhancements = 'enhancements',
alreadyFixedIssues = 'alreadyFixedIssues',
underTheHoodIssues = 'underTheHoodIssues',
}
const issueMap: Record<IssueType, Issue[]> = {
bugFixes: [],
enhancements: [],
alreadyFixedIssues: [],
underTheHoodIssues: [],
};
export const categorizeIssues = (issues: Issue[]) => {
return issues.reduce((issueMap, issue) => {
const category: IssueType = match(issue)
.when(isNonUserFacingIssue, () => IssueType.underTheHoodIssues)
.when(isAlreadyFixedIssue, () => IssueType.alreadyFixedIssues)
.when(isBugIssue, () => IssueType.bugFixes)
.otherwise(() => IssueType.enhancements);
return {
...issueMap,
[category]: [...issueMap[category], issue],
}
}, { ...issueMap });
};
export const generateReleaseNotes = ({
version,
checksum,
......@@ -78,15 +132,16 @@ export const generateReleaseNotes = ({
checksum: string;
issues: Issue[];
}) => {
const bugFixes = issues.filter(isBugIssue);
const enhancements = issues.filter(issue => !isBugIssue(issue));
const issuesByType = categorizeIssues(issues);
return releaseTemplate
.replace(
"{{enhancements}}",
enhancements?.map(formatIssue).join("\n") ?? "",
issuesByType.enhancements.map(formatIssue).join("\n") ?? "",
)
.replace("{{bug-fixes}}", bugFixes?.map(formatIssue).join("\n") ?? "")
.replace("{{bug-fixes}}", issuesByType.bugFixes.map(formatIssue).join("\n") ?? "")
.replace("{{already-fixed}}", issuesByType.alreadyFixedIssues.map(formatIssue).join("\n"))
.replace("{{under-the-hood}}", issuesByType.underTheHoodIssues.map(formatIssue).join("\n"))
.replace("{{docker-tag}}", getDockerTag(version))
.replace("{{download-url}}", getDownloadUrl(version))
.replace("{{version}}", version)
......
import { generateReleaseNotes, getReleaseTitle } from "./release-notes";
import { generateReleaseNotes, getReleaseTitle, categorizeIssues } from "./release-notes";
import type { Issue } from "./types";
describe("Release Notes", () => {
......@@ -25,13 +25,23 @@ describe("Release Notes", () => {
const issues = [
{
number: 1,
title: "Issue 1",
title: "Bug Issue",
labels: [{ name: "Type:Bug" }],
},
{
number: 2,
title: "Issue 2",
labels: [{ name: "Type:Enhancement" }],
title: "Feature Issue",
labels: [{ name: "something" }],
},
{
number: 3,
title: "Issue Already Fixed",
labels: [{ name: ".Already Fixed" }],
},
{
number: 4,
title: "Issue That Users Don't Care About",
labels: [{ name: ".CI & Tests" }],
},
] as Issue[];
......@@ -45,8 +55,10 @@ describe("Release Notes", () => {
expect(notes).toContain("SHA-256 checksum for the v0.2.3 JAR");
expect(notes).toContain("1234567890abcdef");
expect(notes).toContain("**Enhancements**\n\n- Issue 2 (#2)");
expect(notes).toContain("**Bug fixes**\n\n- Issue 1 (#1)");
expect(notes).toContain("**Enhancements**\n\n- Feature Issue (#2)");
expect(notes).toContain("**Bug fixes**\n\n- Bug Issue (#1)");
expect(notes).toContain("**Already Fixed**\n\nIssues that we have recently confirmed to have been fixed at some point in the past.\n\n- Issue Already Fixed (#3)");
expect(notes).toContain("**Under the Hood**\n\n- Issue That Users Don't Care About (#4)");
expect(notes).toContain("metabase/metabase:v0.2.3");
expect(notes).toContain(
......@@ -64,8 +76,10 @@ describe("Release Notes", () => {
expect(notes).toContain("SHA-256 checksum for the v1.2.3 JAR");
expect(notes).toContain("1234567890abcdef");
expect(notes).toContain("**Enhancements**\n\n- Issue 2 (#2)");
expect(notes).toContain("**Bug fixes**\n\n- Issue 1 (#1)");
expect(notes).toContain("**Enhancements**\n\n- Feature Issue (#2)");
expect(notes).toContain("**Bug fixes**\n\n- Bug Issue (#1)");
expect(notes).toContain("**Already Fixed**\n\nIssues that we have recently confirmed to have been fixed at some point in the past.\n\n- Issue Already Fixed (#3)");;
expect(notes).toContain("**Under the Hood**\n\n- Issue That Users Don't Care About (#4)");
expect(notes).toContain("metabase/metabase-enterprise:v1.2.3");
expect(notes).toContain(
......@@ -73,4 +87,106 @@ describe("Release Notes", () => {
);
});
});
describe('categorizeIssues', () => {
it('should categorize bug issues', () => {
const issue = {
number: 1,
title: "Bug Issue",
labels: [{ name: "Type:Bug" }],
} as Issue;
const categorizedIssues = categorizeIssues([issue]);
expect(categorizedIssues.bugFixes).toEqual([issue]);
});
it('should categorize already fixed issues', () => {
const issue = {
number: 3,
title: "Already Fixed Issue",
labels: [{ name: ".Already Fixed" }],
} as Issue;
const categorizedIssues = categorizeIssues([issue]);
expect(categorizedIssues.alreadyFixedIssues).toEqual([issue]);
});
it('should categorize non-user-facing issues', () => {
const issue = {
number: 4,
title: "Non User Facing Issue",
labels: [{ name: ".CI & Tests" }],
} as Issue;
const categorizedIssues = categorizeIssues([issue]);
expect(categorizedIssues.underTheHoodIssues).toEqual([issue]);
});
it('should categorize all other issues as enhancements', () => {
const issue = {
number: 2,
title: "Big Feature",
labels: [{ name: "something" }],
} as Issue;
const categorizedIssues = categorizeIssues([issue]);
expect(categorizedIssues.enhancements).toEqual([issue]);
});
it('should prioritize non-user-facing issues above all', () => {
const issue = {
number: 4,
title: "Non User Facing Issue",
labels: [{ name: ".CI & Tests" }, { name: "Type:Bug" }, { name: ".Already Fixed" }, { name: "Ptitard" } ],
} as Issue;
const categorizedIssues = categorizeIssues([issue]);
expect(categorizedIssues.underTheHoodIssues).toEqual([issue]);
expect(categorizedIssues.bugFixes).toEqual([]);
expect(categorizedIssues.alreadyFixedIssues).toEqual([]);
expect(categorizedIssues.enhancements).toEqual([]);
});
it('should put issues in only one bucket', () => {
const issues = [
{
number: 1,
title: "Bug Issue",
labels: [{ name: "Type:Bug" }],
},
{
number: 2,
title: "Big Feature",
labels: [{ name: "something" }],
},
{
number: 3,
title: "Already Fixed Issue",
labels: [{ name: ".Already Fixed" }],
},
{
number: 4,
title: "Non User Facing Issue",
labels: [{ name: ".CI & Tests" }],
},
{
number: 5,
title: "Docs Issue",
labels: [{ name: "Type:Documentation" }],
},
] as Issue[];
const categorizedIssues = categorizeIssues(issues);
expect(categorizedIssues.bugFixes).toEqual([issues[0]]);
expect(categorizedIssues.enhancements).toEqual([issues[1]]);
expect(categorizedIssues.alreadyFixedIssues).toEqual([issues[2]]);
expect(categorizedIssues.underTheHoodIssues).toEqual([issues[3], issues[4]]);
});
});
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment