Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions commit-types.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
/**
* The single source of truth for which Conventional Commit types this workspace accepts, and which release level (if any) each one triggers. Both `commitlint.config.ts` and `release-workspace.config.ts` import this file so a commit type can never trigger a release without also passing commit-message validation, or the reverse. One definition, not two that could drift apart.
* The single source of truth for which Conventional Commit types this workspace accepts, and which release level each one triggers; every type releases, so no change to a package can go unpublished. Both `commitlint.config.ts` and `release-workspace.config.ts` import this file so a commit type can never trigger a release without also passing commit-message validation, or the reverse. One definition, not two that could drift apart.
*/
export interface CommitTypeDefinition {
readonly type: string;
readonly section: string;
readonly release: "patch" | "minor" | false;
readonly release: "patch" | "minor";
}

export const commitTypes: readonly CommitTypeDefinition[] = [
{ type: "feat", section: "Features", release: "minor" },
{ type: "fix", section: "Bug Fixes", release: "patch" },
{ type: "perf", section: "Performance Improvements", release: "patch" },
{ type: "revert", section: "Reverts", release: "patch" },
{ type: "docs", section: "Documentation", release: false },
{ type: "style", section: "Styles", release: false },
{ type: "refactor", section: "Code Refactoring", release: false },
{ type: "test", section: "Tests", release: false },
{ type: "build", section: "Build System", release: false },
{ type: "ci", section: "Continuous Integration", release: false },
{ type: "chore", section: "Chores", release: false },
{ type: "docs", section: "Documentation", release: "patch" },
{ type: "style", section: "Styles", release: "patch" },
{ type: "refactor", section: "Code Refactoring", release: "patch" },
{ type: "test", section: "Tests", release: "patch" },
{ type: "build", section: "Build System", release: "patch" },
{ type: "ci", section: "Continuous Integration", release: "patch" },
{ type: "chore", section: "Chores", release: "patch" },
];

export const commitTypeNames: readonly string[] = commitTypes.map(
Expand Down
13 changes: 7 additions & 6 deletions release-workspace.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,23 @@ import { commitTypes } from "./commit-types.ts";
*/

/**
* commit-analyzer only falls back to its default rules, which include "a breaking change is a major release", when no custom rule matches a commit. A breaking `feat` matches `feat -> minor` here, so without this rule it would release as a minor version.
* Every commit type releases, at the level `commit-types.ts` names, and appears in the notes under its own section. commit-analyzer only falls back to its default rules, which include "a breaking change is a major release", when no custom rule matches a commit. A breaking `feat` matches `feat -> minor` here, so without this rule it would release as a minor version.
*/
const releaseRules = [
{ breaking: true, release: "major" },
...commitTypes
.filter((entry) => entry.release !== false)
.map((entry) => ({ type: entry.type, release: entry.release })),
...commitTypes.map((entry) => ({
type: entry.type,
release: entry.release,
})),
];

/**
* Every commit type gets its section name. A type that earns no release is hidden from the notes, which is the conventionalcommits preset's own default for those types, stated here so that a type earning a release can never be hidden by omission.
* Every commit type gets its section name and is shown. The conventionalcommits preset hides docs, style, refactor, test, build, ci and chore by default, so `hidden: false` is stated for each type rather than left to that default: a release must never publish empty notes.
*/
const noteTypes = commitTypes.map((entry) => ({
type: entry.type,
section: entry.section,
hidden: entry.release === false,
hidden: false,
}));

const config: Pick<
Expand Down
13 changes: 4 additions & 9 deletions release-workspace.config.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,27 +60,22 @@ const noteEntries = requireArray(
"generateNotes.presetConfig.types",
).filter(isTypedEntry);

const releasingTypes = commitTypes
.filter((entry) => entry.release !== false)
.map((entry) => entry.type);
const releasingTypes = commitTypes.map((entry) => entry.type);

describe("release-workspace.config.ts", () => {
it("analyses and renders commits under the same preset", () => {
// A writer is only guaranteed to render the commit shapes its own preset's parser produces.
expect(generateNotes.preset).toBe(analyzeCommits.preset);
});

it("releases exactly the commit types commit-types.ts marks as releasing", () => {
it("releases every commit type commit-types.ts lists", () => {
expect(releaseRules.map((rule) => rule.type).sort()).toStrictEqual(
[...releasingTypes].sort(),
);
});

it("releases each type at the level commit-types.ts names", () => {
for (const { type, release } of commitTypes) {
if (release === false) {
continue;
}
expect(releaseRules.find((rule) => rule.type === type)?.release).toBe(
release,
);
Expand All @@ -107,8 +102,8 @@ describe("release-workspace.config.ts", () => {
}
});

it("hides no type that earns a release from the notes", () => {
// The preset hides docs, style, chore, refactor, test, build and ci by default; a type that releases must never inherit that, or its release would publish empty notes.
it("hides no type from the notes", () => {
// The preset hides docs, style, chore, refactor, test, build and ci by default; every type releases, so none may inherit that, or its release would publish empty notes.
for (const type of releasingTypes) {
expect(
noteEntries.find((entry) => entry.type === type)?.hidden,
Expand Down
Loading