You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何pnpm在Azure App Service中安装失败?

问题:切换到pnpm后部署出现预安装脚本错误

切换步骤

我按照以下步骤将项目从npm切换为pnpm:

  • npm install pnpm -g
  • 删除node_modules
  • 删除package-lock.json
  • pnpm install

同时在package.json中添加了预安装脚本:

"preinstall": "npx only-allow pnpm",

报错情况

部署应用时遇到错误,报错信息显示:

Error: Could not find the package "only-allow" required by your preinstall script. Please install it first or remove the preinstall script from package.json.

相关配置文件

package.json

{
  "name": "credium-gersthofen-app",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "preinstall": "npx only-allow pnpm",
    "predev": "node scripts/babel/backup.js",
    "prebuild": "node scripts/babel/backup.js",
    "precypress-run-component": "node scripts/babel/restore.js",
    "precypress-open-component": "node scripts/babel/restore.js",
    "precypress-run-e2e": "node scripts/babel/restore.js",
    "precypress-open-e2e": "node scripts/babel/restore.js",
    "dev": "next dev",
    "build": "next build",
    "test": "jest",
    "test-watch": "jest --watch",
    "start": "next start",
    "lint": "next lint",
    "lint-staged": "npx lint-staged",
    "lint-style": "npx stylelint src/*.css src/**/**/*.css",
    "cypress-open-component": "cypress open --component",
    "cypress-open-e2e": "cypress open --e2e",
    "cypress-run-component": "cypress run --component --headless",
    "storybook": "storybook dev -p 6006",
    "build-storybook": "storybook build"
  },
  "dependencies": {
    "@headlessui/tailwindcss": "^0.2.0",
    "@mdx-js/loader": "^3.0.0",
    "@mdx-js/react": "^3.0.0",
    "@next/mdx": "^14.0.2",
    "@radix-ui/react-accordion": "^1.1.2",
    "@radix-ui/react-dialog": "^1.0.5",
    "@radix-ui/react-dropdown-menu": "^2.0.6",
    "@radix-ui/react-label": "^2.0.2",
    "@radix-ui/react-separator": "^1.0.3",
    "@radix-ui/react-slot": "^1.0.2",
    "@reduxjs/toolkit": "^1.9.5",
    "@sentry/nextjs": "^7.80.0",
    "@testing-library/jest-dom": "^6.1.2",
    "@tremor/react": "^3.11.0",
    "@trivago/prettier-plugin-sort-imports": "^4.3.0",
    "@types/mdx": "^2.0.10",
    "@types/node": "20.4.0",
    "@types/react": "^18.2.14",
    "@types/react-dom": "^18.2.6",
    "chai-colors": "^1.0.1",
    "chart.js": "^4.3.0",
    "class-variance-authority": "^0.7.0",
    "classnames": "^2.3.2",
    "clsx": "^2.0.0",
    "contentlayer": "^0.3.4",
    "eslint-config-next": "^14.0.2",
    "eslint-plugin-simple-import-sort": "^10.0.0",
    "framer-motion": "^10.16.4",
    "i18next": "^23.4.3",
    "i18next-browser-languagedetector": "^7.1.0",
    "i18next-resources-to-backend": "^1.1.4",
    "istanbul-lib-coverage": "^3.2.0",
    "jest-environment-jsdom": "^29.7.0",
    "lucide-react": "^0.292.0",
    "next": "^14.0.2",
    "next-contentlayer": "^0.3.4",
    "next-themes": "^0.2.1",
    "normalize.css": "^8.0.1",
    "react": "^18.2.0",
    "react-chartjs-2": "^5.2.0",
    "react-dom": "^18.2.0",
    "react-i18next": "^13.0.3",
    "react-icons": "^4.10.1",
    "react-redux": "^8.1.1",
    "react-to-print": "^2.14.15",
    "redux-persist": "^6.0.0",
    "sharp": "^0.32.3",
    "stylelint": "^15.11.0",
    "swr": "^2.2.0",
    "tailwind-merge": "^2.0.0",
    "tailwindcss-animate": "^1.0.7",
    "tsconfig-paths-jest": "^0.0.1",
    "whatwg-fetch": "^3.6.18",
    "zod": "^3.22.4"
  },
  "devDependencies": {
    "@babel/preset-react": "^7.22.5",
    "@commitlint/cli": "^17.7.2",
    "@commitlint/config-conventional": "^17.7.0",
    "@cypress/code-coverage": "^3.11.0",
    "@istanbuljs/nyc-config-typescript": "^1.0.2",
    "@storybook/addon-essentials": "^7.5.1",
    "@storybook/addon-interactions": "^7.5.1",
    "@storybook/addon-links": "^7.5.1",
    "@storybook/addon-onboarding": "^1.0.8",
    "@storybook/blocks": "^7.5.1",
    "@storybook/nextjs": "^7.5.1",
    "@storybook/react": "^7.5.1",
    "@storybook/testing-library": "^0.2.2",
    "@testing-library/react": "^14.1.2",
    "@typescript-eslint/eslint-plugin": "^6.3.0",
    "@typescript-eslint/parser": "^6.3.0",
    "autoprefixer": "^10.4.16",
    "babel-plugin-istanbul": "^6.1.1",
    "cypress": "^13.0.0",
    "cypress-react-selector": "^3.0.0",
    "eslint": "^8.46.0",
    "eslint-config-prettier": "^9.0.0",
    "eslint-plugin-cypress": "^2.14.0",
    "eslint-plugin-import": "^2.28.0",
    "eslint-plugin-jsx-a11y": "^6.7.1",
    "eslint-plugin-react": "^7.33.1",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-storybook": "^0.6.15",
    "fetch-mock": "^9.11.0",
    "husky": "^8.0.3",
    "jest": "^29.7.0",
    "jest-next-dynamic": "^1.0.1",
    "lint-staged": "^13.2.3",
    "msw": "^1.2.5",
    "node-fetch": "^3.3.2",
    "nyc": "^15.1.0",
    "postcss": "^8.4.31",
    "prettier": "^3.0.1",
    "storybook": "^7.5.1",
    "stylelint-config-standard": "^34.0.0",
    "stylelint-prettier": "^4.0.2",
    "tailwindcss": "^3.3.5",
    "ts-jest": "^29.1.1",
    "ts-node": "^10.9.1",
    "typescript": "^5.1.6"
  }
}

Azure Pipeline执行状态

Azure Pipeline的所有阶段(安装、测试、构建、归档发布)均执行成功,无报错。

Azure Pipeline配置

# Node.js with React Pipeline
# Build a Node.js project that uses React using Azure DevOps.

trigger:
  - dev

pool:
  vmImage: ubuntu-latest

stages:
  - stage: Install
    displayName: "Install"
    jobs:
      - job: InstallJob
        displayName: "Install Node.js and Dependencies"
        steps:
          - task: NodeTool@0
            inputs:
              versionSpec: "^20.x"
            displayName: "Install Node.js"
          - script: |
              npm install -g pnpm
              pnpm install
            displayName: "Install Dependencies"

  - stage: Tests
    displayName: "Run Unit and Component Tests"
    jobs:
      - job: TestsJob
        displayName: "Run Unit and Component Tests"
        steps:
          - script: |
              npm install -g pnpm
              pnpm install
              pnpm test
              pnpm cypress-run-component
            displayName: "Run Unit and Component Tests"

  - stage: Build
    displayName: "Build Application"
    jobs:
      - job: BuildJob
        displayName: "Build Application"
        steps:
          - script: |
              npm install -g pnpm
              pnpm install
              pnpm build
            displayName: "Build Application"

  - stage: ArchiveAndPublish
    displayName: "Archive and Publish Artifacts"
    jobs:
      - job: ArchiveAndPublishJob
        displayName: "Archive Build Artifacts and Publish"
        steps:
          - task: ArchiveFiles@2
            inputs:
              rootFolderOrFile: "."
              includeRootFolder: false
              archiveType: "zip"
              archiveFile: "$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip"
              replaceExistingArchive: true
          - task: PublishBuildArtifacts@1
            inputs:
              PathtoPublish: "$(Build.ArtifactStagingDirectory)"
              ArtifactName: "drop"
              publishLocation: "Container"

解决方案

原因分析

预安装脚本使用npx only-allow pnpm时,部署环境可能无法通过npx临时获取该包,或者网络/权限问题导致下载失败。另外,pnpm的预安装脚本执行逻辑和npm略有不同,直接使用npx可能触发问题。

解决方法

  1. 将only-allow安装为开发依赖
    执行命令:

    pnpm add -D only-allow
    

    然后修改preinstall脚本为:

    "preinstall": "only-allow pnpm",
    

    这样部署环境安装依赖后就能直接调用该工具,无需npx临时下载。

  2. 使用无依赖的包管理器判断脚本
    无需额外安装依赖,直接通过Node.js判断当前包管理器是否为pnpm,修改preinstall脚本为:

    "preinstall": "node -e \"if(process.env.npm_execpath.indexOf('pnpm') === -1) throw new Error('请使用pnpm安装依赖!')\""
    
  3. 调整Pipeline预安装逻辑(可选)
    如果是内部信任环境,可以在Pipeline的安装步骤中提前全局安装only-allow:

    npm install -g pnpm only-allow
    

    但更推荐前两种方法,避免环境差异导致的问题。


内容的提问来源于stack exchange,提问作者Ala Eddine Menai

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 17:47:34