Search code examples
node.jsflutterfirebasegoogle-cloud-functions

How to fix wrong node version in `firebase deploy --only functions`


When i run firebase deploy --only functions, it responds with an error about node version incompatibility. My system has node 20.11.0 and firebase 13.1.0 installed (managed with asdf).

In package.json, "engines": { "node": ">=18.5" } is specified, and "runtime": "nodejs20" is specified in firebase.json.

There is a dependency on sharp which requires node ^18.17.0 || ^20.3.0 || >=21.0.0.

firebase deploy --only functions gets past lint and other steps, but finally fails, complaining about finding node 18.5.0 (which isn’t on my system at all) and requiring at least 18.7.0 due to sharp.

I'd like the whole thing to run using node 20.3.0, but can't figure out how to make that happen.

Here’s the actual output:

✔  functions: Finished running predeploy script.
i  functions: preparing codebase typescript for deployment
i  functions: ensuring required API cloudfunctions.googleapis.com is enabled...
i  functions: ensuring required API cloudbuild.googleapis.com is enabled...
i  artifactregistry: ensuring required API artifactregistry.googleapis.com is enabled...
✔  artifactregistry: required API artifactregistry.googleapis.com is enabled
✔  functions: required API cloudfunctions.googleapis.com is enabled
✔  functions: required API cloudbuild.googleapis.com is enabled
i  functions: Loading and analyzing source code for codebase typescript to determine what to deploy
Serving at port 8928

Error: Could not load the "sharp" module using the darwin-x64 runtime
Possible solutions:
- Please upgrade Node.js:
    Found 18.5.0
    Requires ^18.17.0 || ^20.3.0 || >=21.0.0
- Consult the installation documentation:
    See https://sharp.pixelplumbing.com/install
    at Object.<anonymous> (/Users/troy/Projects/field-tracker-admin-console/functions/node_modules/sharp/lib/sharp.js:114:9)
    at Module._compile (node:internal/modules/cjs/loader:1112:14)
    at Module._compile (pkg/prelude/bootstrap.js:1890:32)
    at Module._extensions..js (node:internal/modules/cjs/loader:1166:10)
    at Module.load (node:internal/modules/cjs/loader:988:32)
    at Module._load (node:internal/modules/cjs/loader:834:12)
    at Module.require (node:internal/modules/cjs/loader:1012:19)
    at Module.require (pkg/prelude/bootstrap.js:1851:31)
    at require (node:internal/modules/cjs/helpers:102:18)
    at Object.<anonymous> (/Users/troy/Projects/field-tracker-admin-console/functions/node_modules/sharp/lib/constructor.js:10:1)


Error: Functions codebase could not be analyzed successfully. It may have a syntax or runtime error

Solution

  • I took a deep dive into nodejs, npm, and a side trip through yarn to figure this out.

    The fundamental issue was that I needed a specific node version and using the globally installed firebase would not support that. I learned that firebase-tools has a specific nodejs version built in so that it can be standalone and not a pain for most cases. But in my case, I needed a slightly newer node version than was in the latest firebase-tools. It seems firebase-tools version 13.1.0 has node 18.5.0 built in, but I needed at least 18.7.0 for my dependencies. I ended up using nodejs 20.11.0.

    It seemed like "runtime": "nodejs20" in firebase.json would do the trick, but it did not. I don't know enough about the situation to say it's a bug in firebase-tools, nodejs, or npm or if I'm just missing something.

    Anyhow, here's how I resolved it:

    With firebase-tools installed globally, the firebase command uses its packaged node version when deploying functions.

    To use a specific version of node, install firebase-tools via npm and then execute it from the local (non packaged) nodejs.

    Step by step:

    1. Optionally install asdf to manage the versions of nodejs and other tools used in various projects

    2. Install the needed version of nodejs - here's how with asdf:

      cd <project root>
      asdf add plugin nodejs
      asdf install nodejs 20.11.0
      asdf local nodejs 20.11.0
      
    3. In the project's functions directory, use asdf again to use the same version of node

      asdf local nodejs 20.11.0
      
    4. In the project's root, use npm to install firebase-tools

      npm install firebase-tools
      
    5. Update firebase.json and functions/package.json to reference the node version

      firebase.json

      "functions": [
        {
          "runtime":"nodejs20",
          <- snip ->
        }]
      

      functions/package.json

      "engine":{"node":">=20"},
      
    6. In the project's root, add some convenience scripts to package.json to have it launch the local firebase in the local nodejs version:

      package.json

      "scripts": {
        "firebase": "firebase",
        "df": "firebase deploy --only functions",
        < add more here as needed >
      }  
      

      Those "scripts" have node_modules/.bin in their path, so, for example, the firebase script runs node_modules/.bin/firebase in the local nodejs.

      I added the df script because I kept forgetting to add the double dashes to make --only functions work. Without those extra double dashes, the switch applies to npm in stead of firebase.

      Here's how to run those scripts in the project root:

      % npm run df # same as `npm run -- firebase deploy --only functions`
      % npm run firebase -V # check the version
      
    7. I think that's it - all the usual firebase commands now work using npm run -- firebase .... Add scripts as you see fit to make it easier to type them correctly.

    Thanks go to @DougStevenson for prodding me in the right direction!