-
Notifications
You must be signed in to change notification settings - Fork 35
chore: refactor changelog script and make it an standalone script #1216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
yeager-eren
wants to merge
13
commits into
next
Choose a base branch
from
chore/rf-2536-changelog-script
base: next
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 5 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
c02ecdc
chore: refactor changelog script and make it an standalone script
yeager-eren e7776b0
chore: conventional-commits-filter and parser have upgraded, fix its …
yeager-eren 33e2804
chore: add root changelog flow and also clean ups for reveiw
yeager-eren 211fed1
chore: we don't need this package anymore
yeager-eren ef80860
fix: return promise for generate changelog
arlert-armin f9c53ad
fix: retrieve save from options
yeager-eren 6d5355f
fix: prepending the newly creating changelog
yeager-eren 25a6a03
chore: clear description for when package is not given to generateCha…
yeager-eren 3f481d6
chore: upgrade conventional-recommended-bump and migrate to new its i…
yeager-eren 42cdda7
fix: emit finish when rename tmp happened
yeager-eren 7b13e62
fix: match the root changelog format with the old format
yeager-eren 1fd10aa
fix: missed a let in a for, you're not missed anymore
yeager-eren 4d5a8a6
feat: integrate all path
arlert-armin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
#!/usr/bin/env node | ||
'use strict'; | ||
|
||
import commandLineArgs from 'command-line-args'; | ||
import { | ||
getInfoBeforeGeneratingChangelog, | ||
generateChangelogAndSave, | ||
generateChangelogAndPrint, | ||
} from '../common/changelog.mjs'; | ||
import { | ||
workspacePackages, | ||
packageNamesToPackagesWithInfo, | ||
} from '../common/utils.mjs'; | ||
|
||
async function generateChangelogForRoot(options) { | ||
const { from, commitsCount } = await getInfoBeforeGeneratingChangelog(); | ||
|
||
if (commitsCount > 0) { | ||
console.log('from:', from || 'start (first release)'); | ||
console.log('commits:', commitsCount); | ||
|
||
if (options.save) { | ||
await generateChangelogAndSave(); | ||
} else { | ||
generateChangelogAndPrint(); | ||
} | ||
} else { | ||
console.log(`No commits found, skipping changelog generation.`); | ||
} | ||
} | ||
|
||
async function generateChangelogForWorkspaceMembers(pkgs) { | ||
for (const pkg of pkgs) { | ||
const { from, commitsCount } = await getInfoBeforeGeneratingChangelog(pkg); | ||
|
||
if (commitsCount > 0) { | ||
console.log('name:', pkg.name); | ||
console.log('from:', from || 'start (first release)'); | ||
console.log('commits:', commitsCount); | ||
|
||
if (save) { | ||
await generateChangelogAndSave(pkg); | ||
} else { | ||
generateChangelogAndPrint(pkg); | ||
} | ||
} else { | ||
console.log( | ||
`No commits found for ${pkg.name}, skipping changelog generation.` | ||
); | ||
} | ||
} | ||
} | ||
|
||
// NOTE 1: changelog should be run before the tagging proccess for the new release. checkout the steps here: https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog#recommended-workflow | ||
// NOTE 2: we use tags. tags with package@semver format. | ||
// NOTE 3: when don't use any flag, we will last valid tag as starting point. | ||
async function run() { | ||
const optionDefinitions = [ | ||
{ name: 'name', type: String }, | ||
{ name: 'save', type: Boolean }, | ||
{ name: 'all', type: Boolean }, | ||
]; | ||
const { name, save, all } = commandLineArgs(optionDefinitions); | ||
|
||
if (name && all) | ||
throw new Error('One of the --name or --all flag should be given'); | ||
|
||
arlert-armin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (name || all) { | ||
// Create a list of packages we are going to create changelog for. | ||
const pkgs = []; | ||
if (name) { | ||
const pkgs = await packageNamesToPackagesWithInfo([name]); | ||
if (pkgs.length !== 1) | ||
throw new Error('Your provided package is not found.', { cause: pkgs }); | ||
|
||
pkgs.push(pkgs[0]); | ||
} else { | ||
const list = await workspacePackages(); | ||
list | ||
.filter((pkg) => !pkg.private) | ||
.forEach((pkg) => { | ||
pkgs.push(pkg); | ||
}); | ||
} | ||
|
||
await generateChangelogForWorkspaceMembers(pkgs); | ||
} else { | ||
await generateChangelogForRoot({ | ||
save, | ||
}); | ||
} | ||
} | ||
|
||
run().catch((e) => { | ||
console.error(e); | ||
process.exit(1); | ||
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,49 +1,157 @@ | ||
import { execa } from 'execa'; | ||
import { GenerateChangelogFailedError, YarnError } from './errors.mjs'; | ||
import { packageNameWithoutScope } from './utils.mjs'; | ||
import { ConventionalChangelog } from 'conventional-changelog'; | ||
import { ConventionalGitClient } from '@conventional-changelog/git-client'; | ||
import { WriteStream } from 'node:fs'; | ||
import fs from 'node:fs'; | ||
import path from 'node:path'; | ||
|
||
// Our tagging is using lerna convention which is package-name@version | ||
// for example for @rango-dev/wallets-core, it will be wallets-core@1.1.0 | ||
const TAG_PACKAGE_PREFIX = (pkg) => `${packageNameWithoutScope(pkg.name)}@`; | ||
const TAG_ROOT_PREFIX = /^[^@]+@/; | ||
|
||
// TODO: this is not correct assumption that the script will be run from the root. | ||
// I made it a function to make it easier correct behaviour in future. | ||
function rootPath() { | ||
return path.join('.'); | ||
} | ||
function rootPackageJson() { | ||
return path.join(rootPath(), 'package.json'); | ||
} | ||
|
||
/** | ||
* Generate a changelog by using convetional commit format. | ||
* Retrieving some useful information when you are going to generate a changelog | ||
* | ||
* @param {import("./typedefs.mjs").Package} pkg | ||
* @param {Object} options | ||
* @param {boolean} options.saveToFile `true` for using it for creating `pkg/CHANGELOG.com` and `false` for Github Release note. | ||
* @param {import("./typedefs.mjs").Package} [pkg] | ||
*/ | ||
export async function generateChangelog(pkg, options) { | ||
const { saveToFile = false } = options || {}; | ||
|
||
const conventionalChangelogBinPath = await execa('yarn', [ | ||
'bin', | ||
'conventional-changelog', | ||
]) | ||
.then((result) => result.stdout) | ||
.catch((err) => { | ||
throw new YarnError(`GetBinaryPathFailed: \n${err.stdout}`); | ||
export async function getInfoBeforeGeneratingChangelog(pkg) { | ||
const gitClient = new ConventionalGitClient(process.cwd()); | ||
|
||
let commitsParams = { | ||
merges: false, | ||
}; | ||
|
||
let startFromTag = undefined; | ||
if (pkg) { | ||
const tagsParams = { | ||
prefix: TAG_PACKAGE_PREFIX(pkg), | ||
}; | ||
const semverTagsStream = gitClient.getSemverTags(tagsParams); | ||
|
||
const semverTags = []; | ||
for await (const tag of semverTagsStream) { | ||
semverTags.push(tag); | ||
} | ||
startFromTag = semverTags[0]; | ||
|
||
commitsParams = { | ||
...commitsParams, | ||
from: startFromTag, | ||
path: pkg.location, | ||
}; | ||
} else { | ||
const semverTag = await gitClient.getLastSemverTag({ | ||
// HEADS UP: | ||
// The following regex pattern supports the `package@1.1.1` format, which meets our needs for now. | ||
// scoped tags like `@a/b@1.1.1` are not currently supported. | ||
prefix: TAG_ROOT_PREFIX, | ||
}); | ||
// If there are no semver tags, null is returned. In that case, we change it undefined to match the `string | undefined` signature. | ||
startFromTag = semverTag || undefined; | ||
|
||
const tagName = packageNameWithoutScope(pkg.name); | ||
const command = [ | ||
'conventional-changelog', | ||
'-p', | ||
'angular', | ||
'-l', | ||
`${tagName}`, | ||
'-k', | ||
pkg.location, | ||
'--commit-path', | ||
pkg.location, | ||
]; | ||
|
||
if (saveToFile) { | ||
const changelogPath = `${pkg.location}/CHANGELOG.md`; | ||
command.push('-i', changelogPath, '-s'); | ||
if (startFromTag) { | ||
commitsParams = { | ||
...commitsParams, | ||
from: startFromTag, | ||
}; | ||
} | ||
} | ||
|
||
const result = await execa(conventionalChangelogBinPath, command) | ||
.then((result) => result.stdout) | ||
.catch((err) => { | ||
throw new GenerateChangelogFailedError(err.stdout); | ||
const commitsStream = gitClient.getCommits(commitsParams); | ||
|
||
const commits = []; | ||
for await (const commit of commitsStream) { | ||
commits.push(commit); | ||
} | ||
|
||
return { | ||
/** Where is considering as starting point, it is genrally a tag. undefined means it's the first release.'*/ | ||
from: startFromTag, | ||
/** How many commits this release has. */ | ||
commitsCount: commits.length, | ||
}; | ||
} | ||
|
||
/** | ||
* Create a write stream for the target package's changelog. | ||
* | ||
* @param {import("./typedefs.mjs").Package} pkg | ||
* @returns {WriteStream} | ||
*/ | ||
export function changelogFileStream(pkg) { | ||
const changelogPath = path.join(pkg.location, 'CHANGELOG.md'); | ||
const file = fs.createWriteStream(changelogPath, { | ||
encoding: 'utf-8', | ||
flags: 'a', | ||
arlert-armin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}); | ||
|
||
return file; | ||
} | ||
|
||
/** | ||
* | ||
* Generate a changelog by using convetional commit format. | ||
* It uses tags to identify releases. | ||
* | ||
* @param {import("./typedefs.mjs").Package} [pkg] | ||
* @returns {ReadableStream} | ||
*/ | ||
export function generateChangelog(pkg) { | ||
const generator = new ConventionalChangelog(process.cwd()); | ||
generator.loadPreset('angular'); | ||
|
||
if (pkg) { | ||
generator.readPackage(`${pkg.location}/package.json`); | ||
generator.commits({ | ||
path: pkg.location, | ||
}); | ||
|
||
return result; | ||
generator.tags({ | ||
prefix: TAG_PACKAGE_PREFIX(pkg), | ||
}); | ||
} else { | ||
generator.readPackage(rootPackageJson()); | ||
generator.tags({ | ||
prefix: TAG_ROOT_PREFIX, | ||
}); | ||
} | ||
|
||
return generator.writeStream(); | ||
} | ||
|
||
/** | ||
* | ||
* @param {import("./typedefs.mjs").Package} [pkg] | ||
*/ | ||
export async function generateChangelogAndSave(pkg) { | ||
return new Promise((resolve, reject) => { | ||
const changelog = generateChangelog(pkg); | ||
|
||
// we only need location for file stream, when pkg is undefined, we will point to root package.json | ||
arlert-armin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (!pkg) pkg = { location: rootPath() }; | ||
|
||
const writeStream = changelog.pipe(changelogFileStream(pkg)); | ||
|
||
writeStream.on('finish', resolve); | ||
writeStream.on('error', reject); | ||
}); | ||
} | ||
|
||
/** | ||
* | ||
* @param {import("./typedefs.mjs").Package} [pkg] | ||
*/ | ||
export function generateChangelogAndPrint(pkg) { | ||
const changelog = generateChangelog(pkg); | ||
changelog.pipe(process.stdout); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.