From df176331c82ad9d27d5325ca29b059e89c7abef3 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Fri, 3 Dec 2021 18:42:51 +1100 Subject: [PATCH] feat!: switch to ESM, dedupe more code w/ changelog-maker Also remove Node.js v12 support Ref: https://github.com/nodejs/changelog-maker/pull/121 --- .github/workflows/test-and-release.yml | 2 +- README.md | 14 +- branch-diff.js | 197 ++++++++----------------- package.json | 15 +- 4 files changed, 74 insertions(+), 154 deletions(-) diff --git a/.github/workflows/test-and-release.yml b/.github/workflows/test-and-release.yml index 3e53310..2b30696 100644 --- a/.github/workflows/test-and-release.yml +++ b/.github/workflows/test-and-release.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - node: [12, 14, 16] + node: [14, 16] # windows support not quite ready: os: [ubuntu-latest, windows-latest] os: [ubuntu-latest] diff --git a/README.md b/README.md index 1416cba..3efdcea 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ ## Usage -**`$ branch-diff [--simple] [--group] [--patch-only] base-branch comparison-branch`** +**`$ branch-diff [--sha] [--plaintext] [--markdown] [--group] [--reverse] [--patch-only] base-branch comparison-branch`** A commit is considered to be in the comparison-branch but not in the base-branch if: @@ -29,12 +29,16 @@ But the comparison isn't quite as strict, generally leading to a shorter list of * `--exclude-label`: Exclude any commits from the list that come from a GitHub pull request with the given label. Multiple `--exclude-label` options may be provided, they will also be split by `,`. e.g. `--exclude-label=semver-major,meta`. * `--require-label`: Only include commits in the list that come from a GitHub pull request with the given label. Multiple `--require-label` options may be provided, they will also be split by `,`. e.g. `--require-label=test,doc`. * `--patch-only`: An alias for `--exclude-label=semver-major,semver-minor`. -* `--format`: Dictates what formatting the output will have. Possible options are: `simple`, `plaintext`, and `sha`. The default is to print markdown-formatted output; `plaintext` also implies that commits will be grouped. +* `--format`: Dictates what formatting the output will have. Possible options are: `simple`, `markdown`, `plaintext`, and `sha`. The default is to print a `simple` output suitable for stdout. - `simple`: Don't print full markdown output, good for console printing without the additional fluff. - - `sha`: Print only the 10-character truncated commit shasums. Good for piping though additional tooling, such as `xargs git cherry-pick` for applying commits. -* `--simple` or `-s`: An alias for `--format=simple`. + - `sha`: Print only the 10-character truncated commit hashes. Good for piping though additional tooling, such as `xargs git cherry-pick` for applying commits. + - `plaintext`: A very simple form, without commit details, implies `--group`. + - `markdown`: A Markdown formatted from, with links and proper escaping. +* `--sha`: Same as `--format=sha`. +* `--plaintext`: Same as `--format=plaintext`. +* `--markdown`: Same as `--format=markdown`. * `--filter-release`: Exclude Node-style release commits from the list. e.g. `Working on v1.0.0` or `2015-10-21 Version 2.0.0`. -* `--reverse`: Reverse the results, this is especially useful when piping output to `xargs` +* `--reverse`: Reverse the results, this is especially useful when piping output to `xargs`. * `--commit-url`:A URL template which will be used to generate commit URLs for a repository not hosted in GitHub. `{ref}` is the placeholder that will be replaced with the commit, i.e. `--commit-url=https://gitlab.com/myUser/myRepo/commit/{ref}`. `{ghUser}` and `{ghRepo}` are available if they can be derived from package.json (Gitlab and Bitbucket URLs should be understood in package.json). ## License diff --git a/branch-diff.js b/branch-diff.js index d1abba6..3436f83 100755 --- a/branch-diff.js +++ b/branch-diff.js @@ -1,21 +1,19 @@ #!/usr/bin/env node -'use strict' - -const fs = require('fs') -const path = require('path') -const commitStream = require('commit-stream') -const split2 = require('split2') -const listStream = require('list-stream') -const pkgtoId = require('pkg-to-id') -const stripAnsi = require('strip-ansi') -const map = require('map-async') -const { commitToOutput } = require('changelog-maker/commit-to-output') -const collectCommitLabels = require('changelog-maker/collect-commit-labels') -const groupCommits = require('changelog-maker/group-commits') -const { isReleaseCommit, toGroups } = require('changelog-maker/groups') -const gitexec = require('gitexec') - +import fs from 'fs' +import path from 'path' +import process from 'process' +import { pipeline as _pipeline } from 'stream' +import { promisify } from 'util' +import commitStream from 'commit-stream' +import split2 from 'split2' +import pkgtoId from 'pkg-to-id' +import minimist from 'minimist' +import { isReleaseCommit } from 'changelog-maker/groups' +import { processCommits } from 'changelog-maker/process-commits' +import gitexec from 'gitexec' + +const pipeline = promisify(_pipeline) const pkgFile = path.join(process.cwd(), 'package.json') const pkgData = fs.existsSync(pkgFile) ? require(pkgFile) : {} const pkgId = pkgtoId(pkgData) @@ -26,23 +24,6 @@ const ghId = { user: pkgId.user || 'nodejs', repo: pkgId.name || 'node' } -const defaultCommitUrl = 'https://github.com/{ghUser}/{ghRepo}/commit/{ref}' - -const formatType = { - PLAINTEXT: 'plaintext', - MARKDOWN: 'markdown', - SIMPLE: 'simple', - SHA: 'sha' -} - -const getFormat = (argv) => { - if (argv.format && Object.values(formatType).includes(argv.format)) { - return argv.format - } else if (argv.simple || argv.s) { - return formatType.SIMPLE - } - return formatType.MARKDOWN -} function replace (s, m) { Object.keys(m).forEach(function (k) { @@ -51,37 +32,26 @@ function replace (s, m) { return s } -function branchDiff (branch1, branch2, options, callback) { +export async function branchDiff (branch1, branch2, options) { if (!branch1 || !branch2) { - return callback(new Error('Must supply two branch names to compare')) + throw new Error('Must supply two branch names to compare') } const repoPath = options.repoPath || process.cwd() - - findMergeBase(repoPath, branch1, branch2, (err, commit) => { - if (err) { return callback(err) } - map( - [branch1, branch2], (branch, callback) => { - collect(repoPath, branch, commit, branch === branch2 && options.endRef).pipe(listStream.obj(callback)) - } - , (err, branchCommits) => err ? callback(err) : diffCollected(options, branchCommits, callback) - ) - }) + const commit = await findMergeBase(repoPath, branch1, branch2) + const branchCommits = await Promise.all([branch1, branch2].map(async (branch) => { + return collect(repoPath, branch, commit, branch === branch2 && options.endRef) + })) + return await diffCollected(options, branchCommits) } -function findMergeBase (repoPath, branch1, branch2, callback) { +async function findMergeBase (repoPath, branch1, branch2) { const gitcmd = `git merge-base ${branch1} ${branch2}` - - gitexec.execCollect(repoPath, gitcmd, (err, data) => { - if (err) { - return callback(err) - } - - callback(null, data.substr(0, 10)) - }) + const data = await promisify(gitexec.execCollect)(repoPath, gitcmd) + return data.substr(0, 10) } -function diffCollected (options, branchCommits, callback) { +async function diffCollected (options, branchCommits) { function isInList (commit) { return branchCommits[0].some((c) => { if (commit.sha === c.sha) { return true } @@ -102,102 +72,55 @@ function diffCollected (options, branchCommits, callback) { let list = branchCommits[1].filter((commit) => !isInList(commit)) - collectCommitLabels(list, (err) => { - if (err) { - return callback(err) - } - - if (options.excludeLabels.length > 0) { - list = list.filter((commit) => { - return !commit.labels || !commit.labels.some((label) => { - return options.excludeLabels.indexOf(label) >= 0 - }) - }) - } - - if (options.requireLabels.length > 0) { - list = list.filter((commit) => { - return commit.labels && commit.labels.some((label) => { - return options.requireLabels.indexOf(label) >= 0 - }) + if (options.excludeLabels.length > 0) { + list = list.filter((commit) => { + return !commit.labels || !commit.labels.some((label) => { + return options.excludeLabels.indexOf(label) >= 0 }) - } - - if (options.group) { - list = groupCommits(list) - } - - callback(null, list) - }) -} - -function printCommits (list, format, reverse, commitUrl) { - if (format === formatType.SHA) { - list = list.map((commit) => `${commit.sha.substr(0, 10)}`) - } else if (format === formatType.SIMPLE) { - list = list.map((commit) => commitToOutput(commit, formatType.SIMPLE, ghId, commitUrl)) - } else if (format === formatType.PLAINTEXT) { - // Plaintext format implies grouping. - list = groupCommits(list) - - const formatted = [] - let currentGroup - for (const commit of list) { - const commitGroup = toGroups(commit.summary) - if (currentGroup !== commitGroup) { - formatted.push(`${commitGroup}:`) - currentGroup = commitGroup - } - formatted.push(commitToOutput(commit, formatType.PLAINTEXT, ghId, commitUrl)) - } - list = formatted - } else { - list = list.map((commit) => { - return commitToOutput(commit, formatType.MARKDOWN, ghId, commitUrl) }) } - if (reverse) { - list = list.reverse() - } - - let out = list.join('\n') + '\n' - - if (!process.stdout.isTTY) { - out = stripAnsi(out) + if (options.requireLabels.length > 0) { + list = list.filter((commit) => { + return commit.labels && commit.labels.some((label) => { + return options.requireLabels.indexOf(label) >= 0 + }) + }) } - process.stdout.write(out) + return list } -function collect (repoPath, branch, startCommit, endRef) { +async function collect (repoPath, branch, startCommit, endRef) { const endrefcmd = endRef && replace(refcmd, { ref: endRef }) const untilcmd = endRef ? replace(commitdatecmd, { refcmd: endrefcmd }) : '' const _gitcmd = replace(gitcmd, { branch, startCommit, untilcmd }) - return gitexec.exec(repoPath, _gitcmd) - .pipe(split2()) - .pipe(commitStream(ghId.user, ghId.repo)) + const commitList = [] + await pipeline( + gitexec.exec(repoPath, _gitcmd), + split2(), + commitStream(ghId.user, ghId.repo), + async function * (source) { + for await (const commit of source) { + commitList.push(commit) + } + }) + return commitList } -module.exports = branchDiff - -function main () { +async function main () { const minimistConfig = { boolean: ['version', 'group', 'patch-only', 'simple', 'filter-release', 'reverse'] } - const argv = require('minimist')(process.argv.slice(2), minimistConfig) + const argv = minimist(process.argv.slice(2), minimistConfig) const branch1 = argv._[0] const branch2 = argv._[1] - const reverse = argv.reverse const group = argv.group || argv.g const endRef = argv['end-ref'] - const commitUrl = argv['commit-url'] || defaultCommitUrl let excludeLabels = [] let requireLabels = [] - const format = getFormat(argv) - if (argv.version || argv.v) { return console.log(`v ${require('./package.json').version}`) } @@ -227,17 +150,15 @@ function main () { endRef } - branchDiff(branch1, branch2, options, (err, list) => { - if (err) { throw err } - - if (argv['filter-release']) { - list = list.filter((commit) => !isReleaseCommit(commit.summary)) - } + let list = await branchDiff(branch1, branch2, options) + if (argv['filter-release']) { + list = list.filter((commit) => !isReleaseCommit(commit.summary)) + } - printCommits(list, format, reverse, commitUrl) - }) + await processCommits(argv, ghId, list) } -if (require.main === module) { - main() -} +main().catch((err) => { + console.error(err) + process.exit(1) +}) diff --git a/package.json b/package.json index c757445..222f96c 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "1.10.5", "description": "A tool to list print the commits on one git branch that are not on another using loose comparison", "main": "branch-diff.js", + "type": "module", "bin": { "branch-diff": "./branch-diff.js" }, @@ -14,18 +15,12 @@ "author": "Rod (http://r.va.gg/)", "license": "MIT", "dependencies": { - "bl": "^5.0.0", - "changelog-maker": "^2.5.0", + "changelog-maker": "^3.0.0", "commit-stream": "^1.1.0", - "deep-equal": "^2.0.5", "gitexec": "^2.0.1", - "list-stream": "^2.0.0", - "map-async": "^0.1.1", "minimist": "^1.2.5", - "pkg-to-id": "0.0.3", - "split2": "^4.0.0", - "strip-ansi": "^6.0.0", - "through2": "^4.0.2" + "pkg-to-id": "^0.0.3", + "split2": "^4.1.0" }, "repository": { "type": "git", @@ -33,7 +28,7 @@ }, "preferGlobal": true, "devDependencies": { - "standard": "^16.0.3" + "standard": "^16.0.4" }, "release": { "branches": [