adachsoft/changelog-linter

Changelog linter tool - validate and convert changelog formats

Maintainers

Package info

gitlab.com/a.adach/changelog-linter

Issues

pkg:composer/adachsoft/changelog-linter

Statistics

Installs: 29

Dependents: 6

Suggesters: 0

Stars: 0

v0.7.0 2026-05-22 11:48 UTC

This package is auto-updated.

Last update: 2026-05-22 10:46:42 UTC


README

A minimal PHP library for validating and converting changelog formats between JSON and Markdown.

It follows a constrained, explicit changelog schema inspired by Keep a Changelog, with first-class support for the following change sections:

  • Added
  • Changed
  • Fixed
  • Removed
  • Deprecated
  • Security
  • Migration Notes (JSON key: migration_notes)

Installation

Install via Composer:

composer require --dev adachsoft/changelog-linter

Usage

CLI Commands

Validate changelog (JSON or Markdown)

vendor/bin/changelog validate --path=changelog.json [--lenient]
vendor/bin/changelog validate --path=CHANGELOG.md [--lenient]
  • For .json files, validation checks the JSON schema, required fields, version ordering and section keys.
  • For .md files, validation parses the Markdown and applies the same rules to the logical model.

--lenient applies only to Markdown input and is ignored for JSON:

  • tolerates unknown/unsupported sections (they are reported as ignored),
  • allows version headers with or without v prefix,
  • recognizes [Unreleased] section.

Generate Markdown from JSON

vendor/bin/changelog generate-md --input=changelog.json --output=CHANGELOG.md
  • Reads a changelog.json file and renders a CHANGELOG.md using the built-in Twig template.
  • All supported sections, including Migration Notes, are rendered as dedicated Markdown sections:
    • ### Added
    • ### Changed
    • ### Fixed
    • ### Removed
    • ### Deprecated
    • ### Security
    • ### Migration Notes

Generate JSON from Markdown

vendor/bin/changelog generate-json \
    --input=CHANGELOG.md \
    --output=changelog.json \
    [--lenient] [--with-created-at]

Options:

  • --lenient: enables tolerant parsing/validation mode for Markdown → JSON.
  • --with-created-at: when set, adds or preserves createdAt for each version.

Behavior of --with-created-at:

  • If the output JSON already exists, the tool reads the existing file and preserves createdAt for known versions.
  • New versions (present only in Markdown) receive a fresh createdAt value.
  • If --with-created-at is not provided, no createdAt field is added (the schema allows it to be omitted).

Programmatic Usage

The library can be used directly from PHP code through the public facade (no internal services needed):

use AdachSoft\ChangelogLinter\Facade\ChangelogFacadeFactory;
use AdachSoft\ChangelogLinter\Facade\Dto\GenerateJsonOptionsDto;

// Create the facade (all dependencies are wired automatically)
$facade = ChangelogFacadeFactory::create();

// 1. Validate a changelog file (JSON or Markdown)
$result = $facade->validate('changelog.json');
if (!$result->isValid()) {
    // handle ValidationResult with errors
}

$resultMd = $facade->validate('CHANGELOG.md');

// 2. Generate Markdown from JSON
$facade->generateMarkdown('changelog.json', 'CHANGELOG.md');

// 3. Generate JSON from Markdown with options
$options = new GenerateJsonOptionsDto(
    inputPath: 'CHANGELOG.md',
    outputPath: 'changelog.json',
    lenient: true,
    withCreatedAt: true,
);
$facade->generateJson($options);

You can also use the facade directly to work with the in-memory changelog model:

use AdachSoft\ChangelogLinter\Facade\Dto\AddVersionEntryDto;
use AdachSoft\ChangelogLinter\Model\ChangeItem;
use AdachSoft\ChangelogLinter\Model\ChangeItemsCollection;

$facade = ChangelogFacadeFactory::create();

// Read from JSON or Markdown
db
$changelog = $facade->readChangelog('changelog.json');

// Add a new version entry with Migration Notes
$dto = new AddVersionEntryDto(
    version: '1.2.0',
    date: '2025-01-01',
    added: new ChangeItemsCollection([new ChangeItem('New feature')]),
    changed: new ChangeItemsCollection([]),
    fixed: new ChangeItemsCollection([]),
    removed: new ChangeItemsCollection([]),
    deprecated: new ChangeItemsCollection([]),
    security: new ChangeItemsCollection([]),
    migrationNotes: new ChangeItemsCollection([new ChangeItem('Run database migrations')]),
);

$updated = $facade->addEntry($changelog, $dto);
$facade->writeChangelog($updated, 'changelog.json');

Composer Scripts

The package can expose convenient composer scripts (depending on your project setup):

composer run changelog:validate    # Validate changelog.json or CHANGELOG.md
composer run changelog:generate    # Generate Markdown from JSON
composer run changelog:from-md     # Generate JSON from Markdown

Configuration (changelog-linter.json)

Optional configuration file located at the repository root.

{
  "markdown": {
    "version_header_prefix": "v",
    "allow_prologue": true
  },
  "json": {
    "created_at_field_name": "createdAt",
    "created_at_format": "Y-m-d H:i:s"
  }
}
  • markdown.version_header_prefix: Prefix in version headings in Markdown (default: "v").
  • markdown.allow_prologue: Whether to allow any text between the title and first version (default: true).
  • json.created_at_field_name: Field name for creation timestamp in JSON (default: "createdAt").
  • json.created_at_format: Datetime format for createdAt (default: "Y-m-d H:i:s").

File Formats

changelog.json (JSON Schema excerpt)

Root-level fields:

  • title (string, optional)
  • intro (string, optional)
  • unreleased (object with changes, optional)
  • versions (array of version objects; required)

unreleased object:

  • changes (object, same structure as for versions)

Version object:

  • version (string, X.Y.Z)
  • date (string, YYYY-MM-DD)
  • createdAt (string, YYYY-MM-DD HH:MM:SS; optional)
  • changes (object) – allowed keys:
    • added: array of strings
    • changed: array of strings
    • fixed: array of strings
    • removed: array of strings
    • deprecated: array of strings
    • security: array of strings
    • migration_notes: array of strings (Migration Notes)

Example changelog.json

{
  "title": "Changelog",
  "intro": "All notable changes to this project will be documented in this file.",
  "unreleased": {
    "changes": {
      "added": ["Upcoming feature"],
      "migration_notes": ["Run database migrations before deploying"]
    }
  },
  "versions": [
    {
      "version": "0.3.0",
      "date": "2025-11-10",
      "createdAt": "2025-11-10 10:00:00",
      "changes": {
        "added": [
          "Support for title/intro/unreleased",
          "New sections: Deprecated/Security/Migration Notes"
        ],
        "changed": ["createdAt optional in schema"],
        "security": [],
        "migration_notes": ["Run migration XYZ before upgrading"]
      }
    }
  ]
}

CHANGELOG.md (Markdown)

# Changelog

## [Unreleased]
### Added
- Upcoming feature

### Migration Notes
- Run database migrations before deploying

## [v0.3.0] - 2025-11-10
### Added
- Support for title/intro/unreleased
- New sections: Deprecated/Security/Migration Notes

### Changed
- createdAt optional in schema

### Migration Notes
- Run migration XYZ before upgrading

Validation Rules

The validator checks for:

  • Valid JSON format
  • Required fields
  • Semantic versioning format (X.Y.Z)
  • Valid date format (YYYY-MM-DD)
  • Optional createdAt format (YYYY-MM-DD HH:ii:ss) when present
  • Versions sorted in descending order
  • No duplicate versions
  • Valid change section keys (added, changed, fixed, removed, deprecated, security, migration_notes)
  • For Markdown: that only supported sections are used (unsupported sections are reported/ignored depending on mode)

Development

Running Tests

composer test

Code Style

composer cs:check    # Check code style
composer cs:fix      # Fix code style

Static Analysis

composer stan

Requirements

  • PHP >= 8.2
  • Composer

Dependencies

  • justinrainbow/json-schema: JSON Schema validation
  • twig/twig: Markdown generation templates
  • adachsoft/collection: Immutable collections
  • phpunit/phpunit: Testing framework