JavaScript 应用的流水线

本文介绍 Azure Pipelines 如何与 JavaScript 应用配合使用。 Microsoft托管的代理 预安装常见的 JavaScript 生成、测试和部署工具,例如 npm、Node.js、Yarn 和 Gulp,而无需设置任何基础结构。 还可以配置 自承载代理

若要快速创建适用于 JavaScript 的管道,请参阅 JavaScript 快速入门

节点工具安装程序

若要安装未预安装的 Node.js 和 npm 版本,或在自承载代理上安装这些工具:

若要安装特定的 Node.js 版本,请将以下代码添加到 azure-pipelines.yml 文件中:

- task: UseNode@1
  inputs:
    version: '16.x' # replace with the version you need

注意

每次运行管道时,这项任务可能需要大量时间来更新到较新的次要版本。 Microsoft托管代理定期更新,因此请仅使用此任务安装未预安装的特定 Node 版本。 若要了解Microsoft托管代理上预安装的 Node.js 和 npm 版本,请参阅 软件

使用多个节点版本

可以使用具有策略的 “使用 Node.js 生态系统 v1 ”任务 matrix 在多个版本的 Node.js上生成和测试应用。 有关详细信息,请参阅 多作业配置

pool:
  vmImage: 'ubuntu-latest'
strategy:
  matrix:
    node_16_x:
      node_version: 16.x
    node_13_x:
      node_version: 18.x

steps:
- task: UseNode@1
  inputs:
    version: $(node_version)

- script: npm install

依赖项工具安装

如果项目中具有开发依赖项工具 package.jsonpackage-lock.json 文件,请通过 npm 安装工具和依赖项。 项目文件定义工具的确切版本,独立于生成代理上存在的其他版本。

若要在生成代理上安装这些工具,请在管道中使用 脚本npm 任务或 命令行 任务。

使用脚本:

- script: npm install --only=dev

若要使用 npm 任务,

- task: Npm@1
  inputs:
     command: 'install'

以这种方式安装的工具使用 npm npx 包运行器来检测其 PATH 解析中的工具。 以下示例调用 mocha 测试运行程序,并使用开发依赖项版本,而非通过 npm install -g 全局安装的版本。

- script: npx mocha

若要安装项目在 package.json中未设置为开发依赖项的工具,请从管道中的脚本调用 npm install -g 。 以下示例使用 安装最新版本的 npm。 然后,管道中的其他脚本可以使用 Angular ng 命令。

- script: npm install -g @angular/cli

注意

在 Microsoft 托管的 Linux 代理上,在命令前面加上 sudo,例如 sudo npm install -g

这些工具安装任务每次运行管道时都会运行,因此请注意它们对生成时间的影响。 如果开销严重影响构建性能,请考虑使用具有所需工具版本的预配置的自承载代理

注意

每次运行管道时,这些工具安装任务都会执行,因此请注意它们可能对构建时间造成的影响。

依赖包下载

可以使用 Yarn 或 Azure Artifacts 从公共 npm 注册表或 你在 *.npmrc 文件中指定的专用 npm 注册表下载包。 若要指定 npm 注册表,请将其 URL 添加到代码存储库中的 *.npmrc 文件。

使用 npm

可以通过以下方式使用 npm 在管道中下载生成包:

  • 若要以最简单的方式下载不进行身份验证的包,请直接运行 npm install
  • 若要使用经过身份验证的注册表,请添加 npm 任务。
  • 若要从任务运行程序 Gulp、Grunt 或 Maven 内部运行 npm install ,请使用 npm 身份验证 任务。

注意

如果 npm 源使用身份验证,则必须在 Azure DevOps 项目设置中的“服务”选项卡上创建 npm 服务连接来管理其凭据。

若要直接安装 npm 包,请在 azure-pipelines.yml中使用以下脚本。 如果您的生成代理不需要开发依赖项,可以通过在 npm install 中添加 --only=prod 选项来加快生成时间。

- script: npm install --only=prod

若要使用 *.npmrc 文件中指定的专用注册表,请将 Npm@1 任务添加到 azure-pipelines.yml

- task: Npm@1
  inputs:
    customEndpoint: <Name of npm service connection>

若要通过任务运行程序(如 Gulp)将注册表凭据传递给 npm 命令,请在调用任务运行程序之前将 npmAuthenticate@0 任务添加到 azure-pipelines.yml

- task: npmAuthenticate@0
  inputs:
    customEndpoint: <Name of npm service connection>

注意

Microsoft托管的代理 在每次构建中使用新计算机。 还原依赖项可能需要很长时间。 若要缓解此问题,可以将 Azure Artifacts 或自承载代理与包缓存配合使用。

如果由于连接问题导致从 npm 注册表还原包时构建偶尔失败,您可以使用 Azure Artifacts 结合 上游源 来缓存这些包。 Azure Artifacts 自动使用管道的凭据,这些凭据通常派生自 Project Collection 生成服务 帐户。

注意

还原依赖项可能需要很长时间。 若要缓解此问题,可以将 Azure Artifacts 或自承载代理与包缓存配合使用。

如果偶尔因为从 npm 注册表还原包时的连接问题导致生成失败,您可以使用 Azure Artifacts 和 上游源 来缓存这些包。 Azure Artifacts 自动使用管道的凭据,这些凭据通常派生自 Project Collection 生成服务 帐户。

使用 Yarn

使用脚本安装 Yarn 来还原依赖项。 Yarn 预安装在某些 Microsoft托管代理上。 可以像任何其他工具一样在自承载代理上安装和配置 Yarn。

- script: yarn install

还可以在管道中使用 CLIBash 任务来调用 Yarn。

JavaScript 编译器

JavaScript 应用使用编译器(如 BabelTypeScripttsc 编译器)将源代码转换为 Node.js 运行时或 Web 浏览器中可以使用的版本。 如果在项目中设置了 脚本对象package.json 文件来运行编译器,则可以在管道中调用它。

- script: npm run compile

还可以使用脚本直接从管道调用编译器。 这些命令从克隆的源代码存储库的根目录运行。

- script: tsc --target ES6 --strict true --project tsconfigs/production.json

如果项目 package.json 定义编译脚本,则可以使用 npm 任务生成代码。 如果未定义编译脚本,可以使用 Bash 任务编译代码。

单元测试

可以将管道配置为运行 JavaScript 测试,以便它们以 JUnit XML 格式生成结果。 然后,可以使用 “发布测试结果 ”任务发布结果。

如果测试框架不支持 JUnit 输出,请通过合作伙伴报告模块(如 mocha-junit-reporter)添加支持。 可以更新测试脚本以使用 JUnit 记者,或者如果记者支持命令行选项,请将这些选项传递到任务定义中。

下表列出了最常用的测试运行程序和可用于生成 XML 结果的报告器:

测试运行程序 XML 报告生成器
摩卡 mocha-junit-reporter
cypress-multi-reporters
茉莉花 jasmine-reporters
Jest jest-junit
jest-junit-reporter
Karma karma-junit-reporter
Ava tap-xunit

以下示例使用 mocha-junit-reporter,并使用脚本直接调用 mocha test。 此脚本在 ./test-results.xml的默认位置生成 JUnit XML 输出。

- script: mocha test --reporter mocha-junit-reporter

如果在项目的 package.json 文件中定义了 test 脚本,则可以使用 npm test 来调用它。

- script: npm test

发布测试结果

若要发布测试结果,请使用 “发布测试结果 ”任务。

- task: PublishTestResults@2
  condition: succeededOrFailed()
  inputs:
    testRunner: JUnit
    testResultsFiles: '**/test-results.xml'

发布代码覆盖率结果

如果测试脚本运行代码覆盖率工具(如 伊斯坦布尔),请添加 发布代码覆盖率结果 任务。 然后,可以在生成摘要中查看覆盖率指标,并下载 HTML 报表以供进一步分析。

任务需要 Cobertura 或 JaCoCo 报告输出。 确保您的代码覆盖率工具使用必要的选项运行,以生成正确的输出,例如--report cobertura

以下示例使用伊斯坦布尔命令行接口 nycmocha-junit-reporter,并调用 npm test

- script: |
    nyc --reporter=cobertura --reporter=html \
    npm test -- --reporter mocha-junit-reporter --reporter-options mochaFile=./test-results.xml
  displayName: 'Build code coverage report'

- task: PublishCodeCoverageResults@2
  inputs: 
    summaryFileLocation: '$(System.DefaultWorkingDirectory)/**/*coverage.xml'

端到端浏览器测试

管道可以使用 ProtractorKarma 等工具在无外设浏览器中运行测试,然后发布测试结果。 若要配置浏览器测试和发布结果,请执行以下步骤:

  1. 在生成代理上安装无外设浏览器测试驱动程序(如无外设 Chrome 或 Firefox)或浏览器模拟工具(如 PhantomJS)。
  2. 根据工具的文档,将测试框架配置为使用你的无头浏览器或驱动程序选项。
  3. 将测试框架配置为输出 JUnit 格式的测试结果,通常通过报告插件或配置来实现。
  4. 添加脚本或 CLI 任务以启动无外设浏览器实例。
  5. 在管道阶段以及单元测试中运行端到端测试。
  6. 使用相同的发布测试结果任务,连同单元测试一起 发布结果

打包和交付

生成并测试应用后,可以:

  • 将生成输出上传到 Azure Pipelines。
  • 创建并发布 npm 或 Maven 包。
  • 将生成输出打包到 ZIP 存档中,以便部署到 Web 应用程序。

将文件发布到 Azure Pipelines

若要上传整个工作目录,请将 发布生成项目 任务添加到 azure-pipelines.yml 文件。

- task: PublishBuildArtifacts@1
  inputs:
    PathtoPublish: '$(System.DefaultWorkingDirectory)'

若要上传文件子集,请先使用 “复制 文件”任务将所需文件从工作目录复制到过渡目录,然后使用 “发布生成项目 ”任务。

- task: CopyFiles@2
  inputs:
    SourceFolder: '$(System.DefaultWorkingDirectory)'
    Contents: |
      **\*.js
      package.json
    TargetFolder: '$(Build.ArtifactStagingDirectory)'

- task: PublishBuildArtifacts@1

将模块发布到 npm 注册表

如果项目的输出是 npm 供其他项目使用的模块,并且不是 Web 应用程序,请使用 npm 任务将模块发布到本地注册表或公共 npm 注册表。 每次发布时提供唯一的名称/版本组合。

以下示例使用脚本发布到公共 npm 注册表。 该示例假定通过版本控制中的 package.json 文件管理版本信息,例如 npm 版本

- script: npm publish

以下示例发布到您存储库的 *.npmrc 文件中定义的自定义注册表。 设置 npm 服务连接,以在生成运行时将身份验证凭据注入连接。

- task: Npm@1
  inputs:
     command: publish
     publishRegistry: useExternalRegistry
     publishEndpoint: https://my.npmregistry.com

以下示例将模块发布到 Azure DevOps Services 包管理源。

- task: Npm@1
  inputs:
     command: publish
     publishRegistry: useFeed
     publishFeed: https://my.npmregistry.com

有关版本控制和发布 npm 包的详细信息,请参阅 发布 npm 包 以及如何在 生成过程中对 npm 包进行版本控制

打包和部署 Web 应用

可以将应用程序打包为将所有模块与中间输出和依赖项捆绑到可供部署的静态资产中。 编译和测试后添加管道阶段,以运行 Webpack 或 Angular CLI ng 生成等工具。

以下示例调用 webpack。 若要使此过程正常工作,请确保 webpackpackage.json 项目文件中配置为开发依赖项。 此脚本使用默认配置运行 webpack ,除非项目根文件夹中有 webpack.config.js 文件。

- script: webpack

以下示例使用 npm run build 来调用项目 package.json 文件中定义的 build 脚本对象。 在项目中使用脚本对象会将生成逻辑移到源代码中,然后移出管道。

- script: npm run build

还可以在管道中使用 CLIBash 任务来调用打包工具,例如 webpack 或 Angular ng build

若要创建准备发布到 Web 应用的 *.zip 文件存档,请使用 存档文件 任务。

- task: ArchiveFiles@2
  inputs:
    rootFolderOrFile: '$(System.DefaultWorkingDirectory)'
    includeRootFolder: false

若要将此存档发布到 Web 应用,请参阅 使用 Azure Pipelines 部署到 Azure 应用服务

JavaScript 框架

可以在管道中安装包以支持各种 JavaScript 框架。

Angular

对于 Angular 应用,可以运行特定于 Angular 的命令,例如 ng testng buildng e2e。 要在管道中使用 Angular CLI 命令,请在生成代理上安装 angular/cli npm 包

- script: |
    npm install -g @angular/cli
    npm install
    ng build --prod

注意

在 Microsoft 托管的 Linux 代理上,在命令前面加上 sudo,例如 sudo npm install -g

对于在管道中需要浏览器运行的测试(例如以 ng test 命令运行 Karma),请使用无头浏览器而不是标准浏览器。 在 Angular 启动器应用中:

  • browsers 项目文件中的 条目从 browsers: ['Chrome'] 更改为 browsers: ['ChromeHeadless']
  • singleRun 项目文件中的 条目从 false 更改为 true。 此更改有助于确保 Karma 进程在运行后停止。

React 和 Vue

React 和 Vue 应用的所有依赖项都在 package.json 文件中捕获。 azure-pipelines.yml文件包含标准npm脚本。

- script: |
    npm install
  displayName: 'npm install'

- script: |
    npm run build
  displayName: 'npm build'

生成文件位于新文件夹中,Vue 使用 dist,而 React 使用 build。 以下示例生成一个可供发布的项目 www。 管道使用Node.js复制文件发布生成工件任务。

trigger:
- main

pool:
  vmImage: 'ubuntu-latest'

steps:
- task: UseNode@1
  inputs:
    version: '16.x'
  displayName: 'Install Node.js'

- script: npm install
  displayName: 'npm install'

- script: npm run build
  displayName: 'npm build'

- task: CopyFiles@2
  inputs:
    Contents: 'build/**' # Pull the build directory (React)
    TargetFolder: '$(Build.ArtifactStagingDirectory)'

- task: PublishBuildArtifacts@1
  inputs: 
    PathtoPublish: $(Build.ArtifactStagingDirectory) # dist or build files
    ArtifactName: 'www' # output artifact named www

若要发布应用,请将发布任务指向 distbuild 工件,并使用 Azure Web 应用 任务。

Webpack

可以使用 Webpack 配置文件来指定编译器(如 Babel 或 TypeScript),将 JavaScript XML(JSX)或 TypeScript 转译为普通 JavaScript,以及捆绑应用。

- script: |
    npm install webpack webpack-cli --save-dev
    npx webpack --config webpack.config.js

生成任务运行程序

通常使用 GulpGrunt 作为任务运行程序来生成和测试 JavaScript 应用。

Gulp

Gulp 预安装在Microsoft托管代理上。

可以在 YAML 管道文件中运行 gulp 该命令。

- script: gulp # add any needed options

如果 gulpfile.js 文件中的步骤需要使用 npm 注册表进行身份验证,请添加 npm 身份验证 任务。

- task: npmAuthenticate@0
  inputs:
    customEndpoint: <Name of npm service connection>

- script: gulp

若要将 JUnit 或 xUnit 测试结果发布到服务器,请添加 “发布测试结果 ”任务。

- task: PublishTestResults@2
  inputs:
    testResultsFiles: '**/TEST-RESULTS.xml'
    testRunTitle: 'Test results for JavaScript using gulp'

若要将代码覆盖率结果发布到服务器,请添加 “发布代码覆盖率结果 ”任务。 可以在生成摘要中找到覆盖率指标,并下载 HTML 报告以供进一步分析。

- task: PublishCodeCoverageResults@1
  inputs: 
    codeCoverageTool: Cobertura
    summaryFileLocation: '$(System.DefaultWorkingDirectory)/**/*coverage.xml'
    reportDirectory: '$(System.DefaultWorkingDirectory)/**/coverage'

Grunt

Grunt 预安装在Microsoft托管的代理上。

可以在 YAML 文件中运行 grunt 命令。

- script: grunt # add any needed options

如果 Gruntfile.js 文件中的步骤需要使用 npm 注册表进行身份验证,请添加 npm 身份验证 任务。

- task: npmAuthenticate@0
  inputs:
    customEndpoint: <Name of npm service connection>

- script: grunt

Troubleshooting

如果可以在开发计算机上生成项目,但无法在 Azure Pipelines 中生成项目,请浏览以下潜在原因和纠正措施。

  • 检查开发计算机上的 Node.js 版本和任务运行程序是否与代理上的版本匹配。

    可以在管道中包含诸如node --version的命令行脚本,以便检查代理上安装的版本。 使用 Use Node.js 任务在代理上安装同一版本,或运行 npm install 命令来更新工具版本。

  • 如果在还原包时,生成会出现间歇性失败,则可能是 npm 注册表出现问题,或者 Azure 数据中心与注册表之间存在网络连接问题。 了解将 Azure Artifacts 与 npm 注册表一起使用作为上游源是否可以提高生成的可靠性。

  • 如果使用 nvm 管理不同版本的 Node.js,请考虑改为切换到 “使用 Node.js”(UseNode@1) 任务。 nvm 在 macOS 映像中是为了历史原因而安装的。 nvm 通过添加 shell 别名和更改 PATH来管理多个 Node.js 版本,这与 Azure Pipelines 在新进程中运行每个任务的方式交互得很差。 有关详细信息,请参阅 管道运行

    Use Node.js 任务正确地处理了此模型。 但是,如果工作需要使用 nvm,可以将以下脚本添加到每个管道的开头:

    steps:
    - bash: |
        NODE_VERSION=16  # or your preferred version
        npm config delete prefix  # avoid a warning
        . ${NVM_DIR}/nvm.sh
        nvm use ${NODE_VERSION}
        nvm alias default ${NODE_VERSION}
        VERSION_PATH="$(nvm_version_path ${NODE_VERSION})"
        echo "##vso[task.prependPath]$VERSION_PATH"
    

    然后,node 和其他命令行工具适用于管道作业的其余部分。 在使用 nvm 命令的每个步骤中,使用以下代码作为脚本开头:

    - bash: |
        . ${NVM_DIR}/nvm.sh
        nvm <command>
    

FAQ

如何修复管道故障,并显示消息“严重错误:CALL_AND_RETRY_LAST分配失败 - JavaScript 堆内存不足”?

当 Node.js 包超出内存使用限制时,会发生此失败类型。 若要解决此问题,请添加一个变量,例如 NODE_OPTIONS 并为其赋值 --max_old_space_size=16384

如何在构建过程中对 npm 包进行版本控制?

一个选项是使用版本控制和 npm 版本的组合。 在管道运行结束时,可以使用新版本更新存储库。 以下 YAML 管道具有 GitHub 存储库,包部署到 npmjs。 如果 npmjs 上的包版本与 package.json 文件不匹配,生成将失败。

variables:
    MAP_NPMTOKEN: $(NPMTOKEN) # Mapping secret var

trigger:
- none

pool:
  vmImage: 'ubuntu-latest'

steps: # Checking out connected repo
- checkout: self
  persistCredentials: true
  clean: true
    
- task: npmAuthenticate@0
  inputs:
    workingFile: .npmrc
    customEndpoint: 'my-npm-connection'
    
- task: UseNode@1
  inputs:
    version: '16.x'
  displayName: 'Install Node.js'

- script: |
    npm install
  displayName: 'npm install'

- script: |
    npm pack
  displayName: 'Package for release'

- bash: | # Grab the package version
    v=`node -p "const p = require('./package.json'); p.version;"`
    echo "##vso[task.setvariable variable=packageVersion]$v"

- task: CopyFiles@2
  inputs:
      contents: '*.tgz'
      targetFolder: $(Build.ArtifactStagingDirectory)/npm
  displayName: 'Copy archives to artifacts staging directory'

- task: CopyFiles@2
  inputs:
    sourceFolder: '$(Build.SourcesDirectory)'
    contents: 'package.json' 
    targetFolder: $(Build.ArtifactStagingDirectory)/npm
  displayName: 'Copy package.json'

- task: PublishBuildArtifacts@1 
  inputs:
    PathtoPublish: '$(Build.ArtifactStagingDirectory)/npm'
    artifactName: npm
  displayName: 'Publish npm artifact'

- script: |  # Config can be set in .npmrc
    npm config set //registry.npmjs.org/:_authToken=$(MAP_NPMTOKEN) 
    npm config set scope "@myscope"
    # npm config list
    # npm --version
    npm version patch --force
    npm publish --access public

- task: CmdLine@2 # Push changes to GitHub (substitute your repo)
  inputs:
    script: |
      git config --global user.email "username@contoso.com"
      git config --global user.name "Azure Pipeline"
      git add package.json
      git commit -a -m "Test Commit from Azure DevOps"
      git push -u origin HEAD:main