Skip to content

Commit

Permalink
feat(codebuild): adds file asset support to build-spec
Browse files Browse the repository at this point in the history
Adds `fromAsset` to the `BuildSpec` class to allow referencing local
files as a project's buildspec. Uploads the file to s3 and references
the object arn in the `buildSpec` property of `Codebuild.Project`.

`isImmediate` is true for AssetBuildSpec because it's actual meaning is
not can be defined at synth time, but it exsists somewhere other  than
the project's source code, which in this case is true.

Requires referencing of the project so adds `scope` as an optional
parameter to `toBuildSpec` method.

fixes: #1138
  • Loading branch information
MrArnoldPalmer committed Feb 23, 2023
1 parent e47646c commit 9c49153
Show file tree
Hide file tree
Showing 15 changed files with 3,262 additions and 6 deletions.
55 changes: 52 additions & 3 deletions packages/@aws-cdk/aws-codebuild/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,58 @@ const gitHubSource = codebuild.Source.gitHub({
});
```

## BuildSpec

The build spec can be provided from a number of different sources

### File path relative to the root of the source

You can specify a specific filename that exists within the project's source artifact to use as the buildspec.

```ts
const project = new codebuild.Project(this, 'MyProject', {
buildSpec: codebuild.BuildSpec.fromSourceFileName('my-buildspec.yml'),
source: codebuild.Source.gitHub({
owner: 'awslabs',
repo: 'aws-cdk',
})
});
```

This will use `my-buildspec.yml` file within the `awslabs/aws-cdk` repository as the build spec.

### File within the CDK project codebuild

You can also specify a file within your cdk project directory to use as the buildspec.

```ts
const project = new codebuild.Project(this, 'MyProject', {
buildSpec: codebuild.BuildSpec.fromAsset('my-buildspec.yml'),
});
```

This file will be uploaded to S3 and referenced from the codebuild project.

### Inline object

```ts
const project = new codebuild.Project(this, 'MyProject', {
buildSpec: codebuild.BuildSpec.fromObject({
version: '0.2',
}),
});
```

This will result in the buildspec being rendered as JSON within the codebuild project, if you prefer it to be rendered as YAML, use `fromObjectToYaml`.

```ts
const project = new codebuild.Project(this, 'MyProject', {
buildSpec: codebuild.BuildSpec.fromObjectToYaml({
version: '0.2',
}),
});
```

## Artifacts

CodeBuild Projects can produce Artifacts and upload them to S3. For example:
Expand All @@ -135,9 +187,6 @@ const project = new codebuild.Project(this, 'MyProject', {
});
```

If you'd prefer your buildspec to be rendered as YAML in the template,
use the `fromObjectToYaml()` method instead of `fromObject()`.

Because we've not set the `name` property, this example will set the
`overrideArtifactName` parameter, and produce an artifact named as defined in
the Buildspec file, uploaded to an S3 bucket (`bucket`). The path will be
Expand Down
52 changes: 50 additions & 2 deletions packages/@aws-cdk/aws-codebuild/lib/build-spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import * as s3_assets from '@aws-cdk/aws-s3-assets';
import { IResolveContext, Lazy, Stack } from '@aws-cdk/core';
import { Construct } from 'constructs';
import * as yaml_cfn from './private/yaml-cfn';
import { Project } from './project';

/**
* BuildSpec for CodeBuild projects
Expand Down Expand Up @@ -27,6 +30,15 @@ export abstract class BuildSpec {
return new FilenameBuildSpec(filename);
}

/**
* Use the contents of a local file as the build spec string
*
* Use this if you have a local .yml or .json file that you want to use as the buildspec
*/
public static fromAsset(path: string): BuildSpec {
return new AssetBuildSpec(path);
}

/**
* Whether the buildspec is directly available or deferred until build-time
*/
Expand All @@ -38,7 +50,43 @@ export abstract class BuildSpec {
/**
* Render the represented BuildSpec
*/
public abstract toBuildSpec(): string;
public abstract toBuildSpec(scope?: Construct): string;
}

/**
* BuildSpec that just returns the contents of a local file
*/
class AssetBuildSpec extends BuildSpec {
public readonly isImmediate: boolean = true;
public asset?: s3_assets.Asset;

constructor(public readonly path: string, private readonly options: s3_assets.AssetOptions = { }) {
super();
}

public toBuildSpec(scope?: Project): string {
if (!scope) {
throw new Error('`AssetBuildSpec` requires a `scope` argument');
}

// If the same AssetCode is used multiple times, retain only the first instantiation.
if (!this.asset) {
this.asset = new s3_assets.Asset(scope, 'Code', {
path: this.path,
...this.options,
});
} else if (Stack.of(this.asset) !== Stack.of(scope)) {
throw new Error(`Asset is already associated with another stack '${Stack.of(this.asset).stackName}'. ` +
'Create a new BuildSpec instance for every stack.');
}

this.asset.grantRead(scope);
return this.asset.bucket.arnForObjects(this.asset.s3ObjectKey);
}

public toString() {
return `<buildspec file: ${this.path}>`;
}
}

/**
Expand Down Expand Up @@ -208,4 +256,4 @@ function mergeDeep(lhs: any, rhs: any): any {
}

return rhs;
};
};
2 changes: 1 addition & 1 deletion packages/@aws-cdk/aws-codebuild/lib/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1090,7 +1090,7 @@ export class Project extends ProjectBase {
description: props.description,
source: {
...sourceConfig.sourceProperty,
buildSpec: buildSpec && buildSpec.toBuildSpec(),
buildSpec: buildSpec && buildSpec.toBuildSpec(this),
},
artifacts: artifactsConfig.artifactsProperty,
serviceRole: this.role.roleArn,
Expand Down
6 changes: 6 additions & 0 deletions packages/@aws-cdk/aws-codebuild/test/build-spec-asset.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: 0.2

phases:
build:
commands:
- echo running stuff
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"version": "30.1.0",
"files": {
"73c20a669c041469f7fc3fc03d574b093b5b97e7c716f76c1e8117e6163e4dc4": {
"source": {
"path": "asset.73c20a669c041469f7fc3fc03d574b093b5b97e7c716f76c1e8117e6163e4dc4.bundle",
"packaging": "zip"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "73c20a669c041469f7fc3fc03d574b093b5b97e7c716f76c1e8117e6163e4dc4.zip",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
},
"c6d793ffaa5add8fe777815df3019dfa5848c214e42229f568e035859171a1b8": {
"source": {
"path": "AssetBuildSpecTestDefaultTestDeployAssertC826AACC.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "c6d793ffaa5add8fe777815df3019dfa5848c214e42229f568e035859171a1b8.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
}
},
"dockerImages": {}
}
Loading

0 comments on commit 9c49153

Please sign in to comment.