diff --git a/packages/aws-cdk-lib/aws-codebuild/lib/build-spec.ts b/packages/aws-cdk-lib/aws-codebuild/lib/build-spec.ts index 2b10975d4f9a6..cbefdf08edc53 100644 --- a/packages/aws-cdk-lib/aws-codebuild/lib/build-spec.ts +++ b/packages/aws-cdk-lib/aws-codebuild/lib/build-spec.ts @@ -2,7 +2,7 @@ import { Construct } from 'constructs'; import * as yaml_cfn from './private/yaml-cfn'; import { Project } from './project'; import * as s3_assets from '../../aws-s3-assets'; -import { IResolveContext, Lazy, Stack } from '../../core'; +import { IResolveContext, Lazy, Stack, UnscopedValidationError } from '../../core'; /** * BuildSpec for CodeBuild projects @@ -66,7 +66,7 @@ class AssetBuildSpec extends BuildSpec { public toBuildSpec(scope?: Project): string { if (!scope) { - throw new Error('`AssetBuildSpec` requires a `scope` argument'); + throw new UnscopedValidationError('`AssetBuildSpec` requires a `scope` argument'); } // If the same AssetCode is used multiple times, retain only the first instantiation. @@ -76,7 +76,7 @@ class AssetBuildSpec extends BuildSpec { ...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}'. ` + + throw new UnscopedValidationError(`Asset is already associated with another stack '${Stack.of(this.asset).stackName}'. ` + 'Create a new BuildSpec instance for every stack.'); } @@ -155,18 +155,18 @@ class YamlBuildSpec extends BuildSpec { */ export function mergeBuildSpecs(lhs: BuildSpec, rhs: BuildSpec): BuildSpec { if (!(lhs instanceof ObjectBuildSpec) || !(rhs instanceof ObjectBuildSpec)) { - throw new Error('Can only merge buildspecs created using BuildSpec.fromObject()'); + throw new UnscopedValidationError('Can only merge buildspecs created using BuildSpec.fromObject()'); } if (lhs.spec.version === '0.1') { - throw new Error('Cannot extend buildspec at version "0.1". Set the version to "0.2" or higher instead.'); + throw new UnscopedValidationError('Cannot extend buildspec at version "0.1". Set the version to "0.2" or higher instead.'); } if (lhs.spec.artifacts && rhs.spec.artifacts) { // We decided to disallow merging of artifact specs, which is // actually impossible since we can't merge two buildspecs with a // single primary output into a buildspec with multiple outputs. // In case of multiple outputs they must have identifiers but we won't have that information. - throw new Error('Only one build spec is allowed to specify artifacts.'); + throw new UnscopedValidationError('Only one build spec is allowed to specify artifacts.'); } const lhsSpec = JSON.parse(JSON.stringify(lhs.spec)); diff --git a/packages/aws-cdk-lib/aws-codebuild/lib/fleet.ts b/packages/aws-cdk-lib/aws-codebuild/lib/fleet.ts index 548e12d1543b7..ea3eefad6ba16 100644 --- a/packages/aws-cdk-lib/aws-codebuild/lib/fleet.ts +++ b/packages/aws-cdk-lib/aws-codebuild/lib/fleet.ts @@ -2,7 +2,7 @@ import { Construct } from 'constructs'; import { CfnFleet } from './codebuild.generated'; import { ComputeType } from './compute-type'; import { EnvironmentType } from './environment-type'; -import { Arn, ArnFormat, IResource, Resource, Size, Token } from '../../core'; +import { Arn, ArnFormat, IResource, Resource, Size, Token, UnscopedValidationError, ValidationError } from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; /** @@ -155,13 +155,13 @@ export class Fleet extends Resource implements IFleet { public readonly fleetArn = fleetArn; public get computeType(): FleetComputeType { - throw new Error('Cannot retrieve computeType property from an imported Fleet'); + throw new UnscopedValidationError('Cannot retrieve computeType property from an imported Fleet'); } public get environmentType(): EnvironmentType { - throw new Error('Cannot retrieve environmentType property from an imported Fleet'); + throw new UnscopedValidationError('Cannot retrieve environmentType property from an imported Fleet'); } public get computeConfiguration(): ComputeConfiguration | undefined { - throw new Error('Cannot retrieve computeConfiguration property from an imported Fleet'); + throw new UnscopedValidationError('Cannot retrieve computeConfiguration property from an imported Fleet'); } } @@ -198,25 +198,25 @@ export class Fleet extends Resource implements IFleet { if (props.fleetName && !Token.isUnresolved(props.fleetName)) { if (props.fleetName.length < 2) { - throw new Error(`Fleet name can not be shorter than 2 characters but has ${props.fleetName.length} characters.`); + throw new ValidationError(`Fleet name can not be shorter than 2 characters but has ${props.fleetName.length} characters.`, this); } if (props.fleetName.length > 128) { - throw new Error(`Fleet name can not be longer than 128 characters but has ${props.fleetName.length} characters.`); + throw new ValidationError(`Fleet name can not be longer than 128 characters but has ${props.fleetName.length} characters.`, this); } } if ((props.baseCapacity ?? 1) < 1) { - throw new Error('baseCapacity must be greater than or equal to 1'); + throw new ValidationError('baseCapacity must be greater than or equal to 1', this); } if ( props.computeType === FleetComputeType.ATTRIBUTE_BASED && (!props.computeConfiguration || Object.keys(props.computeConfiguration).length === 0) ) { - throw new Error('At least one compute configuration criteria must be specified if computeType is "ATTRIBUTE_BASED"'); + throw new ValidationError('At least one compute configuration criteria must be specified if computeType is "ATTRIBUTE_BASED"', this); } if (props.computeConfiguration && props.computeType !== FleetComputeType.ATTRIBUTE_BASED) { - throw new Error(`'computeConfiguration' can only be specified if 'computeType' is 'ATTRIBUTE_BASED', got: ${props.computeType}`); + throw new ValidationError(`'computeConfiguration' can only be specified if 'computeType' is 'ATTRIBUTE_BASED', got: ${props.computeType}`, this); } // Despite what the CloudFormation schema says, the numeric properties are not optional. @@ -257,7 +257,7 @@ export class Fleet extends Resource implements IFleet { private validatePositiveInteger(value: number, fieldName: string) { if (!Token.isUnresolved(value) && (value < 0 || !Number.isInteger(value))) { - throw new Error(`${fieldName} must be a positive integer, got: ${value}`); + throw new ValidationError(`${fieldName} must be a positive integer, got: ${value}`, this); } } } diff --git a/packages/aws-cdk-lib/aws-codebuild/lib/linux-gpu-build-image.ts b/packages/aws-cdk-lib/aws-codebuild/lib/linux-gpu-build-image.ts index d49df714d6427..85d450a1b7a62 100644 --- a/packages/aws-cdk-lib/aws-codebuild/lib/linux-gpu-build-image.ts +++ b/packages/aws-cdk-lib/aws-codebuild/lib/linux-gpu-build-image.ts @@ -114,7 +114,7 @@ export class LinuxGpuBuildImage implements IBindableBuildImage { const imageAccount = account ?? core.Lazy.string({ produce: () => { if (this._imageAccount === undefined) { - throw new Error('Make sure this \'LinuxGpuBuildImage\' is used in a CodeBuild Project construct'); + throw new core.UnscopedValidationError('Make sure this \'LinuxGpuBuildImage\' is used in a CodeBuild Project construct'); } return this._imageAccount; }, diff --git a/packages/aws-cdk-lib/aws-codebuild/lib/project.ts b/packages/aws-cdk-lib/aws-codebuild/lib/project.ts index 5eac7941ef1f4..0976ae5bd7de5 100644 --- a/packages/aws-cdk-lib/aws-codebuild/lib/project.ts +++ b/packages/aws-cdk-lib/aws-codebuild/lib/project.ts @@ -28,7 +28,7 @@ import * as iam from '../../aws-iam'; import * as kms from '../../aws-kms'; import * as s3 from '../../aws-s3'; import * as secretsmanager from '../../aws-secretsmanager'; -import { ArnFormat, Aws, Duration, IResource, Lazy, Names, PhysicalName, Reference, Resource, SecretValue, Stack, Token, TokenComparison, Tokenization } from '../../core'; +import { ArnFormat, Aws, Duration, IResource, Lazy, Names, PhysicalName, Reference, Resource, SecretValue, Stack, Token, TokenComparison, Tokenization, UnscopedValidationError, ValidationError } from '../../core'; import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource'; const VPC_POLICY_SYM = Symbol.for('@aws-cdk/aws-codebuild.roleVpcPolicy'); @@ -275,7 +275,7 @@ abstract class ProjectBase extends Resource implements IProject { */ public get connections(): ec2.Connections { if (!this._connections) { - throw new Error('Only VPC-associated Projects have security groups to manage. Supply the "vpc" parameter when creating the Project'); + throw new ValidationError('Only VPC-associated Projects have security groups to manage. Supply the "vpc" parameter when creating the Project', this); } return this._connections; } @@ -887,7 +887,7 @@ export class Project extends ProjectBase { const fragments = Tokenization.reverseString(cfnEnvVariable.value); for (const token of fragments.tokens) { if (token instanceof SecretValue) { - throw new Error(`Plaintext environment variable '${name}' contains a secret value! ` + + throw new UnscopedValidationError(`Plaintext environment variable '${name}' contains a secret value! ` + 'This means the value of this variable will be visible in plain text in the AWS Console. ' + "Please consider using CodeBuild's SecretsManager environment variables feature instead. " + "If you'd like to continue with having this secret in the plaintext environment variables, " + @@ -921,7 +921,7 @@ export class Project extends ProjectBase { if (envVariableValue.startsWith('arn:')) { const parsedArn = stack.splitArn(envVariableValue, ArnFormat.COLON_RESOURCE_NAME); if (!parsedArn.resourceName) { - throw new Error('SecretManager ARN is missing the name of the secret: ' + envVariableValue); + throw new UnscopedValidationError('SecretManager ARN is missing the name of the secret: ' + envVariableValue); } // the value of the property can be a complex string, separated by ':'; @@ -1077,7 +1077,7 @@ export class Project extends ProjectBase { this.source = props.source || new NoSource(); const sourceConfig = this.source.bind(this, this); if (props.badge && !this.source.badgeSupported) { - throw new Error(`Badge is not supported for source type ${this.source.type}`); + throw new ValidationError(`Badge is not supported for source type ${this.source.type}`, this); } const artifacts = props.artifacts @@ -1096,7 +1096,7 @@ export class Project extends ProjectBase { const environmentVariables = props.environmentVariables || {}; const buildSpec = props.buildSpec; if (this.source.type === NO_SOURCE_TYPE && (buildSpec === undefined || !buildSpec.isImmediate)) { - throw new Error("If the Project's source is NoSource, you need to provide a concrete buildSpec"); + throw new ValidationError("If the Project's source is NoSource, you need to provide a concrete buildSpec", this); } this._secondarySources = []; @@ -1119,7 +1119,7 @@ export class Project extends ProjectBase { if (!Token.isUnresolved(props.autoRetryLimit) && (props.autoRetryLimit !== undefined)) { if (props.autoRetryLimit < 0 || props.autoRetryLimit > 10) { - throw new Error(`autoRetryLimit must be a value between 0 and 10, got ${props.autoRetryLimit}.`); + throw new ValidationError(`autoRetryLimit must be a value between 0 and 10, got ${props.autoRetryLimit}.`, this); } } @@ -1252,7 +1252,7 @@ export class Project extends ProjectBase { @MethodMetadata() public addSecondarySource(secondarySource: ISource): void { if (!secondarySource.identifier) { - throw new Error('The identifier attribute is mandatory for secondary sources'); + throw new ValidationError('The identifier attribute is mandatory for secondary sources', this); } const secondarySourceConfig = secondarySource.bind(this, this); this._secondarySources.push(secondarySourceConfig.sourceProperty); @@ -1284,7 +1284,7 @@ export class Project extends ProjectBase { @MethodMetadata() public addSecondaryArtifact(secondaryArtifact: IArtifacts): void { if (!secondaryArtifact.identifier) { - throw new Error('The identifier attribute is mandatory for secondary artifacts'); + throw new ValidationError('The identifier attribute is mandatory for secondary artifacts', this); } this._secondaryArtifacts.push(secondaryArtifact.bind(this, this).artifactsProperty); } @@ -1372,7 +1372,7 @@ export class Project extends ProjectBase { errors.push(...this.validateLambdaBuildImage(this.buildImage, props)); if (errors.length > 0) { - throw new Error('Invalid CodeBuild environment: ' + errors.join('\n')); + throw new ValidationError('Invalid CodeBuild environment: ' + errors.join('\n'), this); } const imagePullPrincipalType = this.isLambdaBuildImage(this.buildImage) ? undefined : @@ -1449,7 +1449,7 @@ export class Project extends ProjectBase { // If the fleetArn is resolved, the fleet is imported and we cannot validate the environment type if (Token.isUnresolved(fleet.fleetArn) && this.buildImage.type !== fleet.environmentType) { - throw new Error(`The environment type of the fleet (${fleet.environmentType}) must match the environment type of the build image (${this.buildImage.type})`); + throw new ValidationError(`The environment type of the fleet (${fleet.environmentType}) must match the environment type of the build image (${this.buildImage.type})`, this); } return { fleetArn: fleet.fleetArn }; @@ -1463,13 +1463,13 @@ export class Project extends ProjectBase { */ private configureVpc(props: ProjectProps): CfnProject.VpcConfigProperty | undefined { if ((props.securityGroups || props.allowAllOutbound !== undefined) && !props.vpc) { - throw new Error('Cannot configure \'securityGroup\' or \'allowAllOutbound\' without configuring a VPC'); + throw new ValidationError('Cannot configure \'securityGroup\' or \'allowAllOutbound\' without configuring a VPC', this); } if (!props.vpc) { return undefined; } if ((props.securityGroups && props.securityGroups.length > 0) && props.allowAllOutbound !== undefined) { - throw new Error('Configure \'allowAllOutbound\' directly on the supplied SecurityGroup.'); + throw new ValidationError('Configure \'allowAllOutbound\' directly on the supplied SecurityGroup.', this); } let securityGroups: ec2.ISecurityGroup[]; @@ -1515,7 +1515,7 @@ export class Project extends ProjectBase { const status = (cloudWatchLogs.enabled ?? true) ? 'ENABLED' : 'DISABLED'; if (status === 'ENABLED' && !(cloudWatchLogs.logGroup)) { - throw new Error('Specifying a LogGroup is required if CloudWatch logging for CodeBuild is enabled'); + throw new ValidationError('Specifying a LogGroup is required if CloudWatch logging for CodeBuild is enabled', this); } cloudWatchLogs.logGroup?.grantWrite(this); @@ -1591,7 +1591,7 @@ export class Project extends ProjectBase { if ((sourceType === CODEPIPELINE_SOURCE_ARTIFACTS_TYPE || artifactsType === CODEPIPELINE_SOURCE_ARTIFACTS_TYPE) && (sourceType !== artifactsType)) { - throw new Error('Both source and artifacts must be set to CodePipeline'); + throw new ValidationError('Both source and artifacts must be set to CodePipeline', this); } } diff --git a/packages/aws-cdk-lib/aws-codebuild/lib/report-group.ts b/packages/aws-cdk-lib/aws-codebuild/lib/report-group.ts index 6efd8692e0c20..87b1089d535be 100644 --- a/packages/aws-cdk-lib/aws-codebuild/lib/report-group.ts +++ b/packages/aws-cdk-lib/aws-codebuild/lib/report-group.ts @@ -190,7 +190,7 @@ export class ReportGroup extends ReportGroupBase { this.exportBucket = props.exportBucket; if (props.deleteReports && props.removalPolicy !== cdk.RemovalPolicy.DESTROY) { - throw new Error('Cannot use \'deleteReports\' property on a report group without setting removal policy to \'DESTROY\'.'); + throw new cdk.ValidationError('Cannot use \'deleteReports\' property on a report group without setting removal policy to \'DESTROY\'.', this); } } } diff --git a/packages/aws-cdk-lib/aws-codebuild/lib/source.ts b/packages/aws-cdk-lib/aws-codebuild/lib/source.ts index 2bd0251234b34..04f8cf30ae989 100644 --- a/packages/aws-cdk-lib/aws-codebuild/lib/source.ts +++ b/packages/aws-cdk-lib/aws-codebuild/lib/source.ts @@ -11,6 +11,7 @@ import { import * as codecommit from '../../aws-codecommit'; import * as iam from '../../aws-iam'; import * as s3 from '../../aws-s3'; +import { UnscopedValidationError } from '../../core'; /** * The type returned from `ISource#bind`. @@ -246,7 +247,7 @@ export class FilterGroup { private constructor(actions: Set, filters: CfnProject.WebhookFilterProperty[]) { if (actions.size === 0) { - throw new Error('A filter group must contain at least one event action'); + throw new UnscopedValidationError('A filter group must contain at least one event action'); } this.actions = actions; this.filters = filters; @@ -491,7 +492,7 @@ export class FilterGroup { private addBaseRefFilter(refName: string, include: boolean) { if (this.actions.has(EventAction.PUSH)) { - throw new Error('A base reference condition cannot be added if a Group contains a PUSH event action'); + throw new UnscopedValidationError('A base reference condition cannot be added if a Group contains a PUSH event action'); } return this.addFilter(WebhookFilterTypes.BASE_REF, refName, include); } @@ -588,11 +589,11 @@ abstract class ThirdPartyGitSource extends GitSource { const webhook = this.webhook ?? (anyFilterGroupsProvided ? true : undefined); if (!webhook && anyFilterGroupsProvided) { - throw new Error('`webhookFilters` cannot be used when `webhook` is `false`'); + throw new UnscopedValidationError('`webhookFilters` cannot be used when `webhook` is `false`'); } if (!webhook && this.webhookTriggersBatchBuild) { - throw new Error('`webhookTriggersBatchBuild` cannot be used when `webhook` is `false`'); + throw new UnscopedValidationError('`webhookTriggersBatchBuild` cannot be used when `webhook` is `false`'); } const superConfig = super.bind(_scope, project); @@ -829,11 +830,11 @@ class GitHubEnterpriseSource extends CommonGithubSource { public bind(_scope: Construct, _project: IProject): SourceConfig { if (this.hasCommitMessageFilterAndPrEvent()) { - throw new Error('COMMIT_MESSAGE filters cannot be used with GitHub Enterprise Server pull request events'); + throw new UnscopedValidationError('COMMIT_MESSAGE filters cannot be used with GitHub Enterprise Server pull request events'); } if (this.hasFilePathFilterAndPrEvent()) { - throw new Error('FILE_PATH filters cannot be used with GitHub Enterprise Server pull request events'); + throw new UnscopedValidationError('FILE_PATH filters cannot be used with GitHub Enterprise Server pull request events'); } const superConfig = super.bind(_scope, _project); @@ -915,7 +916,7 @@ class BitBucketSource extends ThirdPartyGitSource { public bind(_scope: Construct, _project: IProject): SourceConfig { // BitBucket sources don't support the PULL_REQUEST_REOPENED event action if (this.anyWebhookFilterContainsPrReopenedEventAction()) { - throw new Error('BitBucket sources do not support the PULL_REQUEST_REOPENED webhook event action'); + throw new UnscopedValidationError('BitBucket sources do not support the PULL_REQUEST_REOPENED webhook event action'); } const superConfig = super.bind(_scope, _project);