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

使用azure-devops-node-api的Pulumi动态提供者序列化失败问题

解决Pulumi动态资源提供者序列化错误(azure-devops-node-api引用window变量)

问题根源

Pulumi动态资源提供者的代码会被序列化以在Pulumi运行时上下文执行,而azure-devops-node-api模块包含了浏览器环境的代码分支,其中引用了仅存在于浏览器的window变量。当顶层导入该模块时,模块对象会被提供者类的闭包捕获,序列化过程中检测到未定义的window变量,从而抛出错误。

解决方案

通过类型导入+动态运行时导入隔离模块加载,避免闭包捕获包含浏览器环境代码的模块对象,同时确保在Node.js环境下加载模块的Node.js适配版本:

  • 顶层仅导入azure-devops-node-api的类型(编译后会被移除,不影响运行时)
  • 在需要调用API的函数内部动态导入模块,确保运行时才加载且仅加载Node.js环境的代码
  • 修正updateBuildGeneralSettings的项目参数错误(原代码写死为"project",应传入实际项目名)

修改后的完整代码

import * as pulumi from '@pulumi/pulumi';
// 仅导入类型,编译后不会包含运行时代码
import type * as azdev from 'azure-devops-node-api';

export interface ProjectPipelineSettingsResourceInputs {
  organization: pulumi.Input<string>;
  orgServiceUrl: pulumi.Input<string>;
  project: pulumi.Input<string>;
  auditEnforceSettableVar: pulumi.Input<boolean>;
}

interface ProjectPipelineSettingsInputs {
  organization: string;
  orgServiceUrl: string;
  project: string;
  auditEnforceSettableVar: boolean;
}

interface ProjectPipelineSettingsOutputs extends ProjectPipelineSettingsInputs {
  id: string;
}

class ProjectPipelineSettingsProvider implements pulumi.dynamic.ResourceProvider {
  private async getWebApiClient(orgServiceUrl: string): Promise<azdev.WebApi> {
    // 动态导入模块,仅在运行时加载,避免闭包捕获
    const { WebApi, getPersonalAccessTokenHandler } = await import('azure-devops-node-api');
    const token = process.env.AZDO_PERSONAL_ACCESS_TOKEN;
    if (!token) {
      throw new Error('AZDO_PERSONAL_ACCESS_TOKEN is not set');
    }
    const authHandler = getPersonalAccessTokenHandler(token);
    return new WebApi(orgServiceUrl, authHandler);
  }

  async create(
    inputs: ProjectPipelineSettingsInputs
  ): Promise<pulumi.dynamic.CreateResult<ProjectPipelineSettingsOutputs>> {
    const connection = await this.getWebApiClient(inputs.orgServiceUrl);
    const buildApiClient = await connection.getBuildApi();
    
    // 修正项目参数:传入实际项目名而非字符串"project"
    const result = await buildApiClient.updateBuildGeneralSettings(
      { auditEnforceSettableVar: inputs.auditEnforceSettableVar },
      inputs.project
    );

    const generatedId = `${inputs.organization}-${inputs.project}`.replace(/\s/g, '-').toLowerCase();

    return {
      id: generatedId,
      outs: { id: generatedId, ...inputs, ...result }
    };
  }
}

export class ProjectPipelineSettings extends pulumi.dynamic.Resource {
  readonly organization!: pulumi.Output<string>;
  readonly orgServiceUrl!: pulumi.Output<string>;
  readonly project!: pulumi.Output<string>;
  readonly auditEnforceSettableVar!: pulumi.Output<boolean>;

  constructor(
    name: string,
    args: ProjectPipelineSettingsResourceInputs,
    opts?: pulumi.CustomResourceOptions
  ) {
    super(new ProjectPipelineSettingsProvider(), name, args, opts);
  }
}

验证修改

修改后执行pulumi up,Pulumi序列化提供者代码时不会再捕获到包含window引用的模块对象,同时动态导入确保模块在Node.js环境下加载正确的适配层,错误会被解决。

内容的提问来源于stack exchange,提问作者arodin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 03:06:10