Bump 版本并从一个分支发布包,但将标签保留在另一个分支中
Posted
技术标签:
【中文标题】Bump 版本并从一个分支发布包,但将标签保留在另一个分支中【英文标题】:Bump version and publish packages from one branch but keep tags in another branch 【发布时间】:2019-08-26 14:54:55 【问题描述】:我正在使用Lerna 将包含许多相互依赖的包的项目迁移到monorepo。我们在开发过程中遵循Gitflow workflow 之类的内容。主要概念是在develop
分支和从develop
创建并合并回的所有其他分支(功能、错误修复等)中进行所有源代码更改。只要新版本的包准备好了,我们就会通过npm publish
或yarn publish
发布它,然后将它合并到master
分支并手动标记它,方法如下:
$ git checkout develop
对源代码进行一些更改,包括版本冲突...
$ git add -A
$ git commit -m "Make some changes and version bump."
$ git checkout master
$ git merge --no-ff develop -m "Version 0.14.1."
$ git tag -a 0.14.1 -m "Version 0.14.1."
现在我想用 Lerna 管理所有包来实现同样的目标。查看文档,我说publish 命令依赖于version 命令,而changed 命令在后台使用changed 命令来检测自最新版本以来包中所做的更改:
列出自上次标记发布以来已更改的本地包
考虑在一个包中的develop
分支中进行了一些更改(例如,@geoapps/layout
)
$ lerna changed
说所有的包都变了(这不是我所期望的):
info cli using local version of lerna
lerna notice cli v3.13.1
lerna info Assuming all packages changed
@geoapps/angle
@geoapps/camera-scene-mode-switcher
...
@geoapps/tracer
@geoapps/vector
lerna success found 39 packages ready to publish
我猜这是因为 Lerna 在 develop
分支中寻找标记的提交进行比较,但在那里没有找到任何东西。如果我将源代码更改提交到master
分支
然后 Lerna 在单个 @geoapps/layout
包中正确检测到它们:
$ git checkout master
$ lerna changed
info cli using local version of lerna
lerna notice cli v3.13.1
lerna info Looking for changed packages since 0.14.1
@geoapps/layout
lerna success found 1 package ready to publish
但是在master
分支中进行更改也不是我想做的。 include-merged-tags 是我尝试使用的另一个选项,但似乎它仅在标记提交也是 develop
分支历史的一部分时才有效:
$ git checkout develop
$ git merge --no-ff master -m "Sync with master."
$ lerna changed --include-merged-tags
info cli using local version of lerna
lerna notice cli v3.13.1
lerna info Looking for changed packages since 0.14.1
@geoapps/layout
lerna success found 1 package ready to publish
由于master
分支中标记的所有源代码更改都存在于develop
分支中,我想知道是否可以强制 Lerna 比较 develop
分支中所做的更改,而不是来自 master
的标记提交,而是与他们的父提交 (0.14.1^2
) 也属于 develop
。有可能吗?
环境:
$ node --version
v10.15.0
$ npm --version
6.9.0
$ yarn --version
1.15.2
$ lerna --version
3.13.1
【问题讨论】:
在GitHub 上发布了相同的问题/功能请求。 【参考方案1】:Lerna 的核心开发人员says 认为 Lerna 不适合使用 Gitflow 工作流程。多说一句,prohibited 发布包来检测它们从特定提交(在另一个分支中标记的提交)的更改。最新的标记版本应属于进行更改的同一分支。
牢记它以及我们希望继续使用 Gitflow 的愿望,我决定修补 Lerna 以实现所需的行为。只需created git patch 并使用 Lerna 将其放在我的项目的根目录中。
lerna-version-since.patch
diff --git a/commands/version/command.js b/commands/version/command.js
index da9b1c00..3c5e19e2 100644
--- a/commands/version/command.js
+++ b/commands/version/command.js
@@ -104,6 +104,11 @@ exports.builder = (yargs, composed) =>
requiresArg: true,
defaultDescription: "alpha",
,
+ since:
+ describe: "Look for changes since specified commit instead of last tagged release",
+ type: "string",
+ requiresArg: true,
+ ,
"sign-git-commit":
describe: "Pass the `--gpg-sign` flag to `git commit`.",
type: "boolean",
如果commands/version/command.js
发生变化,我们可能会更新补丁。为了应用补丁,应该运行以下命令:
$ git apply -p3 --directory node_modules/@lerna/version lerna-version-since.patch
修补 Lerna 后,现在可以在 develop
分支中碰撞和发布,并在 master
中标记发布。为了让事情变得更简单,我编写了一个名为lerna-gitflow.js
的脚本,它可以自动生成所有内容。这是package.json
的脚本部分:
"scripts":
"publish:major": "./lerna-gitflow.js publish major",
"publish:minor": "./lerna-gitflow.js publish minor",
"publish:patch": "./lerna-gitflow.js publish patch",
"changes": "./lerna-gitflow.js changes",
"postinstall": "./lerna-gitflow.js patch"
所有这些publish:*
和changes
命令都应该从开发分支运行(默认为develop
)。
changes
命令仅显示自发布分支中的最新发布标记(默认为master
)以来开发分支(develop
)中更改的包。
publish
命令做了两件事:
package.json
文件中的版本,根目录package.json
和lerna.json
,并在本地提交到develop
分支(可以单独运行,例如./lerna-gitflow.js version patch
);
将更改的包从develop
分支发布到npm 注册表,然后将更改合并到master
分支而不使用快进并在那里标记新版本(也可以通过运行./lerna-gitflow.js publish --skip-version
单独完成)。
postinstall
脚本尝试在任何 npm install
或 yarn install
调用上修补 Lerna,否则使一切正常工作所需的更改将丢失。
lerna-gitflow.js
#!/usr/bin/env node
const path = require('path');
const yargs = require('yargs');
const execa = require('execa');
const jsonfile = require('jsonfile');
const noop = () => ;
async function lernaCommand(command, options)
const devBranch = options;
const branch = await getCurrentBranch();
if (branch !== devBranch)
return Promise.reject(
`You should be in "$devBranch" branch to detect changes but current branch is "$branch".`
);
const latestVersion = await getLatestVersion();
const bumpVersion = async bump =>
await lernaVersion(latestVersion, bump);
const version = await getLernaVersion();
const packageJsonPath = path.resolve(__dirname, 'package.json');
const packageJson = await jsonfile.readFile(packageJsonPath);
packageJson.version = version;
await jsonfile.writeFile(packageJsonPath, packageJson, spaces: 2 );
await exec('git', ['add', '-A']);
await exec('git', ['commit', '-m', 'Version bump.']);
return version;
;
const reject = e =>
if (typeof e === 'string')
return Promise.reject(e);
return Promise.reject('Unable to detect any changes in packages, probably nothing has changed.');
;
switch (command)
case 'publish':
const bump, skipVersion, releaseBranch = options;
if (releaseBranch === devBranch)
return Promise.reject('Release and development branches can\'t be the same.');
try
const version = skipVersion ? await getLernaVersion() : await bumpVersion(bump);
await lernaPublish(latestVersion, version);
await exec('git', ['checkout', releaseBranch]);
await exec('git', ['merge', '--no-ff', devBranch, '-m', `Version $version.`]);
await exec('git', ['tag', '-a', version, '-m', `Version $version.`]);
await exec('git', ['checkout', devBranch]);
catch (e)
return reject(e);
break;
case 'version':
const bump = options;
try
await bumpVersion(bump);
catch (e)
return reject(e);
break;
case 'changed':
try
await lernaChanged(latestVersion);
catch (e)
return reject(e);
break;
async function lernaPublish(since, version)
if (since === version)
return Promise.reject(`Unable to publish packages with same version $version.`);
return exec('lerna', ['publish', '--since', since, version, '--no-push', '--no-git-tag-version', '--yes']);
async function lernaVersion(since, bump)
return exec('lerna', ['version', '--since', since, bump, '--no-push', '--no-git-tag-version', '--yes']);
async function lernaChanged(since)
return exec('lerna', ['changed', '--since', since]);
async function patch()
try
await exec('git', ['apply', '-p3', '--directory', 'node_modules/@lerna/version', 'lerna-version-since.patch']);
catch (e)
return Promise.reject('Lerna Gitflow patch is not applied (probably, it\'s already applied before).');
async function getCurrentBranch()
const stdout = await exec('git', ['branch']);
const match = stdout.match(/\* ([\S]+)/);
if (match === null)
return Promise.reject('Unable to detect current git branch.');
return match[1];
async function getLatestTaggedCommit()
const stdout = await exec('git', ['rev-list', '--tags', '--max-count', 1]);
if (!stdout)
return Promise.reject('Unable to find any tagged commit.');
return stdout;
async function getLatestVersion()
const commit = await getLatestTaggedCommit();
const stdout = await exec('git', ['describe', '--tags', commit]);
return stdout;
async function getLernaVersion()
const lernaJson = await jsonfile.readFile(path.resolve(__dirname, 'lerna.json'));
return lernaJson.version;
function exec(cmd, args, opts)
console.log(`$ $cmd $args.join(' ')`);
const promise = execa(cmd, args, opts);
promise.stdout.pipe(process.stdout);
promise.stderr.pipe(process.stderr);
return promise;
yargs
.wrap(null)
.strict(true)
.help(true, 'Show help')
.version(false)
.fail((msg, error) =>
console.error(error);
if (msg)
console.error(msg);
)
.demandCommand()
.command(
'publish <bump>',
'Bump and commit packages\' in development branch, then publish, merge into and tag in release branch',
yargs => yargs
.positional('bump',
describe: 'Type of version update',
type: 'string'
)
.option('skip-version',
describe: 'Skip version bumping and commiting in development branch',
type: 'boolean',
default: false
),
opts => lernaCommand('publish', opts)
)
.command(
'version <bump>',
'Bump and commit packages\' version in development branch',
yargs => yargs
.positional('bump',
describe: 'Type of version update',
type: 'string'
),
opts => lernaCommand('version', opts)
)
.command(
'changes',
'Detect packages changes since latest release',
noop,
opts => lernaCommand('changed', opts)
)
.command('patch', 'Patch Lerna to use with Gitflow', noop, () => patch())
.options(
'dev-branch':
describe: 'Name of git development branch',
type: 'string',
demandOption: true,
default: 'develop'
,
'release-branch':
describe: 'Name of git release branch',
type: 'string',
demandOption: true,
default: 'master'
)
.parse();
【讨论】:
以上是关于Bump 版本并从一个分支发布包,但将标签保留在另一个分支中的主要内容,如果未能解决你的问题,请参考以下文章