From 38ee336cd9fd5ceed3a19c85cd106a82d7073264 Mon Sep 17 00:00:00 2001 From: Romain Marcadier Date: Tue, 26 May 2020 11:49:32 +0200 Subject: [PATCH] fix(kernel): error raised during rename operation on win32 (#1702) When loading a new library into the jsii kernel, the provided `tarball` was extracted to a temporary directory, then moved into it's final location. On Windows, this operation could fail on an `EACCESS` or `EPERM` error (often due to malware scanners accessing the file for inspection on file systems which do not support renaming files that are being accessed). This changes how the `load` API works so taht the `tarball` is extracted directly in it's final install location, so that no rename operation is needed. Fixes #992 --- By submitting this pull request, I confirm that my contribution is made under the terms of the [Apache 2.0 license]. [Apache 2.0 license]: https://www.apache.org/licenses/LICENSE-2.0 --- packages/@jsii/kernel/lib/kernel.ts | 70 ++++++++++++----------------- 1 file changed, 29 insertions(+), 41 deletions(-) diff --git a/packages/@jsii/kernel/lib/kernel.ts b/packages/@jsii/kernel/lib/kernel.ts index 87448fbc05..573f184b11 100644 --- a/packages/@jsii/kernel/lib/kernel.ts +++ b/packages/@jsii/kernel/lib/kernel.ts @@ -104,51 +104,39 @@ export class Kernel { types: Object.keys(assm.metadata.types ?? {}).length, }; } - // untar the archive to a staging directory, read the jsii spec from it - // and then move it to the node_modules directory of the kernel. - const staging = fs.mkdtempSync( - path.join(os.tmpdir(), 'jsii-kernel-install-staging-'), - ); - try { - tar.extract({ - strict: true, - file: req.tarball, - cwd: staging, - sync: true, - }); - - // read .jsii metadata from the root of the package - const jsiiMetadataFile = path.join( - staging, - 'package', - spec.SPEC_FILE_NAME, - ); - if (!fs.pathExistsSync(jsiiMetadataFile)) { - throw new Error( - `Package tarball ${req.tarball} must have a file named ${spec.SPEC_FILE_NAME} at the root`, - ); - } - const assmSpec = fs.readJsonSync(jsiiMetadataFile) as spec.Assembly; - // "install" to "node_modules" directory - fs.moveSync(path.join(staging, 'package'), packageDir); + // Create the install directory (there may be several path components for @scoped/packages) + fs.mkdirpSync(packageDir); + // untar the archive to its final location + tar.extract({ + strict: true, + file: req.tarball, + cwd: packageDir, + strip: 1, // Removes the 'package/' path element from entries + sync: true, + }); - // load the module and capture it's closure - const closure = this._execute( - `require(String.raw\`${packageDir}\`)`, - packageDir, + // read .jsii metadata from the root of the package + const jsiiMetadataFile = path.join(packageDir, spec.SPEC_FILE_NAME); + if (!fs.pathExistsSync(jsiiMetadataFile)) { + throw new Error( + `Package tarball ${req.tarball} must have a file named ${spec.SPEC_FILE_NAME} at the root`, ); - const assm = new Assembly(assmSpec, closure); - this._addAssembly(assm); - - return { - assembly: assmSpec.name, - types: Object.keys(assmSpec.types ?? {}).length, - }; - } finally { - this._debug('removing staging directory:', staging); - fs.removeSync(staging); } + const assmSpec = fs.readJsonSync(jsiiMetadataFile) as spec.Assembly; + + // load the module and capture it's closure + const closure = this._execute( + `require(String.raw\`${packageDir}\`)`, + packageDir, + ); + const assm = new Assembly(assmSpec, closure); + this._addAssembly(assm); + + return { + assembly: assmSpec.name, + types: Object.keys(assmSpec.types ?? {}).length, + }; } public create(req: api.CreateRequest): api.CreateResponse {