11import { CfnOutput , RemovalPolicy , Stack , StackProps } from 'aws-cdk-lib' ;
2- import { Construct } from 'constructs' ;
32import {
4- LayerVersion ,
3+ CfnLayerVersionPermission ,
54 Code ,
5+ LayerVersion ,
66 Runtime ,
7- CfnLayerVersionPermission ,
87} from 'aws-cdk-lib/aws-lambda' ;
98import { 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' ;
1113
1214export interface LayerPublisherStackProps extends StackProps {
1315 readonly layerName ?: string ;
1416 readonly powertoolsPackageVersion ?: string ;
1517 readonly ssmParameterLayerArn : string ;
18+ readonly buildFromLocal ?: boolean ;
1619}
1720
1821export class LayerPublisherStack extends Stack {
@@ -24,7 +27,7 @@ export class LayerPublisherStack extends Stack {
2427 ) {
2528 super ( scope , id , props ) ;
2629
27- const { layerName, powertoolsPackageVersion } = props ;
30+ const { layerName, powertoolsPackageVersion, buildFromLocal } = props ;
2831
2932 console . log (
3033 `publishing layer ${ layerName } version : ${ powertoolsPackageVersion } `
@@ -41,7 +44,112 @@ export class LayerPublisherStack extends Stack {
4144 license : 'MIT-0' ,
4245 // This is needed because the following regions do not support the compatibleArchitectures property #1400
4346 // ...(![ '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+ } ) ,
45153 } ) ;
46154
47155 const layerPermission = new CfnLayerVersionPermission (
0 commit comments