From 6065025b1640fa989e2cdaa69567f15323b7a0ff Mon Sep 17 00:00:00 2001 From: Masashi Tomooka Date: Wed, 1 May 2024 11:31:57 +0900 Subject: [PATCH] feat: allow to download .env file from S3 to a local dev environment (#17) * feat: allow to download .env file with build environment variables from S3 to a local dev environment * integ test * readme --- API.md | 16 +++++++ README.md | 2 + example/index.ts | 18 +++++--- lambda/trigger-codebuild/index.ts | 15 +++++- src/nodejs-build.ts | 35 +++++++++++++- src/types.ts | 1 + ...ContainerImageBuildIntegTest.template.json | 18 ++++---- .../index.js | 15 +++++- .../manifest.json | 24 +++++----- .../tree.json | 18 ++++---- test/integ.nodejs-build.ts | 1 + .../NodejsBuildIntegTest.template.json | 46 ++++++++++++++----- .../index.js | 15 +++++- .../nodejs-build.integ.snapshot/manifest.json | 30 +++++++----- test/nodejs-build.integ.snapshot/tree.json | 28 +++++++---- .../SociIndexBuildIntegTest.template.json | 18 ++++---- .../index.js | 15 +++++- .../manifest.json | 24 +++++----- .../soci-index-build.integ.snapshot/tree.json | 18 ++++---- 19 files changed, 251 insertions(+), 106 deletions(-) rename test/container-image-build.integ.snapshot/{asset.95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49 => asset.aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d}/index.js (91%) rename test/nodejs-build.integ.snapshot/{asset.95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49 => asset.aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d}/index.js (91%) rename test/soci-index-build.integ.snapshot/{asset.95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49 => asset.aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d}/index.js (91%) diff --git a/API.md b/API.md index 057e242..b9e7223 100644 --- a/API.md +++ b/API.md @@ -78,6 +78,8 @@ Then, the extracted directories will be located as the following: You can also override the path where assets are extracted by `extractPath` property for each asset. +With `outputEnvFile` property enabled, a `.env` file is automatically generated and uploaded to your S3 bucket. This file can be used running you frontend project locally. You can download the file to your local machine by running the command added in the stack output. + Please also check [the example directory](./example/) for a complete example. #### Allowing access from the build environment to other AWS resources @@ -1026,6 +1028,7 @@ const nodejsBuildProps: NodejsBuildProps = { ... } | destinationKeyPrefix | string | Key prefix to deploy your build artifact. | | distribution | aws-cdk-lib.aws_cloudfront.IDistribution | The distribution you are using to publish you build artifact. | | nodejsVersion | number | The version of Node.js to use in a build environment. Available versions: 12, 14, 16, 18, 20. | +| outputEnvFile | boolean | If true, a .env file is uploaded to an S3 bucket with values of `buildEnvironment` property. You can copy it to your local machine by running the command in the stack output. | | workingDirectory | string | Relative path from the build directory to the directory where build commands run. | --- @@ -1137,6 +1140,19 @@ The version of Node.js to use in a build environment. Available versions: 12, 14 --- +##### `outputEnvFile`Optional + +```typescript +public readonly outputEnvFile: boolean; +``` + +- *Type:* boolean +- *Default:* false + +If true, a .env file is uploaded to an S3 bucket with values of `buildEnvironment` property. You can copy it to your local machine by running the command in the stack output. + +--- + ##### `workingDirectory`Optional ```typescript diff --git a/README.md b/README.md index 5f7445b..a31500f 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,8 @@ Then, the extracted directories will be located as the following: You can also override the path where assets are extracted by `extractPath` property for each asset. +With `outputEnvFile` property enabled, a `.env` file is automatically generated and uploaded to your S3 bucket. This file can be used running you frontend project locally. You can download the file to your local machine by running the command added in the stack output. + Please also check [the example directory](./example/) for a complete example. #### Allowing access from the build environment to other AWS resources diff --git a/example/index.ts b/example/index.ts index bdb509f..04c881c 100644 --- a/example/index.ts +++ b/example/index.ts @@ -1,13 +1,13 @@ -import { Stack, StackProps, App, RemovalPolicy, CfnOutput } from 'aws-cdk-lib'; +import { Stack, StackProps, App, CfnOutput } from 'aws-cdk-lib'; import { Construct } from 'constructs'; import { MockIntegration, RestApi } from 'aws-cdk-lib/aws-apigateway'; import { ContainerImageBuild, NodejsBuild, SociIndexBuild } from '../src/'; import { BlockPublicAccess, Bucket, BucketEncryption } from 'aws-cdk-lib/aws-s3'; -import { OriginAccessIdentity, CloudFrontWebDistribution } from 'aws-cdk-lib/aws-cloudfront'; import { DockerImageAsset, Platform } from 'aws-cdk-lib/aws-ecr-assets'; import { DockerImageFunction } from 'aws-cdk-lib/aws-lambda'; import { AwsLogDriver, Cluster, CpuArchitecture, FargateTaskDefinition } from 'aws-cdk-lib/aws-ecs'; import { SubnetType, Vpc } from 'aws-cdk-lib/aws-ec2'; +import { OriginAccessIdentity, CloudFrontWebDistribution } from 'aws-cdk-lib/aws-cloudfront'; class NodejsTestStack extends Stack { constructor(scope: Construct, id: string, props: StackProps = {}) { @@ -28,8 +28,8 @@ class NodejsTestStack extends Stack { ); const dstBucket = new Bucket(this, 'DstBucket', { - autoDeleteObjects: true, - removalPolicy: RemovalPolicy.DESTROY, + // autoDeleteObjects: true, + // removalPolicy: RemovalPolicy.DESTROY, blockPublicAccess: BlockPublicAccess.BLOCK_ALL, encryption: BucketEncryption.S3_MANAGED, }); @@ -54,6 +54,10 @@ class NodejsTestStack extends Stack { ], }); + new CfnOutput(this, 'DistributionUrl', { + value: `https://${distribution.distributionDomainName}`, + }); + new NodejsBuild(this, 'ExampleBuild', { assets: [ { @@ -68,13 +72,13 @@ class NodejsTestStack extends Stack { buildCommands: ['npm ci', 'npm run build'], buildEnvironment: { VITE_API_ENDPOINT: api.url, + AAA: 'asdf', + BBB: dstBucket.bucketName, }, nodejsVersion: 20, + outputEnvFile: true, }); - new CfnOutput(this, 'DistributionUrl', { - value: `https://${distribution.distributionDomainName}`, - }); } } diff --git a/lambda/trigger-codebuild/index.ts b/lambda/trigger-codebuild/index.ts index 7e71665..4d7512f 100644 --- a/lambda/trigger-codebuild/index.ts +++ b/lambda/trigger-codebuild/index.ts @@ -38,6 +38,7 @@ export const handler = async (event: Event, context: any) => { value: event.LogicalResourceId, }, ]; + const newPhysicalId = Crypto.randomBytes(16).toString('hex'); let command: StartBuildCommand; switch (props.type) { @@ -67,7 +68,7 @@ export const handler = async (event: Event, context: any) => { { name: 'destinationObjectKey', // This should be random to always trigger a BucketDeployment update process - value: `${Crypto.randomBytes(16).toString('hex')}.zip`, + value: `${newPhysicalId}.zip`, }, { name: 'workingDirectory', @@ -81,6 +82,18 @@ export const handler = async (event: Event, context: any) => { name: 'projectName', value: props.codeBuildProjectName, }, + { + name: 'outputEnvFile', + value: props.outputEnvFile.toString(), + }, + { + name: 'envFileKey', + value: `deploy-time-build/${event.StackId.split('/')[1]}/${event.LogicalResourceId}/${newPhysicalId}.env`, + }, + { + name: 'envNames', + value: Object.keys(props.environment ?? {}).join(','), + }, ...Object.entries(props.environment ?? {}).map(([name, value]) => ({ name, value, diff --git a/src/nodejs-build.ts b/src/nodejs-build.ts index 62dca74..a3b889c 100644 --- a/src/nodejs-build.ts +++ b/src/nodejs-build.ts @@ -1,5 +1,5 @@ import { posix, join, basename } from 'path'; -import { Annotations, CustomResource, Duration } from 'aws-cdk-lib'; +import { Annotations, CfnOutput, CustomResource, Duration } from 'aws-cdk-lib'; import { IDistribution } from 'aws-cdk-lib/aws-cloudfront'; import { BuildSpec, LinuxBuildImage, Project } from 'aws-cdk-lib/aws-codebuild'; import { IGrantable, IPrincipal, PolicyStatement } from 'aws-cdk-lib/aws-iam'; @@ -69,6 +69,12 @@ export interface NodejsBuildProps { * @default 18 */ readonly nodejsVersion?: number; + /** + * If true, a .env file is uploaded to an S3 bucket with values of `buildEnvironment` property. + * You can copy it to your local machine by running the command in the stack output. + * @default false + */ + readonly outputEnvFile?: boolean; } /** @@ -110,11 +116,15 @@ export class NodejsBuild extends Construct implements IGrantable { } const destinationObjectKeyOutputKey = 'destinationObjectKey'; + const envFileKeyOutputKey = 'envFileKey'; const project = new Project(this, 'Project', { environment: { buildImage: LinuxBuildImage.fromCodeBuildImageId(buildImage) }, buildSpec: BuildSpec.fromObject({ version: '0.2', + env: { + shell: 'bash', + }, phases: { install: { 'runtime-versions': { @@ -160,6 +170,21 @@ done 'cd "$outputSourceDirectory"', 'zip -r output.zip ./', 'aws s3 cp output.zip "s3://$destinationBucketName/$destinationObjectKey"', + // Upload .env if required + ` +if [[ $outputEnvFile == "true" ]] +then + # Split the comma-separated string into an array + for var_name in \${envNames//,/ } + do + echo "Element: $var_name" + var_value="\${!var_name}" + echo "$var_name=$var_value" >> tmp.env + done + + aws s3 cp tmp.env "s3://$destinationBucketName/$envFileKey" +fi + `, ], }, post_build: { @@ -182,7 +207,8 @@ cat < payload.json "Status": "$STATUS", "Reason": "$REASON", "Data": { - "${destinationObjectKeyOutputKey}": "$destinationObjectKey" + "${destinationObjectKeyOutputKey}": "$destinationObjectKey", + "${envFileKeyOutputKey}": "$envFileKey" } } EOF @@ -232,6 +258,7 @@ curl -v -i -X PUT -H 'Content-Type:' -d "@payload.json" "$responseURL" environment: props.buildEnvironment, buildCommands: props.buildCommands ?? ['npm run build'], codeBuildProjectName: project.projectName, + outputEnvFile: props.outputEnvFile ?? false, }; const custom = new CustomResource(this, 'Resource', { @@ -248,5 +275,9 @@ curl -v -i -X PUT -H 'Content-Type:' -d "@payload.json" "$responseURL" }); deploy.node.addDependency(custom); + + if (props.outputEnvFile) { + new CfnOutput(this, 'DownloadEnvFile', { value: `aws s3 cp ${bucket.s3UrlForObject(custom.getAttString(envFileKeyOutputKey))} .env.local` }); + } } } diff --git a/src/types.ts b/src/types.ts index 9e9b007..e23f8c6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -14,6 +14,7 @@ export type NodejsBuildResourceProps = { outputSourceDirectory: string; buildCommands: string[]; codeBuildProjectName: string; + outputEnvFile: boolean; }; export type SociIndexBuildResourceProps = { diff --git a/test/container-image-build.integ.snapshot/ContainerImageBuildIntegTest.template.json b/test/container-image-build.integ.snapshot/ContainerImageBuildIntegTest.template.json index 5a61885..954aa5a 100644 --- a/test/container-image-build.integ.snapshot/ContainerImageBuildIntegTest.template.json +++ b/test/container-image-build.integ.snapshot/ContainerImageBuildIntegTest.template.json @@ -240,7 +240,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3Bucket73B303AC" + "Ref": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3BucketA057BD7E" }, "S3Key": { "Fn::Join": [ @@ -253,7 +253,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3VersionKey39332242" + "Ref": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3VersionKey4979DC57" } ] } @@ -266,7 +266,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3VersionKey39332242" + "Ref": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3VersionKey4979DC57" } ] } @@ -1081,17 +1081,17 @@ } }, "Parameters": { - "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3Bucket73B303AC": { + "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3BucketA057BD7E": { "Type": "String", - "Description": "S3 bucket for asset \"95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49\"" + "Description": "S3 bucket for asset \"aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d\"" }, - "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3VersionKey39332242": { + "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3VersionKey4979DC57": { "Type": "String", - "Description": "S3 key for asset version \"95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49\"" + "Description": "S3 key for asset version \"aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d\"" }, - "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49ArtifactHash0DB02EFB": { + "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dArtifactHashC99F560F": { "Type": "String", - "Description": "Artifact hash for asset \"95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49\"" + "Description": "Artifact hash for asset \"aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d\"" }, "AssetParametersf7fa10e7cd7b9b27f49a4a335f4aa9795fb7e68a665c34ca8bf5711f0aa6aeabS3Bucket1FF22598": { "Type": "String", diff --git a/test/container-image-build.integ.snapshot/asset.95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/index.js b/test/container-image-build.integ.snapshot/asset.aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/index.js similarity index 91% rename from test/container-image-build.integ.snapshot/asset.95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/index.js rename to test/container-image-build.integ.snapshot/asset.aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/index.js index e5af2e2..4552f64 100644 --- a/test/container-image-build.integ.snapshot/asset.95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/index.js +++ b/test/container-image-build.integ.snapshot/asset.aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/index.js @@ -51,6 +51,7 @@ var handler = async (event, context) => { value: event.LogicalResourceId } ]; + const newPhysicalId = import_crypto.default.randomBytes(16).toString("hex"); let command; switch (props.type) { case "NodejsBuild": @@ -76,7 +77,7 @@ var handler = async (event, context) => { }, { name: "destinationObjectKey", - value: `${import_crypto.default.randomBytes(16).toString("hex")}.zip` + value: `${newPhysicalId}.zip` }, { name: "workingDirectory", @@ -90,6 +91,18 @@ var handler = async (event, context) => { name: "projectName", value: props.codeBuildProjectName }, + { + name: "outputEnvFile", + value: props.outputEnvFile.toString() + }, + { + name: "envFileKey", + value: `deploy-time-build/${event.StackId.split("/")[1]}/${event.LogicalResourceId}/${newPhysicalId}.env` + }, + { + name: "envNames", + value: Object.keys(props.environment ?? {}).join(",") + }, ...Object.entries(props.environment ?? {}).map(([name, value]) => ({ name, value diff --git a/test/container-image-build.integ.snapshot/manifest.json b/test/container-image-build.integ.snapshot/manifest.json index 93b0127..1d58f0c 100644 --- a/test/container-image-build.integ.snapshot/manifest.json +++ b/test/container-image-build.integ.snapshot/manifest.json @@ -19,13 +19,13 @@ { "type": "aws:cdk:asset", "data": { - "path": "asset.95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49", - "id": "95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49", + "path": "asset.aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d", + "id": "aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d", "packaging": "zip", - "sourceHash": "95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49", - "s3BucketParameter": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3Bucket73B303AC", - "s3KeyParameter": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3VersionKey39332242", - "artifactHashParameter": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49ArtifactHash0DB02EFB" + "sourceHash": "aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d", + "s3BucketParameter": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3BucketA057BD7E", + "s3KeyParameter": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3VersionKey4979DC57", + "artifactHashParameter": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dArtifactHashC99F560F" } }, { @@ -71,22 +71,22 @@ "data": "DeployTimeBuildCustomResourceHandlerdb740fd554364a848a09e6dfcd01f4f306AEFF37" } ], - "/ContainerImageBuildIntegTest/AssetParameters/95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/S3Bucket": [ + "/ContainerImageBuildIntegTest/AssetParameters/aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/S3Bucket": [ { "type": "aws:cdk:logicalId", - "data": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3Bucket73B303AC" + "data": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3BucketA057BD7E" } ], - "/ContainerImageBuildIntegTest/AssetParameters/95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/S3VersionKey": [ + "/ContainerImageBuildIntegTest/AssetParameters/aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/S3VersionKey": [ { "type": "aws:cdk:logicalId", - "data": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3VersionKey39332242" + "data": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3VersionKey4979DC57" } ], - "/ContainerImageBuildIntegTest/AssetParameters/95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/ArtifactHash": [ + "/ContainerImageBuildIntegTest/AssetParameters/aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/ArtifactHash": [ { "type": "aws:cdk:logicalId", - "data": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49ArtifactHash0DB02EFB" + "data": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dArtifactHashC99F560F" } ], "/ContainerImageBuildIntegTest/AssetParameters/f7fa10e7cd7b9b27f49a4a335f4aa9795fb7e68a665c34ca8bf5711f0aa6aeab/S3Bucket": [ diff --git a/test/container-image-build.integ.snapshot/tree.json b/test/container-image-build.integ.snapshot/tree.json index 9651fc6..c2f68cf 100644 --- a/test/container-image-build.integ.snapshot/tree.json +++ b/test/container-image-build.integ.snapshot/tree.json @@ -256,7 +256,7 @@ "aws:cdk:cloudformation:props": { "code": { "s3Bucket": { - "Ref": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3Bucket73B303AC" + "Ref": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3BucketA057BD7E" }, "s3Key": { "Fn::Join": [ @@ -269,7 +269,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3VersionKey39332242" + "Ref": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3VersionKey4979DC57" } ] } @@ -282,7 +282,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3VersionKey39332242" + "Ref": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3VersionKey4979DC57" } ] } @@ -318,13 +318,13 @@ "id": "AssetParameters", "path": "ContainerImageBuildIntegTest/AssetParameters", "children": { - "95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49": { - "id": "95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49", - "path": "ContainerImageBuildIntegTest/AssetParameters/95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49", + "aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d": { + "id": "aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d", + "path": "ContainerImageBuildIntegTest/AssetParameters/aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d", "children": { "S3Bucket": { "id": "S3Bucket", - "path": "ContainerImageBuildIntegTest/AssetParameters/95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/S3Bucket", + "path": "ContainerImageBuildIntegTest/AssetParameters/aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/S3Bucket", "constructInfo": { "fqn": "aws-cdk-lib.CfnParameter", "version": "2.38.0" @@ -332,7 +332,7 @@ }, "S3VersionKey": { "id": "S3VersionKey", - "path": "ContainerImageBuildIntegTest/AssetParameters/95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/S3VersionKey", + "path": "ContainerImageBuildIntegTest/AssetParameters/aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/S3VersionKey", "constructInfo": { "fqn": "aws-cdk-lib.CfnParameter", "version": "2.38.0" @@ -340,7 +340,7 @@ }, "ArtifactHash": { "id": "ArtifactHash", - "path": "ContainerImageBuildIntegTest/AssetParameters/95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/ArtifactHash", + "path": "ContainerImageBuildIntegTest/AssetParameters/aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/ArtifactHash", "constructInfo": { "fqn": "aws-cdk-lib.CfnParameter", "version": "2.38.0" diff --git a/test/integ.nodejs-build.ts b/test/integ.nodejs-build.ts index 8f539d1..172a8de 100644 --- a/test/integ.nodejs-build.ts +++ b/test/integ.nodejs-build.ts @@ -31,6 +31,7 @@ class TestStack extends Stack { VITE_SOME_TOKEN: dstBucket.bucketName, }, nodejsVersion: 20, + outputEnvFile: true, }); } } diff --git a/test/nodejs-build.integ.snapshot/NodejsBuildIntegTest.template.json b/test/nodejs-build.integ.snapshot/NodejsBuildIntegTest.template.json index ec88b1f..513a642 100644 --- a/test/nodejs-build.integ.snapshot/NodejsBuildIntegTest.template.json +++ b/test/nodejs-build.integ.snapshot/NodejsBuildIntegTest.template.json @@ -220,7 +220,7 @@ ] }, "Source": { - "BuildSpec": "{\n \"version\": \"0.2\",\n \"phases\": {\n \"install\": {\n \"runtime-versions\": {\n \"nodejs\": 20\n }\n },\n \"build\": {\n \"commands\": [\n \"current_dir=$(pwd)\",\n \"\\necho \\\"$input\\\"\\nfor obj in $(echo \\\"$input\\\" | jq -r '.[] | @base64'); do\\n decoded=$(echo \\\"$obj\\\" | base64 --decode)\\n assetUrl=$(echo \\\"$decoded\\\" | jq -r '.assetUrl')\\n extractPath=$(echo \\\"$decoded\\\" | jq -r '.extractPath')\\n commands=$(echo \\\"$decoded\\\" | jq -r '.commands')\\n\\n # Download the zip file\\n aws s3 cp \\\"$assetUrl\\\" temp.zip\\n\\n # Extract the zip file to the extractPath directory\\n mkdir -p \\\"$extractPath\\\"\\n unzip temp.zip -d \\\"$extractPath\\\"\\n\\n # Remove the zip file\\n rm temp.zip\\n\\n # Run the specified commands in the extractPath directory\\n cd \\\"$extractPath\\\"\\n ls -la\\n eval \\\"$commands\\\"\\n cd \\\"$current_dir\\\"\\n ls -la\\ndone\\n \",\n \"ls -la\",\n \"cd \\\"$workingDirectory\\\"\",\n \"eval \\\"$buildCommands\\\"\",\n \"ls -la\",\n \"cd \\\"$current_dir\\\"\",\n \"cd \\\"$outputSourceDirectory\\\"\",\n \"zip -r output.zip ./\",\n \"aws s3 cp output.zip \\\"s3://$destinationBucketName/$destinationObjectKey\\\"\"\n ]\n },\n \"post_build\": {\n \"commands\": [\n \"echo Build completed on `date`\",\n \"\\nSTATUS='SUCCESS'\\nif [ $CODEBUILD_BUILD_SUCCEEDING -ne 1 ] # Test if the build is failing\\nthen\\nSTATUS='FAILED'\\nREASON=\\\"NodejsBuild failed. See CloudWatch Log stream for the detailed reason: \\nhttps://$AWS_REGION.console.aws.amazon.com/cloudwatch/home?region=$AWS_REGION#logsV2:log-groups/log-group/\\\\$252Faws\\\\$252Fcodebuild\\\\$252F$projectName/log-events/$CODEBUILD_LOG_PATH\\\"\\nfi\\ncat < payload.json\\n{\\n \\\"StackId\\\": \\\"$stackId\\\",\\n \\\"RequestId\\\": \\\"$requestId\\\",\\n \\\"LogicalResourceId\\\":\\\"$logicalResourceId\\\",\\n \\\"PhysicalResourceId\\\": \\\"$logicalResourceId\\\",\\n \\\"Status\\\": \\\"$STATUS\\\",\\n \\\"Reason\\\": \\\"$REASON\\\",\\n \\\"Data\\\": {\\n \\\"destinationObjectKey\\\": \\\"$destinationObjectKey\\\"\\n }\\n}\\nEOF\\ncurl -v -i -X PUT -H 'Content-Type:' -d \\\"@payload.json\\\" \\\"$responseURL\\\"\\n \"\n ]\n }\n }\n}", + "BuildSpec": "{\n \"version\": \"0.2\",\n \"env\": {\n \"shell\": \"bash\"\n },\n \"phases\": {\n \"install\": {\n \"runtime-versions\": {\n \"nodejs\": 20\n }\n },\n \"build\": {\n \"commands\": [\n \"current_dir=$(pwd)\",\n \"\\necho \\\"$input\\\"\\nfor obj in $(echo \\\"$input\\\" | jq -r '.[] | @base64'); do\\n decoded=$(echo \\\"$obj\\\" | base64 --decode)\\n assetUrl=$(echo \\\"$decoded\\\" | jq -r '.assetUrl')\\n extractPath=$(echo \\\"$decoded\\\" | jq -r '.extractPath')\\n commands=$(echo \\\"$decoded\\\" | jq -r '.commands')\\n\\n # Download the zip file\\n aws s3 cp \\\"$assetUrl\\\" temp.zip\\n\\n # Extract the zip file to the extractPath directory\\n mkdir -p \\\"$extractPath\\\"\\n unzip temp.zip -d \\\"$extractPath\\\"\\n\\n # Remove the zip file\\n rm temp.zip\\n\\n # Run the specified commands in the extractPath directory\\n cd \\\"$extractPath\\\"\\n ls -la\\n eval \\\"$commands\\\"\\n cd \\\"$current_dir\\\"\\n ls -la\\ndone\\n \",\n \"ls -la\",\n \"cd \\\"$workingDirectory\\\"\",\n \"eval \\\"$buildCommands\\\"\",\n \"ls -la\",\n \"cd \\\"$current_dir\\\"\",\n \"cd \\\"$outputSourceDirectory\\\"\",\n \"zip -r output.zip ./\",\n \"aws s3 cp output.zip \\\"s3://$destinationBucketName/$destinationObjectKey\\\"\",\n \"\\nif [[ $outputEnvFile == \\\"true\\\" ]]\\nthen\\n # Split the comma-separated string into an array\\n for var_name in ${envNames//,/ }\\n do\\n echo \\\"Element: $var_name\\\"\\n var_value=\\\"${!var_name}\\\"\\n echo \\\"$var_name=$var_value\\\" >> tmp.env\\n done\\n\\n aws s3 cp tmp.env \\\"s3://$destinationBucketName/$envFileKey\\\"\\nfi\\n \"\n ]\n },\n \"post_build\": {\n \"commands\": [\n \"echo Build completed on `date`\",\n \"\\nSTATUS='SUCCESS'\\nif [ $CODEBUILD_BUILD_SUCCEEDING -ne 1 ] # Test if the build is failing\\nthen\\nSTATUS='FAILED'\\nREASON=\\\"NodejsBuild failed. See CloudWatch Log stream for the detailed reason: \\nhttps://$AWS_REGION.console.aws.amazon.com/cloudwatch/home?region=$AWS_REGION#logsV2:log-groups/log-group/\\\\$252Faws\\\\$252Fcodebuild\\\\$252F$projectName/log-events/$CODEBUILD_LOG_PATH\\\"\\nfi\\ncat < payload.json\\n{\\n \\\"StackId\\\": \\\"$stackId\\\",\\n \\\"RequestId\\\": \\\"$requestId\\\",\\n \\\"LogicalResourceId\\\":\\\"$logicalResourceId\\\",\\n \\\"PhysicalResourceId\\\": \\\"$logicalResourceId\\\",\\n \\\"Status\\\": \\\"$STATUS\\\",\\n \\\"Reason\\\": \\\"$REASON\\\",\\n \\\"Data\\\": {\\n \\\"destinationObjectKey\\\": \\\"$destinationObjectKey\\\",\\n \\\"envFileKey\\\": \\\"$envFileKey\\\"\\n }\\n}\\nEOF\\ncurl -v -i -X PUT -H 'Content-Type:' -d \\\"@payload.json\\\" \\\"$responseURL\\\"\\n \"\n ]\n }\n }\n}", "Type": "NO_SOURCE" }, "Cache": { @@ -296,7 +296,8 @@ ], "codeBuildProjectName": { "Ref": "ExampleBuildProjectEF5CAC49" - } + }, + "outputEnvFile": true }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -444,7 +445,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3Bucket73B303AC" + "Ref": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3BucketA057BD7E" }, "S3Key": { "Fn::Join": [ @@ -457,7 +458,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3VersionKey39332242" + "Ref": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3VersionKey4979DC57" } ] } @@ -470,7 +471,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3VersionKey39332242" + "Ref": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3VersionKey4979DC57" } ] } @@ -682,18 +683,41 @@ ] } }, + "Outputs": { + "ExampleBuildDownloadEnvFileC240151B": { + "Value": { + "Fn::Join": [ + "", + [ + "aws s3 cp s3://", + { + "Ref": "AssetParameters72f05c779a8bc73c6ec86f6eafc720508792e7526696d3ae45a7fddcfc473c9dS3Bucket7BF37945" + }, + "/", + { + "Fn::GetAtt": [ + "ExampleBuild61F1D79B", + "envFileKey" + ] + }, + " .env.local" + ] + ] + } + } + }, "Parameters": { - "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3Bucket73B303AC": { + "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3BucketA057BD7E": { "Type": "String", - "Description": "S3 bucket for asset \"95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49\"" + "Description": "S3 bucket for asset \"aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d\"" }, - "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3VersionKey39332242": { + "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3VersionKey4979DC57": { "Type": "String", - "Description": "S3 key for asset version \"95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49\"" + "Description": "S3 key for asset version \"aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d\"" }, - "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49ArtifactHash0DB02EFB": { + "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dArtifactHashC99F560F": { "Type": "String", - "Description": "Artifact hash for asset \"95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49\"" + "Description": "Artifact hash for asset \"aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d\"" }, "AssetParameters72f05c779a8bc73c6ec86f6eafc720508792e7526696d3ae45a7fddcfc473c9dS3Bucket7BF37945": { "Type": "String", diff --git a/test/nodejs-build.integ.snapshot/asset.95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/index.js b/test/nodejs-build.integ.snapshot/asset.aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/index.js similarity index 91% rename from test/nodejs-build.integ.snapshot/asset.95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/index.js rename to test/nodejs-build.integ.snapshot/asset.aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/index.js index e5af2e2..4552f64 100644 --- a/test/nodejs-build.integ.snapshot/asset.95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/index.js +++ b/test/nodejs-build.integ.snapshot/asset.aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/index.js @@ -51,6 +51,7 @@ var handler = async (event, context) => { value: event.LogicalResourceId } ]; + const newPhysicalId = import_crypto.default.randomBytes(16).toString("hex"); let command; switch (props.type) { case "NodejsBuild": @@ -76,7 +77,7 @@ var handler = async (event, context) => { }, { name: "destinationObjectKey", - value: `${import_crypto.default.randomBytes(16).toString("hex")}.zip` + value: `${newPhysicalId}.zip` }, { name: "workingDirectory", @@ -90,6 +91,18 @@ var handler = async (event, context) => { name: "projectName", value: props.codeBuildProjectName }, + { + name: "outputEnvFile", + value: props.outputEnvFile.toString() + }, + { + name: "envFileKey", + value: `deploy-time-build/${event.StackId.split("/")[1]}/${event.LogicalResourceId}/${newPhysicalId}.env` + }, + { + name: "envNames", + value: Object.keys(props.environment ?? {}).join(",") + }, ...Object.entries(props.environment ?? {}).map(([name, value]) => ({ name, value diff --git a/test/nodejs-build.integ.snapshot/manifest.json b/test/nodejs-build.integ.snapshot/manifest.json index 11d2ec0..cafcafc 100644 --- a/test/nodejs-build.integ.snapshot/manifest.json +++ b/test/nodejs-build.integ.snapshot/manifest.json @@ -19,13 +19,13 @@ { "type": "aws:cdk:asset", "data": { - "path": "asset.95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49", - "id": "95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49", + "path": "asset.aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d", + "id": "aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d", "packaging": "zip", - "sourceHash": "95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49", - "s3BucketParameter": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3Bucket73B303AC", - "s3KeyParameter": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3VersionKey39332242", - "artifactHashParameter": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49ArtifactHash0DB02EFB" + "sourceHash": "aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d", + "s3BucketParameter": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3BucketA057BD7E", + "s3KeyParameter": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3VersionKey4979DC57", + "artifactHashParameter": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dArtifactHashC99F560F" } }, { @@ -107,6 +107,12 @@ "data": "ExampleBuildDeployCustomResourceF69A1525" } ], + "/NodejsBuildIntegTest/ExampleBuild/DownloadEnvFile": [ + { + "type": "aws:cdk:logicalId", + "data": "ExampleBuildDownloadEnvFileC240151B" + } + ], "/NodejsBuildIntegTest/NodejsBuildCustomResourceHandler25648b212c404f09aa65b6bbb0c44659/ServiceRole/Resource": [ { "type": "aws:cdk:logicalId", @@ -125,22 +131,22 @@ "data": "NodejsBuildCustomResourceHandler25648b212c404f09aa65b6bbb0c446591C4101F8" } ], - "/NodejsBuildIntegTest/AssetParameters/95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/S3Bucket": [ + "/NodejsBuildIntegTest/AssetParameters/aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/S3Bucket": [ { "type": "aws:cdk:logicalId", - "data": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3Bucket73B303AC" + "data": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3BucketA057BD7E" } ], - "/NodejsBuildIntegTest/AssetParameters/95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/S3VersionKey": [ + "/NodejsBuildIntegTest/AssetParameters/aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/S3VersionKey": [ { "type": "aws:cdk:logicalId", - "data": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3VersionKey39332242" + "data": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3VersionKey4979DC57" } ], - "/NodejsBuildIntegTest/AssetParameters/95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/ArtifactHash": [ + "/NodejsBuildIntegTest/AssetParameters/aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/ArtifactHash": [ { "type": "aws:cdk:logicalId", - "data": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49ArtifactHash0DB02EFB" + "data": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dArtifactHashC99F560F" } ], "/NodejsBuildIntegTest/AssetParameters/72f05c779a8bc73c6ec86f6eafc720508792e7526696d3ae45a7fddcfc473c9d/S3Bucket": [ diff --git a/test/nodejs-build.integ.snapshot/tree.json b/test/nodejs-build.integ.snapshot/tree.json index 5cb795e..b219109 100644 --- a/test/nodejs-build.integ.snapshot/tree.json +++ b/test/nodejs-build.integ.snapshot/tree.json @@ -308,7 +308,7 @@ }, "source": { "type": "NO_SOURCE", - "buildSpec": "{\n \"version\": \"0.2\",\n \"phases\": {\n \"install\": {\n \"runtime-versions\": {\n \"nodejs\": 20\n }\n },\n \"build\": {\n \"commands\": [\n \"current_dir=$(pwd)\",\n \"\\necho \\\"$input\\\"\\nfor obj in $(echo \\\"$input\\\" | jq -r '.[] | @base64'); do\\n decoded=$(echo \\\"$obj\\\" | base64 --decode)\\n assetUrl=$(echo \\\"$decoded\\\" | jq -r '.assetUrl')\\n extractPath=$(echo \\\"$decoded\\\" | jq -r '.extractPath')\\n commands=$(echo \\\"$decoded\\\" | jq -r '.commands')\\n\\n # Download the zip file\\n aws s3 cp \\\"$assetUrl\\\" temp.zip\\n\\n # Extract the zip file to the extractPath directory\\n mkdir -p \\\"$extractPath\\\"\\n unzip temp.zip -d \\\"$extractPath\\\"\\n\\n # Remove the zip file\\n rm temp.zip\\n\\n # Run the specified commands in the extractPath directory\\n cd \\\"$extractPath\\\"\\n ls -la\\n eval \\\"$commands\\\"\\n cd \\\"$current_dir\\\"\\n ls -la\\ndone\\n \",\n \"ls -la\",\n \"cd \\\"$workingDirectory\\\"\",\n \"eval \\\"$buildCommands\\\"\",\n \"ls -la\",\n \"cd \\\"$current_dir\\\"\",\n \"cd \\\"$outputSourceDirectory\\\"\",\n \"zip -r output.zip ./\",\n \"aws s3 cp output.zip \\\"s3://$destinationBucketName/$destinationObjectKey\\\"\"\n ]\n },\n \"post_build\": {\n \"commands\": [\n \"echo Build completed on `date`\",\n \"\\nSTATUS='SUCCESS'\\nif [ $CODEBUILD_BUILD_SUCCEEDING -ne 1 ] # Test if the build is failing\\nthen\\nSTATUS='FAILED'\\nREASON=\\\"NodejsBuild failed. See CloudWatch Log stream for the detailed reason: \\nhttps://$AWS_REGION.console.aws.amazon.com/cloudwatch/home?region=$AWS_REGION#logsV2:log-groups/log-group/\\\\$252Faws\\\\$252Fcodebuild\\\\$252F$projectName/log-events/$CODEBUILD_LOG_PATH\\\"\\nfi\\ncat < payload.json\\n{\\n \\\"StackId\\\": \\\"$stackId\\\",\\n \\\"RequestId\\\": \\\"$requestId\\\",\\n \\\"LogicalResourceId\\\":\\\"$logicalResourceId\\\",\\n \\\"PhysicalResourceId\\\": \\\"$logicalResourceId\\\",\\n \\\"Status\\\": \\\"$STATUS\\\",\\n \\\"Reason\\\": \\\"$REASON\\\",\\n \\\"Data\\\": {\\n \\\"destinationObjectKey\\\": \\\"$destinationObjectKey\\\"\\n }\\n}\\nEOF\\ncurl -v -i -X PUT -H 'Content-Type:' -d \\\"@payload.json\\\" \\\"$responseURL\\\"\\n \"\n ]\n }\n }\n}" + "buildSpec": "{\n \"version\": \"0.2\",\n \"env\": {\n \"shell\": \"bash\"\n },\n \"phases\": {\n \"install\": {\n \"runtime-versions\": {\n \"nodejs\": 20\n }\n },\n \"build\": {\n \"commands\": [\n \"current_dir=$(pwd)\",\n \"\\necho \\\"$input\\\"\\nfor obj in $(echo \\\"$input\\\" | jq -r '.[] | @base64'); do\\n decoded=$(echo \\\"$obj\\\" | base64 --decode)\\n assetUrl=$(echo \\\"$decoded\\\" | jq -r '.assetUrl')\\n extractPath=$(echo \\\"$decoded\\\" | jq -r '.extractPath')\\n commands=$(echo \\\"$decoded\\\" | jq -r '.commands')\\n\\n # Download the zip file\\n aws s3 cp \\\"$assetUrl\\\" temp.zip\\n\\n # Extract the zip file to the extractPath directory\\n mkdir -p \\\"$extractPath\\\"\\n unzip temp.zip -d \\\"$extractPath\\\"\\n\\n # Remove the zip file\\n rm temp.zip\\n\\n # Run the specified commands in the extractPath directory\\n cd \\\"$extractPath\\\"\\n ls -la\\n eval \\\"$commands\\\"\\n cd \\\"$current_dir\\\"\\n ls -la\\ndone\\n \",\n \"ls -la\",\n \"cd \\\"$workingDirectory\\\"\",\n \"eval \\\"$buildCommands\\\"\",\n \"ls -la\",\n \"cd \\\"$current_dir\\\"\",\n \"cd \\\"$outputSourceDirectory\\\"\",\n \"zip -r output.zip ./\",\n \"aws s3 cp output.zip \\\"s3://$destinationBucketName/$destinationObjectKey\\\"\",\n \"\\nif [[ $outputEnvFile == \\\"true\\\" ]]\\nthen\\n # Split the comma-separated string into an array\\n for var_name in ${envNames//,/ }\\n do\\n echo \\\"Element: $var_name\\\"\\n var_value=\\\"${!var_name}\\\"\\n echo \\\"$var_name=$var_value\\\" >> tmp.env\\n done\\n\\n aws s3 cp tmp.env \\\"s3://$destinationBucketName/$envFileKey\\\"\\nfi\\n \"\n ]\n },\n \"post_build\": {\n \"commands\": [\n \"echo Build completed on `date`\",\n \"\\nSTATUS='SUCCESS'\\nif [ $CODEBUILD_BUILD_SUCCEEDING -ne 1 ] # Test if the build is failing\\nthen\\nSTATUS='FAILED'\\nREASON=\\\"NodejsBuild failed. See CloudWatch Log stream for the detailed reason: \\nhttps://$AWS_REGION.console.aws.amazon.com/cloudwatch/home?region=$AWS_REGION#logsV2:log-groups/log-group/\\\\$252Faws\\\\$252Fcodebuild\\\\$252F$projectName/log-events/$CODEBUILD_LOG_PATH\\\"\\nfi\\ncat < payload.json\\n{\\n \\\"StackId\\\": \\\"$stackId\\\",\\n \\\"RequestId\\\": \\\"$requestId\\\",\\n \\\"LogicalResourceId\\\":\\\"$logicalResourceId\\\",\\n \\\"PhysicalResourceId\\\": \\\"$logicalResourceId\\\",\\n \\\"Status\\\": \\\"$STATUS\\\",\\n \\\"Reason\\\": \\\"$REASON\\\",\\n \\\"Data\\\": {\\n \\\"destinationObjectKey\\\": \\\"$destinationObjectKey\\\",\\n \\\"envFileKey\\\": \\\"$envFileKey\\\"\\n }\\n}\\nEOF\\ncurl -v -i -X PUT -H 'Content-Type:' -d \\\"@payload.json\\\" \\\"$responseURL\\\"\\n \"\n ]\n }\n }\n}" }, "cache": { "type": "NO_CACHE" @@ -494,6 +494,14 @@ "fqn": "aws-cdk-lib.aws_s3_deployment.BucketDeployment", "version": "2.38.0" } + }, + "DownloadEnvFile": { + "id": "DownloadEnvFile", + "path": "NodejsBuildIntegTest/ExampleBuild/DownloadEnvFile", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnOutput", + "version": "2.38.0" + } } }, "constructInfo": { @@ -632,7 +640,7 @@ "aws:cdk:cloudformation:props": { "code": { "s3Bucket": { - "Ref": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3Bucket73B303AC" + "Ref": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3BucketA057BD7E" }, "s3Key": { "Fn::Join": [ @@ -645,7 +653,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3VersionKey39332242" + "Ref": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3VersionKey4979DC57" } ] } @@ -658,7 +666,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3VersionKey39332242" + "Ref": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3VersionKey4979DC57" } ] } @@ -694,13 +702,13 @@ "id": "AssetParameters", "path": "NodejsBuildIntegTest/AssetParameters", "children": { - "95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49": { - "id": "95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49", - "path": "NodejsBuildIntegTest/AssetParameters/95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49", + "aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d": { + "id": "aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d", + "path": "NodejsBuildIntegTest/AssetParameters/aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d", "children": { "S3Bucket": { "id": "S3Bucket", - "path": "NodejsBuildIntegTest/AssetParameters/95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/S3Bucket", + "path": "NodejsBuildIntegTest/AssetParameters/aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/S3Bucket", "constructInfo": { "fqn": "aws-cdk-lib.CfnParameter", "version": "2.38.0" @@ -708,7 +716,7 @@ }, "S3VersionKey": { "id": "S3VersionKey", - "path": "NodejsBuildIntegTest/AssetParameters/95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/S3VersionKey", + "path": "NodejsBuildIntegTest/AssetParameters/aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/S3VersionKey", "constructInfo": { "fqn": "aws-cdk-lib.CfnParameter", "version": "2.38.0" @@ -716,7 +724,7 @@ }, "ArtifactHash": { "id": "ArtifactHash", - "path": "NodejsBuildIntegTest/AssetParameters/95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/ArtifactHash", + "path": "NodejsBuildIntegTest/AssetParameters/aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/ArtifactHash", "constructInfo": { "fqn": "aws-cdk-lib.CfnParameter", "version": "2.38.0" diff --git a/test/soci-index-build.integ.snapshot/SociIndexBuildIntegTest.template.json b/test/soci-index-build.integ.snapshot/SociIndexBuildIntegTest.template.json index 5ccd823..1c8035c 100644 --- a/test/soci-index-build.integ.snapshot/SociIndexBuildIntegTest.template.json +++ b/test/soci-index-build.integ.snapshot/SociIndexBuildIntegTest.template.json @@ -81,7 +81,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3Bucket73B303AC" + "Ref": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3BucketA057BD7E" }, "S3Key": { "Fn::Join": [ @@ -94,7 +94,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3VersionKey39332242" + "Ref": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3VersionKey4979DC57" } ] } @@ -107,7 +107,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3VersionKey39332242" + "Ref": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3VersionKey4979DC57" } ] } @@ -344,17 +344,17 @@ } }, "Parameters": { - "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3Bucket73B303AC": { + "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3BucketA057BD7E": { "Type": "String", - "Description": "S3 bucket for asset \"95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49\"" + "Description": "S3 bucket for asset \"aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d\"" }, - "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3VersionKey39332242": { + "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3VersionKey4979DC57": { "Type": "String", - "Description": "S3 key for asset version \"95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49\"" + "Description": "S3 key for asset version \"aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d\"" }, - "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49ArtifactHash0DB02EFB": { + "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dArtifactHashC99F560F": { "Type": "String", - "Description": "Artifact hash for asset \"95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49\"" + "Description": "Artifact hash for asset \"aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d\"" } } } \ No newline at end of file diff --git a/test/soci-index-build.integ.snapshot/asset.95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/index.js b/test/soci-index-build.integ.snapshot/asset.aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/index.js similarity index 91% rename from test/soci-index-build.integ.snapshot/asset.95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/index.js rename to test/soci-index-build.integ.snapshot/asset.aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/index.js index e5af2e2..4552f64 100644 --- a/test/soci-index-build.integ.snapshot/asset.95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/index.js +++ b/test/soci-index-build.integ.snapshot/asset.aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/index.js @@ -51,6 +51,7 @@ var handler = async (event, context) => { value: event.LogicalResourceId } ]; + const newPhysicalId = import_crypto.default.randomBytes(16).toString("hex"); let command; switch (props.type) { case "NodejsBuild": @@ -76,7 +77,7 @@ var handler = async (event, context) => { }, { name: "destinationObjectKey", - value: `${import_crypto.default.randomBytes(16).toString("hex")}.zip` + value: `${newPhysicalId}.zip` }, { name: "workingDirectory", @@ -90,6 +91,18 @@ var handler = async (event, context) => { name: "projectName", value: props.codeBuildProjectName }, + { + name: "outputEnvFile", + value: props.outputEnvFile.toString() + }, + { + name: "envFileKey", + value: `deploy-time-build/${event.StackId.split("/")[1]}/${event.LogicalResourceId}/${newPhysicalId}.env` + }, + { + name: "envNames", + value: Object.keys(props.environment ?? {}).join(",") + }, ...Object.entries(props.environment ?? {}).map(([name, value]) => ({ name, value diff --git a/test/soci-index-build.integ.snapshot/manifest.json b/test/soci-index-build.integ.snapshot/manifest.json index a2aab45..68353aa 100644 --- a/test/soci-index-build.integ.snapshot/manifest.json +++ b/test/soci-index-build.integ.snapshot/manifest.json @@ -33,13 +33,13 @@ { "type": "aws:cdk:asset", "data": { - "path": "asset.95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49", - "id": "95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49", + "path": "asset.aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d", + "id": "aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d", "packaging": "zip", - "sourceHash": "95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49", - "s3BucketParameter": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3Bucket73B303AC", - "s3KeyParameter": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3VersionKey39332242", - "artifactHashParameter": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49ArtifactHash0DB02EFB" + "sourceHash": "aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d", + "s3BucketParameter": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3BucketA057BD7E", + "s3KeyParameter": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3VersionKey4979DC57", + "artifactHashParameter": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dArtifactHashC99F560F" } }, { @@ -81,22 +81,22 @@ "data": "DeployTimeBuildCustomResourceHandlerdb740fd554364a848a09e6dfcd01f4f306AEFF37" } ], - "/SociIndexBuildIntegTest/AssetParameters/95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/S3Bucket": [ + "/SociIndexBuildIntegTest/AssetParameters/aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/S3Bucket": [ { "type": "aws:cdk:logicalId", - "data": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3Bucket73B303AC" + "data": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3BucketA057BD7E" } ], - "/SociIndexBuildIntegTest/AssetParameters/95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/S3VersionKey": [ + "/SociIndexBuildIntegTest/AssetParameters/aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/S3VersionKey": [ { "type": "aws:cdk:logicalId", - "data": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3VersionKey39332242" + "data": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3VersionKey4979DC57" } ], - "/SociIndexBuildIntegTest/AssetParameters/95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/ArtifactHash": [ + "/SociIndexBuildIntegTest/AssetParameters/aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/ArtifactHash": [ { "type": "aws:cdk:logicalId", - "data": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49ArtifactHash0DB02EFB" + "data": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dArtifactHashC99F560F" } ], "/SociIndexBuildIntegTest/SociIndexBuild024cf76a10034aa4aa4b12c32c09ca3c/Role/Resource": [ diff --git a/test/soci-index-build.integ.snapshot/tree.json b/test/soci-index-build.integ.snapshot/tree.json index 4d6ec9b..53c9422 100644 --- a/test/soci-index-build.integ.snapshot/tree.json +++ b/test/soci-index-build.integ.snapshot/tree.json @@ -227,7 +227,7 @@ "aws:cdk:cloudformation:props": { "code": { "s3Bucket": { - "Ref": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3Bucket73B303AC" + "Ref": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3BucketA057BD7E" }, "s3Key": { "Fn::Join": [ @@ -240,7 +240,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3VersionKey39332242" + "Ref": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3VersionKey4979DC57" } ] } @@ -253,7 +253,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49S3VersionKey39332242" + "Ref": "AssetParametersaa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8dS3VersionKey4979DC57" } ] } @@ -289,13 +289,13 @@ "id": "AssetParameters", "path": "SociIndexBuildIntegTest/AssetParameters", "children": { - "95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49": { - "id": "95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49", - "path": "SociIndexBuildIntegTest/AssetParameters/95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49", + "aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d": { + "id": "aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d", + "path": "SociIndexBuildIntegTest/AssetParameters/aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d", "children": { "S3Bucket": { "id": "S3Bucket", - "path": "SociIndexBuildIntegTest/AssetParameters/95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/S3Bucket", + "path": "SociIndexBuildIntegTest/AssetParameters/aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/S3Bucket", "constructInfo": { "fqn": "aws-cdk-lib.CfnParameter", "version": "2.38.0" @@ -303,7 +303,7 @@ }, "S3VersionKey": { "id": "S3VersionKey", - "path": "SociIndexBuildIntegTest/AssetParameters/95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/S3VersionKey", + "path": "SociIndexBuildIntegTest/AssetParameters/aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/S3VersionKey", "constructInfo": { "fqn": "aws-cdk-lib.CfnParameter", "version": "2.38.0" @@ -311,7 +311,7 @@ }, "ArtifactHash": { "id": "ArtifactHash", - "path": "SociIndexBuildIntegTest/AssetParameters/95e46a828cd9ccdd188870e970e75b59893922504ccc3863966dce2e66fccc49/ArtifactHash", + "path": "SociIndexBuildIntegTest/AssetParameters/aa47254059d94cff79a1f8a5a97c4e8a0e14a3f105d2b089464c0beeeb6cfe8d/ArtifactHash", "constructInfo": { "fqn": "aws-cdk-lib.CfnParameter", "version": "2.38.0"