diff --git a/packages/@aws-cdk/pipelines/lib/main/pipeline-base.ts b/packages/@aws-cdk/pipelines/lib/main/pipeline-base.ts index 34402b0b5cffa..0bb27cb78a7bc 100644 --- a/packages/@aws-cdk/pipelines/lib/main/pipeline-base.ts +++ b/packages/@aws-cdk/pipelines/lib/main/pipeline-base.ts @@ -2,6 +2,8 @@ import { Aspects, Stage } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { AddStageOpts as StageOptions, WaveOptions, Wave, IFileSetProducer, ShellStep, FileSet } from '../blueprint'; +const PIPELINE_SYMBOL = Symbol.for('@aws-cdk/pipelines.PipelineBase'); + /** * Properties for a `Pipeline` */ @@ -32,6 +34,15 @@ export interface PipelineBaseProps { * happens first). */ export abstract class PipelineBase extends Construct { + /** + * Return whether the given object extends {@link PipelineBase}. + * + * We do attribute detection since we can't reliably use 'instanceof'. + */ + public static isPipeline(x: any): x is PipelineBase { + return x !== null && typeof (x) === 'object' && PIPELINE_SYMBOL in x; + } + /** * The build step that produces the CDK Cloud Assembly */ @@ -54,6 +65,8 @@ export abstract class PipelineBase extends Construct { constructor(scope: Construct, id: string, props: PipelineBaseProps) { super(scope, id); + Object.defineProperty(this, PIPELINE_SYMBOL, { value: true }); + if (props.synth instanceof ShellStep && !props.synth.primaryOutput) { props.synth.primaryOutputDirectory('cdk.out'); } diff --git a/packages/@aws-cdk/pipelines/test/main/pipeline-base.test.ts b/packages/@aws-cdk/pipelines/test/main/pipeline-base.test.ts new file mode 100644 index 0000000000000..680f33f7dc602 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/main/pipeline-base.test.ts @@ -0,0 +1,29 @@ +import * as cdk from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import { App } from '../../../core/lib'; +import { PipelineBase, IFileSetProducer, FileSet } from '../../lib'; +import { PIPELINE_ENV } from '../testhelpers'; + +describe('pipeline base', () => { + + test('PipelineBase.isPipeline indicates that a construct extends PipelineBase', () => { + const app = new App(); + const pipelineStack = new cdk.Stack(app, 'PipelineStack', { env: PIPELINE_ENV }); + const construct = new Construct(pipelineStack, 'Construct'); + const pipeline = new class extends PipelineBase { + constructor() { + super(pipelineStack, 'Pipeline', { + synth: new class implements IFileSetProducer { + public readonly primaryOutput = new FileSet('PrimaryOutput'); + }(), + }); + } + protected doBuildPipeline(): void { } + }(); + + expect(PipelineBase.isPipeline(pipelineStack)).toStrictEqual(false); + expect(PipelineBase.isPipeline(construct)).toStrictEqual(false); + expect(PipelineBase.isPipeline(pipeline)).toStrictEqual(true); + }); + +}); \ No newline at end of file