ci: Add support for managing multiple changelogs (#712)

This commit is contained in:
Aaron
2024-06-12 00:36:28 -05:00
committed by GitHub
parent 4437dc404b
commit 7080b36173
10 changed files with 266 additions and 64 deletions
+84
View File
@@ -0,0 +1,84 @@
import {
determineSemverChange,
loadChangelogConfig,
parseCommits,
generateMarkDown,
parseChangelogMarkdown,
} from 'changelogen';
import { execa } from 'execa';
import { getPkgTag, grabPackageDetails, listCommitsInDir } from './git';
import { consola } from 'consola';
import fs from 'fs-extra';
const pkg = process.argv[2];
if (pkg == null) {
throw Error(
'Package name missing. Usage: tsx bump-package-version.ts <package-name>',
);
}
const { pkgDir, pkgName, currentVersion, prevTag, changelogPath, pkgJsonPath } =
await grabPackageDetails(pkg);
consola.info('Bumping:', { pkg, pkgDir, pkgName, currentVersion });
// Get commits
const config = await loadChangelogConfig(process.cwd());
consola.info('Config:', config);
const rawCommits = await listCommitsInDir(pkgDir, prevTag);
const commits = parseCommits(rawCommits, config);
// Bump version
let bumpType = determineSemverChange(commits, config) ?? 'patch';
if (currentVersion.startsWith('0.')) {
if (bumpType === 'major') {
bumpType = 'minor';
} else if (bumpType === 'minor') {
bumpType = 'patch';
}
}
await execa('pnpm', ['version', bumpType], {
cwd: pkgDir,
});
const updatedPkgJson = await fs.readJson(pkgJsonPath);
const newVersion: string = updatedPkgJson.version;
consola.info('Bump:', { currentVersion, bumpType, newVersion });
// Generate changelog
const versionChangelog = await generateMarkDown(commits, {
...config,
from: currentVersion,
to: newVersion,
});
const versionChangelogBody = versionChangelog
.split('\n')
.slice(1)
.join('\n')
.trim();
const { releases: prevReleases } = await fs
.readFile(changelogPath, 'utf8')
.then(parseChangelogMarkdown)
.catch(() => ({ releases: [] }));
const allReleases = [
{
version: newVersion,
body: versionChangelogBody,
},
...prevReleases,
];
const newChangelog =
'# Changelog\n\n' +
allReleases
.map((release) => [`## v${release.version}`, release.body].join('\n\n'))
.join('\n\n');
await fs.writeFile(changelogPath, newChangelog, 'utf8');
consola.success('Updated changelog');
// Commit changes
await execa('git', ['add', pkgJsonPath, changelogPath]);
await execa('git', [
'commit',
'-m',
`chore(release): ${pkgName} v${newVersion}`,
]);
await execa('git', ['tag', getPkgTag(pkg, newVersion)]);
consola.success('Committed version and changelog');
+33
View File
@@ -0,0 +1,33 @@
import {
createGithubRelease,
loadChangelogConfig,
parseChangelogMarkdown,
} from 'changelogen';
import fs from 'fs-extra';
import { grabPackageDetails } from './git';
import consola from 'consola';
const pkg = process.argv[2];
if (pkg == null) {
throw Error(
'Package name missing. Usage: tsx create-github-release.ts <package-name>',
);
}
const { pkgName, prevTag, currentVersion, changelogPath } =
await grabPackageDetails(pkg);
consola.info('Creating release for:', { pkg, pkgName, prevTag });
const { releases } = await fs
.readFile(changelogPath, 'utf8')
.then(parseChangelogMarkdown)
.catch(() => ({ releases: [] }));
const config = await loadChangelogConfig(process.cwd());
config.tokens.github = process.env.GITHUB_TOKEN;
await createGithubRelease(config, {
tag_name: prevTag,
name: `${pkgName} v${currentVersion}`,
body: releases[0].body,
});
consola.success('Created release');
+41
View File
@@ -0,0 +1,41 @@
import { RawGitCommit, getGitDiff } from 'changelogen';
import { consola } from 'consola';
import fs from 'fs-extra';
export async function grabPackageDetails(pkg: string) {
const pkgDir = `packages/${pkg}`;
const pkgJsonPath = `${pkgDir}/package.json`;
const pkgJson = await fs.readJson(pkgJsonPath);
const currentVersion: string = pkgJson.version;
return {
pkgDir,
pkgJsonPath,
changelogPath: `${pkgDir}/CHANGELOG.md`,
pkgJson,
pkgName: pkgJson.name,
currentVersion,
prevTag: getPkgTag(pkg, currentVersion),
};
}
export function getPkgTag(pkg: string, version: string | undefined) {
return `${pkg}-v${version}`;
}
export async function listCommitsInDir(
dir: string,
lastTag: string,
): Promise<RawGitCommit[]> {
consola.info('Listing commits:', { lastTag, dir });
const commits = await getGitDiff(lastTag);
consola.info('All commits:', commits.length);
consola.debug(commits);
// commit.body contains all the files that were modified/added. So just check to make sure "\t" + "packages/storage" + "/" is in the body to include
// '"\n\nM\tpackages/wxt/vitest.config.ts\n"'
const filtered = commits.filter((commit) =>
commit.body.includes(`\t${dir}/`),
);
consola.info('Filtered:', filtered.length);
consola.debug(filtered);
return filtered;
}
-7
View File
@@ -1,7 +0,0 @@
// This file is a stub just so netlify doesn't throw an error when building
// docs. Once another TS file apart of the root project is added, this can be
// removed.
// If the website deploys on a PR branch with this file removed, you can safely
// delete it.
export {};
+40
View File
@@ -0,0 +1,40 @@
import {
getGithubReleaseByTag,
loadChangelogConfig,
parseChangelogMarkdown,
updateGithubRelease,
} from 'changelogen';
import { getPkgTag, grabPackageDetails } from './git';
import fs from 'fs-extra';
import consola from 'consola';
const pkg = process.argv[2];
if (pkg == null) {
throw Error(
'Package name missing. Usage: tsx sync-releases.ts <package-name>',
);
}
// Update
const { changelogPath, pkgName } = await grabPackageDetails(pkg);
const { releases } = await fs
.readFile(changelogPath, 'utf8')
.then(parseChangelogMarkdown)
.catch(() => ({ releases: [] }));
const config = await loadChangelogConfig(process.cwd());
config.tokens.github = process.env.GITHUB_TOKEN;
// Update releases
for (const release of releases) {
const tag = getPkgTag(pkg, release.version);
const existing = await getGithubReleaseByTag(config, tag);
if (existing.body !== release.body) {
await updateGithubRelease(config, existing.id!, {
tag_name: tag,
name: `${pkgName} v${release.version}`,
body: release.body,
});
}
consola.success(`Synced \`${tag}\``);
}
consola.success('Done');