ci: fixed versioning (#5214)
* chore(deps): add lerna-lite * feat: update-1 * feat(db-mongodb): update 2 * chore: lerna init * chore: add version option to lerna config * chore(ci): add gh usernames to changelog and user root package.json for version * chore(ci): whitelist poc branches * chore(ci): add contributors section * chore(ci): use turbo for prepublishOnly scripts, enable caching * chore(deps): update turborepo, add execa * feat(plugin-stripe): adjust type import * chore: remove lerna-lite * chore(ci): new and improved release script for fixed versioning * chore: remove unused lerna-lite packages * chore: sync root package.json version * chore: remove remnants of bundler packages * chore(plugin-seo): update packagea.json from main, disable build * chore: disable turbo caching * chore(ci): update release script * chore: sync pnpm-lock.yaml * chore: ci cleanup
This commit is contained in:
@@ -1,39 +1,82 @@
|
||||
import addStream from 'add-stream'
|
||||
import { ExecSyncOptions } from 'child_process'
|
||||
import conventionalChangelog from 'conventional-changelog'
|
||||
import { default as getConventionalPreset } from 'conventional-changelog-conventionalcommits'
|
||||
import { GitRawCommitsOptions, ParserOptions, WriterOptions } from 'conventional-changelog-core'
|
||||
import fse, { createReadStream, createWriteStream } from 'fs-extra'
|
||||
import minimist from 'minimist'
|
||||
import semver, { ReleaseType } from 'semver'
|
||||
import tempfile from 'tempfile'
|
||||
import { PackageDetails } from '../lib/getPackageDetails'
|
||||
|
||||
import { Octokit } from '@octokit/core'
|
||||
import simpleGit from 'simple-git'
|
||||
import { once } from 'events'
|
||||
|
||||
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN })
|
||||
const git = simpleGit()
|
||||
|
||||
type Args = {
|
||||
pkg: PackageDetails
|
||||
bump: ReleaseType
|
||||
newVersion: string
|
||||
dryRun?: boolean
|
||||
}
|
||||
|
||||
export const updateChangelog = ({ pkg, bump }: Args) => {
|
||||
// Prefix to find prev tag
|
||||
const tagPrefix = pkg.shortName === 'payload' ? 'v' : pkg.prevGitTag.split('/')[0] + '/'
|
||||
export const updateChangelog = async ({ newVersion, dryRun }: Args) => {
|
||||
const monorepoVersion = fse.readJSONSync('package.json')?.version
|
||||
|
||||
if (!monorepoVersion) {
|
||||
throw new Error('Could not find version in package.json')
|
||||
}
|
||||
|
||||
const lastTag = (await git.tags()).all.reverse().filter((t) => t.startsWith('v'))?.[0]
|
||||
|
||||
if (monorepoVersion !== lastTag.replace('v', '')) {
|
||||
throw new Error(
|
||||
`Version in package.json (${monorepoVersion}) does not match last tag (${lastTag})`,
|
||||
)
|
||||
}
|
||||
|
||||
// Load conventional commits preset and modify it
|
||||
const conventionalPreset = (await getConventionalPreset()) as {
|
||||
gitRawCommitsOpts: GitRawCommitsOptions
|
||||
parserOpts: ParserOptions
|
||||
writerOpts: WriterOptions
|
||||
recommmendBumpOpts: unknown
|
||||
conventionalChangelog: unknown
|
||||
}
|
||||
|
||||
// Unbold scope
|
||||
conventionalPreset.writerOpts.commitPartial =
|
||||
conventionalPreset.writerOpts?.commitPartial?.replace('**{{scope}}:**', '{{scope}}:')
|
||||
|
||||
// Add footer to end of main template
|
||||
conventionalPreset.writerOpts.mainTemplate = conventionalPreset.writerOpts?.mainTemplate?.replace(
|
||||
/\n*$/,
|
||||
'{{footer}}\n',
|
||||
)
|
||||
|
||||
// Fetch commits from last tag to HEAD
|
||||
const credits = await createContributorSection(lastTag)
|
||||
|
||||
// Add Credits to footer
|
||||
conventionalPreset.writerOpts.finalizeContext = (context) => {
|
||||
context.footer = credits
|
||||
return context
|
||||
}
|
||||
|
||||
const nextReleaseVersion = semver.inc(pkg.version, bump) as string
|
||||
const changelogStream = conventionalChangelog(
|
||||
// Options
|
||||
{
|
||||
preset: 'conventionalcommits',
|
||||
tagPrefix,
|
||||
pkg: {
|
||||
path: `${pkg.packagePath}/package.json`,
|
||||
},
|
||||
},
|
||||
// Context
|
||||
{
|
||||
version: nextReleaseVersion, // next release
|
||||
version: newVersion, // next release
|
||||
},
|
||||
// GitRawCommitsOptions
|
||||
{
|
||||
path: 'packages',
|
||||
// path: pkg.packagePath,
|
||||
// from: pkg.prevGitTag,
|
||||
// to: 'HEAD'
|
||||
},
|
||||
undefined,
|
||||
conventionalPreset.writerOpts,
|
||||
).on('error', (err) => {
|
||||
console.error(err.stack)
|
||||
console.error(err.toString())
|
||||
@@ -42,13 +85,60 @@ export const updateChangelog = ({ pkg, bump }: Args) => {
|
||||
|
||||
const changelogFile = 'CHANGELOG.md'
|
||||
const readStream = fse.createReadStream(changelogFile)
|
||||
|
||||
const tmp = tempfile()
|
||||
|
||||
changelogStream
|
||||
.pipe(addStream(readStream))
|
||||
.pipe(createWriteStream(tmp))
|
||||
.on('finish', () => {
|
||||
createReadStream(tmp).pipe(createWriteStream(changelogFile))
|
||||
})
|
||||
// Output to stdout if debug is true
|
||||
const emitter = dryRun
|
||||
? changelogStream.pipe(createWriteStream(tmp)).on('finish', () => {
|
||||
createReadStream(tmp).pipe(process.stdout)
|
||||
})
|
||||
: changelogStream
|
||||
.pipe(addStream(readStream))
|
||||
.pipe(createWriteStream(tmp))
|
||||
.on('finish', () => {
|
||||
createReadStream(tmp).pipe(createWriteStream(changelogFile))
|
||||
})
|
||||
|
||||
// Wait for the stream to finish
|
||||
await once(emitter, 'finish')
|
||||
}
|
||||
|
||||
// If file is executed directly, run the function
|
||||
if (require.main === module) {
|
||||
const { newVersion } = minimist(process.argv.slice(2))
|
||||
updateChangelog({ newVersion, dryRun: true })
|
||||
}
|
||||
|
||||
async function createContributorSection(lastTag: string): Promise<string> {
|
||||
const commits = await git.log({ from: lastTag, to: 'HEAD' })
|
||||
console.log(`Fetching contributors from ${commits.total} commits`)
|
||||
const usernames = await Promise.all(
|
||||
commits.all.map((c) =>
|
||||
octokit
|
||||
.request('GET /repos/{owner}/{repo}/commits/{ref}', {
|
||||
owner: 'payloadcms',
|
||||
repo: 'payload',
|
||||
ref: c.hash,
|
||||
})
|
||||
.then(({ data }) => data.author?.login as string),
|
||||
),
|
||||
)
|
||||
|
||||
if (!usernames.length) return ''
|
||||
|
||||
// List of unique contributors
|
||||
const contributors = Array.from(new Set(usernames)).map((c) => `@${c}`)
|
||||
|
||||
const formats = {
|
||||
1: (contributors: string[]) => contributors[0],
|
||||
2: (contributors: string[]) => contributors.join(' and '),
|
||||
// Oxford comma ;)
|
||||
default: (contributors: string[]) => contributors.join(', ').replace(/,([^,]*)$/, ', and$1'),
|
||||
}
|
||||
|
||||
const formattedContributors =
|
||||
formats[contributors.length]?.(contributors) || formats['default'](contributors)
|
||||
|
||||
const credits = `### Credits\n\nThanks to ${formattedContributors} for their contributions!\n`
|
||||
return credits
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user