diff --git a/.github/bin/release-plan/add-to-changelog.mjs b/.github/bin/release-plan/add-to-changelog.mjs new file mode 100644 index 000000000..60295a231 --- /dev/null +++ b/.github/bin/release-plan/add-to-changelog.mjs @@ -0,0 +1,43 @@ +export function addUpdatedPackagesToChangelog(workspace, changelog, changeLogAdditions) { + if (changelog.includes('Unreleased')) { + let unreleasedSectionStart = changelog.indexOf('Unreleased'); + if (unreleasedSectionStart === -1) { + console.log('Unable to update the Changelog for ' + workspace.name); + return changelog; + } + + let unreleasedSectionContent = changelog.indexOf('\n', unreleasedSectionStart); + if (unreleasedSectionContent === -1) { + console.log('Unable to update the Changelog for ' + workspace.name); + return changelog; + } + + let nextSectionStart = changelog.indexOf('### ', unreleasedSectionContent); + if (nextSectionStart === -1) { + nextSectionStart = changelog.length; + } + + let listEnd = changelog.lastIndexOf('- ', nextSectionStart); + if (listEnd === -1) { + console.log('Unable to update the Changelog for ' + workspace.name); + return changelog; + } + + let nextLine = changelog.indexOf('\n', listEnd); + if (nextLine === -1) { + console.log('Unable to update the Changelog for ' + workspace.name); + return changelog; + } + + changelog = changelog.slice(0, nextLine + 1) + changeLogAdditions + '\n' + changelog.slice(nextLine + 1); + } else { + let nextSectionStart = changelog.indexOf('### '); + if (nextSectionStart === -1) { + nextSectionStart = changelog.length; + } + + changelog = changelog.slice(0, nextSectionStart) + `### Unreleased (patch)\n\n${changeLogAdditions}\n\n` + changelog.slice(nextSectionStart); + } + + return changelog; +} diff --git a/.github/bin/release-plan/commit.mjs b/.github/bin/release-plan/commit.mjs index b02936ab5..9be65bcdd 100644 --- a/.github/bin/release-plan/commit.mjs +++ b/.github/bin/release-plan/commit.mjs @@ -40,3 +40,41 @@ export async function commitAfterPackageRelease(newVersion, packageDirectory, pa }); }); } + +export async function commitAfterDependencyUpdates() { + await new Promise((resolve, reject) => { + const commitCmd = spawn( + 'git', + [ + 'commit', + '-am', + `set dependencies to newly released versions` + ] + ); + + let stdoutBuffer = ''; + let stderrBuffer = ''; + + commitCmd.stdout.on('data', (data) => { + stdoutBuffer += data.toString(); + }); + + commitCmd.stderr.on('data', (data) => { + stderrBuffer += data.toString(); + }); + + commitCmd.on('close', (code) => { + if (0 !== code) { + if (stderrBuffer) { + reject(new Error(`'git commit -am' exited with code ${code} and error message: ${stderrBuffer}`)); + return; + } + + reject(new Error(`'git commit -am' exited with code ${code}`)); + return; + } + + resolve(stdoutBuffer); + }); + }); +} diff --git a/.github/bin/release-plan/discord-announce.mjs b/.github/bin/release-plan/discord-announce.mjs index 37d42003d..b073f366e 100644 --- a/.github/bin/release-plan/discord-announce.mjs +++ b/.github/bin/release-plan/discord-announce.mjs @@ -19,7 +19,11 @@ const getChangelog = (changelog) => { export async function discordAnnounce(workspace) { const discordArgument = process.argv.slice(2).find(arg => arg.includes('--discord=')); - const [,webHookUrl] = discordArgument.split('='); + if (!discordArgument) { + return; + } + + const [, webHookUrl] = discordArgument.split('='); if (!webHookUrl || workspace.increment === 'patch') { return; @@ -29,14 +33,14 @@ export async function discordAnnounce(workspace) { const payload = { ...defaultPayload }; if (isNew) { - payload.content = `:tada: New package: ${ workspace.name } :tada:`; + payload.content = `:tada: New package: ${workspace.name} :tada:`; } else { - payload.content = `:rocket: New release: ${ workspace.name }@${ workspace.newVersion } :rocket:`; + payload.content = `:rocket: New release: ${workspace.name}@${workspace.newVersion} :rocket:`; } const relativePath = workspace.path.split('/postcss-plugins/')[1]; payload.embeds[0].title = workspace.name; - payload.embeds[0].url = `${ BASE_URL }/${ relativePath }`; + payload.embeds[0].url = `${BASE_URL}/${relativePath}`; payload.embeds[0].description = getChangelog(workspace.changelog); return fetch(webHookUrl, { diff --git a/.github/bin/release-plan/release-plan.mjs b/.github/bin/release-plan/release-plan.mjs index 6578fed37..fdff9019d 100644 --- a/.github/bin/release-plan/release-plan.mjs +++ b/.github/bin/release-plan/release-plan.mjs @@ -1,13 +1,14 @@ import { listWorkspaces } from '../list-workspaces/list-workspaces.mjs'; import fs from 'fs/promises' import path from 'path' -import { npmVersion } from './npm-version.mjs'; +import { addUpdatedPackagesToChangelog } from './add-to-changelog.mjs'; +import { commitAfterDependencyUpdates, commitAfterPackageRelease } from './commit.mjs'; +import { discordAnnounce } from './discord-announce.mjs'; import { nowFormatted } from './date-format.mjs'; -import { commitAfterPackageRelease } from './commit.mjs'; +import { npmInstall } from './npm-install.mjs'; import { npmPublish } from './npm-publish.mjs'; +import { npmVersion } from './npm-version.mjs'; import { updateDocumentation } from './docs.mjs'; -import { npmInstall } from './npm-install.mjs'; -import { discordAnnounce } from './discord-announce.mjs'; const workspaces = await listWorkspaces(); // Things to release @@ -110,6 +111,8 @@ for (const workspace of notReleasableNow.values()) { const packageInfo = JSON.parse(await fs.readFile(path.join(workspace.path, 'package.json'))); let didChange = false; + let changeLogAdditions = ''; + for (const dependency of workspace.dependencies) { if (needsRelease.has(dependency)) { const updated = needsRelease.get(dependency); @@ -121,6 +124,7 @@ for (const workspace of notReleasableNow.values()) { updated.newVersion ) { packageInfo.dependencies[updated.name] = '^' + updated.newVersion; + changeLogAdditions += `- Updated \`${updated.name}\` to \`${updated.newVersion}\` (${updated.increment})`; didChange = true; } if ( @@ -130,6 +134,7 @@ for (const workspace of notReleasableNow.values()) { updated.newVersion ) { packageInfo.devDependencies[updated.name] = '^' + updated.newVersion; + // dev dependencies are not included in the changelog didChange = true; } if ( @@ -139,6 +144,7 @@ for (const workspace of notReleasableNow.values()) { updated.newVersion ) { packageInfo.peerDependencies[updated.name] = '^' + updated.newVersion; + changeLogAdditions += `- Updated \`${updated.name}\` to \`${updated.newVersion}\` (${updated.increment})\n`; didChange = true; } } @@ -148,11 +154,23 @@ for (const workspace of notReleasableNow.values()) { didChangeDownstreamPackages = true; await fs.writeFile(path.join(workspace.path, 'package.json'), JSON.stringify(packageInfo, null, '\t') + '\n'); } + + if (didChange && changeLogAdditions) { + let changelog = (await fs.readFile(path.join(workspace.path, 'CHANGELOG.md'))).toString(); + changelog = addUpdatedPackagesToChangelog(workspace, changelog, changeLogAdditions); + + await fs.writeFile(path.join(workspace.path, 'CHANGELOG.md'), changelog); + } } console.log('\nUpdating lock file'); await npmInstall(); +if (didChangeDownstreamPackages) { + await npmInstall(); + await commitAfterDependencyUpdates(); +} + console.log('\nDone 🎉'); if (didChangeDownstreamPackages || maybeNextPlan.size > 0) {