From b8ebcbe63f4d14675e4ac24d8bc21d7c2e4dbfdd Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Sun, 29 Jul 2018 01:00:05 -0700 Subject: [PATCH] [BREAKING] Usability improvements for the CodeBuild Construct library. (#412) 1. Rename 'BuildProject' to 'Project'. 2. Allow setting the physical name of a Project, to make it consistent with other L2s. 3. Introduce a BuildImage class that makes it more convenient to specify the used Docker image. 4. Introduce a convenience PipelineProject class for use in CodePipeline that defaults the source and artifacts fields. --- .../aws-codebuild-codepipeline/README.md | 22 +- .../aws-codebuild-codepipeline/lib/index.ts | 1 + .../lib/pipeline-actions.ts | 2 +- .../lib/pipeline-project.ts | 19 ++ .../test/integ.pipeline-code-commit-build.ts | 2 +- .../test/integ.pipeline-events.ts | 3 +- .../test/test.pipeline.ts | 44 +++- packages/@aws-cdk/aws-codebuild/README.md | 32 ++- .../@aws-cdk/aws-codebuild/lib/artifacts.ts | 6 +- .../@aws-cdk/aws-codebuild/lib/project.ts | 216 +++++++++++++----- packages/@aws-cdk/aws-codebuild/lib/source.ts | 10 +- packages/@aws-cdk/aws-codebuild/package.json | 1 - .../test/integ.project-bucket.ts | 2 +- .../test/integ.project-events.ts | 4 +- .../aws-codebuild/test/test.codebuild.ts | 48 ++-- packages/@aws-cdk/aws-codecommit/README.md | 2 +- packages/@aws-cdk/aws-events/README.md | 2 +- 17 files changed, 309 insertions(+), 107 deletions(-) create mode 100644 packages/@aws-cdk/aws-codebuild-codepipeline/lib/pipeline-project.ts diff --git a/packages/@aws-cdk/aws-codebuild-codepipeline/README.md b/packages/@aws-cdk/aws-codebuild-codepipeline/README.md index a26e31ad5626e..dbbd8e1d01a2f 100644 --- a/packages/@aws-cdk/aws-codebuild-codepipeline/README.md +++ b/packages/@aws-cdk/aws-codebuild-codepipeline/README.md @@ -5,17 +5,31 @@ This module contains an Action that allows you to use a CodeBuild Project in Cod Example: ```ts -import codebuild = require('@aws-cdk/aws-codebuild'); import codebuildPipeline = require('@aws-cdk/aws-codebuild-codepipeline'); import codepipeline = require('@aws-cdk/aws-codepipeline'); // see the @aws-cdk/aws-codebuild module for more documentation on how to create CodeBuild Projects -const project = new codebuild.BuildProject( // ... -); +const project = new codebuildPipeline.PipelineProject(this, 'MyProject', { + // ... +}); const pipeline = new codepipeline.Pipeline(this, 'MyPipeline'); const buildStage = new codepipeline.Stage(pipeline, 'Build'); new codebuildPipeline.PipelineBuildAction(buildStage, 'CodeBuild', { - project: project, + project }); ``` + +The `PipelineProject` utility class is a simple sugar around the `Project` +class from the `@aws-cdk/aws-codebuild` module, +it's equivalent to: + +```ts +import codebuild = require('@aws-cdk/aws-codebuild'); + +const project = new codebuild.Project(this, 'MyProject', { + source: new codebuild.CodePipelineSource(), + artifacts: new codebuild.CodePipelineBuildArtifacts(), + // rest of the properties from PipelineProject are passed unchanged... +} +``` diff --git a/packages/@aws-cdk/aws-codebuild-codepipeline/lib/index.ts b/packages/@aws-cdk/aws-codebuild-codepipeline/lib/index.ts index 9b3db900186ee..7dda267e2867d 100644 --- a/packages/@aws-cdk/aws-codebuild-codepipeline/lib/index.ts +++ b/packages/@aws-cdk/aws-codebuild-codepipeline/lib/index.ts @@ -1 +1,2 @@ export * from './pipeline-actions'; +export * from './pipeline-project'; diff --git a/packages/@aws-cdk/aws-codebuild-codepipeline/lib/pipeline-actions.ts b/packages/@aws-cdk/aws-codebuild-codepipeline/lib/pipeline-actions.ts index 65ec9752b29aa..66be0d97b6575 100644 --- a/packages/@aws-cdk/aws-codebuild-codepipeline/lib/pipeline-actions.ts +++ b/packages/@aws-cdk/aws-codebuild-codepipeline/lib/pipeline-actions.ts @@ -19,7 +19,7 @@ export interface PipelineBuildActionProps { /** * The build project */ - project: codebuild.BuildProjectRef; + project: codebuild.ProjectRef; } /** diff --git a/packages/@aws-cdk/aws-codebuild-codepipeline/lib/pipeline-project.ts b/packages/@aws-cdk/aws-codebuild-codepipeline/lib/pipeline-project.ts new file mode 100644 index 0000000000000..452757e84ab7d --- /dev/null +++ b/packages/@aws-cdk/aws-codebuild-codepipeline/lib/pipeline-project.ts @@ -0,0 +1,19 @@ +import codebuild = require('@aws-cdk/aws-codebuild'); +import cdk = require('@aws-cdk/cdk'); + +// tslint:disable-next-line:no-empty-interface +export interface PipelineProjectProps extends codebuild.CommonProjectProps { +} + +/** + * A convenience class for CodeBuild Projects that are used in CodePipeline. + */ +export class PipelineProject extends codebuild.Project { + constructor(parent: cdk.Construct, id: string, props?: PipelineProjectProps) { + super(parent, id, { + source: new codebuild.CodePipelineSource(), + artifacts: new codebuild.CodePipelineBuildArtifacts(), + ...props + }); + } +} diff --git a/packages/@aws-cdk/aws-codebuild-codepipeline/test/integ.pipeline-code-commit-build.ts b/packages/@aws-cdk/aws-codebuild-codepipeline/test/integ.pipeline-code-commit-build.ts index d92f087582616..d7f40b1f7399b 100644 --- a/packages/@aws-cdk/aws-codebuild-codepipeline/test/integ.pipeline-code-commit-build.ts +++ b/packages/@aws-cdk/aws-codebuild-codepipeline/test/integ.pipeline-code-commit-build.ts @@ -20,7 +20,7 @@ const source = new codecommitPipeline.PipelineSource(sourceStage, 'source', { }); const buildStage = new codepipeline.Stage(pipeline, 'build'); -const project = new codebuild.BuildProject(stack, 'MyBuildProject', { +const project = new codebuild.Project(stack, 'MyBuildProject', { source: new codebuild.CodePipelineSource(), }); diff --git a/packages/@aws-cdk/aws-codebuild-codepipeline/test/integ.pipeline-events.ts b/packages/@aws-cdk/aws-codebuild-codepipeline/test/integ.pipeline-events.ts index 356b4849010ef..19bbe822cc0d5 100644 --- a/packages/@aws-cdk/aws-codebuild-codepipeline/test/integ.pipeline-events.ts +++ b/packages/@aws-cdk/aws-codebuild-codepipeline/test/integ.pipeline-events.ts @@ -1,6 +1,5 @@ // Use pipeline as CloudWAtch event target -import codebuild = require('@aws-cdk/aws-codebuild'); import codecommit = require('@aws-cdk/aws-codecommit'); import codecommitPipeline = require('@aws-cdk/aws-codecommit-codepipeline'); import codepipeline = require('@aws-cdk/aws-codepipeline'); @@ -17,7 +16,7 @@ const sourceStage = new codepipeline.Stage(pipeline, 'Source'); const buildStage = new codepipeline.Stage(pipeline, 'Build'); const repository = new codecommit.Repository(stack, 'CodeCommitRepo', { repositoryName: 'foo' }); -const project = new codebuild.BuildProject(stack, 'BuildProject', { source: new codebuild.CodePipelineSource() }); +const project = new codebuildPipeline.PipelineProject(stack, 'BuildProject'); const sourceAction = new codecommitPipeline.PipelineSource(sourceStage, 'CodeCommitSource', { artifactName: 'Source', repository }); new codebuildPipeline.PipelineBuildAction(buildStage, 'CodeBuildAction', { inputArtifact: sourceAction.artifact, project }); diff --git a/packages/@aws-cdk/aws-codebuild-codepipeline/test/test.pipeline.ts b/packages/@aws-cdk/aws-codebuild-codepipeline/test/test.pipeline.ts index 4c6b419c75e1a..7ddcfa8b706f8 100644 --- a/packages/@aws-cdk/aws-codebuild-codepipeline/test/test.pipeline.ts +++ b/packages/@aws-cdk/aws-codebuild-codepipeline/test/test.pipeline.ts @@ -7,7 +7,7 @@ import s3 = require('@aws-cdk/aws-s3'); import sns = require('@aws-cdk/aws-sns'); import cdk = require('@aws-cdk/cdk'); import { Test } from 'nodeunit'; -import { PipelineBuildAction } from '../lib'; +import codebuildPipeline = require('../lib'); // tslint:disable:object-literal-key-quotes @@ -26,11 +26,11 @@ export = { }); const buildStage = new codepipeline.Stage(pipeline, 'build'); - const project = new codebuild.BuildProject(stack, 'MyBuildProject', { + const project = new codebuild.Project(stack, 'MyBuildProject', { source: new codebuild.CodePipelineSource() }); - new PipelineBuildAction(buildStage, 'build', { + new codebuildPipeline.PipelineBuildAction(buildStage, 'build', { project, inputArtifact: source.artifact }); @@ -206,5 +206,41 @@ export = { test.deepEqual([], pipeline.validate()); test.done(); - } + }, + + 'PipelineProject': { + 'with a custom Project Name': { + 'sets the source and artifacts to CodePipeline'(test: Test) { + const stack = new cdk.Stack(); + + new codebuildPipeline.PipelineProject(stack, 'MyProject', { + projectName: 'MyProject', + }); + + expect(stack).to(haveResource('AWS::CodeBuild::Project', { + "Name": "MyProject", + "Source": { + "Type": "CODEPIPELINE" + }, + "Artifacts": { + "Type": "CODEPIPELINE" + }, + "ServiceRole": { + "Fn::GetAtt": [ + "MyProjectRole9BBE5233", + "Arn" + ] + }, + "Environment": { + "Type": "LINUX_CONTAINER", + "PrivilegedMode": false, + "Image": "aws/codebuild/ubuntu-base:14.04", + "ComputeType": "BUILD_GENERAL1_SMALL" + } + })); + + test.done(); + } + } + }, }; diff --git a/packages/@aws-cdk/aws-codebuild/README.md b/packages/@aws-cdk/aws-codebuild/README.md index 6abec2e14b2b8..cad980e5cd09d 100644 --- a/packages/@aws-cdk/aws-codebuild/README.md +++ b/packages/@aws-cdk/aws-codebuild/README.md @@ -5,40 +5,48 @@ Define a project. This will also create an IAM Role and IAM Policy for CodeBuild Create a CodeBuild project with CodePipeline as the source: ```ts -new BuildProject(this, 'MyFirstProject', { - source: new CodePipelineSource() +import codebuild = require('@aws-cdk/aws-codebuild'); + +new codebuild.Project(this, 'MyFirstProject', { + source: new codebuild.CodePipelineSource() }); ``` Create a CodeBuild project with CodeCommit as the source: ```ts -const repo = new Repository(this, 'MyRepo', { repositoryName: 'foo' }); -new BuildProject(this, 'MyFirstCodeCommitProject', { - source: new CodeCommitSource(repo) +import codebuild = require('@aws-cdk/aws-codebuild'); +import codecommit = require('@aws-cdk/aws-codecommit'); + +const repo = new codecommit.Repository(this, 'MyRepo', { repositoryName: 'foo' }); +new codebuild.Project(this, 'MyFirstCodeCommitProject', { + source: new codebuild.CodeCommitSource(repo) }); ``` Create a CodeBuild project with an S3 bucket as the source: ```ts -const bucket = new Bucket(this, 'MyBucket'); -new BuildProject(this, 'MyProject', { - source: new S3BucketSource(bucket, 'path/to/source.zip') +import codebuild = require('@aws-cdk/aws-codebuild'); +import s3 = require('@aws-cdk/aws-s3'); + +const bucket = new s3.Bucket(this, 'MyBucket'); +new codebuild.Project(this, 'MyProject', { + source: new codebuild.S3BucketSource(bucket, 'path/to/source.zip') }); ``` -### Using BuildProject as an event target +### Using Project as an event target -The `BuildProject` construct implements the `IEventRuleTarget` interface. This means that it can be +The `Project` construct implements the `IEventRuleTarget` interface. This means that it can be used as a target for event rules: ```ts // start build when a commit is pushed -codeCommitRepository.onCommit('OnCommit', buildProject); +codeCommitRepository.onCommit('OnCommit', project); ``` -### Using BuildProject as an event source +### Using Project as an event source To define CloudWatch event rules for build projects, use one of the `onXxx` methods: diff --git a/packages/@aws-cdk/aws-codebuild/lib/artifacts.ts b/packages/@aws-cdk/aws-codebuild/lib/artifacts.ts index f166557681c84..27d4b631cb6d9 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/artifacts.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/artifacts.ts @@ -1,10 +1,10 @@ import s3 = require('@aws-cdk/aws-s3'); import { cloudformation } from './codebuild.generated'; -import { BuildProject } from './project'; +import { Project } from './project'; export abstract class BuildArtifacts { public abstract toArtifactsJSON(): cloudformation.ProjectResource.ArtifactsProperty; - public bind(_project: BuildProject) { + public bind(_project: Project) { return; } } @@ -64,7 +64,7 @@ export class S3BucketBuildArtifacts extends BuildArtifacts { super(); } - public bind(project: BuildProject) { + public bind(project: Project) { this.props.bucket.grantReadWrite(project.role); } diff --git a/packages/@aws-cdk/aws-codebuild/lib/project.ts b/packages/@aws-cdk/aws-codebuild/lib/project.ts index 08b9b41336880..684cbde6010bb 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/project.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/project.ts @@ -13,10 +13,10 @@ const CODEPIPELINE_TYPE = 'CODEPIPELINE'; /** * Properties of a reference to a CodeBuild Project. * - * @see BuildProjectRef.import - * @see BuildProjectRef.export + * @see ProjectRef.import + * @see ProjectRef.export */ -export interface BuildProjectRefProps { +export interface ProjectRefProps { /** * The human-readable name of the CodeBuild Project we're referencing. * The Project must be in the same account and region as the root Stack. @@ -28,13 +28,13 @@ export interface BuildProjectRefProps { * Represents a reference to a CodeBuild Project. * * If you're managing the Project alongside the rest of your CDK resources, - * use the {@link BuildProject} class. + * use the {@link Project} class. * * If you want to reference an already existing Project * (or one defined in a different CDK Stack), * use the {@link import} method. */ -export abstract class BuildProjectRef extends cdk.Construct implements events.IEventRuleTarget { +export abstract class ProjectRef extends cdk.Construct implements events.IEventRuleTarget { /** * Import a Project defined either outside the CDK, * or in a different CDK Stack @@ -50,8 +50,8 @@ export abstract class BuildProjectRef extends cdk.Construct implements events.IE * @param props the properties of the referenced Project * @returns a reference to the existing Project */ - public static import(parent: cdk.Construct, name: string, props: BuildProjectRefProps): BuildProjectRef { - return new ImportedBuildProjectRef(parent, name, props); + public static import(parent: cdk.Construct, name: string, props: ProjectRefProps): ProjectRef { + return new ImportedProjectRef(parent, name, props); } /** The ARN of this Project. */ @@ -69,7 +69,7 @@ export abstract class BuildProjectRef extends cdk.Construct implements events.IE /** * Export this Project. Allows referencing this Project in a different CDK Stack. */ - public export(): BuildProjectRefProps { + public export(): ProjectRefProps { return { projectName: new cdk.Output(this, 'ProjectName', { value: this.projectName }).makeImportValue(), }; @@ -273,12 +273,12 @@ export abstract class BuildProjectRef extends cdk.Construct implements events.IE } } -class ImportedBuildProjectRef extends BuildProjectRef { +class ImportedProjectRef extends ProjectRef { public readonly projectArn: ProjectArn; public readonly projectName: ProjectName; public readonly role?: iam.Role = undefined; - constructor(parent: cdk.Construct, name: string, props: BuildProjectRefProps) { + constructor(parent: cdk.Construct, name: string, props: ProjectRefProps) { super(parent, name); this.projectArn = cdk.Arn.fromComponents({ @@ -290,13 +290,7 @@ class ImportedBuildProjectRef extends BuildProjectRef { } } -export interface BuildProjectProps { - - /** - * The source of the build. - */ - source: BuildSource; - +export interface CommonProjectProps { /** * A description of the project. Use the description to identify the purpose * of the project. @@ -351,6 +345,23 @@ export interface BuildProjectProps { */ timeout?: number; + /** + * Additional environment variables to add to the build environment. + */ + environmentVariables?: { [name: string]: BuildEnvironmentVariable }; + + /** + * The physical, human-readable name of the CodeBuild Project. + */ + projectName?: string; +} + +export interface ProjectProps extends CommonProjectProps { + /** + * The source of the build. + */ + source: BuildSource; + /** * Defines where build artifacts will be stored. * Could be: PipelineBuildArtifacts, NoBuildArtifacts and S3BucketBuildArtifacts. @@ -358,18 +369,12 @@ export interface BuildProjectProps { * @default NoBuildArtifacts */ artifacts?: BuildArtifacts; - - /** - * Additional environment variables to add to the build environment. - */ - environmentVariables?: { [name: string]: BuildEnvironmentVariable }; } /** - * A CodeBuild project that is completely driven - * from CodePipeline (does not hot have its own input or output) + * A representation of a CodeBuild Project. */ -export class BuildProject extends BuildProjectRef { +export class Project extends ProjectRef { /** * The IAM role for this project. */ @@ -385,7 +390,7 @@ export class BuildProject extends BuildProjectRef { */ public readonly projectName: ProjectName; - constructor(parent: cdk.Construct, name: string, props: BuildProjectProps) { + constructor(parent: cdk.Construct, name: string, props: ProjectProps) { super(parent, name); this.role = props.role || new iam.Role(this, 'Role', { @@ -429,6 +434,7 @@ export class BuildProject extends BuildProjectRef { encryptionKey: props.encryptionKey && props.encryptionKey.keyArn, badgeEnabled: props.badge, cache, + projectName: props.projectName, }); this.projectArn = resource.projectArn; @@ -467,8 +473,8 @@ export class BuildProject extends BuildProjectRef { } private renderEnvironment(env: BuildEnvironment = {}, - projectVars: { [name: string]: BuildEnvironmentVariable } = {}): cloudformation.ProjectResource.EnvironmentProperty { - + projectVars: { [name: string]: BuildEnvironmentVariable } = {}): + cloudformation.ProjectResource.EnvironmentProperty { const vars: { [name: string]: BuildEnvironmentVariable } = {}; const containerVars = env.environmentVariables || {}; @@ -484,11 +490,17 @@ export class BuildProject extends BuildProjectRef { const hasEnvironmentVars = Object.keys(vars).length > 0; + const buildImage = env.buildImage || LinuxBuildImage.UBUNTU_14_04_BASE; + const errors = buildImage.validate(env); + if (errors.length > 0) { + throw new Error("Invalid CodeBuild environment: " + errors.join('\n')); + } + return { - type: env.type || 'LINUX_CONTAINER', - image: env.image || 'aws/codebuild/ubuntu-base:14.04', + type: buildImage.type, + image: buildImage.imageId, privilegedMode: env.priviledged || false, - computeType: env.computeType || ComputeType.Small, + computeType: env.computeType || buildImage.defaultComputeType, environmentVariables: !hasEnvironmentVars ? undefined : Object.keys(vars).map(name => ({ name, type: vars[name].type || BuildEnvironmentVariableType.PlainText, @@ -497,7 +509,7 @@ export class BuildProject extends BuildProjectRef { }; } - private parseArtifacts(props: BuildProjectProps) { + private parseArtifacts(props: ProjectProps) { if (props.artifacts) { return props.artifacts; } @@ -519,27 +531,28 @@ export class BuildProject extends BuildProjectRef { } } -export interface BuildEnvironment { - /** - * The type of build environment. The only allowed value is LINUX_CONTAINER. - * - * @default LINUX_CONTAINER - */ - type?: string; +/** + * Build machine compute type. + */ +export enum ComputeType { + Small = 'BUILD_GENERAL1_SMALL', + Medium = 'BUILD_GENERAL1_MEDIUM', + Large = 'BUILD_GENERAL1_LARGE' +} +export interface BuildEnvironment { /** - * The Docker image identifier that the build environment uses. + * The image used for the builds. * - * @see https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-available.html - * @default aws/codebuild/ubuntu-base:14.04 + * @default LinuxBuildImage.UBUNTU_14_04_BASE */ - image?: string; + buildImage?: IBuildImage; /** - * The type of compute to use for this build. See the - * ComputeType enum for options. + * The type of compute to use for this build. + * See the {@link ComputeType} enum for the possible values. * - * @default ComputeType.Small + * @default taken from {@link #buildImage#defaultComputeType} */ computeType?: ComputeType; @@ -561,6 +574,108 @@ export interface BuildEnvironment { environmentVariables?: { [name: string]: BuildEnvironmentVariable }; } +/** + * Represents a Docker image used for the CodeBuild Project builds. + * Use the concrete subclasses, either: + * {@link LinuxBuildImage} or {@link WindowsBuildImage}. + */ +export interface IBuildImage { + /** + * The type of build environment. + */ + readonly type: string; + + /** + * The Docker image identifier that the build environment uses. + * + * @see https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-available.html + */ + readonly imageId: string; + + /** + * The default {@link ComputeType} to use with this image, + * if one was not specified in {@link BuildEnvironment#computeType} explicitly. + */ + readonly defaultComputeType: ComputeType; + + /** + * Allows the image a chance to validate whether the passed configuration is correct. + * + * @param buildEnvironment the current build environment + */ + validate(buildEnvironment: BuildEnvironment): string[]; +} + +/** + * A CodeBuild image running Linux. + * This class has a bunch of public constants that represent the most popular images. + * If you need to use with an image that isn't in the named constants, + * you can always instantiate it directly. + * + * @see https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-available.html + */ +export class LinuxBuildImage implements IBuildImage { + public static readonly UBUNTU_14_04_BASE = new LinuxBuildImage('aws/codebuild/ubuntu-base:14.04'); + public static readonly UBUNTU_14_04_ANDROID_JAVA8_24_4_1 = new LinuxBuildImage('aws/codebuild/android-java-8:24.4.1'); + public static readonly UBUNTU_14_04_ANDROID_JAVA8_26_1_1 = new LinuxBuildImage('aws/codebuild/android-java-8:26.1.1'); + public static readonly UBUNTU_14_04_DOCKER_17_09_0 = new LinuxBuildImage('aws/codebuild/docker:17.09.0'); + public static readonly UBUNTU_14_04_GOLANG_1_10 = new LinuxBuildImage('aws/codebuild/golang:1.10'); + public static readonly UBUNTU_14_04_OPEN_JDK_8 = new LinuxBuildImage('aws/codebuild/java:openjdk-8'); + public static readonly UBUNTU_14_04_OPEN_JDK_9 = new LinuxBuildImage('aws/codebuild/java:openjdk-9'); + public static readonly UBUNTU_14_04_NODEJS_10_1_0 = new LinuxBuildImage('aws/codebuild/nodejs:10.1.0'); + public static readonly UBUNTU_14_04_NODEJS_8_11_0 = new LinuxBuildImage('aws/codebuild/nodejs:8.11.0'); + public static readonly UBUNTU_14_04_NODEJS_6_3_1 = new LinuxBuildImage('aws/codebuild/nodejs:6.3.1'); + public static readonly UBUNTU_14_04_PHP_5_6 = new LinuxBuildImage('aws/codebuild/php:5.6'); + public static readonly UBUNTU_14_04_PHP_7_0 = new LinuxBuildImage('aws/codebuild/php:7.0'); + public static readonly UBUNTU_14_04_PYTHON_3_6_5 = new LinuxBuildImage('aws/codebuild/python:3.6.5'); + public static readonly UBUNTU_14_04_PYTHON_3_5_2 = new LinuxBuildImage('aws/codebuild/python:3.5.2'); + public static readonly UBUNTU_14_04_PYTHON_3_4_5 = new LinuxBuildImage('aws/codebuild/python:3.4.5'); + public static readonly UBUNTU_14_04_PYTHON_3_3_6 = new LinuxBuildImage('aws/codebuild/python:3.3.6'); + public static readonly UBUNTU_14_04_PYTHON_2_7_12 = new LinuxBuildImage('aws/codebuild/python:2.7.12'); + public static readonly UBUNTU_14_04_RUBY_2_5_1 = new LinuxBuildImage('aws/codebuild/ruby:2.5.1'); + public static readonly UBUNTU_14_04_RUBY_2_3_1 = new LinuxBuildImage('aws/codebuild/ruby:2.3.1'); + public static readonly UBUNTU_14_04_RUBY_2_2_5 = new LinuxBuildImage('aws/codebuild/ruby:2.2.5'); + public static readonly UBUNTU_14_04_DOTNET_CORE_1_1 = new LinuxBuildImage('aws/codebuild/dot-net:core-1'); + public static readonly UBUNTU_14_04_DOTNET_CORE_2_0 = new LinuxBuildImage('aws/codebuild/dot-net:core-2.0'); + public static readonly UBUNTU_14_04_DOTNET_CORE_2_1 = new LinuxBuildImage('aws/codebuild/dot-net:core-2.1'); + + public readonly type = 'LINUX_CONTAINER'; + public readonly defaultComputeType = ComputeType.Small; + + public constructor(public readonly imageId: string) { + } + + public validate(_: BuildEnvironment): string[] { + return []; + } +} + +/** + * A CodeBuild image running Windows. + * This class has a bunch of public constants that represent the most popular images. + * If you need to use with an image that isn't in the named constants, + * you can always instantiate it directly. + * + * @see https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-available.html + */ +export class WindowsBuildImage implements IBuildImage { + public static readonly WIN_SERVER_CORE_2016_BASE = new WindowsBuildImage('aws/codebuild/windows-base:1.0'); + + public readonly type = 'WINDOWS_CONTAINER'; + public readonly defaultComputeType = ComputeType.Medium; + + public constructor(public readonly imageId: string) { + } + + public validate(buildEnvironment: BuildEnvironment): string[] { + const ret: string[] = []; + if (buildEnvironment.computeType === ComputeType.Small) { + ret.push("Windows images do not support the Small ComputeType"); + } + return ret; + } +} + export interface BuildEnvironmentVariable { /** * The type of environment variable. @@ -587,13 +702,4 @@ export enum BuildEnvironmentVariableType { ParameterStore = 'PARAMETER_STORE' } -/** - * Build machine compute type. - */ -export enum ComputeType { - Small = 'BUILD_GENERAL1_SMALL', - Medium = 'BUILD_GENERAL1_MEDIUM', - Large = 'BUILD_GENERAL1_LARGE' -} - export class ProjectName extends cdk.Token { } diff --git a/packages/@aws-cdk/aws-codebuild/lib/source.ts b/packages/@aws-cdk/aws-codebuild/lib/source.ts index c4dcd300371c4..3a2437d7c3b48 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/source.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/source.ts @@ -2,7 +2,7 @@ import codecommit = require('@aws-cdk/aws-codecommit'); import s3 = require('@aws-cdk/aws-s3'); import cdk = require('@aws-cdk/cdk'); import { cloudformation } from './codebuild.generated'; -import { BuildProject } from './project'; +import { Project } from './project'; /** * Source Provider definition for a CodeBuild project @@ -14,7 +14,7 @@ export abstract class BuildSource { * binding operations on the source. For example, it can grant permissions to the * code build project to read from the S3 bucket. */ - public bind(_project: BuildProject) { + public bind(_project: Project) { return; } @@ -29,7 +29,7 @@ export class CodeCommitSource extends BuildSource { super(); } - public bind(project: BuildProject) { + public bind(project: Project) { // https://docs.aws.amazon.com/codebuild/latest/userguide/setting-up.html project.addToRolePolicy(new cdk.PolicyStatement() .addAction('codecommit:GitPull') @@ -54,7 +54,7 @@ export class CodePipelineSource extends BuildSource { }; } - public bind(_project: BuildProject) { + public bind(_project: Project) { // TODO: permissions on the pipeline bucket? } } @@ -126,7 +126,7 @@ export class S3BucketSource extends BuildSource { }; } - public bind(project: BuildProject) { + public bind(project: Project) { this.bucket.grantRead(project.role); } } diff --git a/packages/@aws-cdk/aws-codebuild/package.json b/packages/@aws-cdk/aws-codebuild/package.json index 72eb17179758f..98070ec3ae7b3 100644 --- a/packages/@aws-cdk/aws-codebuild/package.json +++ b/packages/@aws-cdk/aws-codebuild/package.json @@ -60,7 +60,6 @@ "dependencies": { "@aws-cdk/aws-cloudwatch": "^0.7.4-beta", "@aws-cdk/aws-codecommit": "^0.7.4-beta", - "@aws-cdk/aws-codepipeline": "^0.7.4-beta", "@aws-cdk/aws-events": "^0.7.4-beta", "@aws-cdk/aws-iam": "^0.7.4-beta", "@aws-cdk/aws-kms": "^0.7.4-beta", diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.project-bucket.ts b/packages/@aws-cdk/aws-codebuild/test/integ.project-bucket.ts index aa11377b31d85..b43571b73529e 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.project-bucket.ts +++ b/packages/@aws-cdk/aws-codebuild/test/integ.project-bucket.ts @@ -9,7 +9,7 @@ const stack = new cdk.Stack(app, 'aws-cdk-codebuild'); const bucket = new s3.Bucket(stack, 'MyBucket'); -new codebuild.BuildProject(stack, 'MyProject', { +new codebuild.Project(stack, 'MyProject', { source: new codebuild.S3BucketSource(bucket, 'path/to/my/source.zip'), environment: { computeType: codebuild.ComputeType.Large diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.project-events.ts b/packages/@aws-cdk/aws-codebuild/test/integ.project-events.ts index f706ab0e8558f..7c1632d002af6 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.project-events.ts +++ b/packages/@aws-cdk/aws-codebuild/test/integ.project-events.ts @@ -2,14 +2,14 @@ import codecommit = require('@aws-cdk/aws-codecommit'); import sns = require('@aws-cdk/aws-sns'); import cdk = require('@aws-cdk/cdk'); -import { BuildProject, CodeCommitSource } from '../lib'; +import { CodeCommitSource, Project } from '../lib'; const app = new cdk.App(process.argv); const stack = new cdk.Stack(app, 'aws-cdk-codebuild-events'); const repo = new codecommit.Repository(stack, 'MyRepo', { repositoryName: 'aws-cdk-codebuild-events' }); -const project = new BuildProject(stack, 'MyProject', { source: new CodeCommitSource(repo) }); +const project = new Project(stack, 'MyProject', { source: new CodeCommitSource(repo) }); const topic = new sns.Topic(stack, 'MyTopic'); topic.subscribeEmail('Personal', 'benisrae@amazon.com'); diff --git a/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts b/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts index f2e36e33da6ba..6a719cb7b9d8f 100644 --- a/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts +++ b/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts @@ -13,7 +13,7 @@ export = { const stack = new cdk.Stack(); const source = new codebuild.CodePipelineSource(); - new codebuild.BuildProject(stack, 'MyProject', { + new codebuild.Project(stack, 'MyProject', { source }); @@ -177,7 +177,7 @@ export = { const source = new codebuild.CodeCommitSource(repo); - new codebuild.BuildProject(stack, 'MyProject', { + new codebuild.Project(stack, 'MyProject', { source }); @@ -360,8 +360,11 @@ export = { const stack = new cdk.Stack(); const bucket = new s3.Bucket(stack, 'MyBucket'); - new codebuild.BuildProject(stack, 'MyProject', { - source: new codebuild.S3BucketSource(bucket, 'path/to/source.zip') + new codebuild.Project(stack, 'MyProject', { + source: new codebuild.S3BucketSource(bucket, 'path/to/source.zip'), + environment: { + buildImage: codebuild.WindowsBuildImage.WIN_SERVER_CORE_2016_BASE, + }, }); expect(stack).toMatch({ @@ -551,10 +554,10 @@ export = { ] }, "Environment": { - "Type": "LINUX_CONTAINER", + "Type": "WINDOWS_CONTAINER", "PrivilegedMode": false, - "Image": "aws/codebuild/ubuntu-base:14.04", - "ComputeType": "BUILD_GENERAL1_SMALL" + "Image": "aws/codebuild/windows-base:1.0", + "ComputeType": "BUILD_GENERAL1_MEDIUM" } } } @@ -569,7 +572,7 @@ export = { 'both source and artifacs are set to CodePipeline'(test: Test) { const stack = new cdk.Stack(); - new codebuild.BuildProject(stack, 'MyProject', { + new codebuild.Project(stack, 'MyProject', { source: new codebuild.CodePipelineSource(), artifacts: new codebuild.CodePipelineBuildArtifacts() }); @@ -601,7 +604,7 @@ export = { 'if sourcde is set to CodePipeline, and artifacts are not set, they are defaulted to CodePipeline'(test: Test) { const stack = new cdk.Stack(); - new codebuild.BuildProject(stack, 'MyProject', { + new codebuild.Project(stack, 'MyProject', { source: new codebuild.CodePipelineSource() }); @@ -632,12 +635,12 @@ export = { 'fails if one of source/artifacts is set to CodePipeline and the other isn\'t'(test: Test) { const stack = new cdk.Stack(); - test.throws(() => new codebuild.BuildProject(stack, 'MyProject', { + test.throws(() => new codebuild.Project(stack, 'MyProject', { source: new codebuild.CodePipelineSource(), artifacts: new codebuild.NoBuildArtifacts() }), /Both source and artifacts must be set to CodePipeline/); - test.throws(() => new codebuild.BuildProject(stack, 'YourProject', { + test.throws(() => new codebuild.Project(stack, 'YourProject', { source: new codebuild.CodeCommitSource(new codecommit.Repository(stack, 'MyRepo', { repositoryName: 'boo' })), artifacts: new codebuild.CodePipelineBuildArtifacts() }), /Both source and artifacts must be set to CodePipeline/); @@ -649,7 +652,7 @@ export = { 'events'(test: Test) { const stack = new cdk.Stack(); - const project = new codebuild.BuildProject(stack, 'MyProject', { + const project = new codebuild.Project(stack, 'MyProject', { source: new codebuild.CodePipelineSource() }); @@ -769,7 +772,7 @@ export = { 'environment variables can be overridden at the project level'(test: Test) { const stack = new cdk.Stack(); - new codebuild.BuildProject(stack, 'Project', { + new codebuild.Project(stack, 'Project', { source: new codebuild.CodePipelineSource(), environment: { environmentVariables: { @@ -835,7 +838,7 @@ export = { '.metricXxx() methods can be used to obtain Metrics for CodeBuild projects'(test: Test) { const stack = new cdk.Stack(); - const project = new codebuild.BuildProject(stack, 'MyBuildProject', { source: new codebuild.CodePipelineSource() }); + const project = new codebuild.Project(stack, 'MyBuildProject', { source: new codebuild.CodePipelineSource() }); const metricBuilds = project.metricBuilds(); test.same(metricBuilds.dimensions!.ProjectName, project.projectName); @@ -851,6 +854,23 @@ export = { test.deepEqual(project.metricFailedBuilds().metricName, 'FailedBuilds'); test.deepEqual(project.metricSucceededBuilds().metricName, 'SucceededBuilds'); + test.done(); + }, + + 'using ComputeType.Small with a Windows image fails validation'(test: Test) { + const stack = new cdk.Stack(); + const invalidEnvironment: codebuild.BuildEnvironment = { + buildImage: codebuild.WindowsBuildImage.WIN_SERVER_CORE_2016_BASE, + computeType: codebuild.ComputeType.Small, + }; + + test.throws(() => { + new codebuild.Project(stack, 'MyProject', { + source: new codebuild.CodePipelineSource(), + environment: invalidEnvironment, + }); + }, /Windows images do not support the Small ComputeType/); + test.done(); } }; diff --git a/packages/@aws-cdk/aws-codecommit/README.md b/packages/@aws-cdk/aws-codecommit/README.md index 0ca04f29e017a..dfa40c3ab4a26 100644 --- a/packages/@aws-cdk/aws-codecommit/README.md +++ b/packages/@aws-cdk/aws-codecommit/README.md @@ -31,7 +31,7 @@ targets as a result: ```ts // starts a CodeBuild project when a commit is pushed to the "master" branch of the repo -repo.onCommit('CommitToMaster', buildProject, 'master'); +repo.onCommit('CommitToMaster', project, 'master'); // publishes a message to an SNS topic when a comment is made on a pull request const rule = repo.onCommentOnPullRequest('CommentOnPullRequest'); diff --git a/packages/@aws-cdk/aws-events/README.md b/packages/@aws-cdk/aws-events/README.md index c052d42f91ed5..5b81a0191b255 100644 --- a/packages/@aws-cdk/aws-events/README.md +++ b/packages/@aws-cdk/aws-events/README.md @@ -43,7 +43,7 @@ For example, to define an rule that triggers a CodeBuild project build when a commit is pushed to the "master" branch of a CodeCommit repository: ```ts -const onCommitRule = repo.onCommit('OnCommitToMaster', buildProject, 'master'); +const onCommitRule = repo.onCommit('OnCommitToMaster', project, 'master'); ``` You can add additional targets, with optional [input