Skip to content

Commit

Permalink
chore: changelog parser and update tool (#856)
Browse files Browse the repository at this point in the history
* feat: parse changelog, check version
* feat: create changelog
* feat: handle other headers
* chore: use reduce to handle any heading
* feat: return the complete changelog
* feat: ensure there are release notes
  • Loading branch information
baseonmars committed Mar 15, 2021
1 parent 22885c8 commit d51a115
Show file tree
Hide file tree
Showing 13 changed files with 948 additions and 4 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,11 @@
#### _Jan. 21, 2021_

**New**

- πŸ‘€ Header links: support for no javascript added

**Bugfixes**

- 🌎 Navigation: More / Close arrow correction
- 🌎 Navigation: More dropdoen menu appears in device gesture control flow
- πŸ”— Pagination links: Apply focus state to active page
Expand Down Expand Up @@ -122,25 +124,30 @@
#### _Nov. 26, 2020_

**New**

- Footer: links accept icon option, which is displayed after then link label
## <sub>v3.0.0</sub>
#### _Nov. 23, 2020_

**Breaking Changes**

- Footer: links are no longer provided by default, see documentation for details
- Header: Local options have changed, see documentation for details

**New**

- ⬆️ Footer: Supply feedback link and links using template locals (NP-662)
- πŸ”— Header: Add skip links, provide option to override main content anchor link (NP-1181)

**Removals**

- Remove OISC warnings from design-system (NP-1285)
- Removed standalone `logo_clickable` component. Only used within header and footer
- Removed standalone `website_feedack` component. Only ever used within footer
details of local option structure.

**Bugfixes**

- Callout: Don't render empty `.cads-callout-label` for standard callouts (no label)
- Footer: links are wrapped in a `nav` element and aria labelled as "Footer Navigation"
- Footer: Fix spacing and responsive styling (NP-1033)
Expand Down Expand Up @@ -272,6 +279,7 @@ details of local option structure.
## <sub>v1.9.0</sub>

#### _Sep. 7, 2020_

* NP-744 Run Browser tests inside CI pipeline
* NP-642 Header takes language switcher paramaters

Expand Down
20 changes: 17 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,12 @@
"husky": "^5.1.3",
"inquirer": "^8.0.0",
"jest": "^26.4.2",
"jest-date-mock": "^1.0.8",
"js-beautify": "^1.13.2",
"jsdom": "^16.5.0",
"lint-staged": "^10.5.4",
"markdown-loader": "^6.0.0",
"marked": "^2.0.0",
"moment": "^2.27.0",
"npm-run-all": "^4.1.5",
"postcss-cli": "^8.1.0",
Expand All @@ -93,7 +95,7 @@
"resolve-url-loader": "^3.1.1",
"sass": "^1.32.8",
"sass-loader": "^10.1.1",
"semver": "^7.3.2",
"semver": "^7.3.4",
"shell-loader": "^1.2.1",
"simple-git": "^2.36.1",
"stylelint": "^13.12.0",
Expand Down Expand Up @@ -122,6 +124,9 @@
"/node_modules/",
"/vendor/",
"/lib/"
],
"setupFiles": [
"jest-date-mock"
]
},
"lint-staged": {
Expand Down
151 changes: 151 additions & 0 deletions scripts/changelog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/* eslint-disable import/prefer-default-export */

const assert = require('assert');
const semver = require('semver');
const fs = require('fs');
const path = require('path');
const marked = require('marked');
const moment = require('moment');

// TODO stronger check for version heading?
const getPreviousVersion = (tokens) =>
tokens.find((t) => /v\d+/.test(t.text)).tokens.find((t) => t.type === 'text')
.text;

const releaseHeading = (version) =>
`## <sub>${version}</sub>
#### ${moment().format('_MMM. D, YYYY_')}\n\n`;

const pendingReleaseNotes = (changelog) => {
const tokens = marked.lexer(changelog.toString());

const previousFullReleaseIndex = tokens.findIndex(
(t) => t.type === 'heading' && t.depth === 2 && !/alpha/.test(t.text)
);

return tokens.slice(0, previousFullReleaseIndex);
};

const pastReleaseNotes = (changelog) => {
const tokens = marked.lexer(changelog.toString());

const previousFullReleaseIndex = tokens.findIndex(
(t) => t.type === 'heading' && t.depth === 2 && !/alpha/.test(t.text)
);

return tokens.slice(previousFullReleaseIndex);
};

const isPendingPreRelease = (tokens) => tokens[0].type !== 'heading';

/**
* Given a list of tokens and a heading, find all list items that appear
* under that heading.
*/
const getItemsFor = (heading, tokens) => {
let items = '';

let i = 0;

while (i < tokens.length) {
const currentToken = tokens[i];
const regex = new RegExp(heading, 'i');

if (currentToken.type === 'paragraph' && regex.test(currentToken.text)) {
// search for list
let j = i + 1;
while (j < tokens.length) {
if (tokens[j].type === 'list') {
items += `${tokens[j].raw.trim()}\n`;
i = j + 1;
break;
} else {
j += 1;
}
}
} else {
i += 1;
}
}

return items.length ? `${items}\n` : '';
};

const findSubHeadings = (tokens) => {
const headings = new Set();
const regex = /\*\*([a-zA-Z ]+)\*\*/;
// eslint-disable-next-line no-restricted-syntax
for (const token of tokens) {
if (regex.test(token.raw)) {
headings.add(regex.exec(token.raw)[1].toLowerCase());
}
}

return [...headings];
};

const capitalise = (string) => string.charAt(0).toUpperCase() + string.slice(1);

const preRelease = (version, changelog) =>
releaseHeading(version) + changelog.toString();

const fullRelease = (version, changelog) => {
const tokens = pendingReleaseNotes(changelog);
const subHeadings = findSubHeadings(tokens);

const previousChangelog = pastReleaseNotes(changelog).reduce(
(acc, item) => acc + item.raw,
''
);

return (
subHeadings.reduce((result, subHeading) => {
const items = getItemsFor(subHeading, tokens);
const title = capitalise(subHeading);

return `${result}**${title}**\n\n${items}`;
}, releaseHeading(version)) + previousChangelog
);
};

const release = (version, changelogPath) => {
if (!changelogPath) {
// eslint-disable-next-line no-param-reassign
changelogPath = path.join(process.cwd(), 'CHANGELOG.md');
}

assert.ok(
semver.valid(version),
`Version must be a valid semver - you entered ${version}`
);

assert.ok(fs.existsSync(changelogPath), 'failed to load CHANGELOG.md');

const changelog = fs.readFileSync(changelogPath).toString();
const tokens = marked.lexer(changelog);

const previousVersion = getPreviousVersion(tokens);

assert.ok(
semver.gt(version, previousVersion),
`The release version must be greater than previous releases. version: ${version} previous: ${previousVersion}`
);

if (semver.prerelease(version)) {
assert.ok(
isPendingPreRelease(tokens),
'There are no pending release notes. Did you update the change log?'
);
return preRelease(version, changelog);
}
return fullRelease(version, changelog);
};

module.exports = {
release,
findSubHeadings,
getItemsFor,
pastReleaseNotes,
pendingReleaseNotes,
};
12 changes: 12 additions & 0 deletions scripts/tests/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module.exports = {
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
env: {
browser: true,
},
rules: {
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/no-empty-function': 'warn',
},
};
43 changes: 43 additions & 0 deletions scripts/tests/__fixtures__/pending-release-after-alpha.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
## <sub>v1.2.0-alpha.0</sub>

#### _Feb. 17, 2021_

**New**

- 🐠: Something new

**Bugfixes**

- πŸ›: a bugfix

## <sub>v1.1.0-alpha.1</sub>

#### _Feb. 12, 2021_

**Bugfixes**

- 🎯 Targeted content: will now scroll back to targeted content on close

## <sub>v1.1.0-alpha.0</sub>

#### _Feb. 4, 2021_

**New**

- 🎯 Targeted content: you can now specifiy the heading level of the title element

**Bugfixes**

- πŸ₯– Breadcrumbs: Layout adjustments when viewed on small screen

## <sub>v1.0.0</sub>

#### _Jan. 21, 2021_

**New**

- πŸ‘€ Header links: support for no javascript added

**Bugfixes**

- 🌎 Navigation: More / Close arrow correction
26 changes: 26 additions & 0 deletions scripts/tests/__fixtures__/pending-release-after-production.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
## <sub>v1.1.1</sub>

#### _Feb. 17, 2021_

**New**

- 🐠: Something new
- 🎯 Targeted content: you can now specifiy the heading level of the title element

**Bugfixes**

- πŸ›: a bugfix
- 🎯 Targeted content: will now scroll back to targeted content on close
- πŸ₯– Breadcrumbs: Layout adjustments when viewed on small screen

## <sub>v1.0.0</sub>

#### _Jan. 21, 2021_

**New**

- πŸ‘€ Header links: support for no javascript added

**Bugfixes**

- 🌎 Navigation: More / Close arrow correction
39 changes: 39 additions & 0 deletions scripts/tests/__fixtures__/pending-release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
**New**

- 🐠: Something new

**Bugfixes**

- πŸ›: a bugfix

## <sub>v1.1.0-alpha.1</sub>

#### _Feb. 12, 2021_

**Bugfixes**

- 🎯 Targeted content: will now scroll back to targeted content on close

## <sub>v1.1.0-alpha.0</sub>

#### _Feb. 4, 2021_

**New**

- 🎯 Targeted content: you can now specifiy the heading level of the title element

**Bugfixes**

- πŸ₯– Breadcrumbs: Layout adjustments when viewed on small screen

## <sub>v1.0.0</sub>

#### _Jan. 21, 2021_

**New**

- πŸ‘€ Header links: support for no javascript added

**Bugfixes**

- 🌎 Navigation: More / Close arrow correction

0 comments on commit d51a115

Please sign in to comment.