Skip to content

Commit

Permalink
fix: preserve frontmatter when updating changelog (#108)
Browse files Browse the repository at this point in the history
* chore: add failing test for retaining frontmatter

Signed-off-by: Tim Knight <tim.knight1@engineering.digital.dwp.gov.uk>

* fix: preserve frontmatter when updating changelog

fixes #106

Signed-off-by: Tim Knight <tim.knight1@engineering.digital.dwp.gov.uk>

* chore: add tests for upgrade path from standard-version

Signed-off-by: Tim Knight <tim.knight1@engineering.digital.dwp.gov.uk>

* fix: preserve front matter when swapping from standard-version

- Matches based off # Changelog title position rather than new header position

Signed-off-by: Tim Knight <tim.knight1@engineering.digital.dwp.gov.uk>

* chore: run linting

Signed-off-by: Tim Knight <tim.knight1@engineering.digital.dwp.gov.uk>

* chore: refactoring, extract functions

Signed-off-by: Tim Knight <tim.knight1@engineering.digital.dwp.gov.uk>

* chore: front matter usually on separate lines to title

Signed-off-by: Tim Knight <tim.knight1@engineering.digital.dwp.gov.uk>

* chore: apply eslint . --fix

Signed-off-by: Tim Knight <tim.knight1@engineering.digital.dwp.gov.uk>

---------

Signed-off-by: Tim Knight <tim.knight1@engineering.digital.dwp.gov.uk>
  • Loading branch information
TimKnight-DWP authored Oct 31, 2023
1 parent e4419a7 commit abdcfe2
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 9 deletions.
35 changes: 28 additions & 7 deletions lib/lifecycles/changelog.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,38 @@ Changelog.START_OF_LAST_RELEASE_PATTERN = START_OF_LAST_RELEASE_PATTERN

module.exports = Changelog

/**
* Front matter is only extracted and therefore retained in final output where Changelog "header" begins with #Changelog,
* e.g. meets Standard-Version (last release) or commit-and-tag-version(current) format
*/
function extractFrontMatter (oldContent) {
const headerStart = oldContent.indexOf('# Changelog')
return headerStart !== -1 || headerStart !== 0
? oldContent.substring(0, headerStart)
: ''
}

/**
* find the position of the last release and remove header
*/
function extractChangelogBody (oldContent) {
const oldContentStart = oldContent.search(START_OF_LAST_RELEASE_PATTERN)
return oldContentStart !== -1
? oldContent.substring(oldContentStart)
: oldContent
}

function outputChangelog (args, newVersion) {
return new Promise((resolve, reject) => {
createIfMissing(args)
const header = args.header

let oldContent = args.dryRun || args.releaseCount === 0 ? '' : fs.readFileSync(args.infile, 'utf-8')
const oldContentStart = oldContent.search(START_OF_LAST_RELEASE_PATTERN)
// find the position of the last release and remove header:
if (oldContentStart !== -1) {
oldContent = oldContent.substring(oldContentStart)
}
const oldContent = args.dryRun || args.releaseCount === 0 ? '' : fs.readFileSync(args.infile, 'utf-8')

const oldContentBody = extractChangelogBody(oldContent)

const changelogFrontMatter = extractFrontMatter(oldContent)

let content = ''
const context = { version: newVersion }
const changelogStream = conventionalChangelog({
Expand All @@ -48,7 +69,7 @@ function outputChangelog (args, newVersion) {
changelogStream.on('end', function () {
checkpoint(args, 'outputting changes to %s', [args.infile])
if (args.dryRun) console.info(`\n---\n${chalk.gray(content.trim())}\n---\n`)
else writeFile(args, args.infile, header + '\n' + (content + oldContent).replace(/\n+$/, '\n'))
else writeFile(args, args.infile, changelogFrontMatter + header + '\n' + (content + oldContentBody).replace(/\n+$/, '\n'))
return resolve()
})
})
Expand Down
60 changes: 58 additions & 2 deletions test/core.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,17 +165,73 @@ describe('cli', function () {
})

describe('CHANGELOG.md exists', function () {
it('appends the new release above the last release, removing the old header (legacy format)', async function () {
it('appends the new release above the last release, removing the old header (legacy format), and does not retain any front matter', async function () {
const frontMatter =
'---\nstatus: new\n---\n'
mock({
bump: 'patch',
changelog: 'release 1.0.1\n',
fs: { 'CHANGELOG.md': 'legacy header format<a name="1.0.0">\n' },
fs: { 'CHANGELOG.md': frontMatter + 'legacy header format<a name="1.0.0">\n' },
tags: ['v1.0.0']
})
await exec()
const content = fs.readFileSync('CHANGELOG.md', 'utf-8')
content.should.match(/1\.0\.1/)
content.should.not.match(/legacy header format/)
content.should.not.match(/---status: new---/)
})

it('appends the new release above the last release, replacing the old header (standard-version format) with header (new format), and retains any front matter', async function () {
const { header } = require('../defaults')

const standardVersionHeader =
'# Changelog\n\nAll notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.'

const frontMatter =
'---\nstatus: new\n---\n'

const changelog101 =
'### [1.0.1](/compare/v1.0.0...v1.0.1) (YYYY-MM-DD)\n\n\n### Bug Fixes\n\n* patch release ABCDEFXY\n'

const changelog100 =
'### [1.0.0](/compare/v0.0.1...v1.0.0) (YYYY-MM-DD)\n\n\n### Features\n\n* Version one feature set\n'

const initialChangelog = frontMatter + '\n' + standardVersionHeader + '\n' + changelog100

mock({
bump: 'patch',
changelog: changelog101,
fs: { 'CHANGELOG.md': initialChangelog },
tags: ['v1.0.0']
})
await exec()
const content = fs.readFileSync('CHANGELOG.md', 'utf-8')
content.should.equal(frontMatter + '\n' + header + '\n' + changelog101 + changelog100)
})

it('appends the new release above the last release, removing the old header (new format), and retains any front matter', async function () {
const { header } = require('../defaults')
const frontMatter =
'---\nstatus: new\n---\n'

const changelog101 =
'### [1.0.1](/compare/v1.0.0...v1.0.1) (YYYY-MM-DD)\n\n\n### Bug Fixes\n\n* patch release ABCDEFXY\n'

const changelog100 =
'### [1.0.0](/compare/v0.0.1...v1.0.0) (YYYY-MM-DD)\n\n\n### Features\n\n* Version one feature set\n'

const initialChangelog = frontMatter + '\n' + header + '\n' + changelog100

mock({
bump: 'patch',
changelog: changelog101,
fs: { 'CHANGELOG.md': initialChangelog },
tags: ['v1.0.0']
})
await exec()

const content = fs.readFileSync('CHANGELOG.md', 'utf-8')
content.should.equal(frontMatter + '\n' + header + '\n' + changelog101 + changelog100)
})

it('appends the new release above the last release, removing the old header (new format)', async function () {
Expand Down

0 comments on commit abdcfe2

Please sign in to comment.