Skip to content

Commit 189bf3a

Browse files
authored
Merge pull request #5795 from alphagov/feature/rebrand
Feature: Brand refresh
2 parents 1bc5abe + 3b43bb2 commit 189bf3a

84 files changed

Lines changed: 2670 additions & 314 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build-release.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ jobs:
3434
with:
3535
script: |
3636
const { validateVersion } = await import('${{ github.workspace }}/.github/workflows/scripts/changelog-release-helper.mjs')
37+
const frontendPackage = await import('../../../packages/govuk-frontend/package.json', { with: { type: 'json' })
3738
38-
validateVersion('${{ inputs.version }}')
39+
validateVersion('${{ inputs.version }}', frontendPackage.version)
3940
4041
- name: Update package version
4142
run: npm version --no-git-tag-version --workspace govuk-frontend ${{ inputs.version }}
@@ -45,16 +46,17 @@ jobs:
4546
with:
4647
script: |
4748
const { updateChangelog } = await import('${{ github.workspace }}/.github/workflows/scripts/changelog-release-helper.mjs')
49+
const frontendPackage = await import('../../../packages/govuk-frontend/package.json', { with: { type: 'json' })
4850
49-
updateChangelog('${{ inputs.version }}')
51+
updateChangelog('${{ inputs.version }}', frontendPackage.version)
5052
5153
- name: Generate release notes
5254
uses: actions/github-script@v7.0.1
5355
with:
5456
script: |
5557
const { generateReleaseNotes } = await import('${{ github.workspace }}/.github/workflows/scripts/changelog-release-helper.mjs')
5658
57-
generateReleaseNotes()
59+
generateReleaseNotes('${{ inputs.version }}')
5860
5961
- name: Build release
6062
run: npm run build:release

.github/workflows/publish-release-to-github.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ jobs:
5555
script: |
5656
const { generateReleaseNotes } = await import('${{ github.workspace }}/.github/workflows/scripts/changelog-release-helper.mjs')
5757
58-
await generateReleaseNotes()
58+
await generateReleaseNotes('${{ steps.create-github-tag.outputs.GH_TAG }}')
5959
6060
- name: Create GitHub release
6161
run: |

.github/workflows/scripts/changelog-release-helper.mjs

Lines changed: 81 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -16,26 +16,17 @@ const processingErrorMessage =
1616
* - The version increments the current version by more than one possible
1717
* increment, eg: going from 3.1.0 to 5.0.0, 3.3.0 or 3.1.2
1818
*
19-
* @param {string} newVersion
19+
* @param {string} newVersion - New version to validate
20+
* @param {string} previousVersion - Previous version to compare to
2021
*/
21-
export function validateVersion(newVersion) {
22-
const changelogLines = getChangelogLines()
23-
const previousReleaseLineIndex = getChangelogLineIndexes(changelogLines)[1]
24-
22+
export function validateVersion(newVersion, previousVersion) {
2523
if (!semver.valid(newVersion)) {
2624
throw new Error(
2725
`New version number ${newVersion} could not be processed by Semver. Please ensure you are providing a valid semantic version`
2826
)
2927
}
3028

31-
// Convert the previous release heading into a processable semver
32-
const previousReleaseNumber = convertVersionHeadingToSemver(
33-
changelogLines[previousReleaseLineIndex]
34-
)
35-
36-
if (!previousReleaseNumber) {
37-
throw new Error(processingErrorMessage)
38-
}
29+
const previousReleaseNumber = validatePreviousVersionNumber(previousVersion)
3930

4031
// Check the new version against the old version. Firstly a quick check that
4132
// the new one isn't less than the old one
@@ -86,9 +77,13 @@ export function validateVersion(newVersion) {
8677
* Inserts a new heading between the 'Unreleased' heading and the most recent
8778
* content
8879
*
89-
* @param {string} newVersion
80+
* @param {string} newVersion - New version to add to the changelog. We presume
81+
* that this is a valid version as this function is always run after validateVersion
82+
* has passed.
83+
* @param {string} previousVersion - Previous version. Used for calculating difference
84+
* in versions to build the changelog title
9085
*/
91-
export function updateChangelog(newVersion) {
86+
export function updateChangelog(newVersion, previousVersion) {
9287
// Skip the entire function if the release version is internal eg: 5.1.0-internal.0
9388
if (versionIsAPrerelease(newVersion)) {
9489
const identifier = getPrereleaseIdentifier(newVersion)
@@ -105,17 +100,11 @@ export function updateChangelog(newVersion) {
105100
const [startIndex, previousReleaseLineIndex] =
106101
getChangelogLineIndexes(changelogLines)
107102

108-
// Convert the previous release heading into a processable semver
109-
const previousReleaseNumber = convertVersionHeadingToSemver(
110-
changelogLines[previousReleaseLineIndex]
103+
const versionDiff = semver.diff(
104+
newVersion,
105+
validatePreviousVersionNumber(previousVersion)
111106
)
112107

113-
if (!previousReleaseNumber) {
114-
throw new Error(processingErrorMessage)
115-
}
116-
117-
const versionDiff = semver.diff(newVersion, previousReleaseNumber)
118-
119108
if (!versionDiff) {
120109
throw new Error(processingErrorMessage)
121110
}
@@ -129,40 +118,54 @@ export function updateChangelog(newVersion) {
129118
* Generates release notes from the most recent changelog
130119
*
131120
* Creates a text file 'release-notes-body' from the content between either the
132-
* first release heading (default) or the 'Unreleased' heading and the following
133-
* release heading
121+
* release heading passed to it by newVersion or the 'Unreleased' heading and the
122+
* following release heading if newVersion is tagged as internal
134123
*
135-
* @param {boolean} fromUnreleasedHeading
124+
* @param {string} newVersion - Version used to find start point for release notes
136125
*/
137-
export function generateReleaseNotes(fromUnreleasedHeading = false) {
126+
export function generateReleaseNotes(newVersion) {
127+
// Get the identifier from the version if there is one as we'll use this to
128+
// change what we pass to getChangelogLineIndexes if the version has an
129+
// 'internal' tag
130+
const identifier = versionIsAPrerelease(newVersion)
131+
? getPrereleaseIdentifier(newVersion)
132+
: undefined
138133
const changelogLines = getChangelogLines()
139134
const [startIndex, previousReleaseLineIndex] = getChangelogLineIndexes(
140135
changelogLines,
141-
fromUnreleasedHeading
136+
identifier === 'internal' ? undefined : newVersion
142137
)
143138

144139
const releaseNotes = changelogLines
145140
.slice(startIndex + 1, previousReleaseLineIndex - 1)
146-
.filter((value, index, arr) => {
147-
if (value !== '') {
148-
return true
149-
}
150-
if (
151-
arr[index + 1].startsWith('#') ||
152-
(index > 0 && arr[index - 1].startsWith('#'))
153-
) {
154-
return true
155-
}
156-
return false
157-
})
158-
.map((value) => {
159-
const line = value.replace(/^\s+/, '')
160-
return line.startsWith('##') ? line.substring(1) : line
161-
})
141+
.map((line) =>
142+
line.replace(/^\s+/, '').startsWith('##')
143+
? line.replace(/^\s+/, '').substring(1)
144+
: line
145+
)
162146

163147
writeFileSync('./release-notes-body', releaseNotes.join('\n'))
164148
}
165149

150+
/**
151+
* Validates the previous govuk-frontend version number, presumed passed from
152+
* the govuk-frontend package.json
153+
*
154+
* @param {string} previousVersion - pervious version number
155+
* @returns {string} - Validated semver of previous version
156+
*/
157+
function validatePreviousVersionNumber(previousVersion) {
158+
const previousReleaseNumber = semver.valid(previousVersion)
159+
160+
if (!previousReleaseNumber) {
161+
throw new Error(
162+
`Previous version number ${previousVersion} could not be processed by Semver. Please ensure a valid version is being passed to the script via the govuk-frontend package.json package.`
163+
)
164+
}
165+
166+
return previousReleaseNumber
167+
}
168+
166169
/**
167170
* Get the changelog and split it into an array separated by lines
168171
*
@@ -176,25 +179,32 @@ function getChangelogLines() {
176179
* Gets the start and end headings in the changelog for processing by the
177180
* exported functions
178181
*
179-
* @param {Array<string>} changelogLines
180-
* @param {boolean} fromUnreleasedHeading - Specifies if we get the first index from the 'Unreleased' heading or the first version heading we find
182+
* @param {Array<string>} changelogLines - Produced from getChangelogLines
183+
* @param {string|undefined} heading - Optional query to look for heading
184+
* where the first index is pulled from eg: 'Unreleased'
181185
* @returns {Array<number>} - Indexes in the changelog identifying start and end lines
182186
*/
183-
function getChangelogLineIndexes(changelogLines, fromUnreleasedHeading = true) {
184-
const versionTitleRegex = /^\s*#+\s+v\d+\.\d+\.\d+(-.+\.\d+)?\s+\(.+\)$/i
187+
function getChangelogLineIndexes(changelogLines, heading = undefined) {
188+
// Build regex for finding the correct heading in the changelog
189+
// If a heading hasn't been passed to the function, use 'Unreleased'
190+
const defaultHeadingRegex = '\\d+\\.\\d+\\.\\d+(-.+\\.\\d+)?'
191+
const headingRegex = heading
192+
? heading.replaceAll('.', '\\.').replace('v', '')
193+
: 'Unreleased'
194+
185195
const startIndex = findIndexOfFirstMatchingLine(
186196
changelogLines,
187-
fromUnreleasedHeading ? /^\s*#+\s+Unreleased\s*$/i : versionTitleRegex
197+
buildHeadingRegexQuery(headingRegex)
188198
)
189199

190-
if (!startIndex) {
200+
if (startIndex === -1) {
191201
throw new Error(processingErrorMessage)
192202
}
193203

194204
const endIndex = findIndexOfFirstMatchingLine(
195205
changelogLines,
196-
versionTitleRegex,
197-
fromUnreleasedHeading ? 0 : startIndex + 1
206+
buildHeadingRegexQuery(defaultHeadingRegex),
207+
startIndex + 1
198208
)
199209

200210
if (endIndex === -1) {
@@ -204,12 +214,22 @@ function getChangelogLineIndexes(changelogLines, fromUnreleasedHeading = true) {
204214
return [startIndex, endIndex]
205215
}
206216

217+
/**
218+
* Builds the search query for headings when getting indexes in the changelog
219+
*
220+
* @param {string} identifier - Either the semantic version or 'Unreleased'
221+
* @returns {RegExp} - Complete heading regex including hashes and release type formatting
222+
*/
223+
function buildHeadingRegexQuery(identifier) {
224+
return new RegExp(`^\\s*#+\\s+v?${identifier}\\s*(\\(.+\\))?$`, 'i')
225+
}
226+
207227
/**
208228
* Get the first matching line in the changelog that matches the passed regex
209229
*
210-
* @param {Array<string>} changelogLines
211-
* @param {RegExp} regExp
212-
* @param {number} offset
230+
* @param {Array<string>} changelogLines - Produced from getChangelogLines
231+
* @param {RegExp} regExp - Regular Expression to match against
232+
* @param {number} offset - Offset from start of the changelogLines array
213233
* @returns {number} - Index in changeLogLines or -1 if we can't locate the index
214234
*/
215235
function findIndexOfFirstMatchingLine(changelogLines, regExp, offset = 0) {
@@ -221,23 +241,6 @@ function findIndexOfFirstMatchingLine(changelogLines, regExp, offset = 0) {
221241
return foundIndex ? foundIndex + offset : -1
222242
}
223243

224-
/**
225-
* Convert a release heading into a semver
226-
*
227-
* Presumes the heading param follows the changelog heading format of:
228-
* '## v{version} ({release type})'
229-
*
230-
* @param {string} heading
231-
* @returns {string|null} - Processed semver which we expect to have the format
232-
* X.Y.Z(-{identifier}.{base})
233-
*/
234-
function convertVersionHeadingToSemver(heading) {
235-
const trimmedHeading = heading.trim()
236-
return semver.valid(
237-
trimmedHeading.trim().substring(4, trimmedHeading.indexOf(' ('))
238-
)
239-
}
240-
241244
/**
242245
* Checks if a version string is a pre-release or not
243246
*
@@ -280,10 +283,11 @@ function getPrereleaseIdentifier(version) {
280283
* and the new version is 6.3.0-beta.0, the generated string would be
281284
* 'Beta feature'
282285
*
283-
* @param {string} incType
284-
* @param {string|null} version
285-
* @param {boolean} capitalise
286-
* @param {string|null} lastReleaseTitle
286+
* @param {string} incType - SemVer increment type
287+
* @param {string|null} version - SemVer version
288+
* @param {boolean} capitalise - If the returned string should start with a capital
289+
* letter or not
290+
* @param {string|null} lastReleaseTitle - Previous release title
287291
* @returns {string} - The reworded increment type
288292
*/
289293
function convertIncTypeWord(

0 commit comments

Comments
 (0)