Skip to content

Commit

Permalink
feat: configure backmerge strategy
Browse files Browse the repository at this point in the history
Allows configuring the strategy to be used for backmerge.
Default is "rebase", i.e. develop is rebased onto master.
Another option is "merge", i.e. the master is merged into develop.
  • Loading branch information
saitho committed Mar 25, 2021
1 parent 68998b8 commit 7098421
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 10 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ If you do, you may set the [clearWorkspace](#clearWorkspace) option to stash the
| Options | Description | Default |
|-----------|---------------------------------------------------------------------------------|-----------|
| `branchName` | The branch where the release is merged into. See [branchName](#branchName). | develop |
| `backmergeStrategy` | How to perform the backmerge. See [backmergeStrategy](#backmergeStrategy). | rebase |
| `plugins` | Plugins defined here may stage files to be included in a back-merge commit. See [plugins](#plugins). | [] |
| `message` | The message for the back-merge commit (if files were changed by plugins. See [message](#message). | `chore(release): Preparations for next release [skip ci]` |
| `forcePush` | If set the back-merge will be force-pushed. See [forcePush](#forcePush). | false |
Expand Down Expand Up @@ -121,3 +122,8 @@ Setting this option will stash all uncommitted changes from Git workspace before
#### `restoreWorkspace`

Setting this option will restore the stashed changes after the backmerge completed.

#### `backmergeStrategy`

This setting will determine whether the _develop_ branch should be rebased onto _master_ or _master_ should be merged into _develop_.
Allowed values: rebase (default), merge
3 changes: 3 additions & 0 deletions src/definitions/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
export type BackmergeStrategy = "rebase" | "merge";

export interface Config {
branchName: string;
backmergeStrategy: BackmergeStrategy;
plugins: any;
forcePush: boolean;
message: string;
Expand Down
15 changes: 15 additions & 0 deletions src/helpers/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,19 @@ export default class Git {
async rebase(branch: string) {
await execa('git', ['rebase', `origin/${branch}`], this.execaOpts);
}

/**
* Rebases the currently checked out branch onto another branch.
*
* @param {String} branch The branch to rebase onto.
* @param {boolean} fromOrigin
*
* @throws {Error} if the rebase failed.
*/
async merge(branch: string, fromOrigin = true) {
if (fromOrigin) {
branch = 'origin/' + branch;
}
await execa('git', ['merge', branch], this.execaOpts);
}
}
3 changes: 2 additions & 1 deletion src/helpers/resolve-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import {isNil} from 'lodash';
import {Config} from "../definitions/config";

export function resolveConfig(config: Partial<Config>): Config {
const {branchName, plugins, message, forcePush, allowSameBranchMerge, clearWorkspace, restoreWorkspace} = config
const {branchName, backmergeStrategy, plugins, message, forcePush, allowSameBranchMerge, clearWorkspace, restoreWorkspace} = config
return {
branchName: isNil(branchName) ? 'develop' : branchName,
backmergeStrategy: isNil(backmergeStrategy) ? 'rebase' : backmergeStrategy,
plugins: isNil(plugins) ? [] : plugins,
message: message,
forcePush: isNil(forcePush) ? false : forcePush,
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export async function verifyConditions(pluginConfig: Config, context: Context) {
const preparePlugin = castArray(options.prepare)
.find(config => config.path && config.path === '@saithodev/semantic-release-backmerge') || {};
pluginConfig.branchName = defaultTo(pluginConfig.branchName, preparePlugin.branchName);
pluginConfig.backmergeStrategy = defaultTo(pluginConfig.backmergeStrategy, preparePlugin.backmergeStrategy);
pluginConfig.plugins = defaultTo(pluginConfig.plugins, preparePlugin.plugins);
pluginConfig.forcePush = defaultTo(pluginConfig.forcePush, preparePlugin.forcePush);
pluginConfig.message = defaultTo(pluginConfig.message, preparePlugin.message);
Expand Down
6 changes: 5 additions & 1 deletion src/perform-backmerge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,11 @@ export async function performBackmerge(git: Git, pluginConfig: Partial<Config>,
// Branch is detached. Checkout master first to be able to check out other branches
await git.checkout(masterBranchName);
await git.checkout(developBranchName);
await git.rebase(masterBranchName);
if (options.backmergeStrategy === 'merge') {
await git.merge(masterBranchName);
} else {
await git.rebase(masterBranchName);
}
} else {
await git.checkout(developBranchName);
}
Expand Down
9 changes: 9 additions & 0 deletions test/helpers/git.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,15 @@ describe("git", () => {
);
});

it("merge", async () => {
await subject.merge('master');
expect(execa).toHaveBeenCalledWith(
'git',
['merge', 'origin/master'],
expect.objectContaining(execaOpts)
);
});

it("getStagedFiles", async () => {
const execaMock: any = execa;
execaMock.mockResolvedValueOnce({
Expand Down
47 changes: 39 additions & 8 deletions test/perform-backmerge.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe("perform-backmerge", () => {
when(mockedGit.push(anyString(), anyString(), anything())).thenResolve();

const context = {logger: instance(mockedLogger), branch: {name: 'master'}, options: {repositoryUrl: 'my-repo'}};
performBackmerge(instance(mockedGit), {branchName: 'develop', plugins: []}, context)
performBackmerge(instance(mockedGit), {branchName: 'develop'}, context)
.then(() => {
verify(mockedLogger.log('Release succeeded. Performing back-merge into branch "develop".')).once();
verify(mockedGit.checkout('master')).once();
Expand All @@ -44,7 +44,7 @@ describe("perform-backmerge", () => {
when(mockedGit.push(anyString(), anyString(), anything())).thenResolve();

const context = {logger: instance(mockedLogger), branch: {name: 'master'}, options: {repositoryUrl: 'my-repo'}};
performBackmerge(instance(mockedGit), {branchName: 'master', plugins: [], allowSameBranchMerge: true}, context)
performBackmerge(instance(mockedGit), {branchName: 'master', allowSameBranchMerge: true}, context)
.then(() => {
verify(mockedLogger.log('Release succeeded. Performing back-merge into branch "master".')).once();
verify(mockedGit.configFetchAllRemotes()).once();
Expand Down Expand Up @@ -76,7 +76,7 @@ describe("perform-backmerge", () => {
when(mockedGit.push(anyString(), anyString(), anything())).thenResolve();

const context = {logger: instance(mockedLogger), branch: {name: 'master'}, options: {repositoryUrl: 'my-repo'}};
performBackmerge(instance(mockedGit), {branchName: '${branch.name}', plugins: [], allowSameBranchMerge: true}, context)
performBackmerge(instance(mockedGit), {branchName: '${branch.name}', allowSameBranchMerge: true}, context)
.then(() => {
verify(mockedLogger.log('Release succeeded. Performing back-merge into branch "master".')).once();
verify(mockedGit.configFetchAllRemotes()).once();
Expand Down Expand Up @@ -125,7 +125,7 @@ describe("perform-backmerge", () => {
when(mockedGit.push(anyString(), anyString(), anything())).thenResolve();

const context = {logger: instance(mockedLogger), branch: {name: 'master'}, options: {repositoryUrl: 'my-repo'}};
performBackmerge(instance(mockedGit), {branchName: 'develop', plugins: [], forcePush: true}, context)
performBackmerge(instance(mockedGit), {branchName: 'develop', forcePush: true}, context)
.then(() => {
verify(mockedLogger.log('Release succeeded. Performing back-merge into branch "develop".')).once();
verify(mockedGit.checkout('master')).once();
Expand Down Expand Up @@ -157,8 +157,7 @@ describe("perform-backmerge", () => {
instance(mockedGit),
{
branchName: 'develop',
message: 'my-commit-message',
plugins: [] // plugin interactions are tested in helpers/plugins test
message: 'my-commit-message'
},
context
);
Expand Down Expand Up @@ -191,8 +190,7 @@ describe("perform-backmerge", () => {
{
branchName: 'develop',
clearWorkspace: true,
restoreWorkspace: true,
plugins: [] // plugin interactions are tested in helpers/plugins test
restoreWorkspace: true
},
context
);
Expand All @@ -210,4 +208,37 @@ describe("perform-backmerge", () => {
verify(pushAction).once();
verify(mockedGit.unstash()).calledAfter(pushAction);
});

it("merge as backmerge strategy", async () => {
const mockedGit = mock(Git);
const mockedLogger = mock(NullLogger);
when(mockedGit.checkout(anyString())).thenResolve();
when(mockedGit.configFetchAllRemotes()).thenResolve();
when(mockedGit.getStagedFiles())
.thenReturn(new Promise<string[]>(resolve => resolve([])));
when(mockedGit.fetch()).thenResolve();
when(mockedGit.commit(anyString())).thenResolve();
when(mockedGit.merge(anyString())).thenResolve();
when(mockedGit.push(anyString(), anyString(), anything())).thenResolve();

const context = {logger: instance(mockedLogger), branch: {name: 'master'}, options: {repositoryUrl: 'my-repo'}};

await performBackmerge(
instance(mockedGit),
{
branchName: 'develop',
backmergeStrategy: 'merge'
},
context
);
verify(mockedLogger.log('Release succeeded. Performing back-merge into branch "develop".')).once();
verify(mockedGit.checkout('master')).once();
verify(mockedGit.configFetchAllRemotes()).once();
verify(mockedGit.fetch()).once();

verify(mockedGit.checkout('develop')).once();
verify(mockedGit.merge('master')).once();

verify(mockedGit.push('my-repo', 'develop', false)).once();
});
});

0 comments on commit 7098421

Please sign in to comment.