Changelog Generation
Relevant source files
The following files were used as context for generating this wiki page:
The Changelog Generation system in pubm is responsible for aggregating changes from various sources—primarily changesets and conventional commits—and formatting them into a human-readable CHANGELOG.md file. This process occurs during the versioning phase of the release pipeline or can be triggered manually via CLI commands.
The system ensures that every package within a workspace maintains an up-to-date record of its evolution, categorized by the impact of changes (Major, Minor, or Patch). In monorepo configurations, the system supports both fixed versioning (where all packages share a changelog entry) and independent versioning (where each package receives its own localized changelog update).
Sources: packages/core/src/tasks/phases/version.ts, packages/pubm/src/commands/changelog.ts
Core Architecture and Data Flow
The generation process follows a structured pipeline: discovery of changes, transformation into internal entry models, rendering into Markdown, and finally, persistence to the filesystem.
Changelog Pipeline Flow
The following diagram illustrates the sequence from detecting pending changes to updating the physical changelog file.
The system first gathers raw data from .pubm/changesets or Git history, filters them based on the package path, and processes them through specific writers before the final rendering.
Sources: packages/core/src/changeset/changelog.ts:46-64, packages/core/src/changelog/file.ts:4-16
Components and Logic
Entry Processing and Writing
Changes are handled by specialized writer classes that implement the ChangelogWriter interface. For conventional commits, the ConventionalCommitChangelogWriter categorizes entries based on commit types like feat, fix, and perf.
| Component | Description |
|---|---|
ChangelogEntry | An interface representing a single change, including summary, bump type (major/minor/patch), and a unique ID. |
ConventionalCommitChangelogWriter | Transforms commit entries into categorized sections (e.g., Features, Bug Fixes). |
generateChangelog | A high-level function that converts a list of entries and dependency updates into a formatted Markdown string. |
buildChangelogEntries | Iterates through changesets to extract releases specific to a given package path. |
Sources: packages/core/src/changeset/changelog.ts:5-18, packages/core/src/changelog/conventional-commit-writer.ts:16-18, packages/core/src/changeset/changelog.ts:46-64
Category Mapping (Conventional Commits)
The system uses a predefined order and mapping to organize conventional commit entries into sections:
- Features: Derived from
feattypes. - Bug Fixes: Derived from
fixtypes. - Performance: Derived from
perftypes. - Refactoring: Derived from
refactortypes. - Documentation: Derived from
docstypes. - Other Changes: Default category for unknown types.
Sources: packages/core/src/changelog/conventional-commit-writer.ts:7-14
Markdown Rendering
The renderChangelog function in the core package is the primary engine for string construction. It sorts change groups by severity and supports both flat lists and categorized sub-sections.
Rendering Sequence
The renderer ensures that the most impactful changes (Major) appear at the top of the new version section, followed by Minor and Patch changes.
Sources: packages/core/src/changelog/renderer.ts:18-42
Filesystem Integration
The writeChangelogToFile function manages the actual file I/O. It is designed to be non-destructive, prepending new versions to existing files while preserving the standard # Changelog header.
export function writeChangelogToFile(cwd: string, newContent: string): void {
const changelogPath = path.join(cwd, "CHANGELOG.md");
let existing = "";
if (existsSync(changelogPath)) {
existing = readFileSync(changelogPath, "utf-8");
}
const header = "# Changelog\n\n";
const doubleNewline = existing.indexOf("\n\n");
const body =
existing.startsWith("# Changelog") && doubleNewline !== -1
? existing.slice(doubleNewline + 2)
: existing;
writeFileSync(changelogPath, `${header}${newContent}\n${body}`, "utf-8");
}
Sources: packages/core/src/changelog/file.ts:4-16
CLI Commands and Tasks
Changelog generation is exposed through two primary avenues: the pubm changelog command for manual previews and the createVersionTask used during automated releases.
Command Options: pubm changelog
| Option | Description |
|---|---|
--dry-run | Previews the generated content in the console without writing to CHANGELOG.md. |
--version <ver> | Explicitly sets the version header for the new section (defaults to "Unreleased"). |
Sources: packages/pubm/src/commands/changelog.ts:52-57
Versioning Task Logic
In the release pipeline, the system handles different monorepo modes:
- Single/Fixed Mode: A single
CHANGELOG.mdis updated at the root of the project with aggregated entries from all affected packages. - Independent Mode: The system iterates through every package in the version plan, identifies its specific changesets, and updates the
CHANGELOG.mdwithin the respective package directory.
Sources: packages/core/src/tasks/phases/version.ts:66-79, packages/core/src/tasks/phases/version.ts:170-192
The Changelog Generation system provides a robust mechanism for maintaining project history, supporting both automated workflows and manual intervention to ensure clear communication of changes to end-users.