ci: Add support for managing multiple changelogs (#712)
This commit is contained in:
@@ -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');
|
||||
@@ -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');
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 {};
|
||||
@@ -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');
|
||||
Reference in New Issue
Block a user