How to deploy Azure function with pnpm and rush?

859 Views Asked by At

How do you deploy an Azure function app with pnpm?

I'm using a rush monorepo and the rush deploy task which works great locally. The problem is that I'm unable to get ALL of my pnpm node_modules dependencies AND get my Azure function started in the cloud runtime environment

Min repro project here: https://github.com/luisnaranjo733/pnpm-rush-azure-functions

After running rush deploy, I get a nice ready to deploy project at the common/deploy path. When I try to deploy the commmon/apps/functions directory which contains the package.json for my functions project, Azure Functions mostly works except for when I import a dependency that itself has a dependency on something else.

This makes sense because of the way pnpm node_modules works, but in this case it's causing a huge headache with Azure Functions. See screenshot below for more details

The green highlighted node_modules is the one that only contains the dependencies declared by my project. As expected, only @azure/service-bus is there because that is the only dependency my functions project is using.

The orange highlighted node_modules is the pnpm global content adressable module store. This is where the dependencies of @azure/service-bus end up such a @azure/core-amqp.

When running locally via npm run start, my function is able to resolve the import to @azure/service-bus from the green node_modules and its internal dependencies from the orange node_modules. When running on the cloud runtime environment, this doesn't work :(

Example project structure

I can deploy the common/deploy/apps/functions project which works fine on the cloud runtime until I try to import @azure/service-bus which fails because it can't find its internal dependencies such as @azure/core-amqp.

Example cloud runtime file system when deploying the common/deploy/apps/functions folder Example cloud runtime file system when deploying the functions folder

I see a few possible paths forward, but no clear winner

  • Revert back to npm, which sucks because I really wanted to use pnpm+rush.
  • Configure pnpm to copy and flatten my common/deploy/apps/functions/node_modules indistinguishable from a npm or yarn one (no symlinks to global content addressable module store). But this kind of negates the whole point of pnpm
  • Configure Azure Functions cloud runtime environment to use some kind of pnpm mode and figure out what's happening and solve it for me

Has anyone trodden this path or have any words of wisdom? Thanks, Luis

2

There are 2 best solutions below

0
On

You can use cp -LR to resolve the symlinks:

      - name: 'Resolve Project Dependencies Using pnpm'
        shell: bash
        run: |
          pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}'
          pnpm install
          cp -LR node_modules tmp && rm -rf node_modules && mv tmp node_modules
          pnpm run build
          popd
2
On

I am using pnpm for a monorepo with one of my packages as an Azure Function app and was having a similar issue. Found your post here and just got mine working so adding an answer that hopefully might help you or others who stumble across this post.

So like I said, I have an Azure Functions app and then some shared packages. My pnpm-workspace.yaml file looks something like this:

packages:
  - "functions"
  - "packages/*"

The problem I was facing was when trying to deploy via github actions. After running pnpm install inside the Azure Function app package, the node_modules there with all the 3rd party code I needed would all be symlinked to the virtual store at the root node_modules/.pnpm.

After building and deploying, I noticed functions failing because it was trying to run code in a folder that only contained the path to the folder in the virtual store. For example, node_modules/@azure/storage-queue would not have any files, just, ../../../node_modules/.pnpm symlink which wouldn't work because those files were outside of the Function App and therefore not deployed.

My solution had two key parts:

  1. Add .npmrc at root and add config node-linker=hoisted

(Documenation): hoisted - a flat node_modules without symlinks is created. Same as the node_modules created by npm or Yarn Classic.

  1. Add extra step in github action to copy all the files in the root node_modules folder into the Azure Function App's node_modules folder:

xcopy node_modules functions\node_modules /s /e /y /i

Additional info about my project that might help:

In my tsconfig.json for the Azure Function app, I have "include" set to "./**/*" - builds all my code, and then also I have "node_modules/<shared package>/**/*" for each of my shared packages.

Then in "compilerOptions", I have "paths" set to that output in the dist folder.

Hope this helps!