我正在将包含许多相互依赖的包的项目迁移到带有Lerna 的monorepo 。我们在开发过程中遵循类似Gitflow 的工作流程。主要概念是在develop
分支中进行所有源代码更改以及从develop
. 只要包的新版本准备好,我们就可以通过npm publish
或发布它yarn publish
,然后将其合并到master
分支并通过以下方式手动标记它:
$ git checkout develop
Run Code Online (Sandbox Code Playgroud)
对源代码进行一些更改,包括版本碰撞...
$ 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."
Run Code Online (Sandbox Code Playgroud)
现在我想用 Lerna 管理所有包来实现同样的事情。查看文档,我指出发布命令依赖于version命令,而version命令又在幕后使用更改的命令来检测自最新版本以来包中所做的更改:
列出自上次标记发布以来已更改的本地包
考虑develop
在一个包中的分支中进行了一些更改(例如,@geoapps/layout
)
$ lerna changed
Run Code Online (Sandbox Code Playgroud)
说所有软件包都已更改(这不是我所期望的):
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
Run Code Online (Sandbox Code Playgroud)
我猜这是因为 Lerna 在develop
分支中寻找标记的提交来进行比较,但在那里什么也没找到。如果我将源代码更改提交到master
分支
然后 Lerna 在单个@geoapps/layout
包中正确检测它们:
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
但是在master
分支中进行更改也不是我想要做的。include-merged-tags是我尝试使用的另一个选项,但似乎只有当标记的提交也是develop
分支历史的一部分时才有效:
$ git checkout develop
$ git merge --no-ff master -m "Sync with master."
Run Code Online (Sandbox Code Playgroud)
$ git checkout master
$ lerna changed
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
由于所有的源代码更改标签的master
分支存在于develop
分支我不知道是否有可能迫使勒拿湖来比较所做的更改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
Run Code Online (Sandbox Code Playgroud)
Lerna 的核心开发人员说Lerna 不适合与 Gitflow 工作流一起工作。更重要的是,禁止发布从特定提交(在另一个分支中标记提交)检测其更改的包。最新的标记版本应该属于进行更改的同一分支。
牢记它以及我们希望继续使用 Gitflow,我决定修补 Lerna 以实现所需的行为。刚刚创建了 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",
Run Code Online (Sandbox Code Playgroud)
如果有什么变化,commands/version/command.js
那么我们可能会更新补丁。为了应用补丁,应该运行以下命令:
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",
Run Code Online (Sandbox Code Playgroud)
修补 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"
}
Run Code Online (Sandbox Code Playgroud)
所有这些publish:*
和changes
命令都应该从开发分支运行(develop
默认情况下)。
changes
命令仅显示develop
自发布分支中的最新发布标记(master
默认情况下)以来开发分支()中更改的包。
publish
命令做了两件事:
package.json
已更改包文件中的版本,在 root 中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();
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
3507 次 |
最近记录: |