其他
编写 Pulumi ComponentResource…
在创建、设计、重构、调试 Pulumi 组件,或为多语言消费者打包和发布组件包时,指导组件结构、Args 接口、输出暴露、设计模式与分发方式。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 35/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 39/100 ×0.15
适用场景
编写 Pulumi ComponentR特定
解决思路
- 【组件结构】继承 ComponentResource,并在构造函数中调用 super(),第一个参数传入类型 URN,格式为 <package>:<module>:<type>。
- 【组件结构】构造函数接受标准参数:name、args 和 ComponentResourceOptions,通常写作 opts?。
- 【组件结构】把所有子资源的创建选项设为 { parent: this }。
- 【组件结构】在构造函数最后调用 registerOutputs(),传入要暴露的输出。
- 【类型 URN】package 段使用组织或包名,module 段通常用 index,type 段用 PascalCase 类名,例如 myorg:index:StaticSite、acme:index:KubernetesCluster。
- 【registerOutputs】必须调用 registerOutputs(),否则组件在 Pulumi 控制台会卡在 creating 状态,输出也不会持久化到 state。
- 【子资源命名】子资源名称从组件 name 派生,例如 `${name}-bucket`,不要硬编码,以避免组件被多次实例化时名称冲突。
- 【Args 接口】把属性用 pulumi.Input<T> 包装,以便同时接受普通值和 Output<T>,避免消费者手动 .apply() 解包。
- 【Args 接口】保持结构扁平,避免深层嵌套对象,扁平接口更易使用和演进。
- 【Args 接口】不要使用联合类型,例如 pulumi.Input<string | number>,它会破坏 Python、Go、C# 的 SDK 生成。
- 【Args 接口】若需要接受多种形式,改用多个可选属性,例如 sizeGb 和 sizeMb。
- 【Args 接口】不要使用函数或回调,因为无法跨语言序列化。
- 【Args 接口】需要名称变换时改用配置属性,例如 namePrefix、nameSuffix,而不是传函数。
- 【Args 接口】为可选属性在构造函数中设置合理默认值,使用 ?? 运算符,让消费者只覆盖需要修改的项。
- 【输出暴露】只暴露消费者需要的输出,不要暴露所有内部资源,以免泄漏实现细节。
- 【输出暴露】用 pulumi.interpolate 或 pulumi.concat 派生组合输出,例如 connectionString。
- 【设计模式】把最佳实践编码为默认值,并允许消费者覆盖。例如默认开启版本控制、加密、阻止公开访问,用 args.enableVersioning !== false 等条件创建。
- 【设计模式】用可选参数控制子资源是否创建。例如只有 enableMonitoring 为真时才创建 SNS topic、订阅和 CloudWatch alarm。
- 【设计模式】通过组合构建更高层组件,从低层组件组合出高层组件,每层只管理一个关注点。
- 【设计模式】接受显式 provider 以支持多区域或多账号部署;ComponentResourceOptions 会自动把 provider 传给子资源,消费者传 providers: [usWest],子资源因 parent: this 自动继承。
- 【多语言判断】问:是否会有其他语言的人消费这个组件?
- 【多语言判断】单语言组件无需打包:团队只用一种语言、组件留在同一代码库或单项目、无需 PulumiPlugin.yaml,直接 import 类即可。
- 【多语言判断】多语言组件需要打包:其他团队用不同语言消费、平台团队为自选语言的开发者构建抽象、YAML 消费者需要使用你的组件、构建组织共享组件库,或发布到 Pulumi private/public registry;发布不是多语言支持的必要条件。
- 【多语言判断】常见错误:TypeScript 平台团队只构建 TypeScript 用户能用的组件;如果应用开发者用 Python 或 YAML,没有多语言打包就看不到这些组件。
- 【多语言设置】在组件目录创建 PulumiPlugin.yaml 声明 runtime:TypeScript 用 runtime: nodejs,Python 用 runtime: python。
- 【多语言序列化】允许:string、number、boolean;Input<T> 包装;基本类型数组和 map;枚举。
- 【多语言序列化】不允许:联合类型;函数和回调;复杂嵌套泛型;平台特定类型。
- 【多语言消费】消费者用 pulumi package add <git-repo-url> 安装组件;可加 @v1.0.0 指定版本。它会下载 provider plugin、在消费者语言中生成本地 SDK、更新 Pulumi.yaml。
- 【多语言消费】在全新 checkout 或 CI 中运行 pulumi install,确保所有包依赖可用;消费者无需手动生成 SDK。
- 【多语言消费】作者若要发布 SDK 到包管理器,可选使用 pulumi package gen-sdk 生成语言特定 SDK;大多数组件作者不需要,因为 pulumi package add 会在消费端处理 SDK 生成。
- 【多语言入口】已发布的多语言组件需要入口点来承载组件 provider 进程。
- 【多语言入口】TypeScript 入口点:从 index.ts 导出组件类,无需单独入口文件,Pulumi 会自动内省导出的类。
- 【多语言入口】Python 入口点:创建 __main__.py,调用 component_provider_host,并用 components=[StaticSite, SecureBucket] 传入所有组件类。
- 【多语言入口】Go 入口点:创建 main.go,用 infer.NewProviderBuilder().WithComponents(...).Build() 构建并运行 provider,调用 p.Run(context.Background(), "my-components", "0.1.0")。
- 【多语言入口】C# 入口点:创建 Program.cs,在 Main 中调用 Pulumi.Experimental.Provider.ComponentProviderHost.Serve(args)。
- 【多语言入口】可参考完整多语言示例 https://github.com/mikhailshilkov/comp-as-comp。
- 【分发选择】按受众选择分发方式:同一项目直接 import;同组织用 private registry 并运行 pulumi package publish 到 Pulumi Cloud;同组织也可用 git 仓库配合版本 tag,消费者 pulumi package add <repo>;语言生态发布到 npm、PyPI、NuGet、Maven;公开社区提交到 pulumi/registry GitHub repo。
- 【Private Registry】private registry 是组织组件的集中目录,提供自动 API 文档、版本管理和团队可发现性。
- 【Private Registry】发布到 private registry:pulumi package publish https://github.com/myorg/my-component --publisher myorg。
- 【Private Registry】用带 v 前缀的 git tag 做版本,例如 git tag v1.0.0、git push origin v1.0.0。
- 【Private Registry】发布时 README 必填,Pulumi 会把它作为 registry 中的文档页。
- 【Private Registry】用 GitHub Actions OIDC 自动化发布:workflow 触发 tag v*,permissions 包含 id-token: write 和 contents: read,设置 PULUMI_ORG,使用 actions/checkout@v4 且 fetch-depth: 0,使用 pulumi/auth-actions@v1 获取 organization token,最后运行 pulumi package publish https://github.com/${{ github.repository }} --publisher ${{ env.PULUMI_ORG }}。
- 【Private Registry】使用该 workflow 前,需要先在 Pulumi Cloud 配置 GitHub OIDC 集成;非 OIDC 场景用 GITHUB_TOKEN 或 GITLAB_TOKEN 环境变量认证;registry 支持私有 GitHub 和 GitLab 仓库。
- 【Private Registry】private registry 会自动生成 SDK 文档;通过给输入和输出加类型注解来丰富文档:TypeScript 用 JSDoc,Python 用 docstrings,Go 用 Annotate() 方法。
- 【Git 分发】打版本 tag,消费者用 pulumi package add https://github.com/myorg/my-component@v1.0.0 固定版本。
- 【包管理器分发】TypeScript/JavaScript 用 npm publish;Python 用 twine upload;.NET 用 dotnet nuget push;Java 用 Maven Central 标准发布。
- 【反模式】不要在 apply() 内创建资源,否则 pulumi preview 不可见;应把资源创建移到 apply 外。
- 【反模式】缺少 registerOutputs() 会让组件卡在 creating;始终在构造函数最后调用。
- 【反模式】缺少 { parent: this } 会让子资源出现在根层级;给所有子资源传 parent。
- 【反模式】Args 中使用联合类型会破坏 Python、Go、C# SDK;用单一类型,变体用独立属性。
- 【反模式】Args 中使用函数无法跨语言序列化;改用配置属性。
- 【反模式】硬编码子资源名会导致多实例冲突;用 `${name}-suffix` 派生。
- 【反模式】过度暴露输出会泄漏实现细节;只导出消费者需要的。
- 【反模式】一次性组件会带来不必要的抽象开销;在模式重复出现前先用内联资源。
- 【反模式】深层嵌套 Args 难以使用和演进;保持扁平并多用可选属性。
- 【快速参考】类型 URN 为 <package>:<module>:<type>,module 通常 index。
- 【快速参考】构造函数顺序为 super(type, name, {}, opts) -> 创建子资源 -> registerOutputs()。
- 【快速参考】子资源总是 { parent: this },名称从 `${name}-suffix` 派生。
- 【快速参考】Args 用 Input<T> 包装、无联合、无函数、扁平结构。
- 【快速参考】输出用 public readonly Output<T> 属性,只暴露必要内容。
- 【快速参考】默认值用 ?? 在构造函数中设置。
- 【快速参考】用低层组件组合高层组件。
- 【快速参考】多语言用 PulumiPlugin.yaml 加入口点;消费者用 pulumi package add。
- 【快速参考】分发可用 private registry、git tags、包管理器或公开 Pulumi Registry。
适用边界 · 注意事项
- 不适用于非 Pulumi 项目;一般 Pulumi 编码模式(Output 处理、secrets、aliases、preview 工作流)应使用 pulumi-best-practices 技能。
- 如果组件只在一个项目内单次使用、模式尚未重复,不要为了抽象而抽象,先用内联资源。
- 如果只有单语言团队在单一代码库内使用组件,不需要做多语言打包;只有跨语言、跨团队、YAML 消费或共享组件库时才需要。