diff --git a/packages/@aws-cdk/aws-codebuild/lib/project.ts b/packages/@aws-cdk/aws-codebuild/lib/project.ts index 99fca662b8866..f510e27eb8783 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/project.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/project.ts @@ -1,3 +1,4 @@ +import assets = require('@aws-cdk/assets'); import cloudwatch = require('@aws-cdk/aws-cloudwatch'); import codepipeline = require('@aws-cdk/aws-codepipeline-api'); import events = require('@aws-cdk/aws-events'); @@ -8,9 +9,11 @@ import cdk = require('@aws-cdk/cdk'); import { BuildArtifacts, CodePipelineBuildArtifacts, NoBuildArtifacts } from './artifacts'; import { cloudformation, ProjectArn, ProjectName } from './codebuild.generated'; import { CommonPipelineBuildActionProps, PipelineBuildAction } from './pipeline-actions'; -import { BuildSource } from './source'; +import { BuildSource, NoSource } from './source'; const CODEPIPELINE_TYPE = 'CODEPIPELINE'; +const S3_BUCKET_ENV = 'SCRIPT_S3_BUCKET'; +const S3_KEY_ENV = 'SCRIPT_S3_KEY'; /** * Properties of a reference to a CodeBuild Project. @@ -322,6 +325,26 @@ export interface CommonProjectProps { */ buildSpec?: any; + /** + * Run a script from an asset as build script + * + * If supplied together with buildSpec, the asset script will be run + * _after_ the existing commands in buildspec. + * + * This feature can also be used without a source, to simply run an + * arbitrary script in a serverless way. + * + * @default No asset build script + */ + buildScriptAsset?: assets.Asset; + + /** + * The script in the asset to run. + * + * @default build.sh + */ + buildScriptAssetEntrypoint?: string; + /** * Service Role to assume while running the build. * If not specified, a role will be created. @@ -378,8 +401,10 @@ export interface CommonProjectProps { export interface ProjectProps extends CommonProjectProps { /** * The source of the build. + * + * @default NoSource */ - source: BuildSource; + source?: BuildSource; /** * Defines where build artifacts will be stored. @@ -409,49 +434,70 @@ export class Project extends ProjectRef { */ public readonly projectName: ProjectName; + private readonly source: BuildSource; + private readonly buildImage: IBuildImage; + constructor(parent: cdk.Construct, name: string, props: ProjectProps) { super(parent, name); + if (props.buildScriptAssetEntrypoint && !props.buildScriptAsset) { + throw new Error('To use buildScriptAssetEntrypoint, supply buildScriptAsset as well.'); + } + this.role = props.role || new iam.Role(this, 'Role', { assumedBy: new cdk.ServicePrincipal('codebuild.amazonaws.com') }); - const environment = this.renderEnvironment(props.environment, props.environmentVariables); - let cache: cloudformation.ProjectResource.ProjectCacheProperty | undefined; if (props.cacheBucket) { - const cacheDir = props.cacheDir != null ? props.cacheDir : ''; + const cacheDir = props.cacheDir != null ? props.cacheDir : new cdk.AwsNoValue(); cache = { type: 'S3', - location: props.cacheBucket.arnForObjects(cacheDir) + location: new cdk.FnJoin('/', [props.cacheBucket.bucketName, cacheDir]), }; props.cacheBucket.grantReadWrite(this.role); } + this.buildImage = (props.environment && props.environment.buildImage) || LinuxBuildImage.UBUNTU_14_04_BASE; + // let source "bind" to the project. this usually involves granting permissions // for the code build role to interact with the source. - const source = props.source; - source.bind(this); + this.source = props.source || new NoSource(); + this.source.bind(this); const artifacts = this.parseArtifacts(props); artifacts.bind(this); - const sourceJson = source.toSourceJSON(); - if (typeof props.buildSpec === 'string') { - sourceJson.buildSpec = props.buildSpec; - } else { - sourceJson.buildSpec = JSON.stringify(props.buildSpec); + // Inject download commands for asset if requested + const environmentVariables = props.environmentVariables || {}; + const buildSpec = props.buildSpec || {}; + + if (props.buildScriptAsset) { + environmentVariables[S3_BUCKET_ENV] = { value: props.buildScriptAsset.s3BucketName }; + environmentVariables[S3_KEY_ENV] = { value: props.buildScriptAsset.s3ObjectKey }; + extendBuildSpec(buildSpec, this.buildImage.runScriptBuildspec(props.buildScriptAssetEntrypoint || 'build.sh')); + props.buildScriptAsset.grantRead(this.role); } - this.validateCodePipelineSettings(source, artifacts); + // Render the source and add in the buildspec + const sourceJson = this.source.toSourceJSON(); + if (typeof buildSpec === 'string') { + sourceJson.buildSpec = buildSpec; // Filename to buildspec file + } else if (Object.keys(buildSpec).length > 0) { + // We have to pretty-print the buildspec, otherwise + // CodeBuild will not recognize it as an inline buildspec. + sourceJson.buildSpec = JSON.stringify(buildSpec, undefined, 2); // Literal buildspec + } + + this.validateCodePipelineSettings(artifacts); const resource = new cloudformation.ProjectResource(this, 'Resource', { description: props.description, source: sourceJson, artifacts: artifacts.toArtifactsJSON(), serviceRole: this.role.roleArn, - environment, + environment: this.renderEnvironment(props.environment, environmentVariables), encryptionKey: props.encryptionKey && props.encryptionKey.keyArn, badgeEnabled: props.badge, cache, @@ -513,17 +559,16 @@ export class Project extends ProjectRef { const hasEnvironmentVars = Object.keys(vars).length > 0; - const buildImage = env.buildImage || LinuxBuildImage.UBUNTU_14_04_BASE; - const errors = buildImage.validate(env); + const errors = this.buildImage.validate(env); if (errors.length > 0) { throw new Error("Invalid CodeBuild environment: " + errors.join('\n')); } return { - type: buildImage.type, - image: buildImage.imageId, + type: this.buildImage.type, + image: this.buildImage.imageId, privilegedMode: env.priviledged || false, - computeType: env.computeType || buildImage.defaultComputeType, + computeType: env.computeType || this.buildImage.defaultComputeType, environmentVariables: !hasEnvironmentVars ? undefined : Object.keys(vars).map(name => ({ name, type: vars[name].type || BuildEnvironmentVariableType.PlainText, @@ -536,15 +581,15 @@ export class Project extends ProjectRef { if (props.artifacts) { return props.artifacts; } - if (props.source.toSourceJSON().type === CODEPIPELINE_TYPE) { + if (this.source.toSourceJSON().type === CODEPIPELINE_TYPE) { return new CodePipelineBuildArtifacts(); } else { return new NoBuildArtifacts(); } } - private validateCodePipelineSettings(source: BuildSource, artifacts: BuildArtifacts) { - const sourceType = source.toSourceJSON().type; + private validateCodePipelineSettings(artifacts: BuildArtifacts) { + const sourceType = this.source.toSourceJSON().type; const artifactsType = artifacts.toArtifactsJSON().type; if ((sourceType === CODEPIPELINE_TYPE || artifactsType === CODEPIPELINE_TYPE) && @@ -627,6 +672,11 @@ export interface IBuildImage { * @param buildEnvironment the current build environment */ validate(buildEnvironment: BuildEnvironment): string[]; + + /** + * Make a buildspec to run the indicated script + */ + runScriptBuildspec(entrypoint: string): any; } /** @@ -671,6 +721,34 @@ export class LinuxBuildImage implements IBuildImage { public validate(_: BuildEnvironment): string[] { return []; } + + public runScriptBuildspec(entrypoint: string): any { + return { + version: '0.2', + phases: { + pre_build: { + commands: [ + // Better echo the location here; if this fails, the error message only contains + // the unexpanded variables by default. It might fail if you're running an old + // definition of the CodeBuild project--the permissions will have been changed + // to only allow downloading the very latest version. + `echo "Downloading scripts from s3://\${${S3_BUCKET_ENV}}/\${${S3_KEY_ENV}}"`, + `aws s3 cp s3://\${${S3_BUCKET_ENV}}/\${${S3_KEY_ENV}} /tmp`, + `mkdir -p /tmp/scriptdir`, + `unzip /tmp/$(basename \$${S3_KEY_ENV}) -d /tmp/scriptdir`, + ] + }, + build: { + commands: [ + 'export SCRIPT_DIR=/tmp/scriptdir', + `echo "Running ${entrypoint}"`, + `chmod +x /tmp/scriptdir/${entrypoint}`, + `/tmp/scriptdir/${entrypoint}`, + ] + } + } + }; + } } /** @@ -697,6 +775,31 @@ export class WindowsBuildImage implements IBuildImage { } return ret; } + + public runScriptBuildspec(entrypoint: string): any { + return { + version: '0.2', + phases: { + pre_build: { + // Would love to do downloading here and executing in the next step, + // but I don't know how to propagate the value of $TEMPDIR. + // + // Punting for someone who knows PowerShell well enough. + commands: [] + }, + build: { + commands: [ + `Set-Variable -Name TEMPDIR -Value (New-TemporaryFile).DirectoryName`, + `aws s3 cp s3://$env:${S3_BUCKET_ENV}/$env:${S3_KEY_ENV} $TEMPDIR\\scripts.zip`, + 'New-Item -ItemType Directory -Path $TEMPDIR\\scriptdir', + 'Expand-Archive -Path $TEMPDIR/scripts.zip -DestinationPath $TEMPDIR\\scriptdir', + '$env:SCRIPT_DIR = "$TEMPDIR\\scriptdir"', + `& $TEMPDIR\\scriptdir\\${entrypoint}` + ] + } + } + }; + } } export interface BuildEnvironmentVariable { @@ -723,4 +826,31 @@ export enum BuildEnvironmentVariableType { * An environment variable stored in Systems Manager Parameter Store. */ ParameterStore = 'PARAMETER_STORE' -} \ No newline at end of file +} + +/** + * Extend buildSpec phases with the contents of another one + */ +function extendBuildSpec(buildSpec: any, extend: any) { + if (typeof buildSpec === 'string') { + throw new Error('Cannot extend buildspec that is given as a string. Pass the buildspec as a structure instead.'); + } + if (buildSpec.version === '0.1') { + throw new Error('Cannot extend buildspec at version "0.1". Set the version to "0.2" or higher instead.'); + } + if (buildSpec.version === undefined) { + buildSpec.version = extend.version; + } + + if (!buildSpec.phases) { + buildSpec.phases = {}; + } + + for (const phaseName of Object.keys(extend.phases)) { + if (!(phaseName in buildSpec.phases)) { buildSpec.phases[phaseName] = {}; } + const phase = buildSpec.phases[phaseName]; + + if (!(phase.commands)) { phase.commands = []; } + phase.commands.push(...extend.phases[phaseName].commands); + } +} diff --git a/packages/@aws-cdk/aws-codebuild/lib/source.ts b/packages/@aws-cdk/aws-codebuild/lib/source.ts index 3a2437d7c3b48..0f47d0a75e754 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/source.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/source.ts @@ -21,6 +21,18 @@ export abstract class BuildSource { public abstract toSourceJSON(): cloudformation.ProjectResource.SourceProperty; } +export class NoSource extends BuildSource { + constructor() { + super(); + } + + public toSourceJSON(): cloudformation.ProjectResource.SourceProperty { + return { + type: SourceType.None, + }; + } +} + /** * CodeCommit Source definition for a CodeBuild project */ @@ -135,10 +147,11 @@ export class S3BucketSource extends BuildSource { * Source types for CodeBuild Project */ export enum SourceType { + None = 'NO_SOURCE', CodeCommit = 'CODECOMMIT', CodePipeline = 'CODEPIPELINE', GitHub = 'GITHUB', GitHubEnterPrise = 'GITHUB_ENTERPRISE', BitBucket = 'BITBUCKET', - S3 = 'S3' + S3 = 'S3', } diff --git a/packages/@aws-cdk/aws-codebuild/package.json b/packages/@aws-cdk/aws-codebuild/package.json index 5d2f4e13f00d7..58c2313f0f4b8 100644 --- a/packages/@aws-cdk/aws-codebuild/package.json +++ b/packages/@aws-cdk/aws-codebuild/package.json @@ -58,6 +58,7 @@ "pkglint": "^0.8.2" }, "dependencies": { + "@aws-cdk/assets": "^0.8.2", "@aws-cdk/aws-cloudwatch": "^0.8.2", "@aws-cdk/aws-codecommit": "^0.8.2", "@aws-cdk/aws-codepipeline-api": "^0.8.2", diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.caching.expected.json b/packages/@aws-cdk/aws-codebuild/test/integ.caching.expected.json new file mode 100644 index 0000000000000..1b7782ac70f96 --- /dev/null +++ b/packages/@aws-cdk/aws-codebuild/test/integ.caching.expected.json @@ -0,0 +1,190 @@ +{ + "Resources": { + "CacheBucket41D9D0B0": { + "Type": "AWS::S3::Bucket" + }, + "MyProjectRole9BBE5233": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codebuild.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "MyProjectRoleDefaultPolicyB19B7C29": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*", + "s3:PutObject*", + "s3:Abort*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "CacheBucket41D9D0B0", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "CacheBucket41D9D0B0", + "Arn" + ] + }, + "/", + "*" + ] + ] + } + ] + }, + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn", + ":", + { + "Ref": "AWS::Partition" + }, + ":", + "logs", + ":", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + "log-group", + ":", + "/aws/codebuild/", + { + "Ref": "MyProject39F7B0AE" + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::Join": [ + "", + [ + "arn", + ":", + { + "Ref": "AWS::Partition" + }, + ":", + "logs", + ":", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + "log-group", + ":", + "/aws/codebuild/", + { + "Ref": "MyProject39F7B0AE" + } + ] + ] + }, + ":*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "MyProjectRoleDefaultPolicyB19B7C29", + "Roles": [ + { + "Ref": "MyProjectRole9BBE5233" + } + ] + } + }, + "MyProject39F7B0AE": { + "Type": "AWS::CodeBuild::Project", + "Properties": { + "Artifacts": { + "Type": "NO_ARTIFACTS" + }, + "Environment": { + "ComputeType": "BUILD_GENERAL1_SMALL", + "Image": "aws/codebuild/ubuntu-base:14.04", + "PrivilegedMode": false, + "Type": "LINUX_CONTAINER" + }, + "ServiceRole": { + "Fn::GetAtt": [ + "MyProjectRole9BBE5233", + "Arn" + ] + }, + "Source": { + "BuildSpec": "{\n \"build\": {\n \"commands\": [\n \"echo Hello\"\n ]\n },\n \"cache\": {\n \"paths\": [\n \"/root/.cache/pip/**/*\"\n ]\n }\n}", + "Type": "NO_SOURCE" + }, + "Cache": { + "Location": { + "Fn::Join": [ + "/", + [ + { + "Ref": "CacheBucket41D9D0B0" + }, + { + "Ref": "AWS::NoValue" + } + ] + ] + }, + "Type": "S3" + } + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.caching.ts b/packages/@aws-cdk/aws-codebuild/test/integ.caching.ts new file mode 100644 index 0000000000000..180afef6d82b3 --- /dev/null +++ b/packages/@aws-cdk/aws-codebuild/test/integ.caching.ts @@ -0,0 +1,24 @@ +#!/usr/bin/env node +import s3 = require('@aws-cdk/aws-s3'); +import cdk = require('@aws-cdk/cdk'); +import codebuild = require('../lib'); + +const app = new cdk.App(process.argv); + +const stack = new cdk.Stack(app, 'aws-cdk-codebuild'); + +const bucket = new s3.Bucket(stack, 'CacheBucket'); + +new codebuild.Project(stack, 'MyProject', { + cacheBucket: bucket, + buildSpec: { + build: { + commands: ['echo Hello'] + }, + cache: { + paths: ['/root/.cache/pip/**/*'] + } + } +}); + +process.stdout.write(app.run()); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.project-shell.expected.json b/packages/@aws-cdk/aws-codebuild/test/integ.project-shell.expected.json new file mode 100644 index 0000000000000..efb562c9c4c2d --- /dev/null +++ b/packages/@aws-cdk/aws-codebuild/test/integ.project-shell.expected.json @@ -0,0 +1,276 @@ +{ + "Parameters": { + "BundleS3Bucket0EFC11B0": { + "Type": "String", + "Description": "S3 bucket for asset \"aws-cdk-codebuild-events/Bundle\"" + }, + "BundleS3VersionKey720F2199": { + "Type": "String", + "Description": "S3 key for asset version \"aws-cdk-codebuild-events/Bundle\"" + } + }, + "Resources": { + "MyProjectRole9BBE5233": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codebuild.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "MyProjectRoleDefaultPolicyB19B7C29": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn", + ":", + { + "Ref": "AWS::Partition" + }, + ":", + "s3", + ":", + "", + ":", + "", + ":", + { + "Ref": "BundleS3Bucket0EFC11B0" + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::Join": [ + "", + [ + "arn", + ":", + { + "Ref": "AWS::Partition" + }, + ":", + "s3", + ":", + "", + ":", + "", + ":", + { + "Ref": "BundleS3Bucket0EFC11B0" + } + ] + ] + }, + "/", + { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "BundleS3VersionKey720F2199" + } + ] + } + ] + }, + "*" + ] + ] + } + ] + ] + } + ] + }, + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn", + ":", + { + "Ref": "AWS::Partition" + }, + ":", + "logs", + ":", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + "log-group", + ":", + "/aws/codebuild/", + { + "Ref": "MyProject39F7B0AE" + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::Join": [ + "", + [ + "arn", + ":", + { + "Ref": "AWS::Partition" + }, + ":", + "logs", + ":", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + "log-group", + ":", + "/aws/codebuild/", + { + "Ref": "MyProject39F7B0AE" + } + ] + ] + }, + ":*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "MyProjectRoleDefaultPolicyB19B7C29", + "Roles": [ + { + "Ref": "MyProjectRole9BBE5233" + } + ] + } + }, + "MyProject39F7B0AE": { + "Type": "AWS::CodeBuild::Project", + "Properties": { + "Artifacts": { + "Type": "NO_ARTIFACTS" + }, + "Environment": { + "ComputeType": "BUILD_GENERAL1_SMALL", + "EnvironmentVariables": [ + { + "Name": "SCRIPT_S3_BUCKET", + "Type": "PLAINTEXT", + "Value": { + "Ref": "BundleS3Bucket0EFC11B0" + } + }, + { + "Name": "SCRIPT_S3_KEY", + "Type": "PLAINTEXT", + "Value": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "BundleS3VersionKey720F2199" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "BundleS3VersionKey720F2199" + } + ] + } + ] + } + ] + ] + } + } + ], + "Image": "aws/codebuild/ubuntu-base:14.04", + "PrivilegedMode": false, + "Type": "LINUX_CONTAINER" + }, + "ServiceRole": { + "Fn::GetAtt": [ + "MyProjectRole9BBE5233", + "Arn" + ] + }, + "Source": { + "BuildSpec": "{\n \"version\": \"0.2\",\n \"phases\": {\n \"pre_build\": {\n \"commands\": [\n \"echo \\\"Downloading scripts from s3://${SCRIPT_S3_BUCKET}/${SCRIPT_S3_KEY}\\\"\",\n \"aws s3 cp s3://${SCRIPT_S3_BUCKET}/${SCRIPT_S3_KEY} /tmp\",\n \"mkdir -p /tmp/scriptdir\",\n \"unzip /tmp/$(basename $SCRIPT_S3_KEY) -d /tmp/scriptdir\"\n ]\n },\n \"build\": {\n \"commands\": [\n \"export SCRIPT_DIR=/tmp/scriptdir\",\n \"echo \\\"Running build.sh\\\"\",\n \"chmod +x /tmp/scriptdir/build.sh\",\n \"/tmp/scriptdir/build.sh\"\n ]\n }\n }\n}", + "Type": "NO_SOURCE" + } + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.project-shell.ts b/packages/@aws-cdk/aws-codebuild/test/integ.project-shell.ts new file mode 100644 index 0000000000000..76dd45a3b7148 --- /dev/null +++ b/packages/@aws-cdk/aws-codebuild/test/integ.project-shell.ts @@ -0,0 +1,14 @@ +#!/usr/bin/env node +import assets = require('@aws-cdk/assets'); +import cdk = require('@aws-cdk/cdk'); +import { Project } from '../lib'; + +const app = new cdk.App(process.argv); + +const stack = new cdk.Stack(app, 'aws-cdk-codebuild-events'); + +new Project(stack, 'MyProject', { + buildScriptAsset: new assets.ZipDirectoryAsset(stack, 'Bundle', { path: 'script_bundle' }) +}); + +process.stdout.write(app.run()); diff --git a/packages/@aws-cdk/aws-codebuild/test/script_bundle/build.sh b/packages/@aws-cdk/aws-codebuild/test/script_bundle/build.sh new file mode 100644 index 0000000000000..49374d2e3cc2b --- /dev/null +++ b/packages/@aws-cdk/aws-codebuild/test/script_bundle/build.sh @@ -0,0 +1,2 @@ +#!/bin/bash +echo "Hello, World" diff --git a/packages/@aws-cdk/aws-codebuild/test/test.project.ts b/packages/@aws-cdk/aws-codebuild/test/test.project.ts index eb8a16507df03..0ed0cc0b89122 100644 --- a/packages/@aws-cdk/aws-codebuild/test/test.project.ts +++ b/packages/@aws-cdk/aws-codebuild/test/test.project.ts @@ -1,4 +1,5 @@ import { expect, haveResource } from '@aws-cdk/assert'; +import assets = require('@aws-cdk/assets'); import cdk = require('@aws-cdk/cdk'); import { Test } from 'nodeunit'; import codebuild = require('../lib'); @@ -39,10 +40,49 @@ export = { // THEN expect(stack).to(haveResource('AWS::CodeBuild::Project', { Source: { - BuildSpec: '{"phases":["say hi"]}' + BuildSpec: "{\n \"phases\": [\n \"say hi\"\n ]\n}", } })); test.done(); - } -}; \ No newline at end of file + }, + + 'construct from asset'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + new codebuild.Project(stack, 'Project', { + buildScriptAsset: new assets.ZipDirectoryAsset(stack, 'Asset', { path: '.' }), + buildScriptAssetEntrypoint: 'hello.sh', + }); + + // THEN + expect(stack).to(haveResource('AWS::CodeBuild::Project', { + Environment: { + ComputeType: "BUILD_GENERAL1_SMALL", + EnvironmentVariables: [ + { + Name: "SCRIPT_S3_BUCKET", + Type: "PLAINTEXT", + Value: { Ref: "AssetS3Bucket235698C0" } + }, + { + Name: "SCRIPT_S3_KEY", + Type: "PLAINTEXT", + Value: { "Fn::Join": [ "", [ + { "Fn::Select": [ 0, { "Fn::Split": [ "||", { Ref: "AssetS3VersionKeyA852DDAE" } ] } ] }, + { "Fn::Select": [ 1, { "Fn::Split": [ "||", { Ref: "AssetS3VersionKeyA852DDAE" } ] } ] } + ] ] } + } + ], + }, + Source: { + // Not testing BuildSpec, it's too big and finicky + Type: "NO_SOURCE" + } + })); + + test.done(); + }, +};