Husky prepare script failing firebase function deployment
Asked Answered
N

3

6

I have installed husky in my npm project as a prepare script like below

{
  "name": "functions",
  "scripts": {
    "build": "tsc",
    "start": "npm run serve",
    "deploy": "firebase deploy --only functions",
    "prepare": "husky install functions/.husky"
  }
  "dependencies": {
    "firebase-admin": "^11.4.1",
    "firebase-functions": "^4.1.1",
  },
  "devDependencies": {
    "husky": "^8.0.2",
    "typescript": "^4.9.4"
  }
}

husky is declared as devDependencies as this npm module is only required while local development and has no need in runtime app.

So when I run npm run deploy, I get the below error

i  functions: updating Node.js 16 function funName(us-central1)...
Build failed:

> prepare
> husky install functions/.husky

sh: 1: husky: not found
npm ERR! code 127
npm ERR! path /workspace
npm ERR! command failed
npm ERR! command sh -c -- husky install functions/.husky

This error clearly states that husky is not installed.

One possible solution is to create a prepare.js script which checks if the script is running while in local development or in the firebase server(to prepare the project) and then conditionally run the husky npm module command

Nitrite answered 28/12, 2022 at 19:56 Comment(3)
If you're saying that it works when listed under "dependencies" but does not only when you move it to "devDependencies", that sounds like a bug report or feature request to send directly to Firebase. It's entirely possible that what you're trying to do is just not supported. I suggest filing here: github.com/firebase/firebase-tools/issuesErysipelas
As suggested by @DougStevenson you can file bug here as StackOverflow is not proper forum for these kind of issuesRomeliaromelle
Hi @DougStevenson sorry, I just tried.. It doesn't work even the dependencies block.. Let me update the question. Now it seems like I am missing some setup of husky with firebase functionsNitrite
B
11

I just ran into this exact same issue but with tsc. I'm not sure why, but the prepare script is also run in the cloud function (not just locally) while deploying. However, considering you likely have the node_modules directory in the functions.ignore list in the firebase.json, the node_modules directory doesn't get uploaded as part of the deployment and so the husky package isn't visible to the script when it gets run in the cloud function environment.

You likely don't need the husky script to be run in the function environment either way, so you can add a condition to check for an environment variable that is usually set in the function environment (I am using the GOOGLE_FUNCTION_TARGET environment variable in my case), and only run the command if that environment is not set. You also need to wrap this in a bash script instead of adding it inline in the package.json because of how the prepare script is run.

For example, here's the content of my scripts/prepare.sh file.

#!/bin/bash
set -o verbose

# Only run if the GOOGLE_FUNCTION_TARGET is not set
if [[ -z "$GOOGLE_FUNCTION_TARGET" ]]; then
    npm run build
fi

Then I use it in my package.json prepare script:

// ...
"prepare": "./scripts/prepare.sh",
// ...

There's potentially a better solution to this, but this is how I got it to work for me. Hope this helps!

Branca answered 29/12, 2022 at 22:40 Comment(2)
Hi, Samuel GOOGLE_FUNCTION_TARGET is exactly what I was looking for. I have tested it and it works well. Thanks a lotNitrite
Thanks. This is exact issue why firebase was failing to update function. It failed during Cloud Build, which performs npm ci, which runs prepare script, which in my case had tsc to transpile typescript, which is only devDependencies.Quadri
N
1

This SO answer is spot on and uses bash script. I used the same concept mentioned in the answer to write the prepare script in js in scripts/ folder with the name of prepare.mjs

"use-strict";
import * as path from "path";
import { fileURLToPath } from "url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// Firebase sets the GOOGLE_FUNCTION_TARGET when running in the firebase environment
const isFirebase = process.env.GOOGLE_FUNCTION_TARGET !== undefined;
if (!isFirebase) {
  process.chdir("../"); // path where .git file is present. In my case it was ..
  const husky = await import("husky");
  husky.install("functions/.husky"); // path where .husjy config file is present w.r.t. .git file
}

And in my package.json I have used the above script as follows

{
  "name": "functions",
  "scripts": {
    "build": "tsc",
    "start": "npm run serve",
    "deploy": "firebase deploy --only functions",
    "prepare": "node ./scripts/prepare.mjs"
  }
  "dependencies": {
    "firebase-admin": "^11.4.1",
    "firebase-functions": "^4.1.1",
  },
  "devDependencies": {
    "husky": "^8.0.2",
    "typescript": "^4.9.4"
  }
}

This uses the environment variable(GOOGLE_FUNCTION_TARGET) documented by the Google at the doc

Nitrite answered 30/12, 2022 at 17:8 Comment(0)
G
1

like the previous answer, you can work with GOOGLE_FUNCTION_TARGET env variable. In my case, I edit my "prepare" node script :

"scripts": {
    "prepare": "if [ -z \"$GOOGLE_FUNCTION_TARGET\" ]; then cd .. && husky install functions/.husky; fi"
},
Guidotti answered 22/11, 2023 at 10:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.