为 Azure Pipelines 自定义 JavaScript

可以使用 Azure Pipelines 生成 JavaScript 应用,而无需设置自己的任何基础结构。 通常用于生成、测试和运行 JavaScript 应用(如 npm、Node、Yarn 和 Gulp)的工具将预安装在 Azure Pipelines 中的 Microsoft 托管代理上。

有关预安装的 Node.js 和 npm 版本,请参阅 Microsoft 托管代理。 要在 Microsoft 托管的代理上安装这些工具的特定版本,请将“Node 工具安装程序”任务添加到进程的开头。 你还可以使用自托管代理。

若要使用 JavaScript 创建第一个管道,请参阅 JavaScript 快速入门

使用特定版本的 Node.js

如果 Microsoft 托管代理上没有安装需要的 Node.js 和 npm 版本,请使用 Node 工具安装程序任务。 要将以下代码片段添加到 azure-pipelines.yml 文件。

注意

托管代理会定期更新,设置此任务会导致在每次运行管道时花费大量时间更新到较新的次要版本。 仅在管道中需要特定 Node 版本时使用此任务。

- task: UseNode@1
  inputs:
    version: '16.x' # replace this value with the version that you need for your project

如果代理上没有安装需要的 Node.js/npm 版本:

  1. 在管道中,选择“任务”,选择运行生成任务的阶段,然后选择 + 将新任务添加到该阶段。

  2. 在任务目录中,查找并添加“Node 工具安装程序”任务。

  3. 选择任务并指定要安装的 Node.js 运行时版本。

要仅更新 npm 工具,请在生成过程中运行 npm i -g npm@version-number 命令。

使用多个节点版本

可以使用“Node 工具安装程序”任务在多个版本的 Node 上生成和测试应用。

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 任务

使用脚本与 package.json 一起安装

- script: npm install --only=dev

使用 npm 任务与 package.json 一起安装

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

使用 npm npx 包运行器以这种方式运行安装的工具,该运行器在其路径解析中检测以这种方式安装的工具。 以下示例调用 mocha 测试运行程序,但在使用全局安装的(通过 npm install -g)版本之前,会查找作为开发依赖项安装的版本。

- script: npx mocha

若要安装项目需要但未在 package.json 中设置为开发依赖项的工具,请从管道中的脚本阶段调用 npm install -g

以下示例使用 npm 安装最新版本的 Angular CLI。 然后,管道的其余部分可以从其他 script 阶段使用 ng 工具。

注意

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

- script: npm install -g @angular/cli

提示

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

使用管道中的 npm命令行任务在生成代理上安装工具。

管理依赖项

在生成中,使用 Yarn 或 Azure Artifacts 从公共 npm 注册表下载包。 此注册表是在 .npmrc 文件中指定的一种专用 npm 注册表。

使用 npm

可以通过以下方式使用 npm 下载生成的包:

  • 直接在管道中运行 npm install,因为这是从注册表下载包而无需身份验证的最简单方法。 如果生成不需要代理上的开发依赖项就能运行,则可以使用 --only=prod 选项进行 npm install 来加快生成时间。
  • 使用 npm 任务。 使用经过身份验证的注册表时,此任务非常有用。
  • 使用 npm 身份验证任务。 从任务运行程序(Gulp、Grunt 或 Maven)内部运行 npm install 时,此任务非常有用。

如果要指定 npm 注册表,请将 URL 放入存储库的 .npmrc 文件中。 如果源经过身份验证,请在“项目设置”中的“服务”选项卡上创建 npm 服务连接,以管理其凭据。

要在管道中使用脚本安装 npm 包,请将以下代码片段添加到 azure-pipelines.yml

- script: npm install

要使用 .npmrc 文件中指定的专用注册表,请将以下代码片段添加到 azure-pipelines.yml

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

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

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

使用管道中的 npmnpm 身份验证 任务下载和安装包。

如果从 npm 注册表还原包时,生成偶尔因连接问题而失败,则可以将 Azure Artifacts 与上游源配合使用,并缓存包。 连接到 Azure Artifacts 时,会自动使用管道的凭据。 这些凭据通常派生自“项目集合生成服务”帐户。

如果使用 Microsoft 托管代理,则每次运行生成时都会获得一台新计算机,这意味着每次都还原依赖项,这可能需要很长时间。 若要缓解问题,可以使用 Azure Artifacts 或自托管代理 - 然后,便可体会到使用包缓存的好处。

使用 Yarn

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

- script: yarn install

使用管道中的 CLIBash 任务调用 Yarn

运行 JavaScript 编译器

使用编译器(如 BabelTypeScript tsc 编译器)将源代码转换为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 mocha-junit-reporter
cypress-multi-reporters
jasmine 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'

发布代码覆盖率结果

如果测试脚本运行代码覆盖率工具(如 Istanbul),请添加发布代码覆盖率结果任务。 执行此操作时,可以在生成摘要中找到覆盖率指标,并下载 HTML 报告以供进一步分析。 该任务需要 Cobertura 或 JaCoCo 报告输出,因此请确保代码覆盖率工具使用必要的选项运行以生成正确的输出。 例如,--report cobertura

以下示例使用 nyc(Istanbul 命令行接口)以及 mocha-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'

通过管道中的发布测试结果发布代码覆盖率结果任务,使用 Istanbul 发布测试结果以及代码覆盖率结果。

设置“发布测试结果”任务的“控制选项”,以运行该任务,即使上一个任务失败,除非取消部署。

端到端测试浏览器

使用 ProtractorKarma 等工具在无界面浏览器中作为管道的一部分运行测试。 然后,使用以下步骤将生成的结果发布到 Azure DevOps:

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

打包 Web 应用

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

第一个示例调用 webpack。 为此,请确保 webpack 在 package.json 项目文件中配置为开发依赖项。 这样将使用默认配置运行 webpack,除非项目根文件夹中有文件 webpack.config.js

- script: webpack

下一个示例使用 npm 任务调用 npm run build 来调用项目 package.json 中定义的 build 脚本对象。 在项目中使用脚本对象会将生成的逻辑移入源代码并移出管道。

- script: npm run build

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

实施 JavaScript 框架

Angular

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

注意

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

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

将以下任务添加到管道:

  • npm

    • 命令custom
    • 命令和参数: install -g @angular/cli
  • npm

    • 命令install
  • bash

    • 类型:inline
    • 脚本ng build --prod

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

  1. karma.conf.js 项目文件中的 browsers 条目从 browsers: ['Chrome'] 更改为 browsers: ['ChromeHeadless']

  2. karma.conf.js 项目文件中的 singleRun 条目从值 false 更改为 true。 此更改有助于确保 Karma 进程在运行后停止。

React 和 Vue

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

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

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

生成文件位于新文件夹 dist(适用于 Vue)或 build(用于 React)中。 此代码片段生成一个可供发布的工件 www。 它使用节点安装程序复制文件发布生成工件任务。

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),将 JSX 或 TypeScript 转译为纯 JavaScript,以及捆绑应用。

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

将以下任务添加到管道:

  • npm

    • 命令custom
    • 命令和参数: install -g webpack webpack-cli --save-dev
  • bash

    • 类型:inline
    • 脚本npx webpack --config webpack.config.js

生成任务运行程序

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

Gulp

Gulp 将预安装在某些 Microsoft 托管的代理上。 在 YAML 文件中运行 gulp 命令:

- script: gulp                       # include any additional options that are needed

如果 gulpfile.js 文件中的步骤需要使用 npm 注册表进行身份验证:

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

- script: gulp                       # include any additional options that are needed

添加发布测试结果任务,以将 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'

如果应用使用 Gulp,则创建管道的最简单方法是在创建管道时将 Node.js 与 gulp 生成模板配合使用。 此模板会自动添加各种任务来调用 Gulp 命令和发布工件。 在该任务中,选择“启用代码覆盖率”以使用 Istanbul 启用代码覆盖率。

Grunt

Grunt 将预安装在某些 Microsoft 托管的代理上。 在 YAML 文件中运行 grunt 命令:

- script: grunt                      # include any additional options that are needed

如果 Gruntfile.js 文件中的步骤需要使用 npm 注册表进行身份验证:

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

- script: grunt                      # include any additional options that are needed

如果应用使用 Grunt,则创建管道的最简单方法是在创建管道时将 Node.js 与 Grunt 生成模板配合使用。 此模板会自动添加各种任务来调用 Gulp 命令和发布工件。 在此任务中,选择“发布到 TFS/Team Services”选项以发布测试结果,然后选择“启用代码覆盖率”以使用 Istanbul 启用代码覆盖率。

打包和交付代码

生成并测试应用后,可以将生成输出上传到 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 注册表

如果项目的输出是供其他项目而不是 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 应用

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

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

要将此存档发布到 Web 应用,请参阅 Azure Web 应用部署

将工件发布到 Azure Pipelines

使用发布生成工件任务将文件从生成发布到 Azure Pipelines。

发布到 npm 注册表

要创建和发布 npm 包,请使用 npm 任务。 有关版本控制以及发布 npm 包的详细信息,请参阅发布 npm 包

部署 Web 应用

要创建准备发布到 Web 应用的 .zip 文件存档,请使用存档文件任务。 要将此存档发布到 Web 应用,请参阅 Azure Web 应用部署

生成映像并将其推送到容器注册表

源代码成功生成且单元测试成功执行后,还可以生成映像并将其推送到容器注册表

疑难解答

如果可以在开发计算机上生成项目,但在 Azure Pipelines 上生成项目时遇到问题,请了解以下潜在原因和纠正措施:

  • 检查开发计算机上的 Node.js 版本和任务运行器的版本是否与代理上的版本匹配。 可以在管道中包含命令行脚本(例如 node --version),以检查代理上安装的内容。 使用 Node 工具安装程序(如本指南中所述),在代理上部署相同的版本,或者运行 npm install 命令将工具更新为所需版本。

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

  • 如果使用 nvm 来管理不同版本的 Node.js,请考虑切换到 Node 工具安装程序任务。 (nvm 由于历史原因安装在 macOS 映像上。)nvm 通过添加 shell 别名和更改 PATH 来管理多个 Node.js 版本,而这与 Azure Pipelines 在新进程中运行每个任务的方式交互效果很差。

    Node 工具安装程序任务可正确处理此模型。 但是,如果工作需要使用 nvm,则可以将以下脚本添加到每个管道的开头:

    steps:
    - bash: |
        NODE_VERSION=16  # or whatever your preferred version is
        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>
    

常见问题解答

问:在哪里可以了解有关 Azure Artifacts 和包管理服务的详细信息?

答:Azure Artifacts 中的包管理

问:我可在何处了解任务的详细信息?

答:生成、发布和测试任务

问:如何修复消息为“严重错误: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