packages/aws-cdk-lib/aws-codebuild/lib/source.ts (481 lines of code) (raw):
import { Construct } from 'constructs';
import { CfnProject } from './codebuild.generated';
import { IProject } from './project';
import {
BITBUCKET_SOURCE_TYPE,
CODECOMMIT_SOURCE_TYPE,
GITHUB_ENTERPRISE_SOURCE_TYPE,
GITHUB_SOURCE_TYPE,
S3_SOURCE_TYPE,
} from './source-types';
import * as codecommit from '../../aws-codecommit';
import * as iam from '../../aws-iam';
import * as s3 from '../../aws-s3';
import { UnscopedValidationError } from '../../core';
/**
* The type returned from `ISource#bind`.
*/
export interface SourceConfig {
readonly sourceProperty: CfnProject.SourceProperty;
readonly buildTriggers?: CfnProject.ProjectTriggersProperty;
/**
* `AWS::CodeBuild::Project.SourceVersion`
* @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-codebuild-project.html#cfn-codebuild-project-sourceversion
* @default the latest version
*/
readonly sourceVersion?: string;
}
/**
* The abstract interface of a CodeBuild source.
* Implemented by `Source`.
*/
export interface ISource {
readonly identifier?: string;
readonly type: string;
readonly badgeSupported: boolean;
bind(scope: Construct, project: IProject): SourceConfig;
}
/**
* Properties common to all Source classes.
*/
export interface SourceProps {
/**
* The source identifier.
* This property is required on secondary sources.
*/
readonly identifier?: string;
}
/**
* Source provider definition for a CodeBuild Project.
*/
export abstract class Source implements ISource {
public static s3(props: S3SourceProps): ISource {
return new S3Source(props);
}
public static codeCommit(props: CodeCommitSourceProps): ISource {
return new CodeCommitSource(props);
}
public static gitHub(props: GitHubSourceProps): ISource {
return new GitHubSource(props);
}
public static gitHubEnterprise(props: GitHubEnterpriseSourceProps): ISource {
return new GitHubEnterpriseSource(props);
}
public static bitBucket(props: BitBucketSourceProps): ISource {
return new BitBucketSource(props);
}
public readonly identifier?: string;
public abstract readonly type: string;
public readonly badgeSupported: boolean = false;
protected constructor(props: SourceProps) {
this.identifier = props.identifier;
}
/**
* Called by the project when the source is added so that the source can perform
* binding operations on the source. For example, it can grant permissions to the
* code build project to read from the S3 bucket.
*/
public bind(_scope: Construct, _project: IProject): SourceConfig {
return {
sourceProperty: {
sourceIdentifier: this.identifier,
type: this.type,
},
};
}
}
/**
* The construction properties common to all build sources that are backed by Git.
*/
interface GitSourceProps extends SourceProps {
/**
* The depth of history to download. Minimum value is 0.
* If this value is 0, greater than 25, or not provided,
* then the full history is downloaded with each build of the project.
*/
readonly cloneDepth?: number;
/**
* The commit ID, pull request ID, branch name, or tag name that corresponds to
* the version of the source code you want to build
*
* @example 'mybranch'
* @default the default branch's HEAD commit ID is used
*/
readonly branchOrRef?: string;
/**
* Whether to fetch submodules while cloning git repo.
*
* @default false
*/
readonly fetchSubmodules?: boolean;
}
/**
* A common superclass of all build sources that are backed by Git.
*/
abstract class GitSource extends Source {
private readonly cloneDepth?: number;
private readonly branchOrRef?: string;
private readonly fetchSubmodules?: boolean;
protected constructor(props: GitSourceProps) {
super(props);
this.cloneDepth = props.cloneDepth;
this.branchOrRef = props.branchOrRef;
this.fetchSubmodules = props.fetchSubmodules;
}
public bind(_scope: Construct, _project: IProject): SourceConfig {
const superConfig = super.bind(_scope, _project);
return {
sourceVersion: this.branchOrRef,
sourceProperty: {
...superConfig.sourceProperty,
gitCloneDepth: this.cloneDepth,
gitSubmodulesConfig: this.fetchSubmodules ? {
fetchSubmodules: this.fetchSubmodules,
} : undefined,
},
};
}
}
/**
* The types of webhook event actions.
*/
export enum EventAction {
/**
* A push (of a branch, or a tag) to the repository.
*/
PUSH = 'PUSH',
/**
* Creating a Pull Request.
*/
PULL_REQUEST_CREATED = 'PULL_REQUEST_CREATED',
/**
* Updating a Pull Request.
*/
PULL_REQUEST_UPDATED = 'PULL_REQUEST_UPDATED',
/**
* Closing a Pull Request.
*/
PULL_REQUEST_CLOSED = 'PULL_REQUEST_CLOSED',
/**
* Merging a Pull Request.
*/
PULL_REQUEST_MERGED = 'PULL_REQUEST_MERGED',
/**
* Re-opening a previously closed Pull Request.
* Note that this event is only supported for GitHub and GitHubEnterprise sources.
*/
PULL_REQUEST_REOPENED = 'PULL_REQUEST_REOPENED',
/**
* A release is created in the repository.
* Works with GitHub only.
*/
RELEASED = 'RELEASED',
/**
* A prerelease is created in the repository.
* Works with GitHub only.
*/
PRERELEASED = 'PRERELEASED',
/**
* A workflow job is queued in the repository.
* Works with GitHub only.
*/
WORKFLOW_JOB_QUEUED = 'WORKFLOW_JOB_QUEUED',
}
enum WebhookFilterTypes {
FILE_PATH = 'FILE_PATH',
COMMIT_MESSAGE = 'COMMIT_MESSAGE',
HEAD_REF = 'HEAD_REF',
ACTOR_ACCOUNT_ID = 'ACTOR_ACCOUNT_ID',
BASE_REF = 'BASE_REF',
REPOSITORY_NAME = 'REPOSITORY_NAME',
}
/**
* An object that represents a group of filter conditions for a webhook.
* Every condition in a given FilterGroup must be true in order for the whole group to be true.
* You construct instances of it by calling the `#inEventOf` static factory method,
* and then calling various `andXyz` instance methods to create modified instances of it
* (this class is immutable).
*
* You pass instances of this class to the `webhookFilters` property when constructing a source.
*/
export class FilterGroup {
/**
* Creates a new event FilterGroup that triggers on any of the provided actions.
*
* @param actions the actions to trigger the webhook on
*/
public static inEventOf(...actions: EventAction[]): FilterGroup {
return new FilterGroup(new Set(actions), []);
}
private readonly actions: Set<EventAction>;
private readonly filters: CfnProject.WebhookFilterProperty[];
private constructor(actions: Set<EventAction>, filters: CfnProject.WebhookFilterProperty[]) {
if (actions.size === 0) {
throw new UnscopedValidationError('A filter group must contain at least one event action');
}
this.actions = actions;
this.filters = filters;
}
/**
* Create a new FilterGroup with an added condition:
* the event must affect the given branch.
*
* @param branchName the name of the branch (can be a regular expression)
*/
public andBranchIs(branchName: string): FilterGroup {
return this.addHeadBranchFilter(branchName, true);
}
/**
* Create a new FilterGroup with an added condition:
* the event must not affect the given branch.
*
* @param branchName the name of the branch (can be a regular expression)
*/
public andBranchIsNot(branchName: string): FilterGroup {
return this.addHeadBranchFilter(branchName, false);
}
/**
* Create a new FilterGroup with an added condition:
* the event must affect a head commit with the given message.
*
* @param commitMessage the commit message (can be a regular expression)
*/
public andCommitMessageIs(commitMessage: string): FilterGroup {
return this.addCommitMessageFilter(commitMessage, true);
}
/**
* Create a new FilterGroup with an added condition:
* the event must not affect a head commit with the given message.
*
* @param commitMessage the commit message (can be a regular expression)
*/
public andCommitMessageIsNot(commitMessage: string): FilterGroup {
return this.addCommitMessageFilter(commitMessage, false);
}
/**
* Create a new FilterGroup with an added condition:
* the event must affect the given tag.
*
* @param tagName the name of the tag (can be a regular expression)
*/
public andTagIs(tagName: string): FilterGroup {
return this.addHeadTagFilter(tagName, true);
}
/**
* Create a new FilterGroup with an added condition:
* the event must not affect the given tag.
*
* @param tagName the name of the tag (can be a regular expression)
*/
public andTagIsNot(tagName: string): FilterGroup {
return this.addHeadTagFilter(tagName, false);
}
/**
* Create a new FilterGroup with an added condition:
* the event must affect a Git reference (ie., a branch or a tag)
* that matches the given pattern.
*
* @param pattern a regular expression
*/
public andHeadRefIs(pattern: string) {
return this.addHeadRefFilter(pattern, true);
}
/**
* Create a new FilterGroup with an added condition:
* the event must not affect a Git reference (ie., a branch or a tag)
* that matches the given pattern.
*
* @param pattern a regular expression
*/
public andHeadRefIsNot(pattern: string) {
return this.addHeadRefFilter(pattern, false);
}
/**
* Create a new FilterGroup with an added condition:
* the account ID of the actor initiating the event must match the given pattern.
*
* @param pattern a regular expression
*/
public andActorAccountIs(pattern: string): FilterGroup {
return this.addActorAccountId(pattern, true);
}
/**
* Create a new FilterGroup with an added condition:
* the account ID of the actor initiating the event must not match the given pattern.
*
* @param pattern a regular expression
*/
public andActorAccountIsNot(pattern: string): FilterGroup {
return this.addActorAccountId(pattern, false);
}
/**
* Create a new FilterGroup with an added condition:
* the Pull Request that is the source of the event must target the given base branch.
* Note that you cannot use this method if this Group contains the `PUSH` event action.
*
* @param branchName the name of the branch (can be a regular expression)
*/
public andBaseBranchIs(branchName: string): FilterGroup {
return this.addBaseBranchFilter(branchName, true);
}
/**
* Create a new FilterGroup with an added condition:
* the Pull Request that is the source of the event must not target the given base branch.
* Note that you cannot use this method if this Group contains the `PUSH` event action.
*
* @param branchName the name of the branch (can be a regular expression)
*/
public andBaseBranchIsNot(branchName: string): FilterGroup {
return this.addBaseBranchFilter(branchName, false);
}
/**
* Create a new FilterGroup with an added condition:
* the Pull Request that is the source of the event must target the given Git reference.
* Note that you cannot use this method if this Group contains the `PUSH` event action.
*
* @param pattern a regular expression
*/
public andBaseRefIs(pattern: string): FilterGroup {
return this.addBaseRefFilter(pattern, true);
}
/**
* Create a new FilterGroup with an added condition:
* the Pull Request that is the source of the event must not target the given Git reference.
* Note that you cannot use this method if this Group contains the `PUSH` event action.
*
* @param pattern a regular expression
*/
public andBaseRefIsNot(pattern: string): FilterGroup {
return this.addBaseRefFilter(pattern, false);
}
/**
* Create a new FilterGroup with an added condition:
* the push that is the source of the event must affect a file that matches the given pattern.
* Note that you can only use this method if this Group contains only the `PUSH` event action,
* and only for GitHub, Bitbucket and GitHubEnterprise sources.
*
* @param pattern a regular expression
*/
public andFilePathIs(pattern: string): FilterGroup {
return this.addFilePathFilter(pattern, true);
}
/**
* Create a new FilterGroup with an added condition:
* the push that is the source of the event must not affect a file that matches the given pattern.
* Note that you can only use this method if this Group contains only the `PUSH` event action,
* and only for GitHub, Bitbucket and GitHubEnterprise sources.
*
* @param pattern a regular expression
*/
public andFilePathIsNot(pattern: string): FilterGroup {
return this.addFilePathFilter(pattern, false);
}
/**
* Create a new FilterGroup with an added condition:
* the push that is the source of the event affect only a repository name that matches the given pattern.
* Note that you can only use this method if this Group contains only the `WORKFLOW_JOB_QUEUED` event action,
* only for GitHub sources and only for organization webhook.
*
* @param pattern a regular expression
*/
public andRepositoryNameIs(pattern: string): FilterGroup {
return this.addRepositoryNameFilter(pattern, true);
}
/**
* Create a new FilterGroup with an added condition:
* the push that is the source of the event must not affect a repository name that matches the given pattern.
* Note that you can only use this method if this Group contains only the `WORKFLOW_JOB_QUEUED` event action,
* only for GitHub sources and only for organization webhook.
*
* @param pattern a regular expression
*/
public andRepositoryNameIsNot(pattern: string): FilterGroup {
return this.addRepositoryNameFilter(pattern, false);
}
/** @internal */
public get _actions(): EventAction[] {
return set2Array(this.actions);
}
/** @internal */
public get _filters(): CfnProject.WebhookFilterProperty[] {
return this.filters.slice();
}
/** @internal */
public _toJson(): CfnProject.WebhookFilterProperty[] {
const eventFilter: CfnProject.WebhookFilterProperty = {
type: 'EVENT',
pattern: set2Array(this.actions).join(', '),
};
return [eventFilter].concat(this.filters);
}
private addCommitMessageFilter(commitMessage: string, include: boolean): FilterGroup {
return this.addFilter(WebhookFilterTypes.COMMIT_MESSAGE, commitMessage, include);
}
private addHeadBranchFilter(branchName: string, include: boolean): FilterGroup {
return this.addHeadRefFilter(`refs/heads/${branchName}`, include);
}
private addHeadTagFilter(tagName: string, include: boolean): FilterGroup {
return this.addHeadRefFilter(`refs/tags/${tagName}`, include);
}
private addHeadRefFilter(refName: string, include: boolean) {
return this.addFilter(WebhookFilterTypes.HEAD_REF, refName, include);
}
private addActorAccountId(accountId: string, include: boolean) {
return this.addFilter(WebhookFilterTypes.ACTOR_ACCOUNT_ID, accountId, include);
}
private addBaseBranchFilter(branchName: string, include: boolean): FilterGroup {
return this.addBaseRefFilter(`refs/heads/${branchName}`, include);
}
private addBaseRefFilter(refName: string, include: boolean) {
if (this.actions.has(EventAction.PUSH)) {
throw new UnscopedValidationError('A base reference condition cannot be added if a Group contains a PUSH event action');
}
return this.addFilter(WebhookFilterTypes.BASE_REF, refName, include);
}
private addFilePathFilter(pattern: string, include: boolean): FilterGroup {
return this.addFilter(WebhookFilterTypes.FILE_PATH, pattern, include);
}
private addRepositoryNameFilter(pattern: string, include: boolean): FilterGroup {
return this.addFilter(WebhookFilterTypes.REPOSITORY_NAME, pattern, include);
}
private addFilter(type: WebhookFilterTypes, pattern: string, include: boolean) {
return new FilterGroup(this.actions, this.filters.concat([{
type,
pattern,
excludeMatchedPattern: include ? undefined : true,
}]));
}
}
/**
* The construction properties common to all third-party build sources that are backed by Git.
*/
interface ThirdPartyGitSourceProps extends GitSourceProps {
/**
* Whether to send notifications on your build's start and end.
*
* @default true
*/
readonly reportBuildStatus?: boolean;
/**
* Whether to create a webhook that will trigger a build every time an event happens in the repository.
*
* @default true if any `webhookFilters` were provided, false otherwise
*/
readonly webhook?: boolean;
/**
* Trigger a batch build from a webhook instead of a standard one.
*
* Enabling this will enable batch builds on the CodeBuild project.
*
* @default false
*/
readonly webhookTriggersBatchBuild?: boolean;
/**
* A list of webhook filters that can constraint what events in the repository will trigger a build.
* A build is triggered if any of the provided filter groups match.
* Only valid if `webhook` was not provided as false.
*
* @default every push and every Pull Request (create or update) triggers a build
*/
readonly webhookFilters?: FilterGroup[];
/**
* The URL that the build will report back to the source provider.
* Can use built-in CodeBuild variables, like $AWS_REGION.
*
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/create-project-cli.html#cli.source.buildstatusconfig.targeturl
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html
*
* @example "$CODEBUILD_PUBLIC_BUILD_URL"
* @default - link to the AWS Console for CodeBuild to a particular build execution
*/
readonly buildStatusUrl?: string;
}
/**
* A common superclass of all third-party build sources that are backed by Git.
*/
abstract class ThirdPartyGitSource extends GitSource {
public readonly badgeSupported: boolean = true;
protected readonly webhookFilters: FilterGroup[];
private readonly reportBuildStatus: boolean;
private readonly webhook?: boolean;
private readonly webhookTriggersBatchBuild?: boolean;
protected readonly buildStatusUrl?: string;
protected constructor(props: ThirdPartyGitSourceProps) {
super(props);
this.webhook = props.webhook;
this.reportBuildStatus = props.reportBuildStatus ?? true;
this.webhookFilters = props.webhookFilters || [];
this.webhookTriggersBatchBuild = props.webhookTriggersBatchBuild;
this.buildStatusUrl = props.buildStatusUrl;
}
public bind(_scope: Construct, project: IProject): SourceConfig {
const anyFilterGroupsProvided = this.webhookFilters.length > 0;
const webhook = this.webhook ?? (anyFilterGroupsProvided ? true : undefined);
if (!webhook && anyFilterGroupsProvided) {
throw new UnscopedValidationError('`webhookFilters` cannot be used when `webhook` is `false`');
}
if (!webhook && this.webhookTriggersBatchBuild) {
throw new UnscopedValidationError('`webhookTriggersBatchBuild` cannot be used when `webhook` is `false`');
}
const superConfig = super.bind(_scope, project);
if (this.webhookTriggersBatchBuild) {
project.enableBatchBuilds();
}
return {
sourceProperty: {
...superConfig.sourceProperty,
reportBuildStatus: this.reportBuildStatus,
},
sourceVersion: superConfig.sourceVersion,
buildTriggers: webhook === undefined ? undefined : {
webhook,
buildType: this.webhookTriggersBatchBuild ? 'BUILD_BATCH' : undefined,
filterGroups: anyFilterGroupsProvided ? this.webhookFilters.map(fg => fg._toJson()) : undefined,
},
};
}
}
/**
* Construction properties for `CodeCommitSource`.
*/
export interface CodeCommitSourceProps extends GitSourceProps {
readonly repository: codecommit.IRepository;
}
/**
* CodeCommit Source definition for a CodeBuild project.
*/
class CodeCommitSource extends GitSource {
public readonly badgeSupported = true;
public readonly type = CODECOMMIT_SOURCE_TYPE;
private readonly repo: codecommit.IRepository;
constructor(props: CodeCommitSourceProps) {
super(props);
this.repo = props.repository;
}
public bind(_scope: Construct, project: IProject): SourceConfig {
// https://docs.aws.amazon.com/codebuild/latest/userguide/setting-up.html
project.addToRolePolicy(new iam.PolicyStatement({
actions: ['codecommit:GitPull'],
resources: [this.repo.repositoryArn],
}));
const superConfig = super.bind(_scope, project);
return {
sourceProperty: {
...superConfig.sourceProperty,
location: this.repo.repositoryCloneUrlHttp,
},
sourceVersion: superConfig.sourceVersion,
};
}
}
/**
* Construction properties for `S3Source`.
*/
export interface S3SourceProps extends SourceProps {
readonly bucket: s3.IBucket;
readonly path: string;
/**
* The version ID of the object that represents the build input ZIP file to use.
*
* @default latest
*/
readonly version?: string;
}
/**
* S3 bucket definition for a CodeBuild project.
*/
class S3Source extends Source {
public readonly type = S3_SOURCE_TYPE;
private readonly bucket: s3.IBucket;
private readonly path: string;
private readonly version?: string;
constructor(props: S3SourceProps) {
super(props);
this.bucket = props.bucket;
this.path = props.path;
this.version = props.version;
}
public bind(_scope: Construct, project: IProject): SourceConfig {
this.bucket.grantRead(project, this.path);
const superConfig = super.bind(_scope, project);
return {
sourceProperty: {
...superConfig.sourceProperty,
location: `${this.bucket.bucketName}/${this.path}`,
},
sourceVersion: this.version,
};
}
}
/**
* Common properties between `GitHubSource` and `GitHubEnterpriseSource`.
*/
interface CommonGithubSourceProps extends ThirdPartyGitSourceProps {
/**
* This parameter is used for the `context` parameter in the GitHub commit status.
* Can use built-in CodeBuild variables, like $AWS_REGION.
*
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/create-project-cli.html#cli.source.buildstatusconfig.context
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html
*
* @example "My build #$CODEBUILD_BUILD_NUMBER"
* @default "AWS CodeBuild $AWS_REGION ($PROJECT_NAME)"
*/
readonly buildStatusContext?: string;
}
abstract class CommonGithubSource extends ThirdPartyGitSource {
private readonly buildStatusContext?: string;
constructor(props: CommonGithubSourceProps) {
super(props);
this.buildStatusContext = props.buildStatusContext;
}
public bind(scope: Construct, project: IProject): SourceConfig {
const superConfig = super.bind(scope, project);
return {
sourceProperty: {
...superConfig.sourceProperty,
buildStatusConfig: this.buildStatusContext !== undefined || this.buildStatusUrl !== undefined
? {
context: this.buildStatusContext,
targetUrl: this.buildStatusUrl,
}
: undefined,
},
sourceVersion: superConfig.sourceVersion,
buildTriggers: superConfig.buildTriggers,
};
}
}
/**
* Construction properties for `GitHubSource` and `GitHubEnterpriseSource`.
*/
export interface GitHubSourceProps extends CommonGithubSourceProps {
/**
* The GitHub Organization/user that owns the repo.
*
* @example 'awslabs'
*/
readonly owner: string;
/**
* The name of the repo (without the username).
*
* @example 'aws-cdk'
* @default undefined will create an organization webhook
*/
readonly repo?: string;
}
/**
* GitHub Source definition for a CodeBuild project.
*/
class GitHubSource extends CommonGithubSource {
public readonly type = GITHUB_SOURCE_TYPE;
private readonly sourceLocation: string;
private readonly organization?: string;
protected readonly webhookFilters: FilterGroup[];
constructor(props: GitHubSourceProps) {
super(props);
this.organization = props.repo === undefined ? props.owner : undefined;
this.webhookFilters = props.webhookFilters ?? (this.organization ? [FilterGroup.inEventOf(EventAction.WORKFLOW_JOB_QUEUED)] : []);
this.sourceLocation = this.organization ? 'CODEBUILD_DEFAULT_WEBHOOK_SOURCE_LOCATION' : `https://github.com/${props.owner}/${props.repo}.git`;
}
public bind(_scope: Construct, project: IProject): SourceConfig {
const superConfig = super.bind(_scope, project);
return {
sourceProperty: {
...superConfig.sourceProperty,
location: this.sourceLocation,
},
sourceVersion: superConfig.sourceVersion,
buildTriggers: this.organization
? {
...superConfig.buildTriggers,
scopeConfiguration: {
name: this.organization,
},
} : superConfig.buildTriggers,
};
}
}
/**
* Construction properties for `GitHubEnterpriseSource`.
*/
export interface GitHubEnterpriseSourceProps extends CommonGithubSourceProps {
/**
* The HTTPS URL of the repository in your GitHub Enterprise installation.
*/
readonly httpsCloneUrl: string;
/**
* Whether to ignore SSL errors when connecting to the repository.
*
* @default false
*/
readonly ignoreSslErrors?: boolean;
}
/**
* GitHub Enterprise Source definition for a CodeBuild project.
*/
class GitHubEnterpriseSource extends CommonGithubSource {
public readonly type = GITHUB_ENTERPRISE_SOURCE_TYPE;
private readonly httpsCloneUrl: string;
private readonly ignoreSslErrors?: boolean;
constructor(props: GitHubEnterpriseSourceProps) {
super(props);
this.httpsCloneUrl = props.httpsCloneUrl;
this.ignoreSslErrors = props.ignoreSslErrors;
}
public bind(_scope: Construct, _project: IProject): SourceConfig {
if (this.hasCommitMessageFilterAndPrEvent()) {
throw new UnscopedValidationError('COMMIT_MESSAGE filters cannot be used with GitHub Enterprise Server pull request events');
}
if (this.hasFilePathFilterAndPrEvent()) {
throw new UnscopedValidationError('FILE_PATH filters cannot be used with GitHub Enterprise Server pull request events');
}
const superConfig = super.bind(_scope, _project);
return {
sourceProperty: {
...superConfig.sourceProperty,
location: this.httpsCloneUrl,
insecureSsl: this.ignoreSslErrors,
},
sourceVersion: superConfig.sourceVersion,
buildTriggers: superConfig.buildTriggers,
};
}
private hasCommitMessageFilterAndPrEvent() {
return this.webhookFilters.some(fg => (
fg._filters.some(fp => fp.type === WebhookFilterTypes.COMMIT_MESSAGE) &&
this.hasPrEvent(fg._actions)));
}
private hasFilePathFilterAndPrEvent() {
return this.webhookFilters.some(fg => (
fg._filters.some(fp => fp.type === WebhookFilterTypes.FILE_PATH) &&
this.hasPrEvent(fg._actions)));
}
private hasPrEvent(actions: EventAction[]) {
return actions.includes(
EventAction.PULL_REQUEST_CREATED ||
EventAction.PULL_REQUEST_MERGED ||
EventAction.PULL_REQUEST_REOPENED ||
EventAction.PULL_REQUEST_UPDATED);
}
}
/**
* Construction properties for `BitBucketSource`.
*/
export interface BitBucketSourceProps extends ThirdPartyGitSourceProps {
/**
* The BitBucket account/user that owns the repo.
*
* @example 'awslabs'
*/
readonly owner: string;
/**
* The name of the repo (without the username).
*
* @example 'aws-cdk'
*/
readonly repo: string;
/**
* This parameter is used for the `name` parameter in the Bitbucket commit status.
* Can use built-in CodeBuild variables, like $AWS_REGION.
*
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/create-project-cli.html#cli.source.buildstatusconfig.context
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html
*
* @example "My build #$CODEBUILD_BUILD_NUMBER"
* @default "AWS CodeBuild $AWS_REGION ($PROJECT_NAME)"
*/
readonly buildStatusName?: string;
}
/**
* BitBucket Source definition for a CodeBuild project.
*/
class BitBucketSource extends ThirdPartyGitSource {
public readonly type = BITBUCKET_SOURCE_TYPE;
private readonly httpsCloneUrl: any;
private readonly buildStatusName?: string;
constructor(props: BitBucketSourceProps) {
super(props);
this.httpsCloneUrl = `https://bitbucket.org/${props.owner}/${props.repo}.git`;
this.buildStatusName = props.buildStatusName;
}
public bind(_scope: Construct, _project: IProject): SourceConfig {
// BitBucket sources don't support the PULL_REQUEST_REOPENED event action
if (this.anyWebhookFilterContainsPrReopenedEventAction()) {
throw new UnscopedValidationError('BitBucket sources do not support the PULL_REQUEST_REOPENED webhook event action');
}
const superConfig = super.bind(_scope, _project);
return {
sourceProperty: {
...superConfig.sourceProperty,
location: this.httpsCloneUrl,
buildStatusConfig: this.buildStatusName !== undefined || this.buildStatusUrl !== undefined
? {
context: this.buildStatusName,
targetUrl: this.buildStatusUrl,
}
: undefined,
},
sourceVersion: superConfig.sourceVersion,
buildTriggers: superConfig.buildTriggers,
};
}
private anyWebhookFilterContainsPrReopenedEventAction() {
return this.webhookFilters.findIndex(fg => {
return fg._actions.findIndex(a => a === EventAction.PULL_REQUEST_REOPENED) !== -1;
}) !== -1;
}
}
function set2Array<T>(set: Set<T>): T[] {
const ret: T[] = [];
set.forEach(el => ret.push(el));
return ret;
}