Skip to content

Commit f5f50cb

Browse files
committed
Fix CodeBuild provider spec_inline support
**What was the problem?** 1. The `spec_inline` property was documented and type checked as a string. Whereas the CDK function used to include the `spec_inline` expects an object instead. 2. If CodeBuild was used as a deployment provider, it would not be able to use the `spec_inline`. Nor would you be able to specify `spec_inline` as part of a target definition. Even though the schema validation made it look like it would support `spec_inline`. **What has been changed?** * Updates the schema validation to support objects, as required by the CDK BuildSpec construct. * Changes the CodeBuild provider for deployment and build stages, such that both support inline and file reference usage of BuildSpec definitions. * When both the `spec_inline` and `spec_filename` properties are defined as part of the default provider or target properties, an error message will be thrown. * Tests have been added to verify that the added method works as intended.
1 parent 5d86f33 commit f5f50cb

File tree

4 files changed

+362
-19
lines changed

4 files changed

+362
-19
lines changed

docs/providers-guide.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,12 +197,25 @@ Provider type: `codebuild`.
197197
> If you wish to pass in a custom inline Buildspec as a string for the
198198
> CodeBuild Project this would override any `buildspec.yml` file.
199199
> Read more [here](https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html#build-spec-ref-example).
200+
>
201+
> Note: Either specify the `spec_inline` or the `spec_filename` in the
202+
> properties block. If both are supplied, the pipeline generator will throw
203+
> an error instead.
200204
- *spec_filename* *(String)* default: `buildspec.yml`.
201205
> If you wish to pass in a custom Buildspec file that is within the
202206
> repository. This is useful for custom deploy type actions where CodeBuild
203207
> will perform the execution of the commands. Path is relational to the
204208
> root of the repository, so `build/buidlspec.yml` refers to the
205209
> `buildspec.yml` stored in the `build` directory of the repository.
210+
>
211+
> In case CodeBuild is used as a deployment provider, the default BuildSpec
212+
> file name is `deployspec.yml` instead. In case you would like to test
213+
> a given environment using CodeBuild, you can rename it to `testspec.yml`
214+
> or something similar using this property.
215+
>
216+
> Note: Either specify the `spec_inline` or the `spec_filename` in the
217+
> properties block. If both are supplied, the pipeline generator will throw
218+
> an error instead.
206219

207220
### Jenkins
208221

src/lambda_codebase/initial_commit/bootstrap_repository/adf-build/shared/cdk/cdk_constructs/adf_codebuild.py

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
ADF_DEPLOYMENT_REGION = os.environ["AWS_REGION"]
2020
ADF_DEPLOYMENT_ACCOUNT_ID = os.environ["ACCOUNT_ID"]
2121
DEFAULT_CODEBUILD_IMAGE = "UBUNTU_14_04_PYTHON_3_7_1"
22+
DEFAULT_BUILD_SPEC_FILENAME = 'buildspec.yml'
23+
DEFAULT_DEPLOY_SPEC_FILENAME = 'deployspec.yml'
2224

2325
class CodeBuild(core.Construct):
2426
# pylint: disable=no-value-for-parameter
@@ -45,10 +47,10 @@ def __init__(self, scope: core.Construct, id: str, shared_modules_bucket: str, d
4547
environment_variables=CodeBuild.generate_build_env_variables(_codebuild, shared_modules_bucket, map_params, target),
4648
privileged=target.get('properties', {}).get('privileged', False) or map_params['default_providers']['build'].get('properties', {}).get('privileged', False)
4749
)
48-
_spec_filename = (
49-
target.get('properties', {}).get('spec_filename') or
50-
map_params['default_providers']['deploy'].get('properties', {}).get('spec_filename') or
51-
'deployspec.yml'
50+
build_spec = CodeBuild.determine_build_spec(
51+
id,
52+
map_params['default_providers']['deploy'].get('properties', {}),
53+
target,
5254
)
5355
_codebuild.PipelineProject(
5456
self,
@@ -59,7 +61,7 @@ def __init__(self, scope: core.Construct, id: str, shared_modules_bucket: str, d
5961
project_name="adf-deploy-{0}".format(id),
6062
timeout=core.Duration.minutes(_timeout),
6163
role=_iam.Role.from_role_arn(self, 'build_role', role_arn=_build_role, mutable=False),
62-
build_spec=_codebuild.BuildSpec.from_source_filename(_spec_filename)
64+
build_spec=build_spec,
6365
)
6466
self.deploy = Action(
6567
name="{0}".format(id),
@@ -85,18 +87,10 @@ def __init__(self, scope: core.Construct, id: str, shared_modules_bucket: str, d
8587
)
8688
if map_params['default_providers']['build'].get('properties', {}).get('role'):
8789
ADF_DEFAULT_BUILD_ROLE = 'arn:aws:iam::{0}:role/{1}'.format(ADF_DEPLOYMENT_ACCOUNT_ID, map_params['default_providers']['build'].get('properties', {}).get('role'))
88-
_build_stage_spec = map_params['default_providers']['build'].get('properties', {}).get('spec_filename')
89-
_build_inline_spec = map_params['default_providers']['build'].get(
90-
'properties', {}).get(
91-
'spec_inline', '') or map_params['default_providers']['build'].get(
92-
'properties', {}).get(
93-
'spec_inline', '')
94-
if _build_stage_spec:
95-
_spec = _codebuild.BuildSpec.from_source_filename(_build_stage_spec)
96-
elif _build_inline_spec:
97-
_spec = _codebuild.BuildSpec.from_object(_build_inline_spec)
98-
else:
99-
_spec = None
90+
build_spec = CodeBuild.determine_build_spec(
91+
id,
92+
map_params['default_providers']['build'].get('properties', {})
93+
)
10094
_codebuild.PipelineProject(
10195
self,
10296
'project',
@@ -105,7 +99,7 @@ def __init__(self, scope: core.Construct, id: str, shared_modules_bucket: str, d
10599
description="ADF CodeBuild Project for {0}".format(map_params['name']),
106100
project_name="adf-build-{0}".format(map_params['name']),
107101
timeout=core.Duration.minutes(_timeout),
108-
build_spec=_spec,
102+
build_spec=build_spec,
109103
role=_iam.Role.from_role_arn(self, 'default_build_role', role_arn=_build_role, mutable=False)
110104
)
111105
self.build = _codepipeline.CfnPipeline.StageDeclarationProperty(
@@ -122,6 +116,49 @@ def __init__(self, scope: core.Construct, id: str, shared_modules_bucket: str, d
122116
]
123117
)
124118

119+
@staticmethod
120+
def _determine_stage_build_spec(codebuild_id, props, stage_name, default_filename):
121+
filename = props.get('spec_filename')
122+
spec_inline = props.get('spec_inline', {})
123+
if filename and spec_inline:
124+
raise Exception(
125+
"The spec_filename and spec_inline are both present "
126+
"inside the {0} stage definition of {1}. "
127+
"Whereas only one of these two is allowed.".format(
128+
stage_name,
129+
codebuild_id,
130+
),
131+
)
132+
133+
if spec_inline:
134+
return _codebuild.BuildSpec.from_object(spec_inline)
135+
136+
return _codebuild.BuildSpec.from_source_filename(
137+
filename or default_filename,
138+
)
139+
140+
@staticmethod
141+
def determine_build_spec(codebuild_id, default_props, target=None):
142+
if target:
143+
target_props = target.get('properties', {})
144+
if 'spec_inline' in target_props or 'spec_filename' in target_props:
145+
return CodeBuild._determine_stage_build_spec(
146+
codebuild_id=codebuild_id,
147+
props=target_props,
148+
stage_name='deploy target',
149+
default_filename=DEFAULT_DEPLOY_SPEC_FILENAME,
150+
)
151+
return CodeBuild._determine_stage_build_spec(
152+
codebuild_id=codebuild_id,
153+
props=default_props,
154+
stage_name='default {}'.format('deploy' if target else 'build'),
155+
default_filename=(
156+
DEFAULT_DEPLOY_SPEC_FILENAME
157+
if target
158+
else DEFAULT_BUILD_SPEC_FILENAME
159+
),
160+
)
161+
125162
@staticmethod
126163
def determine_build_image(scope, target, map_params):
127164
specific_image = None

0 commit comments

Comments
 (0)