serverless / serverless-plugin-typescript

Serverless plugin for zero-config Typescript support
MIT License
784 stars 224 forks source link

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

Open nancyatdoshii opened 2 years ago

nancyatdoshii commented 2 years ago

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

medikoo commented 2 years ago

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

jebricker commented 2 years ago

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.

Airaken commented 2 years ago

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

NoxHarmonium commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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
milindsingh commented 1 year ago

Any update on this ?

luccasmaso commented 1 year ago

I can also confirm that downgrading to 2.1.1 fixes the issue

sehrish30 commented 1 year ago

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

bernardodesousa commented 1 year ago

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.

makeryoungjin commented 1 year ago

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