serverless-plugin-typescript icon indicating copy to clipboard operation
serverless-plugin-typescript copied to clipboard

Lambda layer artifact: no such file or directory since version 2.12

Open nancyatdoshii opened this issue 3 years ago • 11 comments

Hi, we 've been having this error message since the 2.12 update ENOENT: no such file or directory, open 'project/.serverless/LambdaNodeModules.zip

Our Lambda layer config is as below

layers:
  LambdaNodeModules:
    description: Dependencies for project
    package:
      artifact: LambdaNodeModules.zip
    compatibleArchitectures:
      - x86_64
    compatibleRuntimes:
      - nodejs14.x

We are using serverless 2.72.3

nancyatdoshii avatar Apr 05 '22 03:04 nancyatdoshii

@mmeyers-xomly do you know why it would be the case?

medikoo avatar Apr 05 '22 07:04 medikoo

Have you tried just using the arn of the layer? That is what I use in my serverless.yml and have not had an issue.

jebricker avatar Jun 01 '22 12:06 jebricker

It seems that the plugin is not recognizing the layers, so it does not add them in the .build and when deploying it does not find the files and that is when the error occurs

Airaken avatar Aug 27 '22 04:08 Airaken

I'm having the same issue which is fixed after I downgrade to 2.1.1

In my case the layer is built by an external plugin, then the layer zip file is referenced directly like the original poster. This config works with all the other bundlers I've tried so I think it is definitely an serverless-plugin-typescript issue introduced in 2.1.2

I suspect it might be this line:

https://github.com/mmeyers-xomly/serverless-plugin-typescript/blob/d8032babc29905c778805033333c0ce2da5841a2/src/index.ts#L255

I'd say that we should only overwrite artifact if it hasn't already been set by something else explicitly.

Something like this:

if (service.layers[name].package.artifact === undefined) {
   service.layers[name].package.artifact = path.join(
        this.originalServicePath,
        SERVERLESS_FOLDER,
        path.basename(service.layers[name].package.artifact)
      )
}

What do you think?

NoxHarmonium avatar Sep 15 '22 20:09 NoxHarmonium

After playing around with this for a bit, I have realised that the breaking change actually transforms the existing value of "service.layers[name].package.artifact" so my suggestion won't work. I'll have to keep looking in to it.

NoxHarmonium avatar Sep 20 '22 03:09 NoxHarmonium

I think we need to break the assumption that every layer has been moved by serverless-plugin-typescript and only reset the reference if the artifact was originally from the source folder.

Something like this might work better. I still need to give it a good test.

---
 src/index.ts | 28 +++++++++++++++++++++-------
 1 file changed, 21 insertions(+), 7 deletions(-)

diff --git a/src/index.ts b/src/index.ts
index b610004..1b4eb3d 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -254,18 +254,32 @@ export class TypeScriptPlugin {
   async moveArtifacts(): Promise<void> {
     const { service } = this.serverless
 
+    const sourcePath = path.join(this.originalServicePath, BUILD_FOLDER, SERVERLESS_FOLDER)
+    const destinationPath = path.join(this.originalServicePath, SERVERLESS_FOLDER)
+
     await fs.copy(
-      path.join(this.originalServicePath, BUILD_FOLDER, SERVERLESS_FOLDER),
-      path.join(this.originalServicePath, SERVERLESS_FOLDER)
+      sourcePath,
+      destinationPath
     )
 
+    // After this function returns, the build folder will be deleted
+    // because the files have been copied into the .serverless folder.
+    // However, serverless will still be looking for that build folder
+    // at deploy time because it doesn't know that it has been deleted.
+    // This updates the reference.
     const layerNames = service.getAllLayers()
     layerNames.forEach(name => {
-      service.layers[name].package.artifact = path.join(
-        this.originalServicePath,
-        SERVERLESS_FOLDER,
-        path.basename(service.layers[name].package.artifact)
-      )
+      const existingArtifactPath = service.layers[name].package.artifact;
+      // Only reset this reference if the artifact was originally copied by
+      // this plugin. Otherwise, leave it alone, because the user (or another plugin)
+      // has set this to a specific location.
+      if (path.dirname(existingArtifactPath) === sourcePath) {
+        service.layers[name].package.artifact = path.join(
+          this.originalServicePath,
+          SERVERLESS_FOLDER,
+          path.basename(existingArtifactPath)
+        )
+      }
     })
 
     if (this.options.function) {
-- 
2.37.3

NoxHarmonium avatar Sep 20 '22 04:09 NoxHarmonium

Any update on this ?

milindsingh avatar Jan 26 '23 16:01 milindsingh

I can also confirm that downgrading to 2.1.1 fixes the issue

luccasmaso avatar Jan 26 '23 19:01 luccasmaso

Facing the same issue however, it works when I deploy the whole service using sls deploy but single function deployment does not work

sehrish30 avatar May 19 '23 17:05 sehrish30

I'm seeing this problem with Serverless 3.30.1. and plugin 2.1.5. Here's how the layer is configured:

layers:
  ffmpeg:
    package:
      artifact: layers/ffmpeg.zip
    name: ${self:custom.stacks.this}-ffmpeg
    description: FFMPEG binary
    compatibleRuntimes:
      - nodejs14.x
    licenseInfo: GPL v2+, for more info see https://github.com/FFmpeg/FFmpeg/blob/master/LICENSE.md
    retain: false

When I try to package with serverless package, I get the following error:

OperationalError: ENOENT: no such file or directory, open '<project-root>/.serverless/ffmpeg.zip'

This error only shows up when I add the plugin. Things work as expected without the plugin.

I confirm that the problem is introduced with 2.1.2.

For now, we'll downgrade to 2.1.1 and follow this thread.

bernardodesousa avatar Jun 06 '23 19:06 bernardodesousa

I fixed issue using another plugin "serverless-plugin-esbuild'

makeryoungjin avatar Sep 21 '23 07:09 makeryoungjin