Skip to content
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

feat!: support stable release branch names #720

Merged
merged 25 commits into from
Feb 3, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
0189031
feat: add BranchName parser classes
chingor13 Jan 26, 2021
e877066
add toString/of helpers to branch name
chingor13 Jan 27, 2021
7e16262
fix name tests
chingor13 Jan 27, 2021
a7a3137
Merge branch 'master' into stable-branch
bcoe Jan 27, 2021
9c0a5b4
use regex capture group names
chingor13 Jan 27, 2021
df54ccb
refactor!: move release-notes helper to its own separate module"
chingor13 Jan 27, 2021
8bce7e5
feat: add findMergedPullRequests to GitHub class
chingor13 Jan 27, 2021
ce7eb3b
refactor: move release building into individual releasers
chingor13 Jan 28, 2021
2633486
cleanup commented out code
chingor13 Jan 28, 2021
e7fcb16
feat: java-yoshi uses stable branch name
chingor13 Jan 28, 2021
8ccc6c8
fix: java includes target branch in PR title
chingor13 Jan 28, 2021
1acc0ab
test: add more edge case tests
chingor13 Jan 28, 2021
5323632
chore: fix lint
chingor13 Jan 28, 2021
17dfdec
remove unused static tagSeparator()
chingor13 Jan 28, 2021
beb85e1
Merge branch 'master' into stable-branch
bcoe Feb 1, 2021
19327a0
refactor: findMergedPullRequests uses the REST API. findMergedRelease…
chingor13 Feb 1, 2021
45598ab
refactor: move version parsing logic out of findMergedPullRequest
chingor13 Feb 1, 2021
b7d1614
refactor: ReleasePR.findMergedRelease() uses a filter to leverage aut…
chingor13 Feb 2, 2021
08aa4bd
MergedPullRequestFilter interface => type
chingor13 Feb 2, 2021
7090df1
fix: add test for nodejs monorepo tags overriding package name
chingor13 Feb 2, 2021
cc1a239
Merge branch 'master' into stable-branch
bcoe Feb 2, 2021
723c038
Merge branch 'master' into stable-branch
chingor13 Feb 2, 2021
662b34e
Merge branch 'master' into stable-branch
chingor13 Feb 2, 2021
b1a2035
fix: release tagging should only find pending release PRs
chingor13 Feb 3, 2021
3d76bae
refactor: use Array#every
chingor13 Feb 3, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
115 changes: 115 additions & 0 deletions src/util/branch-name.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

type BranchNameType = typeof BranchName;

function getAllResourceNames(): BranchNameType[] {
return [AutoreleaseBranchName, ComponentBranchName, DefaultBranchName];
}

export class BranchName {
component?: string;
targetBranch?: string;
version?: string;

static parse(branchName: string): BranchName | undefined {
const branchNameClass = getAllResourceNames().find(clazz => {
return clazz.matches(branchName);
});
if (!branchNameClass) {
return undefined;
}
return new branchNameClass(branchName);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like this BranchName implementation, nice work!

One small nit: It seems like BranchName.constructor duplicates the work done by BranchName.matches.

If BranchName.parse is the factory entry point that will always get used, then the constructor could be changed to something like constructor(matches: RegExpMatchArray). BranchName.matches would return the match array and BranchName.parse updated accordingly to instantiate a clazz with that output?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was running into typescript issues, but I agree that it's not ideal duplicating the regex match. I will try this again.

}
static ofComponentVersion(branchPrefix: string, version: string): BranchName {
return new AutoreleaseBranchName(`release-${branchPrefix}-v${version}`);
}
static ofVersion(version: string): BranchName {
return new AutoreleaseBranchName(`release-v${version}`);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
constructor(branchName: string) {}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
static matches(branchName: string): boolean {
return false;
}
getTargetBranch(): string | undefined {
return this.targetBranch;
}
getComponent(): string | undefined {
return this.component;
}
getVersion(): string | undefined {
return this.version;
}
toString(): string {
return '';
}
}

const AUTORELEASE_PATTERN = /^release-?([\w-.]*)?-v([0-9].*)$/;
class AutoreleaseBranchName extends BranchName {
static matches(branchName: string): boolean {
return !!branchName.match(AUTORELEASE_PATTERN);
}
constructor(branchName: string) {
super(branchName);
const match = branchName.match(AUTORELEASE_PATTERN);
if (match) {
this.component = match[1];
this.version = match[2];
}
}
toString(): string {
if (this.component) {
return `release-${this.component}-v${this.version}`;
}
return `release-v${this.version}`;
}
}

const DEFAULT_PATTERN = /^release-please\/branches\/([^/]+)$/;
class DefaultBranchName extends BranchName {
static matches(branchName: string): boolean {
return !!branchName.match(DEFAULT_PATTERN);
}
constructor(branchName: string) {
super(branchName);
const match = branchName.match(DEFAULT_PATTERN);
if (match) {
this.targetBranch = match[1];
}
}
toString(): string {
return `release-please/branches/${this.targetBranch}`;
}
}

const COMPONENT_PATTERN = /^release-please\/branches\/([^/]+)\/components\/([^/]+)$/;
chingor13 marked this conversation as resolved.
Show resolved Hide resolved
class ComponentBranchName extends BranchName {
static matches(branchName: string): boolean {
return !!branchName.match(COMPONENT_PATTERN);
}
constructor(branchName: string) {
super(branchName);
const match = branchName.match(COMPONENT_PATTERN);
if (match) {
this.targetBranch = match[1];
this.component = match[2];
}
}
toString(): string {
return `release-please/branches/${this.targetBranch}/components/${this.component}`;
}
}
78 changes: 78 additions & 0 deletions test/util/branch-name.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import {BranchName} from '../../src/util/branch-name';
import {describe, it} from 'mocha';
import {expect} from 'chai';

describe('BranchName', () => {
describe('parse', () => {
describe('autorelease branch name', () => {
it('parses a versioned branch name', () => {
const name = 'release-v1.2.3';
const branchName = BranchName.parse(name);
expect(branchName).to.not.be.undefined;
expect(branchName?.getTargetBranch()).to.be.undefined;
expect(branchName?.getComponent()).to.be.undefined;
expect(branchName?.getVersion()).to.eql('1.2.3');
expect(branchName?.toString()).to.eql(name);
});
it('parses a versioned branch name with component', () => {
const name = 'release-storage-v1.2.3';
const branchName = BranchName.parse(name);
expect(branchName).to.not.be.undefined;
expect(branchName?.getTargetBranch()).to.be.undefined;
expect(branchName?.getComponent()).to.eql('storage');
expect(branchName?.getVersion()).to.eql('1.2.3');
expect(branchName?.toString()).to.eql(name);
});
});
it('parses a target branch', () => {
const name = 'release-please/branches/main';
const branchName = BranchName.parse(name);
expect(branchName).to.not.be.undefined;
expect(branchName?.getTargetBranch()).to.eql('main');
expect(branchName?.getComponent()).to.be.undefined;
expect(branchName?.getVersion()).to.be.undefined;
expect(branchName?.toString()).to.eql(name);
});

it('parses a target branch and component', () => {
const name = 'release-please/branches/main/components/storage';
const branchName = BranchName.parse(name);
expect(branchName).to.not.be.undefined;
expect(branchName?.getTargetBranch()).to.eql('main');
expect(branchName?.getComponent()).to.eql('storage');
expect(branchName?.getVersion()).to.be.undefined;
expect(branchName?.toString()).to.eql(name);
});

it('fails to parse', () => {
const branchName = BranchName.parse('release-foo');
expect(branchName).to.be.undefined;
});
});
describe('ofVersion', () => {
it('builds the autorelease versioned branch name', () => {
const branchName = BranchName.ofVersion('1.2.3');
expect(branchName.toString()).to.eql('release-v1.2.3');
});
});
describe('ofComponentVersion', () => {
it('builds the autorelease versioned branch name with component', () => {
const branchName = BranchName.ofComponentVersion('storage', '1.2.3');
expect(branchName.toString()).to.eql('release-storage-v1.2.3');
});
});
});