22import { nodeFileTrace } from '@vercel/nft' ;
33import * as childProcess from 'child_process' ;
44import * as fs from 'fs' ;
5+ import * as os from 'os' ;
56import * as path from 'path' ;
67import { version } from '../package.json' ;
78
@@ -21,14 +22,21 @@ function run(cmd: string, options?: childProcess.ExecSyncOptions): string {
2122 */
2223async function buildLambdaLayer ( ) : Promise < void > {
2324 console . log ( 'Building Lambda layer.' ) ;
24- const tarballDir = buildTarballsAndPackageJson ( ) ;
25- console . log ( 'Installing @sentry/aws-serverless from tarballs into build/aws/dist-serverless/nodejs.' ) ;
26- run ( 'yarn install --prod --cwd ./build/aws/dist-serverless/nodejs' ) ;
25+ buildPackageJson ( ) ;
26+ console . log ( 'Installing local @sentry/aws-serverless into build/aws/dist-serverless/nodejs.' ) ;
27+ // Use a temporary cache folder to avoid stale cache references to local file: packages.
28+ // Yarn's global cache can contain outdated references to build artifacts from other
29+ // @sentry /* packages (e.g., build/node_modules paths that no longer exist), causing
30+ // ENOENT errors during file copying.
31+ // The cache folder must be outside the monorepo to avoid recursive nesting when Yarn
32+ // follows file: links and copies package directories.
33+ const cacheFolder = path . join ( os . tmpdir ( ) , `sentry-lambda-build-cache-${ Date . now ( ) } ` ) ;
34+ run ( `yarn install --prod --cwd ./build/aws/dist-serverless/nodejs --cache-folder "${ cacheFolder } "` ) ;
2735
2836 await pruneNodeModules ( ) ;
2937 fs . rmSync ( './build/aws/dist-serverless/nodejs/package.json' , { force : true } ) ;
3038 fs . rmSync ( './build/aws/dist-serverless/nodejs/yarn.lock' , { force : true } ) ;
31- fs . rmSync ( tarballDir , { recursive : true , force : true } ) ;
39+ fs . rmSync ( cacheFolder , { recursive : true , force : true } ) ;
3240
3341 // The layer also includes `awslambda-auto.js`, a helper file which calls `Sentry.init()` and wraps the lambda
3442 // handler. It gets run when Node is launched inside the lambda, using the environment variable
@@ -162,90 +170,43 @@ function getAllFiles(dir: string): string[] {
162170 return files ;
163171}
164172
165- /**
166- * Pack @sentry/* packages into tarballs and generate a package.json that references them.
167- *
168- * Using tarballs instead of `file:` directory references avoids stale yarn cache issues
169- * (where yarn's global cache retains outdated build artifact paths from previous builds)
170- * and allows us to use yarn's global cache for faster installs.
171- *
172- * Only packs packages that are transitive dependencies of @sentry/aws-serverless to keep
173- * the packing step fast.
174- */
175- function buildTarballsAndPackageJson ( ) : string {
176- const tarballDir = path . resolve ( './build/aws/tarballs' ) ;
177- fsForceMkdirSync ( tarballDir ) ;
178-
173+ function buildPackageJson ( ) : void {
174+ console . log ( 'Building package.json' ) ;
179175 const packagesDir = path . resolve ( __dirname , '../..' ) ;
180- const sentryPackages = collectSentryDependencies ( packagesDir ) ;
181-
182- console . log ( `Packing ${ sentryPackages . size } @sentry/* packages into tarballs.` ) ;
176+ const packageDirs = fs
177+ . readdirSync ( packagesDir , { withFileTypes : true } )
178+ . filter ( dirent => dirent . isDirectory ( ) )
179+ . map ( dirent => dirent . name )
180+ . filter ( name => ! name . startsWith ( '.' ) ) // Skip hidden directories
181+ . sort ( ) ;
183182
184183 const resolutions : Record < string , string > = { } ;
185184
186- for ( const [ packageName , packageDir ] of sentryPackages ) {
187- run ( `npm pack --pack-destination "${ tarballDir } "` , {
188- cwd : path . join ( packagesDir , packageDir ) ,
189- stdio : 'pipe' ,
190- } ) ;
191- const tarballName = `${ packageName . replace ( '@' , '' ) . replace ( '/' , '-' ) } -${ version } .tgz` ;
192- resolutions [ packageName ] = `file:${ path . join ( tarballDir , tarballName ) } ` ;
193- }
194-
195- const awsServerlessTarball = resolutions [ '@sentry/aws-serverless' ] ;
196- if ( ! awsServerlessTarball ) {
197- throw new Error ( 'Failed to pack @sentry/aws-serverless' ) ;
185+ for ( const packageDir of packageDirs ) {
186+ const packageJsonPath = path . join ( packagesDir , packageDir , 'package.json' ) ;
187+ if ( fs . existsSync ( packageJsonPath ) ) {
188+ try {
189+ const packageContent = JSON . parse ( fs . readFileSync ( packageJsonPath , 'utf-8' ) ) as { name ?: string } ;
190+ const packageName = packageContent . name ;
191+ if ( typeof packageName === 'string' && packageName ) {
192+ resolutions [ packageName ] = `file:../../../../../../packages/${ packageDir } ` ;
193+ }
194+ } catch {
195+ console . warn ( `Warning: Could not read package.json for ${ packageDir } ` ) ;
196+ }
197+ }
198198 }
199199
200200 const packageJson = {
201201 dependencies : {
202- '@sentry/aws-serverless' : awsServerlessTarball ,
202+ '@sentry/aws-serverless' : 'file:../../../../../../packages/aws-serverless' ,
203203 } ,
204204 resolutions,
205205 } ;
206206
207207 fsForceMkdirSync ( './build/aws/dist-serverless/nodejs' ) ;
208- fs . writeFileSync ( './build/aws/dist-serverless/nodejs/package.json' , JSON . stringify ( packageJson , null , 2 ) ) ;
209-
210- return tarballDir ;
211- }
212-
213- /**
214- * Collect all @sentry/* and @sentry-internal/* packages that are transitive
215- * dependencies of @sentry/aws-serverless.
216- * Returns a Map of packageName -> directory name.
217- */
218- function collectSentryDependencies ( packagesDir : string ) : Map < string , string > {
219- const result = new Map < string , string > ( ) ;
220-
221- // Build a lookup of package name -> directory name
222- const nameToDir = new Map < string , string > ( ) ;
223- for ( const dirent of fs . readdirSync ( packagesDir , { withFileTypes : true } ) ) {
224- if ( ! dirent . isDirectory ( ) || dirent . name . startsWith ( '.' ) ) continue ;
225- const pkgPath = path . join ( packagesDir , dirent . name , 'package.json' ) ;
226- if ( ! fs . existsSync ( pkgPath ) ) continue ;
227- const pkg = JSON . parse ( fs . readFileSync ( pkgPath , 'utf-8' ) ) as { name ?: string } ;
228- if ( pkg . name ) {
229- nameToDir . set ( pkg . name , dirent . name ) ;
230- }
231- }
232-
233- function collect ( packageName : string ) : void {
234- if ( result . has ( packageName ) ) return ;
235- const dir = nameToDir . get ( packageName ) ;
236- if ( ! dir ) return ;
237- result . set ( packageName , dir ) ;
238- const pkgPath = path . join ( packagesDir , dir , 'package.json' ) ;
239- const pkg = JSON . parse ( fs . readFileSync ( pkgPath , 'utf-8' ) ) as { dependencies ?: Record < string , string > } ;
240- for ( const dep of Object . keys ( pkg . dependencies || { } ) ) {
241- if ( dep . startsWith ( '@sentry/' ) || dep . startsWith ( '@sentry-internal/' ) ) {
242- collect ( dep ) ;
243- }
244- }
245- }
246-
247- collect ( '@sentry/aws-serverless' ) ;
248- return result ;
208+ const packageJsonPath = './build/aws/dist-serverless/nodejs/package.json' ;
209+ fs . writeFileSync ( packageJsonPath , JSON . stringify ( packageJson , null , 2 ) ) ;
249210}
250211
251212function replaceSDKSource ( ) : void {
0 commit comments