Skip to content

Commit

Permalink
feat(codebuild): add filter groups feature for GitHub, GitHubEnterpri…
Browse files Browse the repository at this point in the history
…se and BitBucket

Fixes #1842
  • Loading branch information
Kaixiang-AWS committed Apr 18, 2019
1 parent 8705af3 commit dd903ea
Show file tree
Hide file tree
Showing 2 changed files with 203 additions and 35 deletions.
137 changes: 102 additions & 35 deletions packages/@aws-cdk/aws-codebuild/lib/source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ export interface GitBuildSourceProps extends BuildSourceProps {
* A common superclass of all build sources that are backed by Git.
*/
export abstract class GitBuildSource extends BuildSource {
public readonly badgeSupported: boolean = true;
private readonly cloneDepth?: number;

protected constructor(props: GitBuildSourceProps) {
Expand All @@ -106,6 +105,67 @@ export abstract class GitBuildSource extends BuildSource {
}
}

/**
* The construction properties common to all external build sources that are backed by Git.
*/
export interface ExternalGitBuildSourceProps extends GitBuildSourceProps {
/**
* Whether to create a webhook that will trigger a build every time a commit is pushed to the repository.
*
* @default false
*/
readonly webhook?: boolean;

/**
* Whether to send notifications on your build's start and end.
*
* @default true
*/
readonly reportBuildStatus?: boolean;

/**
* A list of lists of WebhookFilter objects used to determine which webhook events are triggered.
*/
readonly filterGroups?: WebhookFilter[][];
}

/**
* A common superclass of all external build sources that are backed by Git.
*/
export abstract class ExternalGitBuildSource extends GitBuildSource {
public readonly badgeSupported: boolean = true;
private readonly reportBuildStatus: boolean;
private readonly webhook?: boolean;
private readonly filterGroups?: WebhookFilter[][];

protected constructor(props: ExternalGitBuildSourceProps) {
super(props);

this.webhook = props.webhook;
this.reportBuildStatus = props.reportBuildStatus === undefined ? true : props.reportBuildStatus;
this.filterGroups = props.filterGroups;
}

public buildTriggers(): CfnProject.ProjectTriggersProperty | undefined {
if (!this.webhook && this.filterGroups) {
throw new Error("filterGroups property could only be set when webhook property is true");
}
return this.webhook === undefined
? undefined
: {
webhook: this.webhook,
filterGroups: this.filterGroups,
};
}

public toSourceJSON(): CfnProject.SourceProperty {
return {
...super.toSourceJSON(),
reportBuildStatus: this.reportBuildStatus,
};
}
}

/**
* Construction properties for {@link CodeCommitSource}.
*/
Expand All @@ -118,7 +178,6 @@ export interface CodeCommitSourceProps extends GitBuildSourceProps {
*/
export class CodeCommitSource extends GitBuildSource {
public readonly type: SourceType = SourceType.CodeCommit;
public readonly badgeSupported: boolean = false;
private readonly repo: codecommit.IRepository;

constructor(props: CodeCommitSourceProps) {
Expand Down Expand Up @@ -195,7 +254,7 @@ export class CodePipelineSource extends BuildSource {
/**
* Construction properties for {@link GitHubSource} and {@link GitHubEnterpriseSource}.
*/
export interface GitHubSourceProps extends GitBuildSourceProps {
export interface GitHubSourceProps extends ExternalGitBuildSourceProps {
/**
* The GitHub account/user that owns the repo.
*
Expand All @@ -209,58 +268,31 @@ export interface GitHubSourceProps extends GitBuildSourceProps {
* @example 'aws-cdk'
*/
readonly repo: string;

/**
* Whether to create a webhook that will trigger a build every time a commit is pushed to the GitHub repository.
*
* @default false
*/
readonly webhook?: boolean;

/**
* Whether to send GitHub notifications on your build's start and end.
*
* @default true
*/
readonly reportBuildStatus?: boolean;
}

/**
* GitHub Source definition for a CodeBuild project.
*/
export class GitHubSource extends GitBuildSource {
export class GitHubSource extends ExternalGitBuildSource {
public readonly type: SourceType = SourceType.GitHub;
private readonly httpsCloneUrl: string;
private readonly reportBuildStatus: boolean;
private readonly webhook?: boolean;

constructor(props: GitHubSourceProps) {
super(props);
this.httpsCloneUrl = `https://github.com/${props.owner}/${props.repo}.git`;
this.webhook = props.webhook;
this.reportBuildStatus = props.reportBuildStatus === undefined ? true : props.reportBuildStatus;
}

public buildTriggers(): CfnProject.ProjectTriggersProperty | undefined {
return this.webhook === undefined
? undefined
: {
webhook: this.webhook,
};
}

protected toSourceProperty(): any {
return {
location: this.httpsCloneUrl,
reportBuildStatus: this.reportBuildStatus,
};
}
}

/**
* Construction properties for {@link GitHubEnterpriseSource}.
*/
export interface GitHubEnterpriseSourceProps extends GitBuildSourceProps {
export interface GitHubEnterpriseSourceProps extends ExternalGitBuildSourceProps {
/**
* The HTTPS URL of the repository in your GitHub Enterprise installation.
*/
Expand All @@ -277,7 +309,7 @@ export interface GitHubEnterpriseSourceProps extends GitBuildSourceProps {
/**
* GitHub Enterprise Source definition for a CodeBuild project.
*/
export class GitHubEnterpriseSource extends GitBuildSource {
export class GitHubEnterpriseSource extends ExternalGitBuildSource {
public readonly type: SourceType = SourceType.GitHubEnterprise;
private readonly httpsCloneUrl: string;
private readonly ignoreSslErrors?: boolean;
Expand All @@ -299,7 +331,7 @@ export class GitHubEnterpriseSource extends GitBuildSource {
/**
* Construction properties for {@link BitBucketSource}.
*/
export interface BitBucketSourceProps extends GitBuildSourceProps {
export interface BitBucketSourceProps extends ExternalGitBuildSourceProps {
/**
* The BitBucket account/user that owns the repo.
*
Expand All @@ -318,7 +350,7 @@ export interface BitBucketSourceProps extends GitBuildSourceProps {
/**
* BitBucket Source definition for a CodeBuild project.
*/
export class BitBucketSource extends GitBuildSource {
export class BitBucketSource extends ExternalGitBuildSource {
public readonly type: SourceType = SourceType.BitBucket;
private readonly httpsCloneUrl: any;

Expand All @@ -334,6 +366,41 @@ export class BitBucketSource extends GitBuildSource {
}
}

/**
* Filter used to determine which webhooks trigger a build
*/
export interface WebhookFilter {
/**
* The type of webhook filter.
*/
readonly type: WebhookFilterType,

/**
* A regular expression pattern.
*/
readonly pattern: string,

/**
* Used to indicate that the pattern determines which webhook events do not trigger a build.
* If true, then a webhook event that does not match the pattern triggers a build.
* If false, then a webhook event that matches the pattern triggers a build.
*
* @default false
*/
readonly excludeMatchedPattern?: boolean,
}

/**
* Filter types for webhook filters
*/
export enum WebhookFilterType {
Event = 'EVENT',
ActorAccountId = 'ACTOR_ACCOUNT_ID',
HeadRef = 'HEAD_REF',
BaseRef = 'BASE_REF',
FilePath = 'FILE_PATH',
}

/**
* Source types for CodeBuild Project
*/
Expand Down
101 changes: 101 additions & 0 deletions packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import s3 = require('@aws-cdk/aws-s3');
import cdk = require('@aws-cdk/cdk');
import { Test } from 'nodeunit';
import codebuild = require('../lib');
import { WebhookFilterType } from '../lib';

// tslint:disable:object-literal-key-quotes

Expand Down Expand Up @@ -480,6 +481,16 @@ export = {
cloneDepth: 3,
webhook: true,
reportBuildStatus: false,
filterGroups: [
[
{ type: WebhookFilterType.Event, pattern: 'PUSH' },
{ type: WebhookFilterType.HeadRef, pattern: 'master', excludeMatchedPattern: false },
],
[
{ type: WebhookFilterType.Event, pattern: 'PULL_REQUEST_OPEN' },
{ type: WebhookFilterType.BaseRef, pattern: 'master' },
],
],
})
});

Expand All @@ -495,6 +506,16 @@ export = {
expect(stack).to(haveResourceLike('AWS::CodeBuild::Project', {
Triggers: {
Webhook: true,
FilterGroups: [
[
{ Type: 'EVENT', Pattern: 'PUSH' },
{ Type: 'HEAD_REF', Pattern: 'master', ExcludeMatchedPattern: false },
],
[
{ Type: 'EVENT', Pattern: 'PULL_REQUEST_OPEN' },
{ Type: 'BASE_REF', Pattern: 'master' },
],
],
},
}));

Expand All @@ -508,6 +529,18 @@ export = {
httpsCloneUrl: 'https://github.testcompany.com/testowner/testrepo',
ignoreSslErrors: true,
cloneDepth: 4,
webhook: true,
reportBuildStatus: false,
filterGroups: [
[
{ type: WebhookFilterType.Event, pattern: 'PUSH' },
{ type: WebhookFilterType.HeadRef, pattern: 'master', excludeMatchedPattern: false },
],
[
{ type: WebhookFilterType.Event, pattern: 'PULL_REQUEST_OPEN' },
{ type: WebhookFilterType.BaseRef, pattern: 'master' },
],
],
})
});

Expand All @@ -516,10 +549,27 @@ export = {
Type: "GITHUB_ENTERPRISE",
InsecureSsl: true,
GitCloneDepth: 4,
ReportBuildStatus: false,
Location: 'https://github.testcompany.com/testowner/testrepo'
}
}));

expect(stack).to(haveResourceLike('AWS::CodeBuild::Project', {
Triggers: {
Webhook: true,
FilterGroups: [
[
{ Type: 'EVENT', Pattern: 'PUSH' },
{ Type: 'HEAD_REF', Pattern: 'master', ExcludeMatchedPattern: false },
],
[
{ Type: 'EVENT', Pattern: 'PULL_REQUEST_OPEN' },
{ Type: 'BASE_REF', Pattern: 'master' },
],
],
},
}));

test.done();
},
'with Bitbucket source'(test: Test) {
Expand All @@ -530,6 +580,18 @@ export = {
owner: 'testowner',
repo: 'testrepo',
cloneDepth: 5,
webhook: true,
reportBuildStatus: false,
filterGroups: [
[
{ type: WebhookFilterType.Event, pattern: 'PUSH' },
{ type: WebhookFilterType.HeadRef, pattern: 'master', excludeMatchedPattern: false },
],
[
{ type: WebhookFilterType.Event, pattern: 'PULL_REQUEST_OPEN' },
{ type: WebhookFilterType.BaseRef, pattern: 'master' },
],
],
})
});

Expand All @@ -538,6 +600,23 @@ export = {
Type: 'BITBUCKET',
Location: 'https://bitbucket.org/testowner/testrepo.git',
GitCloneDepth: 5,
ReportBuildStatus: false,
},
}));

expect(stack).to(haveResourceLike('AWS::CodeBuild::Project', {
Triggers: {
Webhook: true,
FilterGroups: [
[
{ Type: 'EVENT', Pattern: 'PUSH' },
{ Type: 'HEAD_REF', Pattern: 'master', ExcludeMatchedPattern: false },
],
[
{ Type: 'EVENT', Pattern: 'PULL_REQUEST_OPEN' },
{ Type: 'BASE_REF', Pattern: 'master' },
],
],
},
}));

Expand Down Expand Up @@ -1153,6 +1232,28 @@ export = {
}
});

test.done();
},

'filter groups validation'(test: Test) {
const stack = new cdk.Stack();

// should throw exception when webhook is not specified but filterGroups is
test.throws(() => {
new codebuild.Project(stack, 'Project', {
source: new codebuild.GitHubSource({
owner: 'testowner',
repo: 'testrepo',
cloneDepth: 3,
filterGroups: [
[
{ type: WebhookFilterType.Event, pattern: 'PUSH' }
]
],
})
});
}, Error, "filterGroups property could only be set when webhook property is true");

test.done();
}
};

0 comments on commit dd903ea

Please sign in to comment.