Skip to content

Commit

Permalink
feat(codepipeline): add filters for CodeStarSourceConnection action
Browse files Browse the repository at this point in the history
This change deprecates the old filters and re-scopes adding them to the only action where they are valid.

This also adds classes for simplifying the input of filters for all filter types
  • Loading branch information
TheRealAmazonKendra committed Jun 11, 2024
1 parent 5114955 commit 85495b9
Show file tree
Hide file tree
Showing 9 changed files with 998 additions and 291 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { Construct } from 'constructs';
import { Trigger } from './trigger';
import * as codepipeline from '../../../aws-codepipeline';
import * as iam from '../../../aws-iam';
import { Action } from '../action';
import { sourceArtifactBounds } from '../common';

const ACTION_PROVIDER = 'CodeStarSourceConnection';

/**
* The CodePipeline variables emitted by CodeStar source Action.
*/
Expand Down Expand Up @@ -83,8 +86,23 @@ export interface CodeStarConnectionsSourceActionProps extends codepipeline.Commo
*
* @default true
* @see https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-CodestarConnectionSource.html
*
* @deprecated - use `trigger` instead.
*/
readonly triggerOnPush?: boolean;

/**
* The trigger configuration for this action.
*
* In a V1 pipeline, only `Trigger.ALL` or `Trigger.NONE` may be used.
*
* In a V2 pipeline, a trigger may have up to three of each type of filter
* (Pull Request or Push) or may trigger on every change to the default branch
* if no filters are provided.
*
* @default - The pipeline is triggered on all changes to the default branch.
*/
readonly trigger?: Trigger;
}

/**
Expand All @@ -107,7 +125,7 @@ export class CodeStarConnectionsSourceAction extends Action {
...props,
category: codepipeline.ActionCategory.SOURCE,
owner: 'AWS', // because props also has a (different!) owner property!
provider: 'CodeStarSourceConnection',
provider: ACTION_PROVIDER,
artifactBounds: sourceArtifactBounds(),
outputs: [props.output],
});
Expand Down Expand Up @@ -158,8 +176,41 @@ export class CodeStarConnectionsSourceAction extends Action {
OutputArtifactFormat: this.props.codeBuildCloneOutput === true
? 'CODEBUILD_CLONE_REF'
: undefined,
DetectChanges: this.props.triggerOnPush,
// For a v2 pipeline, if `trigger` is set this configuration property is disabled
// and the trigger setting is used. For a v1 pipeline, `trigger` can still be used to
// turn on/off `DetectChanges`.
//
// __attachActionToPipeline() will update this value so that it is handled correctly depending
// on the pipeline version.
DetectChanges: this.props.trigger ? this.renderDetectChanges(this.props.trigger) : this.props.triggerOnPush,
},
};
}

private renderTriggerGitConfiguration(trigger: Trigger): codepipeline.CfnPipeline.PipelineTriggerDeclarationProperty {
const providerType = ACTION_PROVIDER;
const push: codepipeline.CfnPipeline.GitPushFilterProperty[] = trigger._filters.map((_filter) =>
_filter._push).filter(item => item) as codepipeline.CfnPipeline.GitPushFilterProperty[];

const pullRequest: codepipeline.CfnPipeline.GitPullRequestFilterProperty[] = trigger._filters.map((_filter) =>
_filter._pullRequest).filter(item => item) as codepipeline.CfnPipeline.GitPullRequestFilterProperty[];

return (push.length === 0 && pullRequest.length === 0) ? {
providerType,
} : {
providerType,
gitConfiguration: {
push: push.length > 0 ? push : undefined,
pullRequest: pullRequest.length > 0 ? pullRequest : undefined,
sourceActionName: this.actionProperties.actionName,
},
};
}

private renderDetectChanges(trigger: Trigger): boolean | codepipeline.CfnPipeline.PipelineTriggerDeclarationProperty {
// No trigger or change detection
if (!trigger._enabled) return false;

return this.renderTriggerGitConfiguration(trigger);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
import { CfnPipeline } from '../../../aws-codepipeline/lib';

/**
* The patterns used for filtering criteria.
*/
export interface FilterPattern {
/**
* The excludes patterns to use. If any pattern are included in both includes and excludes,
* excludes take precedence.
*
* @default - No patterns are excluded in this filter.
*/
readonly excludes?: string[];

/**
* The includes patterns to use. If any pattern are included in both includes and excludes,
* excludes take precedence.
*
* @default - No patterns are included in this filter.
*/
readonly includes?: string[];
}

/**
* Filtering options for filtering on a branch.
*/
export interface BranchFilterOptions {
/**
* The list of branches to filter on.
*/
readonly branches: FilterPattern;
/**
* The list of filepaths to filter on.
*
* @default - No filtering for filepaths.
*/
readonly filePaths?: FilterPattern;
}

/**
* Filtering options for pull requests
*/
export interface PullRequestFilterOptions extends BranchFilterOptions { }

/**
* Filtering options for filtering on a tag,
*/
export interface TagFilterOptions extends FilterPattern { }

enum Events {
OPENED = 'OPENED',
UPDATED = 'UPDATED',
CLOSED = 'CLOSED',
};

/**
* Adds a filter to the trigger.
*/
export class Filter {
/**
* Triggers on all pull request events. These include: OPENED, UPDATED, and CLOSED.
* @param filter The filters to use to limit which pull requests are included in the trigger
*/
public static pullRequestEvents(filter: PullRequestFilterOptions) {
return Filter._pullRequest([Events.OPENED, Events.UPDATED, Events.CLOSED], filter);
}

/**
* Triggers on OPENED pull request events.
* @param filter The filters to use to limit which pull requests are included in the trigger
*/
public static pullRequestOpened(filter: PullRequestFilterOptions) {
return Filter._pullRequest([Events.OPENED], filter);
}

/**
* Triggers on UPDATED pull request events.
* @param filter The filters to use to limit which pull requests are included in the trigger
*/
public static pullRequestUpdated(filter: PullRequestFilterOptions) {
return Filter._pullRequest([Events.UPDATED], filter);
}

/**
* Triggers on CLOSED pull request events.
* @param filter The filters to use to limit which pull requests are included in the trigger
*/
public static pullRequestClosed(filter: PullRequestFilterOptions) {
return Filter._pullRequest([Events.CLOSED], filter);
}

/**
* Triggers on OPENED or UPDATED pull request events.
* @param filter The filters to use to limit which pull requests are included in the trigger
*/
public static pullRequestOpenedOrUpdated(filter: PullRequestFilterOptions) {
return Filter._pullRequest([Events.OPENED, Events.UPDATED], filter);
}

/**
* Triggers on OPENED or CLOSED pull request events.
* @param filter The filters to use to limit which pull requests are included in the trigger
*/
public static pullRequestOpenedOrClosed(filter: PullRequestFilterOptions) {
return Filter._pullRequest([Events.OPENED, Events.CLOSED], filter);
}

/**
* Triggers on UPDATED or CLOSED pull request events.
* @param filter The filters to use to limit which pull requests are included in the trigger
*/
public static pullRequestUpdatedOrClosed(filter: PullRequestFilterOptions) {
return Filter._pullRequest([Events.UPDATED, Events.CLOSED], filter);
}

/**
* Trigger on push events.
* @param filter The filters to use to limit which push events are included in the trigger
*/
public static push(filter: PushFilter) {
return new Filter(undefined, { tags: filter._tags, branches: filter._branches, filePaths: filter._filePaths });
}

private static _pullRequest(events: Events[], filter: PullRequestFilterOptions) {
mustContainValue('Functions filtering on a pull request', filter.branches, ' on the \'branches\' field');
return new Filter({ events, branches: filter.branches, filePaths: filter.filePaths }, undefined);
}

/**
* @internal
*/
public readonly _pullRequest?: CfnPipeline.GitPullRequestFilterProperty;

/**
* @internal
*/
public readonly _push?: CfnPipeline.GitPushFilterProperty;

constructor(
pullRequest?: CfnPipeline.GitPullRequestFilterProperty,
push?: CfnPipeline.GitPushFilterProperty,
) {
this._pullRequest = pullRequest;
this._push = push;
}
}

/**
* Represents a CodePipeline V2 Pipeline trigger. Each trigger may include filters to limit the
* circumstances in which the pipeline will trigger.
*/
export class Trigger {
/**
* Trigger on all code pushes to the default branch.
*/
public static readonly ENABLED = new Trigger(true);

/**
* Disables triggers for the pipeline.
*/
public static readonly DISABLED = new Trigger(false);

/**
* Enables a trigger for the pipeline, filtering for specific events.
* Requires at least one filter.
* @param filters Additional filters for this trigger
*/
public static withFilters(filter: Filter, ...filters: Filter[]) {
const trigger = new Trigger(true);
trigger._filters.push(filter, ...filters);
return trigger;
}

/**
* @internal
*/
public _filters: Filter[] = [];

/**
* @internal
*/
public _enabled: boolean;

constructor(enabled: boolean) {
this._enabled = enabled;
}
}

/**
* Filters specific to push triggers.
*/
export class PushFilter {
/**
* Filter on tags
* @param options The filtering options for tags
*/
public static onTags(options: TagFilterOptions) {
mustContainValue('PushFilter.onTags()', options);
return new PushFilter(options);
}

/**
* Filter on branches
* @param options The filtering options for branches
*/
public static onBranches(options: BranchFilterOptions) {
mustContainValue('PushFilter.onBranches()', options.branches, ' on the \'branches\' field');
return new PushFilter(undefined, options.branches, options.filePaths);
}

/**
* @internal
*/
public readonly _tags?: CfnPipeline.GitTagFilterCriteriaProperty;

/**
* @internal
*/
public readonly _branches?: CfnPipeline.GitBranchFilterCriteriaProperty;

/**
* @internal
*/
public readonly _filePaths?: CfnPipeline.GitFilePathFilterCriteriaProperty;

constructor(
tags?: CfnPipeline.GitTagFilterCriteriaProperty,
branches?: CfnPipeline.GitBranchFilterCriteriaProperty,
filePaths?: CfnPipeline.GitFilePathFilterCriteriaProperty,
) {
this._tags = maybeUndefined(tags);
this._branches = maybeUndefined(branches);
this._filePaths = maybeUndefined(filePaths);
}
}

function maybeUndefined(input?: FilterPattern) {
return (input?.excludes || input?.includes) ?
(input?.excludes?.length == 0 && input.includes?.length == 0 ? undefined : input) :
undefined;
}

function mustContainValue(type: string, input?: FilterPattern, additionalDetails?: string) {
if (!maybeUndefined(input)) {
throw new Error(`${type} must contain at least one 'includes' or 'excludes' pattern${additionalDetails ?? ''}.`);
}
}
1 change: 1 addition & 0 deletions packages/aws-cdk-lib/aws-codepipeline-actions/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './alexa-ask/deploy-action';
export * from './bitbucket/source-action';
export * from './codestar-connections/source-action';
export * from './codestar-connections/trigger';
export * from './cloudformation';
export * from './codebuild/build-action';
export * from './codecommit/source-action';
Expand Down
Loading

0 comments on commit 85495b9

Please sign in to comment.