From 6f28c43417396078e3ade0dbcc7e79ffaa556280 Mon Sep 17 00:00:00 2001 From: kornicameister Date: Sat, 18 Dec 2021 23:40:45 +0100 Subject: [PATCH 1/7] feat(code_pipeline_actions): variables for source action Adds `variables` method that allows to retrieve action's [variables](https://docs.aws.amazon.com/codepipeline/latest/userguide/reference-variables.html) using type safe approach. With that we can rely on compiler to pick up incorrect access instead of hacking it with plain strings. fixes: #17807 --- .../lib/codestar-connections/source-action.ts | 30 ++++ ...codestar-connections-source-action.test.ts | 149 ++++++++++++++++-- 2 files changed, 170 insertions(+), 9 deletions(-) diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/codestar-connections/source-action.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/codestar-connections/source-action.ts index ca47af5c29c8e..71ff24eb35114 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/lib/codestar-connections/source-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/codestar-connections/source-action.ts @@ -8,6 +8,24 @@ import { sourceArtifactBounds } from '../common'; // eslint-disable-next-line no-duplicate-imports, import/order import { Construct } from '@aws-cdk/core'; +/** + * The CodePipeline variables emitted by CodeStar source Action. + */ +export interface CodeStarSourceVariables { + /** The name of the repository this action points to. */ + readonly fullRepositoryName: string; + /** The name of the branch this action tracks. */ + readonly branchName: string; + /** The date the currently last commit on the tracked branch was authored, in ISO-8601 format. */ + readonly authorDate: string; + /** The SHA1 hash of the currently last commit on the tracked branch. */ + readonly commitId: string; + /** The message of the currently last commit on the tracked branch. */ + readonly commitMessage: string; + /** The connection ARN this source uses. */ + readonly connectionArn: string; +} + /** * Construction properties for {@link CodeStarConnectionsSourceAction}. */ @@ -101,6 +119,18 @@ export class CodeStarConnectionsSourceAction extends Action { this.props = props; } + /** The variables emitted by this action. */ + public get variables(): CodeStarSourceVariables { + return { + fullRepositoryName: this.variableExpression('FullRepositoryName'), + branchName: this.variableExpression('BranchName'), + authorDate: this.variableExpression('AuthorDate'), + commitId: this.variableExpression('CommitId'), + commitMessage: this.variableExpression('CommitMessage'), + connectionArn: this.variableExpression('ConnectionArn'), + }; + } + protected bound(_scope: Construct, _stage: codepipeline.IStage, options: codepipeline.ActionBindOptions): codepipeline.ActionConfig { // https://docs.aws.amazon.com/codepipeline/latest/userguide/security-iam.html#how-to-update-role-new-services options.role.addToPolicy(new iam.PolicyStatement({ diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/codestar-connections/codestar-connections-source-action.test.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/codestar-connections/codestar-connections-source-action.test.ts index 75fe764109b25..97cc21cbb99ef 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/codestar-connections/codestar-connections-source-action.test.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/codestar-connections/codestar-connections-source-action.test.ts @@ -1,5 +1,5 @@ import '@aws-cdk/assert-internal/jest'; -import { arrayWith, objectLike } from '@aws-cdk/assert-internal'; +import { arrayWith, objectLike, SynthUtils } from '@aws-cdk/assert-internal'; import * as codebuild from '@aws-cdk/aws-codebuild'; import * as codepipeline from '@aws-cdk/aws-codepipeline'; import { Stack } from '@aws-cdk/core'; @@ -148,27 +148,155 @@ describe('CodeStar Connections source Action', () => { ], }); + }); + + test('exposes variables', () => { + const stack = new Stack(); + + createBitBucketAndCodeBuildPipeline(stack, { + triggerOnPush: false, + }); + + expect(stack).toHaveResourceLike('AWS::CodePipeline::Pipeline', { + 'Stages': [ + { + 'Name': 'Source', + }, + { + 'Name': 'Build', + 'Actions': [ + { + 'Name': 'CodeBuild', + 'Configuration': { + 'EnvironmentVariables': '[{"name":"CommitId","type":"PLAINTEXT","value":"#{Source_BitBucket_NS.CommitId}"}]', + }, + }, + ], + }, + ], + }); }); + + test('exposes variables with custom namespace', () => { + const stack = new Stack(); + + createBitBucketAndCodeBuildPipeline(stack, { + triggerOnPush: false, + variablesNamespace: 'kornicameister', + }); + + expect(stack).toHaveResourceLike('AWS::CodePipeline::Pipeline', { + 'Stages': [ + { + 'Name': 'Source', + 'Actions': [ + { + 'Name': 'BitBucket', + 'Namespace': 'kornicameister', + }, + ], + }, + { + 'Name': 'Build', + 'Actions': [ + { + 'Name': 'CodeBuild', + 'Configuration': { + 'EnvironmentVariables': '[{"name":"CommitId","type":"PLAINTEXT","value":"#{kornicameister.CommitId}"}]', + }, + }, + ], + }, + ], + }); + + + }); + + test('fail if variable from unused action is referenced', () => { + const stack = new Stack(); + + createUnreferencedVariablePipeline(stack); + + expect(() => { + SynthUtils.synthesize(stack); + }).toThrow(/Cannot reference variables of action 'BitBucketUnused', as that action was never added to a pipeline/); + + }); + + test('fail if variable from unused action with custom namespace is referenced', () => { + const stack = new Stack(); + + createUnreferencedVariablePipeline(stack, 'kornicameister'); + + expect(() => { + SynthUtils.synthesize(stack); + }).toThrow(/Cannot reference variables of action 'BitBucketUnused', as that action was never added to a pipeline/); + + }); + }); -function createBitBucketAndCodeBuildPipeline(stack: Stack, props: Partial): void { +function createUnreferencedVariablePipeline(stack: Stack, variablesNamespace?: string) { const sourceOutput = new codepipeline.Artifact(); + const sourceAction = new cpactions.CodeStarConnectionsSourceAction({ + actionName: 'BitBucketUsed', + owner: 'aws', + repo: 'aws-cdk', + output: sourceOutput, + connectionArn: 'arn:aws:codestar-connections:us-east-1:123456789012:connection/12345678-abcd-12ab-34cdef5678gh', + }); + const unusedSourceAction = new cpactions.CodeStarConnectionsSourceAction({ + actionName: 'BitBucketUnused', + owner: 'aws', + repo: 'aws-cdk', + output: sourceOutput, + connectionArn: 'arn:aws:codestar-connections:us-east-1:123456789012:connection/12345678-abcd-12ab-34cdef5678gh', + variablesNamespace: variablesNamespace, + }); + new codepipeline.Pipeline(stack, 'Pipeline', { stages: [ { stageName: 'Source', + actions: [sourceAction], + }, + { + stageName: 'Build', actions: [ - new cpactions.CodeStarConnectionsSourceAction({ - actionName: 'BitBucket', - owner: 'aws', - repo: 'aws-cdk', - output: sourceOutput, - connectionArn: 'arn:aws:codestar-connections:us-east-1:123456789012:connection/12345678-abcd-12ab-34cdef5678gh', - ...props, + new cpactions.CodeBuildAction({ + actionName: 'CodeBuild', + project: new codebuild.PipelineProject(stack, 'MyProject'), + input: sourceOutput, + outputs: [new codepipeline.Artifact()], + environmentVariables: { + CommitId: { value: unusedSourceAction.variables.commitId }, + }, }), ], }, + ], + }); +} + +function createBitBucketAndCodeBuildPipeline(stack: Stack, props: Partial): void { + const sourceOutput = new codepipeline.Artifact(); + const sourceAction = new cpactions.CodeStarConnectionsSourceAction({ + actionName: 'BitBucket', + owner: 'aws', + repo: 'aws-cdk', + output: sourceOutput, + connectionArn: 'arn:aws:codestar-connections:us-east-1:123456789012:connection/12345678-abcd-12ab-34cdef5678gh', + ...props, + }); + + new codepipeline.Pipeline(stack, 'Pipeline', { + stages: [ + { + stageName: 'Source', + actions: [sourceAction], + }, { stageName: 'Build', actions: [ @@ -177,6 +305,9 @@ function createBitBucketAndCodeBuildPipeline(stack: Stack, props: Partial Date: Tue, 28 Dec 2021 22:50:14 +0100 Subject: [PATCH 2/7] Add entry to package README.md --- .../aws-codepipeline-actions/README.md | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/packages/@aws-cdk/aws-codepipeline-actions/README.md b/packages/@aws-cdk/aws-codepipeline-actions/README.md index 7625ed08bfb8e..b67d29b6282d7 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/README.md +++ b/packages/@aws-cdk/aws-codepipeline-actions/README.md @@ -198,6 +198,33 @@ const sourceAction = new codepipeline_actions.CodeStarConnectionsSourceAction({ You can also use the `CodeStarConnectionsSourceAction` to connect to GitHub, in the same way (you just have to select GitHub as the source when creating the connection in the console). +Similarly to `GitHubSourceAction`, `CodeStarConnectionsSourceAction` also emits the variables: + +```ts +const sourceOutput = new codepipeline.Artifact(); +const sourceAction = new codepipeline_actions.CodeStarConnectionsSourceAction({ + actionName: 'BitBucket_Source', + owner: 'aws', + repo: 'aws-cdk', + output: sourceOutput, + connectionArn: 'arn:aws:codestar-connections:us-east-1:123456789012:connection/12345678-abcd-12ab-34cdef5678gh', + variablesNamespace: 'SomeSpace', // optional - by default, a name will be generated for you +}); + +// later: + +new codepipeline_actions.CodeBuildAction({ + actionName: 'CodeBuild', + project, + input: sourceOutput, + environmentVariables: { + COMMIT_ID: { + value: sourceAction.variables.commitId, + }, + }, +}); +``` + ### AWS S3 Source To use an S3 Bucket as a source in CodePipeline: From 78c70d41cabe1cb6f486ed2484df8d36c224aeb1 Mon Sep 17 00:00:00 2001 From: kornicameister Date: Tue, 28 Dec 2021 23:45:21 +0100 Subject: [PATCH 3/7] Refactor test function to return pipeline --- ...codestar-connections-source-action.test.ts | 108 ++++++++---------- 1 file changed, 47 insertions(+), 61 deletions(-) diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/codestar-connections/codestar-connections-source-action.test.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/codestar-connections/codestar-connections-source-action.test.ts index 97cc21cbb99ef..b478896aaedfa 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/codestar-connections/codestar-connections-source-action.test.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/codestar-connections/codestar-connections-source-action.test.ts @@ -152,10 +152,7 @@ describe('CodeStar Connections source Action', () => { test('exposes variables', () => { const stack = new Stack(); - - createBitBucketAndCodeBuildPipeline(stack, { - triggerOnPush: false, - }); + createBitBucketAndCodeBuildPipeline(stack, {}); expect(stack).toHaveResourceLike('AWS::CodePipeline::Pipeline', { 'Stages': [ @@ -175,14 +172,11 @@ describe('CodeStar Connections source Action', () => { }, ], }); - }); test('exposes variables with custom namespace', () => { const stack = new Stack(); - createBitBucketAndCodeBuildPipeline(stack, { - triggerOnPush: false, variablesNamespace: 'kornicameister', }); @@ -210,77 +204,69 @@ describe('CodeStar Connections source Action', () => { }, ], }); - - }); test('fail if variable from unused action is referenced', () => { const stack = new Stack(); - - createUnreferencedVariablePipeline(stack); + const pipeline = createBitBucketAndCodeBuildPipeline(stack, {}); + + const unusedSourceOutput = new codepipeline.Artifact() + const unusedSourceAction = new cpactions.CodeStarConnectionsSourceAction({ + actionName: 'UnusedBitBucket', + owner: 'aws', + repo: 'aws-cdk', + output: unusedSourceOutput, + connectionArn: 'arn:aws:codestar-connections:us-east-1:123456789012:connection/12345678-abcd-12ab-34cdef5678gh', + }); + const unusedBuildAction = new cpactions.CodeBuildAction({ + actionName: 'UnusedCodeBuild', + project: new codebuild.PipelineProject(stack, 'UnusedMyProject'), + input: unusedSourceOutput, + outputs: [new codepipeline.Artifact()], + environmentVariables: { + CommitId: { value: unusedSourceAction.variables.commitId }, + }, + }); + pipeline.stage('Build').addAction(unusedBuildAction); expect(() => { SynthUtils.synthesize(stack); - }).toThrow(/Cannot reference variables of action 'BitBucketUnused', as that action was never added to a pipeline/); + }).toThrow(/Cannot reference variables of action 'UnusedBitBucket', as that action was never added to a pipeline/); }); test('fail if variable from unused action with custom namespace is referenced', () => { const stack = new Stack(); - - createUnreferencedVariablePipeline(stack, 'kornicameister'); + const pipeline = createBitBucketAndCodeBuildPipeline(stack, { + variablesNamespace: 'kornicameister', + }); + const unusedSourceOutput = new codepipeline.Artifact() + const unusedSourceAction = new cpactions.CodeStarConnectionsSourceAction({ + actionName: 'UnusedBitBucket', + owner: 'aws', + repo: 'aws-cdk', + output: unusedSourceOutput, + connectionArn: 'arn:aws:codestar-connections:us-east-1:123456789012:connection/12345678-abcd-12ab-34cdef5678gh', + variablesNamespace: 'kornicameister', + }); + const unusedBuildAction = new cpactions.CodeBuildAction({ + actionName: 'UnusedCodeBuild', + project: new codebuild.PipelineProject(stack, 'UnusedProject'), + input: unusedSourceOutput, + outputs: [new codepipeline.Artifact()], + environmentVariables: { + CommitId: { value: unusedSourceAction.variables.commitId }, + }, + }); + pipeline.stage('Build').addAction(unusedBuildAction); expect(() => { SynthUtils.synthesize(stack); - }).toThrow(/Cannot reference variables of action 'BitBucketUnused', as that action was never added to a pipeline/); - + }).toThrow(/Cannot reference variables of action 'UnusedBitBucket', as that action was never added to a pipeline/); }); - }); -function createUnreferencedVariablePipeline(stack: Stack, variablesNamespace?: string) { - const sourceOutput = new codepipeline.Artifact(); - const sourceAction = new cpactions.CodeStarConnectionsSourceAction({ - actionName: 'BitBucketUsed', - owner: 'aws', - repo: 'aws-cdk', - output: sourceOutput, - connectionArn: 'arn:aws:codestar-connections:us-east-1:123456789012:connection/12345678-abcd-12ab-34cdef5678gh', - }); - const unusedSourceAction = new cpactions.CodeStarConnectionsSourceAction({ - actionName: 'BitBucketUnused', - owner: 'aws', - repo: 'aws-cdk', - output: sourceOutput, - connectionArn: 'arn:aws:codestar-connections:us-east-1:123456789012:connection/12345678-abcd-12ab-34cdef5678gh', - variablesNamespace: variablesNamespace, - }); - - new codepipeline.Pipeline(stack, 'Pipeline', { - stages: [ - { - stageName: 'Source', - actions: [sourceAction], - }, - { - stageName: 'Build', - actions: [ - new cpactions.CodeBuildAction({ - actionName: 'CodeBuild', - project: new codebuild.PipelineProject(stack, 'MyProject'), - input: sourceOutput, - outputs: [new codepipeline.Artifact()], - environmentVariables: { - CommitId: { value: unusedSourceAction.variables.commitId }, - }, - }), - ], - }, - ], - }); -} - -function createBitBucketAndCodeBuildPipeline(stack: Stack, props: Partial): void { +function createBitBucketAndCodeBuildPipeline(stack: Stack, props: Partial): codepipeline.Pipeline { const sourceOutput = new codepipeline.Artifact(); const sourceAction = new cpactions.CodeStarConnectionsSourceAction({ actionName: 'BitBucket', @@ -291,7 +277,7 @@ function createBitBucketAndCodeBuildPipeline(stack: Stack, props: Partial Date: Wed, 29 Dec 2021 00:40:44 +0100 Subject: [PATCH 4/7] Fix unnoticed build issue --- .../codestar-connections-source-action.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/codestar-connections/codestar-connections-source-action.test.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/codestar-connections/codestar-connections-source-action.test.ts index b478896aaedfa..43a2079e957a3 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/codestar-connections/codestar-connections-source-action.test.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/codestar-connections/codestar-connections-source-action.test.ts @@ -210,7 +210,7 @@ describe('CodeStar Connections source Action', () => { const stack = new Stack(); const pipeline = createBitBucketAndCodeBuildPipeline(stack, {}); - const unusedSourceOutput = new codepipeline.Artifact() + const unusedSourceOutput = new codepipeline.Artifact(); const unusedSourceAction = new cpactions.CodeStarConnectionsSourceAction({ actionName: 'UnusedBitBucket', owner: 'aws', @@ -240,7 +240,7 @@ describe('CodeStar Connections source Action', () => { const pipeline = createBitBucketAndCodeBuildPipeline(stack, { variablesNamespace: 'kornicameister', }); - const unusedSourceOutput = new codepipeline.Artifact() + const unusedSourceOutput = new codepipeline.Artifact(); const unusedSourceAction = new cpactions.CodeStarConnectionsSourceAction({ actionName: 'UnusedBitBucket', owner: 'aws', From f0c3724d57bf36477303bfac19c18829d7e7d64a Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Tue, 28 Dec 2021 15:59:41 -0800 Subject: [PATCH 5/7] Apply suggestions from code review --- .../codestar-connections-source-action.test.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/codestar-connections/codestar-connections-source-action.test.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/codestar-connections/codestar-connections-source-action.test.ts index 43a2079e957a3..6485b26b77597 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/codestar-connections/codestar-connections-source-action.test.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/codestar-connections/codestar-connections-source-action.test.ts @@ -152,7 +152,7 @@ describe('CodeStar Connections source Action', () => { test('exposes variables', () => { const stack = new Stack(); - createBitBucketAndCodeBuildPipeline(stack, {}); + createBitBucketAndCodeBuildPipeline(stack); expect(stack).toHaveResourceLike('AWS::CodePipeline::Pipeline', { 'Stages': [ @@ -208,7 +208,7 @@ describe('CodeStar Connections source Action', () => { test('fail if variable from unused action is referenced', () => { const stack = new Stack(); - const pipeline = createBitBucketAndCodeBuildPipeline(stack, {}); + const pipeline = createBitBucketAndCodeBuildPipeline(stack); const unusedSourceOutput = new codepipeline.Artifact(); const unusedSourceAction = new cpactions.CodeStarConnectionsSourceAction({ @@ -222,7 +222,6 @@ describe('CodeStar Connections source Action', () => { actionName: 'UnusedCodeBuild', project: new codebuild.PipelineProject(stack, 'UnusedMyProject'), input: unusedSourceOutput, - outputs: [new codepipeline.Artifact()], environmentVariables: { CommitId: { value: unusedSourceAction.variables.commitId }, }, @@ -232,7 +231,6 @@ describe('CodeStar Connections source Action', () => { expect(() => { SynthUtils.synthesize(stack); }).toThrow(/Cannot reference variables of action 'UnusedBitBucket', as that action was never added to a pipeline/); - }); test('fail if variable from unused action with custom namespace is referenced', () => { @@ -253,7 +251,6 @@ describe('CodeStar Connections source Action', () => { actionName: 'UnusedCodeBuild', project: new codebuild.PipelineProject(stack, 'UnusedProject'), input: unusedSourceOutput, - outputs: [new codepipeline.Artifact()], environmentVariables: { CommitId: { value: unusedSourceAction.variables.commitId }, }, @@ -266,7 +263,7 @@ describe('CodeStar Connections source Action', () => { }); }); -function createBitBucketAndCodeBuildPipeline(stack: Stack, props: Partial): codepipeline.Pipeline { +function createBitBucketAndCodeBuildPipeline(stack: Stack, props: Partial = {}): codepipeline.Pipeline { const sourceOutput = new codepipeline.Artifact(); const sourceAction = new cpactions.CodeStarConnectionsSourceAction({ actionName: 'BitBucket', From f8af3652056556f56b15b1370b62b8dfcfa257a6 Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Tue, 28 Dec 2021 16:45:10 -0800 Subject: [PATCH 6/7] Update packages/@aws-cdk/aws-codepipeline-actions/README.md --- packages/@aws-cdk/aws-codepipeline-actions/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/@aws-cdk/aws-codepipeline-actions/README.md b/packages/@aws-cdk/aws-codepipeline-actions/README.md index b67d29b6282d7..d3569b4ea5872 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/README.md +++ b/packages/@aws-cdk/aws-codepipeline-actions/README.md @@ -201,6 +201,8 @@ You can also use the `CodeStarConnectionsSourceAction` to connect to GitHub, in Similarly to `GitHubSourceAction`, `CodeStarConnectionsSourceAction` also emits the variables: ```ts +declare const project: codebuild.Project; + const sourceOutput = new codepipeline.Artifact(); const sourceAction = new codepipeline_actions.CodeStarConnectionsSourceAction({ actionName: 'BitBucket_Source', From f0f31e023eb7f02ab3db666752fbf244588a8247 Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Tue, 28 Dec 2021 22:42:29 -0800 Subject: [PATCH 7/7] Shorten the line to less than 150 chars --- .../codestar-connections-source-action.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/codestar-connections/codestar-connections-source-action.test.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/codestar-connections/codestar-connections-source-action.test.ts index 6485b26b77597..312251bd8457a 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/codestar-connections/codestar-connections-source-action.test.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/codestar-connections/codestar-connections-source-action.test.ts @@ -263,7 +263,9 @@ describe('CodeStar Connections source Action', () => { }); }); -function createBitBucketAndCodeBuildPipeline(stack: Stack, props: Partial = {}): codepipeline.Pipeline { +function createBitBucketAndCodeBuildPipeline( + stack: Stack, props: Partial = {}, +): codepipeline.Pipeline { const sourceOutput = new codepipeline.Artifact(); const sourceAction = new cpactions.CodeStarConnectionsSourceAction({ actionName: 'BitBucket',