1
1
import { CfnOutput , RemovalPolicy , Stack , StackProps } from 'aws-cdk-lib' ;
2
- import { Construct } from 'constructs' ;
3
2
import {
4
- LayerVersion ,
3
+ CfnLayerVersionPermission ,
5
4
Code ,
5
+ LayerVersion ,
6
6
Runtime ,
7
- CfnLayerVersionPermission ,
8
7
} from 'aws-cdk-lib/aws-lambda' ;
9
8
import { StringParameter } from 'aws-cdk-lib/aws-ssm' ;
10
- import { resolve } from 'node:path' ;
9
+ import { Construct } from 'constructs' ;
10
+ import { execSync } from 'node:child_process' ;
11
+ import { randomUUID } from 'node:crypto' ;
12
+ import { join , resolve , sep } from 'node:path' ;
11
13
12
14
export interface LayerPublisherStackProps extends StackProps {
13
15
readonly layerName ?: string ;
14
16
readonly powertoolsPackageVersion ?: string ;
15
17
readonly ssmParameterLayerArn : string ;
18
+ readonly buildFromLocal ?: boolean ;
16
19
}
17
20
18
21
export class LayerPublisherStack extends Stack {
@@ -24,7 +27,7 @@ export class LayerPublisherStack extends Stack {
24
27
) {
25
28
super ( scope , id , props ) ;
26
29
27
- const { layerName, powertoolsPackageVersion } = props ;
30
+ const { layerName, powertoolsPackageVersion, buildFromLocal } = props ;
28
31
29
32
console . log (
30
33
`publishing layer ${ layerName } version : ${ powertoolsPackageVersion } `
@@ -41,7 +44,112 @@ export class LayerPublisherStack extends Stack {
41
44
license : 'MIT-0' ,
42
45
// This is needed because the following regions do not support the compatibleArchitectures property #1400
43
46
// ...(![ 'eu-south-2', 'eu-central-2', 'ap-southeast-4' ].includes(Stack.of(this).region) ? { compatibleArchitectures: [Architecture.X86_64] } : {}),
44
- code : Code . fromAsset ( resolve ( __dirname , '..' , '..' , 'tmp' ) ) ,
47
+ code : Code . fromAsset ( resolve ( __dirname ) , {
48
+ bundling : {
49
+ // This is here only because is required by CDK, however it is not used since the bundling is done locally
50
+ image : Runtime . NODEJS_18_X . bundlingImage ,
51
+ // We need to run a command to generate a random UUID to force the bundling to run every time
52
+ command : [ `echo "${ randomUUID ( ) } "` ] ,
53
+ local : {
54
+ tryBundle ( outputDir : string ) {
55
+ // This folder are relative to the layers folder
56
+ const tmpBuildPath = resolve ( __dirname , '..' , 'tmp' ) ;
57
+ const tmpBuildDir = join ( tmpBuildPath , 'nodejs' ) ;
58
+ // This folder is the project root, relative to the current file
59
+ const projectRoot = resolve ( __dirname , '..' , '..' ) ;
60
+
61
+ // This is the list of packages that we need include in the Lambda Layer
62
+ // the name is the same as the npm workspace name
63
+ const utilities = [ 'commons' , 'logger' , 'metrics' , 'tracer' ] ;
64
+
65
+ // These files are relative to the tmp folder
66
+ const filesToRemove = [
67
+ 'node_modules/@types' ,
68
+ 'package.json' ,
69
+ 'package-lock.json' ,
70
+ 'node_modules/**/README.md' ,
71
+ 'node_modules/.bin/semver' ,
72
+ 'node_modules/async-hook-jl/test' ,
73
+ 'node_modules/shimmer/test' ,
74
+ 'node_modules/jmespath/artifacts' ,
75
+ // We remove the type definitions since they can't be used in the Lambda Layer
76
+ 'node_modules/@aws-lambda-powertools/*/lib/*.d.ts' ,
77
+ 'node_modules/@aws-lambda-powertools/*/lib/*.d.ts.map' ,
78
+ ] ;
79
+ const buildCommands : string [ ] = [ ] ;
80
+ const modulesToInstall : string [ ] = [ ] ;
81
+
82
+ if ( buildFromLocal ) {
83
+ for ( const util of utilities ) {
84
+ // Build latest version of the package
85
+ buildCommands . push ( `npm run build -w packages/${ util } ` ) ;
86
+ // Pack the package to a .tgz file
87
+ buildCommands . push ( `npm pack -w packages/${ util } ` ) ;
88
+ // Move the .tgz file to the tmp folder
89
+ buildCommands . push (
90
+ `mv aws-lambda-powertools-${ util } -*.tgz ${ tmpBuildDir } `
91
+ ) ;
92
+ }
93
+ modulesToInstall . push (
94
+ ...utilities . map ( ( util ) =>
95
+ join ( tmpBuildDir , `aws-lambda-powertools-${ util } -*.tgz` )
96
+ )
97
+ ) ;
98
+ filesToRemove . push (
99
+ ...utilities . map ( ( util ) =>
100
+ join ( `aws-lambda-powertools-${ util } -*.tgz` )
101
+ )
102
+ ) ;
103
+ } else {
104
+ // Dependencies to install in the Lambda Layer
105
+ modulesToInstall . push (
106
+ ...utilities . map (
107
+ ( util ) =>
108
+ `@aws-lambda-powertools/${ util } @${ powertoolsPackageVersion } `
109
+ )
110
+ ) ;
111
+ }
112
+
113
+ // Phase 1: Cleanup & create tmp folder
114
+ execSync (
115
+ [
116
+ // Clean up existing tmp folder from previous builds
117
+ `rm -rf ${ tmpBuildDir } ` ,
118
+ // Create tmp folder again
119
+ `mkdir -p ${ tmpBuildDir } ` ,
120
+ ] . join ( ' && ' )
121
+ ) ;
122
+
123
+ // Phase 2: (Optional) Build packages & pack them
124
+ buildFromLocal &&
125
+ execSync ( buildCommands . join ( ' && ' ) , { cwd : projectRoot } ) ;
126
+
127
+ // Phase 3: Install dependencies to tmp folder
128
+ execSync (
129
+ `npm i --prefix ${ tmpBuildDir } ${ modulesToInstall . join ( ' ' ) } `
130
+ ) ;
131
+
132
+ // Phase 4: Remove unnecessary files
133
+ execSync (
134
+ `rm -rf ${ filesToRemove
135
+ . map ( ( filePath ) => `${ tmpBuildDir } /${ filePath } ` )
136
+ . join ( ' ' ) } `
137
+ ) ;
138
+
139
+ // Phase 5: Copy files from tmp folder to cdk.out asset folder (the folder is created by CDK)
140
+ execSync ( `cp -R ${ tmpBuildPath } ${ sep } * ${ outputDir } ` ) ;
141
+
142
+ // Phase 6: (Optional) Restore changes to the project root made by the build
143
+ buildFromLocal &&
144
+ execSync ( 'git restore packages/*/package.json' , {
145
+ cwd : projectRoot ,
146
+ } ) ;
147
+
148
+ return true ;
149
+ } ,
150
+ } ,
151
+ } ,
152
+ } ) ,
45
153
} ) ;
46
154
47
155
const layerPermission = new CfnLayerVersionPermission (
0 commit comments