Skip to content
This repository was archived by the owner on Feb 1, 2025. It is now read-only.

Commit 7604d1d

Browse files
author
true
committed
fix(cdk,cli): correctly read git config, include BUILD_ID in assets, calculate zip hases ourselves
1 parent 1219354 commit 7604d1d

File tree

5 files changed

+82
-24
lines changed

5 files changed

+82
-24
lines changed

lib/cli.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ program
6060
const codeOutputPath = path.resolve(outputFolder, 'code.zip')
6161

6262
// Assets bundle configuration
63+
const buildIdPath = path.resolve(commandCwd, './.next/BUILD_ID')
6364
const generatedStaticContentPath = path.resolve(commandCwd, '.next/static')
6465
const generatedStaticRemapping = '_next/static'
6566
const assetsOutputPath = path.resolve(outputFolder, 'assetsLayer.zip')
@@ -79,6 +80,11 @@ program
7980
await zipMultipleFoldersOrFiles({
8081
outputName: assetsOutputPath,
8182
inputDefinition: [
83+
{
84+
isFile: true,
85+
name: 'BUILD_ID',
86+
path: buildIdPath,
87+
},
8288
{
8389
path: publicFolder,
8490
},
@@ -169,8 +175,8 @@ program
169175
.option('-a, --autoPush', 'This will automatically create release branch and tag commit in master.', Boolean, true)
170176
.option('-t, --tagPrefix <prefix>', 'Prefix version with string of your choice.', 'v')
171177
.option('-r, --releaseBranchPrefix <prefix>', 'Prefix for release branch fork.', 'release/')
172-
.option('--gitUser', 'User name to be used for commits.', 'Bender')
173-
.option('--gitEmail', 'User email to be used for commits.', '[email protected]')
178+
.option('--gitUser <user>', 'User name to be used for commits.', 'Bender')
179+
.option('--gitEmail <email>', 'User email to be used for commits.', '[email protected]')
174180
.action(async (options) => {
175181
const { tagPrefix, failOnMissingCommit, releaseBranchPrefix, forceBump, gitUser, gitEmail } = options
176182

@@ -259,6 +265,7 @@ program
259265
.push(remote.name, `${branch.current}:${releaseBranch}`)
260266

261267
// @Note: CI/CD should not be listening for tags in master, it should listen to release branch.
268+
// @TODO: Include commits and commit bodies in release commit so Jira can pick it up.
262269

263270
console.log(`Successfuly tagged and created new branch - ${releaseBranch}`)
264271
})

lib/construct.ts

+48-20
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { HttpApi } from '@aws-cdk/aws-apigatewayv2-alpha'
22
import { HttpLambdaIntegration } from '@aws-cdk/aws-apigatewayv2-integrations-alpha'
3-
import { AssetHashType, CfnOutput, Duration, RemovalPolicy, Stack, StackProps, SymlinkFollowMode } from 'aws-cdk-lib'
3+
import { AssetHashType, CfnOutput, Duration, RemovalPolicy, Stack, SymlinkFollowMode } from 'aws-cdk-lib'
44
import { CloudFrontAllowedMethods, CloudFrontWebDistribution, ViewerCertificate } from 'aws-cdk-lib/aws-cloudfront'
55
import { Code, Function, LayerVersion, Runtime } from 'aws-cdk-lib/aws-lambda'
66
import { Bucket, BucketAccessControl } from 'aws-cdk-lib/aws-s3'
@@ -9,58 +9,77 @@ import { Construct } from 'constructs'
99
import packageJson from '../package.json'
1010

1111
import { imageHandlerZipPath, sharpLayerZipPath, nextLayerZipPath } from './consts'
12+
import { md5FileSync } from './utils'
1213

13-
interface NextConstructProps extends StackProps {
14+
interface NextConstructProps {
15+
// Required paths, output of pack CLI command.
16+
codeZipPath: string
17+
dependenciesZipPath: string
18+
assetsZipPath: string
19+
// Optional for additional customizations.
1420
customServerHandler?: string
1521
customImageHandler?: string
16-
1722
cfnViewerCertificate?: ViewerCertificate
18-
1923
imageHandlerZipPath?: string
2024
sharpLayerZipPath?: string
2125
nextLayerZipPath?: string
22-
codeZipPath: string
23-
dependenciesZipPath: string
24-
assetsZipPath: string
2526
}
2627

27-
export class NextStandaloneStack extends Stack {
28+
export class NextStandaloneConstruct extends Construct {
2829
private readonly cfnDistro: CloudFrontWebDistribution
2930
private readonly serverLambda: Function
3031
private readonly imageLambda: Function
3132
private readonly serverApigatewayProxy: HttpApi
3233
private readonly imageApigatewayProxy: HttpApi
34+
private readonly region: string
3335

3436
constructor(scope: Construct, id: string, props: NextConstructProps) {
35-
super(scope, id, props)
37+
super(scope, id)
38+
39+
const config = {
40+
sharpLayerZipPath: sharpLayerZipPath,
41+
nextLayerZipPath: nextLayerZipPath,
42+
imageHandlerZipPath: imageHandlerZipPath,
43+
...props,
44+
}
45+
46+
this.region = Stack.of(scope).region
3647

3748
const depsLayer = new LayerVersion(this, 'DepsLayer', {
38-
code: Code.fromAsset(props.dependenciesZipPath),
49+
code: Code.fromAsset(props.dependenciesZipPath, {
50+
assetHash: md5FileSync(props.dependenciesZipPath),
51+
assetHashType: AssetHashType.CUSTOM,
52+
}),
3953
})
4054

4155
const sharpLayer = new LayerVersion(this, 'SharpLayer', {
42-
code: Code.fromAsset(props.sharpLayerZipPath ?? sharpLayerZipPath, {
43-
assetHash: `static-sharp-${packageJson.version}`,
56+
code: Code.fromAsset(config.sharpLayerZipPath, {
57+
assetHash: md5FileSync(config.sharpLayerZipPath),
4458
assetHashType: AssetHashType.CUSTOM,
4559
}),
4660
})
4761

4862
const nextLayer = new LayerVersion(this, 'NextLayer', {
49-
code: Code.fromAsset(props.nextLayerZipPath ?? nextLayerZipPath, {
50-
assetHash: `static-next-${packageJson.version}`,
63+
code: Code.fromAsset(config.nextLayerZipPath, {
64+
assetHash: md5FileSync(config.nextLayerZipPath),
5165
assetHashType: AssetHashType.CUSTOM,
5266
}),
5367
})
5468

5569
const assetsBucket = new Bucket(this, 'NextAssetsBucket', {
70+
// Those settings are necessary for bucket to be removed on stack removal.
71+
removalPolicy: RemovalPolicy.DESTROY,
72+
autoDeleteObjects: true,
5673
// @NOTE: Considering not having public ACL.
5774
publicReadAccess: true,
58-
autoDeleteObjects: true,
59-
removalPolicy: RemovalPolicy.DESTROY,
6075
})
6176

6277
this.serverLambda = new Function(this, 'DefaultNextJs', {
63-
code: Code.fromAsset(props.codeZipPath, { followSymlinks: SymlinkFollowMode.NEVER }),
78+
code: Code.fromAsset(config.codeZipPath, {
79+
followSymlinks: SymlinkFollowMode.NEVER,
80+
assetHash: md5FileSync(config.codeZipPath),
81+
assetHashType: AssetHashType.CUSTOM,
82+
}),
6483
runtime: Runtime.NODEJS_16_X,
6584
handler: props.customServerHandler ?? 'handler.handler',
6685
layers: [depsLayer, nextLayer],
@@ -70,7 +89,10 @@ export class NextStandaloneStack extends Stack {
7089
})
7190

7291
this.imageLambda = new Function(this, 'ImageOptimizationNextJs', {
73-
code: Code.fromAsset(props.imageHandlerZipPath ?? imageHandlerZipPath),
92+
code: Code.fromAsset(config.imageHandlerZipPath, {
93+
assetHash: md5FileSync(config.imageHandlerZipPath),
94+
assetHashType: AssetHashType.CUSTOM,
95+
}),
7496
runtime: Runtime.NODEJS_16_X,
7597
handler: props.customImageHandler ?? 'index.handler',
7698
layers: [sharpLayer, nextLayer],
@@ -152,18 +174,24 @@ export class NextStandaloneStack extends Stack {
152174
// This can be handled by `aws s3 sync` but we need to ensure invalidation of Cfn after deploy.
153175
new BucketDeployment(this, 'PublicFilesDeployment', {
154176
destinationBucket: assetsBucket,
155-
sources: [Source.asset(props.assetsZipPath)],
156177
accessControl: BucketAccessControl.PUBLIC_READ,
178+
sources: [
179+
Source.asset(config.assetsZipPath, {
180+
assetHashType: AssetHashType.CUSTOM,
181+
assetHash: md5FileSync(config.assetsZipPath),
182+
}),
183+
],
157184
// Invalidate all paths after deployment.
158-
distributionPaths: ['/*'],
159185
distribution: this.cfnDistro,
186+
distributionPaths: ['/*'],
160187
})
161188

162189
new CfnOutput(this, 'cfnDistroUrl', { value: this.cfnDistro.distributionDomainName })
163190
new CfnOutput(this, 'cfnDistroId', { value: this.cfnDistro.distributionId })
164191
new CfnOutput(this, 'defaultApiGwUrl', { value: this.serverApigatewayProxy.apiEndpoint })
165192
new CfnOutput(this, 'imagesApiGwUrl', { value: this.imageApigatewayProxy.apiEndpoint })
166193
new CfnOutput(this, 'assetsBucketUrl', { value: assetsBucket.bucketDomainName })
194+
new CfnOutput(this, 'nextConstructRegion', { value: this.region })
167195
}
168196

169197
get cloudfrontDistribution() {

lib/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export { NextStandaloneStack } from './construct'
1+
export { NextStandaloneConstruct } from './construct'
22
export { imageHandlerZipPath, serverHandlerZipPath, sharpLayerZipPath } from './consts'
33

44
export { handler as serverHandler } from './standalone/server-handler'

lib/utils.ts

+23-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3'
22
import archiver from 'archiver'
3-
import { createWriteStream, readFileSync, symlinkSync } from 'fs'
3+
import { closeSync, createWriteStream, openSync, readFileSync, readSync, symlinkSync } from 'fs'
44
import { IOptions as GlobOptions } from 'glob'
55
import { IncomingMessage, ServerResponse } from 'http'
66
import { NextUrlWithParsedQuery } from 'next/dist/server/request-meta'
77
import { replaceInFileSync } from 'replace-in-file'
88
import { Readable } from 'stream'
9+
import crypto from 'crypto'
910

1011
// Make header keys lowercase to ensure integrity.
1112
export const normalizeHeaders = (headers: Record<string, any>) =>
@@ -254,3 +255,24 @@ interface SymlinkProps {
254255
}
255256

256257
export const createSymlink = ({ linkLocation, sourcePath }: SymlinkProps) => symlinkSync(sourcePath, linkLocation)
258+
259+
const BUFFER_SIZE = 8192
260+
261+
export const md5FileSync = (path: string) => {
262+
const fd = openSync(path, 'r')
263+
const hash = crypto.createHash('md5')
264+
const buffer = Buffer.alloc(BUFFER_SIZE)
265+
266+
try {
267+
let bytesRead
268+
269+
do {
270+
bytesRead = readSync(fd, buffer, 0, BUFFER_SIZE, null)
271+
hash.update(buffer.subarray(0, bytesRead))
272+
} while (bytesRead === BUFFER_SIZE)
273+
} finally {
274+
closeSync(fd)
275+
}
276+
277+
return hash.digest('hex')
278+
}

scripts/pack-nextjs.sh

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ mkdir -p $ASSETS_FOLDER
7070
mkdir -p $ASSETS_FOLDER/_next/static
7171
cp -r $MY_ROOT/.next/static/* $ASSETS_FOLDER/_next/static/
7272
cp -r $MY_ROOT/$PUBLIC_FOLDER/* $ASSETS_FOLDER/
73+
cp .next/BUILD_ID $ASSETS_FOLDER/
7374

7475
echo "Zipping assets."
7576
cd $ASSETS_FOLDER

0 commit comments

Comments
 (0)