Skip to content

Commit

Permalink
Add customizable section header (#201)
Browse files Browse the repository at this point in the history
* Adds sectionHeader argument to allow users to specify a section for dependabot entries. Defaults to dependencies
  • Loading branch information
ChristopherBeltran authored Nov 9, 2023
1 parent c7caf99 commit 4b4ea65
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 35 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)

## [UNRELEASED]
### Added
- Adds a new `sectionHeader` option. See the [README](./README.md#sectionheader) for more details.

### Dependencies
- Bump `stefanzweifel/git-auto-commit-action` from 4 to 5 ([#198](https://github.com/dangoslen/dependabot-changelog-helper/pull/198))
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,11 @@ This is a way to incrementally build a version over time and only release a vers
| `Bump` | The starting word of a dependency bump entry line. Currently only supports single world prefixes. |

If a previous entry was written with a different entry (`Bump` vs `Bumps`), the entry will still get updated for updates within the same version as long as the prefix is a single word.

#### `sectionHeader`

| Default | Description |
| -------------- | ------------------------------------------------- |
| `Dependencies` | The name of section to add Dependabot entries to. |

If `sectionHeader` is not provided, the action will look for a section header matching the pattern `/^### [(Dependencies|DEPENDENCIES)]`.
51 changes: 26 additions & 25 deletions __tests__/changelog-updater.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const CHANGELOG_WITH_PROPER_SECTIONS_AND_ENTRIES = `# Changelog
test('adds an entry to the changelog when section already exists with section', async () => {
mockReadStream(CHANGELOG_WITH_PROPER_SECTIONS_AND_ENTRIES)

await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump')
await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump', 'Dependencies')

expectWrittenChangelogToBe(`# Changelog
Expand All @@ -43,7 +43,7 @@ const CHANGELOG_WITH_PROPER_SECTIONS_AND_ENTRIES_UNRELEASED = `# Changelog
test('adds an entry to the changelog when section exists under default unreleased version', async () => {
mockReadStream(CHANGELOG_WITH_PROPER_SECTIONS_AND_ENTRIES_UNRELEASED)

await updateChangelog(PACKAGE_ENTRY, 'nope', './CHANGELOG.md', 'Bump')
await updateChangelog(PACKAGE_ENTRY, 'nope', './CHANGELOG.md', 'Bump', 'Dependencies')

expectWrittenChangelogToBe(`# Changelog
Expand All @@ -61,7 +61,7 @@ const CHANGELOG_WITH_PROPER_SECTIONS_UNRELEASED = `# Changelog
test('adds an entry to the changelog when section already exists, but no entry', async () => {
mockReadStream(CHANGELOG_WITH_PROPER_SECTIONS_UNRELEASED)

await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump')
await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump', 'Dependencies')

expectWrittenChangelogToBe(`# Changelog
Expand All @@ -79,12 +79,12 @@ test('adds section and an entry to the changelog when version exists but section
fs.createReadStream.mockReturnValue(readable)
fs.readFileSync.mockReturnValue(CHANGELOG_MISSING_DEPENDENCIES)

await updateChangelog(PACKAGE_ENTRY, 'UNRELEASED', './CHANGELOG.md', 'Bump')
await updateChangelog(PACKAGE_ENTRY, 'UNRELEASED', './CHANGELOG.md', 'Bump', 'Changed')

expectWrittenChangelogToBe(`# Changelog
## [UNRELEASED]
### Dependencies
### Changed
- Bump \`package\` from v1 to v2 ([#123](https://github.com/owner/repo/pull/123))`)
})

Expand All @@ -102,7 +102,7 @@ const CHANGELOG_WITH_MULTIPLE_VERSIONS = `# Changelog
test('adds an entry to the changelog - multiple versions', async () => {
mockReadStream(CHANGELOG_WITH_MULTIPLE_VERSIONS)

await updateChangelog(PACKAGE_ENTRY, 'UNRELEASED', './CHANGELOG.md', 'Bump')
await updateChangelog(PACKAGE_ENTRY, 'UNRELEASED', './CHANGELOG.md', 'Bump', 'Dependencies')

expectWrittenChangelogToBe(`# Changelog
Expand All @@ -123,7 +123,7 @@ test('errors when there is no version section', async () => {
mockReadStream(CHANGELOG_WITH_NO_VERSION)

try {
await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump')
await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump', 'Dependencies')
} catch (err) {
expect(err).not.toBeNull()
expect(fs.writeFileSync).toBeCalledTimes(0)
Expand All @@ -139,7 +139,7 @@ const CHANGELOG_WITH_DUPLICATE_ENTRY = `# Changelog
test('does not update the changelog on duplicate entry', async () => {
mockReadStream(CHANGELOG_WITH_DUPLICATE_ENTRY)

await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump')
await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump', 'Dependencies')

expect(fs.writeFileSync).toBeCalledTimes(0)
})
Expand All @@ -157,7 +157,7 @@ const CHANGELOG_WITH_DUPLICATE_ENTRY_NOT_LAST_LINE = `# Changelog
test('does not update the changelog on duplicate entry when not the last item', async () => {
mockReadStream(CHANGELOG_WITH_DUPLICATE_ENTRY_NOT_LAST_LINE)

await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump')
await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump', 'Dependencies')

expect(fs.writeFileSync).toBeCalledTimes(0)
})
Expand All @@ -171,7 +171,7 @@ const CHANGELOG_WITH_ENTRY_TO_UPDATE = `# Changelog
test('updates an entry for an existing package in the same version', async () => {
mockReadStream(CHANGELOG_WITH_ENTRY_TO_UPDATE)

await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump')
await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump', 'Dependencies')

expectWrittenChangelogToBe(
`# Changelog
Expand All @@ -191,7 +191,7 @@ const CHANGELOG_WITH_ENTRY_TO_UPDATE_WITH_PULL_REQUEST = `# Changelog
test('updates an entry with pull request context for an existing package in the same version', async () => {
mockReadStream(CHANGELOG_WITH_ENTRY_TO_UPDATE_WITH_PULL_REQUEST)

await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump')
await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump', 'Dependencies')

expectWrittenChangelogToBe(
`# Changelog
Expand All @@ -211,7 +211,7 @@ const CHANGELOG_WITH_VERSION_MISSING_DEP_SECTION_BUT_HAS_OTHERS = `# Changelog
test('updates version with new section and entry', async () => {
mockReadStream(CHANGELOG_WITH_VERSION_MISSING_DEP_SECTION_BUT_HAS_OTHERS)

await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump')
await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump', 'Dependencies')

expectWrittenChangelogToBe(
`# Changelog
Expand All @@ -237,7 +237,7 @@ const CHANGELOG_WITH_MULTI_VERSION_PACKAGE_UPDATES = `# Changelog
test('does not update lines additional times', async () => {
mockReadStream(CHANGELOG_WITH_MULTI_VERSION_PACKAGE_UPDATES)

await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump')
await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump', 'Dependencies')

expectWrittenChangelogToBe(
`# Changelog
Expand All @@ -256,8 +256,8 @@ test('does not update lines additional times, even with multiple invocations', a
mockReadStream(CHANGELOG_WITH_MULTI_VERSION_PACKAGE_UPDATES)

// Run twice to make sure we only add the PR context once
await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump')
await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump')
await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump', 'Dependencies')
await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump', 'Dependencies')

expectWrittenChangelogToBe(
`# Changelog
Expand Down Expand Up @@ -291,7 +291,7 @@ const CHANGELOG_WITH_EXISTING_SECTION_AND_SEPARATED_SECTIONS = `# Changelog
test('updates existing section when sections separated by blank lines', async () => {
mockReadStream(CHANGELOG_WITH_EXISTING_SECTION_AND_SEPARATED_SECTIONS)

await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump')
await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump', 'Dependencies')

expectWrittenChangelogToBe(
`# Changelog
Expand Down Expand Up @@ -333,7 +333,7 @@ test('updates existing section when sections separated by blank lines contain ne
CHANGELOG_WITH_EXISTING_SECTION_AND_SEPARATED_SECTIONS_WITH_NESTED_ENTRIES
)

await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump')
await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump', 'Dependencies')

expectWrittenChangelogToBe(
`# Changelog
Expand Down Expand Up @@ -373,7 +373,7 @@ const CHANGELOG_WITHOUT_EXISTING_SECTION_AND_SEPARATED_SECTIONS = `# Changelog
test('adds section when sections separated by blank lines', async () => {
mockReadStream(CHANGELOG_WITHOUT_EXISTING_SECTION_AND_SEPARATED_SECTIONS)

await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump')
await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump', 'Dependencies')

expectWrittenChangelogToBe(
`# Changelog
Expand Down Expand Up @@ -417,7 +417,7 @@ test('adds section when sections separated by blank lines contain nested entries
CHANGELOG_WITHOUT_EXISTING_SECTION_AND_SEPARATED_SECTIONS_WITH_NESTED_ENTRIES
)

await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump')
await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump', 'Dependencies')

expectWrittenChangelogToBe(
`# Changelog
Expand Down Expand Up @@ -462,7 +462,7 @@ const CHANGELOG_WITH_EXISTING_SECTION_BETWEEN_OTHERS = `# Changelog
test('updates existing section when between other sections', async () => {
mockReadStream(CHANGELOG_WITH_EXISTING_SECTION_BETWEEN_OTHERS)

await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump')
await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Bump', 'Dependencies')

expectWrittenChangelogToBe(
`# Changelog
Expand Down Expand Up @@ -495,7 +495,7 @@ const CHANGELOG_WITH_PROPER_SECTIONS_AND_ENTRIES_DIFFERENT_PREFIX = `# Changelog
test('adds an entry with a different prefix to the changelog when section already exists with entry', async () => {
mockReadStream(CHANGELOG_WITH_PROPER_SECTIONS_AND_ENTRIES_DIFFERENT_PREFIX)

await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Update')
await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Update', 'Dependencies')

expectWrittenChangelogToBe(`# Changelog
Expand All @@ -514,7 +514,7 @@ const CHANGELOG_WITH_DUPLICATE_ENTRY_DIFFERENT_PREFIX = `# Changelog
test('keeps prefix on entry with a different prefix but is otherwise a duplicate', async () => {
mockReadStream(CHANGELOG_WITH_DUPLICATE_ENTRY_DIFFERENT_PREFIX)

await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Update')
await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Update', 'Dependencies')

expectWrittenChangelogToBe(`# Changelog
Expand All @@ -532,7 +532,7 @@ const CHANGELOG_WITH_EXISTING_ENTRY_DIFFERENT_PREFIX = `# Changelog
test('keeps prefix on entry with a different prefix', async () => {
mockReadStream(CHANGELOG_WITH_EXISTING_ENTRY_DIFFERENT_PREFIX)

await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Update')
await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Update', 'Dependencies')

expectWrittenChangelogToBe(`# Changelog
Expand All @@ -554,7 +554,7 @@ const CHANGELOG_WITH_MULTI_LINE_ENTRY_NO_DEPENDENCY_SECTION = `# Changelog
test('add section and accounts for multi-line entry', async () => {
mockReadStream(CHANGELOG_WITH_MULTI_LINE_ENTRY_NO_DEPENDENCY_SECTION)

await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Update')
await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Update', 'Dependencies')

expectWrittenChangelogToBe(`# Changelog
Expand Down Expand Up @@ -584,7 +584,7 @@ const CHANGELOG_WITH_MULTI_LINE_ENTRY_AND_DEPENDENCY_SECTION_EXISTS = `# Changel
test('updates section with an entry and accounts for multi-line entry', async () => {
mockReadStream(CHANGELOG_WITH_MULTI_LINE_ENTRY_AND_DEPENDENCY_SECTION_EXISTS)

await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Update')
await updateChangelog(PACKAGE_ENTRY, 'v1.0.0', './CHANGELOG.md', 'Update', 'Dependencies')

expectWrittenChangelogToBe(`# Changelog
Expand All @@ -600,6 +600,7 @@ test('updates section with an entry and accounts for multi-line entry', async ()
- Update \`package\` from v1 to v2 ([#123](https://github.com/owner/repo/pull/123))`)
})


function mockReadStream(changelog: string) {
fs.createReadStream.mockImplementation((_: PathLike) => {
return Readable.from([changelog])
Expand Down
5 changes: 5 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ inputs:
"The prefix word (after the hyphen) of the changelog entry, for example: '- [entryPrefix] `dependency` from v1.0 to v2.0'"
required: true
default: 'Bump'
sectionHeader:
description: |
"The section header to add the changelog entry under"
required: true
default: 'Dependencies'
runs:
using: 'node16'
main: 'dist/index.js'
Expand Down
27 changes: 18 additions & 9 deletions src/changelog-updater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ interface ParsedResult {
const UNRELEASED_REGEX = new RegExp(
/^## \[(unreleased|Unreleased|UNRELEASED)\]/
)
const DEPENDENCY_SECTION_REGEX = new RegExp(/^### (Dependencies|DEPENDENCIES)/)
const EMPTY_LINE_REGEX = new RegExp(/^\s*$/)
const SECTION_ENTRY_REGEX = new RegExp(/^\s*- /)

export async function updateChangelog(
entry: DependabotEntry,
version: string,
changelogPath: fs.PathLike,
entryPrefix: string
entryPrefix: string,
sectionHeader: string
): Promise<void> {
const versionRegex: RegExp = buildVersionRegex(version)

Expand All @@ -33,7 +33,8 @@ export async function updateChangelog(
regex,
entry,
changelogPath,
entryPrefix
entryPrefix,
sectionHeader
)

// If we found the version, we have updated the changelog or we had a duplicate
Expand All @@ -49,13 +50,15 @@ async function searchAndUpdateVersion(
versionRegex: RegExp,
entry: DependabotEntry,
changelogPath: fs.PathLike,
entryPrefix: string
entryPrefix: string,
sectionHeader: string
): Promise<Boolean> {
const result = await parseChangelogForEntry(
versionRegex,
entryPrefix,
entry,
changelogPath
changelogPath,
sectionHeader
)

// We could not find the desired version to update by the configuration of the action
Expand All @@ -66,7 +69,7 @@ async function searchAndUpdateVersion(
if (result.foundEntryToUpdate) {
updateEntry(entry, changelogPath, result)
} else if (!result.foundDuplicateEntry) {
addNewEntry(entryPrefix, entry, changelogPath, result)
addNewEntry(entryPrefix, entry, changelogPath, result, sectionHeader)
}

return true
Expand Down Expand Up @@ -103,14 +106,15 @@ function addNewEntry(
prefix: string,
entry: DependabotEntry,
changelogPath: fs.PathLike,
result: ParsedResult
result: ParsedResult,
sectionHeader: string
): void {
// We build the entry string "backwards" so that we can only do one write, and base it on if the correct
// sections exist
let changelogEntry = buildEntryLine(prefix, entry)
const lineNumber = result.lineToUpdate
if (!result.dependencySectionFound) {
changelogEntry = `### Dependencies${EOL}${changelogEntry}`
changelogEntry = `### ${sectionHeader}${EOL}${changelogEntry}`

// Check if the line number is last.
// If not, add a blank line between the last section and the next version
Expand Down Expand Up @@ -199,13 +203,18 @@ async function parseChangelogForEntry(
versionRegex: RegExp,
entryPrefix: string,
entry: DependabotEntry,
changelogPath: fs.PathLike
changelogPath: fs.PathLike,
sectionHeader: string
): Promise<ParsedResult> {
const fileStream = readline.createInterface({
input: fs.createReadStream(changelogPath),
terminal: false
})

const DEPENDENCY_SECTION_REGEX = new RegExp(
`^### (${sectionHeader}|${sectionHeader.toUpperCase()})`
)

let lineNumber = 0
let lineToUpdate = 0
let versionFound = false
Expand Down
9 changes: 8 additions & 1 deletion src/dependabot-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,17 @@ async function run(): Promise<void> {
const label: string = core.getInput('activationLabel')
const changelogPath: PathLike = core.getInput('changelogPath')
const entryPrefix: string = core.getInput('entryPrefix')
const sectionHeader: string = core.getInput('sectionHeader')

if (label !== '' && pullRequestHasLabel(label)) {
const entry: DependabotEntry = getDependabotEntry(github.context.payload)
await updateChangelog(entry, version, changelogPath, entryPrefix)
await updateChangelog(
entry,
version,
changelogPath,
entryPrefix,
sectionHeader
)
}
} catch (err) {
if (err instanceof Error) {
Expand Down

0 comments on commit 4b4ea65

Please sign in to comment.