Skip to content

About

Extracts Changelog from a Common Markdown Format to JSON

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

JTL Changelog Extractor

PHP tool and GitHub Actions bundle that parses a CHANGELOG.md in CommonMark format into structured JSON and feeds the result into the central jtl-software/changelog-page repository.

This repo ships two things:

  1. The PHP CLI tool (src/, bin/changelog-extractor, distributed as a PHAR)
  2. A reusable workflow (.github/workflows/update-changelog.yaml) for the end-to-end flow including cross-repo PR and auto-merge

Purpose

Every release published by a consumer repo (e.g. connector-shopify) should automatically produce a structured changelog entry in the central changelog-page. The extractor parses the consumer's CHANGELOG.md, merges the result into the system context file (changelog-page/storage/systems/<system-name>.json), opens a pull request, and lets auto-merge finish the job.

Architecture decisions

The following decisions come from the DP-379 migration plan and are wired into this repo:

E2.1 PHP 8.3, hard-coded

The reusable workflow pins PHP 8.3. There is no php-version input. Reason: consistency with the JTL production servers and self-hosted runners. When the internal PHP target version changes, four places in this repo have to be updated:

  • .github/workflows/update-changelog.yaml — setup-php step
  • .github/workflows/check.yaml — every setup-php step
  • .github/workflows/release.yaml — setup-php step
  • composer.json — require.php

See the "PHP version upgrade" section below.

E2.2 Distribution as PHAR (release asset)

The reusable workflow downloads the extractor as a PHAR from the GitHub release asset, not via composer global require. There is no rolling release to download from: the workflow resolves which release tag was built from the exact commit the consumer pinned uses: to (see E2.4) and downloads that release's PHAR specifically. This gives us:

  • Reproducible builds (the resolved release pins an exact commit plus its resolved dependencies)
  • Supply chain robustness (no remote package resolution at runtime, and no rolling pointer that can silently change what a pinned consumer runs)
  • Faster cold runs (one curl instead of a Composer install)

E2.3 Hard fail on missing context file

If changelog-page/storage/systems/<system-name>.json does not exist, the workflow aborts with a clear error message. This matches the GitLab behaviour. New systems have to be created manually in changelog-page first (see "Onboarding new systems").

E2.4 Versioning: v2.x.y tags, no rolling pointer

The reusable workflow is released via v2.x.y tags, each with its own GitHub Release carrying the PHAR built at that commit. Consumers pin uses: to the exact commit SHA that tag points at - never a rolling tag (@v2 no longer exists) and never the bare v2.x.y tag name either (tags are mutable; a SHA isn't). Find the SHA for a given tag with:

git rev-parse v2.2.0

The legacy 1.0.x tags from the GitLab era remain as history but are no longer advanced.

Using the reusable workflow

The regular case. The reusable workflow handles the end-to-end flow: generating the app token, checking out the required repos, running the extractor, opening a PR in changelog-page, and enabling auto-merge.

In the consumer repo as .github/workflows/update-changelog.yaml:

name: update-changelog

on:
  release:
    types: [published]

jobs:
  call:
    # Pin to the exact commit SHA of a release tag, never the tag name or a
    # rolling ref - see "E2.4 Versioning" above.
    uses: jtl-software/changelog-extractor/.github/workflows/update-changelog.yaml@e4c939fe6535edeb4b432e6a6945664bd2a6d036 # v2.2.0
    with:
      system-name: shopify
      # changelog-file: CHANGELOG.md  # optional, default
    secrets:
      APP_ID: ${{ secrets.CHANGELOG_PAGE_APP_ID }}
      APP_PRIVATE_KEY: ${{ secrets.CHANGELOG_PAGE_APP_PRIVATE_KEY }}

Inputs

Input Required Default Description
system-name yes — System name (see above)
changelog-file no CHANGELOG.md Path to the changelog file

Secrets

Secret Description
APP_ID App ID of the jtl-changelog-page-writer GitHub App. Org secret CHANGELOG_PAGE_APP_ID
APP_PRIVATE_KEY Private key of the app. Org secret CHANGELOG_PAGE_APP_PRIVATE_KEY

Both org secrets have visibility "Selected repositories" — new consumer repos must be added to that list before they can call this workflow.

The generated app token is restricted to repositories: [changelog-page] (E3.2). The caller repo does not need any token access to changelog-page.

Release process

Releases are automated via .github/workflows/release.yaml when a v* tag is pushed:

  1. Create the tag:
    git tag v2.0.1
    git push origin v2.0.1
  2. The release workflow:
    • Installs Composer dependencies (--no-dev --prefer-dist)
    • Builds changelog-extractor.phar via buildPhar.php
    • Runs the fixture self-test (.github/scripts/verify-fixture.sh)
    • Creates a GitHub release with changelog-extractor.phar as an asset

If the fixture self-test fails, the release is not published. Existing consumers, pinned to older commit SHAs, are unaffected either way - there is nothing rolling for a new release to move underneath them.

First release after migration

v2.0.0 is created manually by Q after review of this PR. The workflow handles every subsequent release automatically.

PHP version upgrade

When the internal PHP target version changes (e.g. to 8.4), four places in this repo need to be touched:

  1. .github/workflows/update-changelog.yaml — setup-php step
  2. .github/workflows/check.yaml — every setup-php step
  3. .github/workflows/release.yaml — setup-php step
  4. composer.json — require.php

After the change run composer update locally and commit composer.lock. Bump to a new major tag v3.0.0 when consumers have to migrate as well, otherwise do a minor bump on v2.

Fallback: Composer install (no PHAR)

For local development or when the PHAR distribution is temporarily unavailable:

git clone https://github.com/jtl-software/changelog-extractor.git
cd changelog-extractor
composer install
php extractor.php --file path/to/CHANGELOG.md --output out.json
# or: ./bin/changelog-extractor --file ...

CLI options:

Option Default Description
--file, -f CHANGELOG.md Input markdown
--output, -o CHANGELOG.json Output JSON
--context, -c (empty) Context JSON. When set, the result is merged into the changelog key of the context
--ticket-link, -t https://issues.jtl-software.de/issues/%s URL template for tickets without an explicit link

Onboarding new systems

The workflow hard-fails when changelog-page/storage/systems/<system-name>.json does not exist (E2.3). To add a new system:

  1. Create a branch in jtl-software/changelog-page.
  2. Create storage/systems/<system-name>.json, minimal shape:
    {
      "name": "<system-name>",
      "label": "<Display name>",
      "description": "<Short description of the system>"
    }
    (changelog is filled in by the extractor later; do not add it by hand.)
  3. Merge the PR.
  4. Invoke the reusable workflow from the consumer repo (see above). On the next release the file is automatically extended with the changelog key.

The exact set of required fields in the context file comes from the template in changelog-page — please keep this doc in sync with changes there.

Tests

.github/workflows/check.yaml runs on every PR (and on push to the default branch):

  • PHPCS against src/, bin/, extractor.php, buildPhar.php using the ConnectorStandard ruleset shipped in phpcs.xml.dist (PSR12 base plus the JTL-connector sniffs from slevomat/coding-standard).
  • PHPStan (level 5) against src/. Runs as an informational, non-blocking step (the legacy GitLab CI defaulted to ENABLE_PHPSTAN=false and never ran PHPStan at all; we keep the findings visible in the step log without gating the merge).
  • Fixture self-test: builds the PHAR, runs it against tests/fixtures/ and asserts structural properties of the result (see tests/fixtures/README.md).
  • Negative test: missing context file must produce a non-zero exit code (E2.3).

PHPCS, PHPStan and their standards are declared in composer.json under require-dev so composer install provisions the full toolchain locally; CI invokes them via vendor/bin/phpcs and vendor/bin/phpstan.

License / author

Original tool: Tim Platzke, JTL-Software GmbH.

About

Extracts Changelog from a Common Markdown Format to JSON

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages