Salesforce 第二代软件包 (2GP) 开发人员深度指南

背景与应用场景

我是一名 Salesforce 开发人员。在我的日常工作中,构建可扩展、可维护且易于分发的应用程序至关重要。Salesforce 平台上的应用程序分发机制被称为 Packaging (打包)。长期以来,First-Generation Packaging (1GP) (第一代软件包) 是我们的主要工具,但它与传统的基于 Org (组织) 的开发模式紧密相连,存在版本控制困难、依赖管理不灵活、自动化程度低等诸多挑战。

为了应对现代软件开发的需求,Salesforce 推出了 Second-Generation Packaging (2GP) (第二代软件包)。2GP 是一种全新的打包框架,它彻底改变了我们在 Salesforce 平台上构建和部署应用程序的方式。它的核心是Source-Driven Development (源码驱动开发) 模式,即以版本控制系统(如 Git)中的源代码为唯一信任源,而不是某个特定的 Salesforce Org。

这种转变使得 2GP 成为现代 DevOps 和 CI/CD (持续集成/持续部署) 实践的基石。对于我们开发人员来说,这意味着我们可以像开发其他平台的应用一样,利用强大的工具和自动化的流程来管理 Salesforce 应用的整个生命周期。

2GP 的核心应用场景包括:

  • ISV (独立软件供应商) 合作伙伴: 为 AppExchange 开发和分发商业应用程序。2GP 提供了更强大的模块化、依赖管理和版本控制能力,使 ISV 能够构建更复杂、更可靠的解决方案。
  • 企业内部应用开发: 大型企业通常有多个 Salesforce Orgs(例如,按业务部门或地理区域划分)。通过 2GP,企业可以将共享的功能(如自定义的触发器框架、集成中间件、公共组件库)打包,并在所有 Org 中统一安装和升级,极大地提高了代码复用性和治理能力。
  • 大型项目模块化: 将一个庞大、复杂的单体应用("Happy Soup")分解为多个逻辑上独立的、可独立开发和部署的小型软件包。这种微服务的思想有助于降低复杂性,提升团队的开发效率和敏捷性。
  • 系统集成商 (SI): 为客户构建可重复使用的解决方案或加速器。SI 可以将通用的功能模块打包,在新项目中快速部署,缩短交付周期。

原理说明

要深入理解 2GP,我们需要掌握几个核心概念,这些概念共同构成了一个围绕源码和自动化的开发生态系统。

Dev Hub (开发中心)

Dev Hub 是 2GP 的指挥中心。它通常是你的生产 Org 或一个特殊的业务 Org。你需要在 Dev Hub 中启用 Salesforce DX 功能,它负责跟踪你所有的 Scratch Orgs (临时组织)、软件包以及软件包版本。所有 2GP 软件包的创建和版本管理命令都必须在授权到 Dev Hub 的环境下执行。

Source-Driven Development (源码驱动开发)

这是 2GP 的灵魂。所有元数据(Apex 类、对象、字段、权限集等)都以文件形式存储在版本控制系统中。sfdx-project.json 文件是项目的清单文件,它定义了项目的结构、软件包、依赖关系和命名空间等关键信息,是整个开发流程的起点和真实来源。

Scratch Orgs (临时组织)

Scratch Orgs 是一种临时的、可配置的、可通过 API 创建和销毁的 Salesforce Org。它们是 2GP 开发的理想环境。我们可以在 Scratch Org 中进行功能开发、单元测试和集成测试,而不会影响任何持久化环境。由于它们是基于源码创建的,因此可以确保每次都从一个干净、一致的环境开始。

Package 和 Package Version (软件包和软件包版本)

在 2GP 中,Package 是一个逻辑容器,它在 sfdx-project.json 文件中定义,并与 Dev Hub 中的一个记录相关联。它本身不是可安装的。真正可安装的实体是 Package Version。每次你准备好发布一个软件包的新快照时,你都会创建一个新的版本。这个版本是一个不可变的、经过版本控制的元数据构件,可以被安装到任何 Salesforce Org(如 Sandbox 或生产 Org)。

Namespace (命名空间)

对于计划在 AppExchange 上分发的 Managed Package (受管软件包),Namespace 是必需的。它是一个唯一的标识符,会作为你所有自定义组件(对象、字段、Apex 类等)API 名称的前缀。这可以防止你的软件包组件与其他组织或软件包中的组件发生命名冲突。一个 Namespace 只能链接到一个 Dev Hub,并且这个操作是不可逆的。

Package Dependencies (软件包依赖关系)

2GP 最强大的功能之一就是其灵活的依赖管理。你可以在 sfdx-project.json 文件中明确声明一个软件包依赖于另一个软件包的特定版本。当安装主软件包时,Salesforce 会自动处理并安装其所有依赖项。这使得构建由多个小型、独立、可重用模块组成的复杂应用程序成为可能。


示例代码

以下示例将引导你完成 2GP 的核心生命周期,所有命令均基于 Salesforce CLI。这些示例直接来源于 Salesforce 官方文档,确保了准确性和可靠性。

1. 配置 sfdx-project.json

这是我们项目的核心配置文件。在这个例子中,我们定义了两个软件包:base-appfeature-app。其中,feature-app 依赖于 base-app 的特定版本。

{
  "packageDirectories": [
    {
      "path": "force-app/main/default",
      "default": true,
      "package": "base-app",
      "versionName": "ver 0.1",
      "versionNumber": "0.1.0.NEXT",
      "definitionFile": "config/project-scratch-def.json"
    },
    {
      "path": "force-app/feature/default",
      "package": "feature-app",
      "versionName": "ver 0.1",
      "versionNumber": "0.1.0.NEXT",
      "definitionFile": "config/project-scratch-def.json",
      "dependencies": [
        {
          "package": "base-app",
          "versionNumber": "0.1.0.LATEST"
        }
      ]
    }
  ],
  "namespace": "my_namespace",
  "sfdcLoginUrl": "https://login.salesforce.com",
  "sourceApiVersion": "58.0",
  "packageAliases": {
    "base-app": "0HoB00000004CFpKAM",
    "base-app@0.1.0-1": "04tB0000000IB1fIAG",
    "feature-app": "0HoB00000004CFqKAM"
  }
}

注释:

  • packageDirectories: 定义了项目中的源代码路径和它们对应的软件包。
  • package: 软件包的别名,在整个项目中保持唯一。
  • versionNumber: 定义了软件包版本的格式。NEXT 是一个关键字,每次创建版本时会自动递增。
  • dependencies: 在 feature-app 中,我们声明了它依赖于 base-app 的最新(LATEST)版本。
  • packageAliases: 用于存储软件包 ID(0Ho...)和软件包版本 ID(04t...)的别名,方便在命令行中引用。

2. 创建一个软件包 (Package)

这个命令在 Dev Hub 中注册一个新的软件包实体。你只需要为每个软件包执行一次此操作。

sf package create --name "base-app" --package-type Managed --path "force-app/main/default" --target-dev-hub MyDevHub

注释:

  • --name: 软件包的名称,应与 sfdx-project.json 中的别名匹配。
  • --package-type: 指定软件包类型,通常是 Managed(受管)或 Unlocked(非受管)。
  • --path: 包含此软件包元数据的目录路径。
  • --target-dev-hub: 用于执行此操作的 Dev Hub 的别名。

3. 创建一个软件包版本 (Package Version)

这是最常用的命令,它会获取指定路径下的源代码,并创建一个不可变的软件包版本构件。

sf package version create --package "base-app" --path "force-app/main/default" --installation-key "MyInstallKey123" --wait 20 --code-coverage --target-dev-hub MyDevHub

注释:

  • --package: 要创建版本的软件包的别名。
  • --installation-key: 为此版本设置安装密码,增加安全性。
  • --wait: 指定等待版本创建完成的分钟数。
  • --code-coverage: 强制计算 Apex 代码覆盖率。如果覆盖率低于75%,对于受管软件包,版本创建将失败。

4. 推广软件包版本 (Promote)

一旦一个软件包版本经过充分测试并准备好发布,你需要将其“推广”为可发布状态。只有被推广的版本才能安装到生产 Org。

sf package version promote --package "base-app@0.1.0-1" --target-dev-hub MyDevHub

注释:

  • --package: 要推广的软件包版本的别名或 ID (04t...)。你可以在 sfdx-project.json 的 `packageAliases` 中找到它。


注意事项

权限与设置

  • Dev Hub 启用: 必须在你的业务组织(通常是生产 Org)中启用 Dev Hub 功能。
  • 用户权限: 在 Dev Hub 中执行操作的用户需要 "Salesforce DX" 权限集,或者至少拥有“创建和更新第二代软件包”和“管理第二代软件包”的系统权限。

命名空间 (Namespace)

  • 不可逆转: 将一个 Namespace 链接到你的 Dev Hub 是一个不可逆转的操作。一旦链接,该 Namespace 只能用于从此 Dev Hub 创建的软件包。
  • 唯一性: Namespace 在所有 Salesforce Org 中是唯一的。在注册前,请仔细规划你的命名空间。

依赖管理

  • 禁止循环依赖: 你不能创建循环依赖,例如包 A 依赖包 B,同时包 B 又依赖包 A。
  • 版本锁定: 依赖项必须指向一个具体的、已发布的软件包版本 ID (04t...),或者使用 LATEST 关键字指向最新已推广的版本。在 CI/CD 流程中,建议锁定到具体的版本 ID 以确保构建的确定性。

版本控制与升级

  • Package Ancestry (软件包血缘): 2GP 强制执行一个清晰的“血缘”概念。每个新版本都必须有一个“祖先”,通常是前一个版本。这确保了升级路径的平滑。如果你尝试创建一个没有有效祖先的版本(例如,基于一个完全不同的代码分支),版本创建可能会失败。
  • 破坏性变更: 从受管软件包中删除组件(如 Apex 类、对象字段)是一个破坏性变更,需要非常谨慎。你不能简单地从代码库中删除它们,而需要遵循一个弃用流程,或者在某些情况下,这些变更是不被允许的。

错误处理

  • 版本创建失败: 如果 package version create 命令失败,请仔细检查输出日志。常见原因包括 Apex 测试失败、代码覆盖率不足、元数据格式错误或无效的依赖关系。
  • 日志查询: 你可以使用 sf package version create list 来查看版本创建请求的历史记录和状态。

总结与最佳实践

作为一名 Salesforce 开发人员,拥抱 Second-Generation Packaging (2GP) 是迈向现代化、专业化应用开发的关键一步。它将我们从繁琐的手动部署和不透明的 Org 状态中解放出来,让我们能够专注于编写高质量的代码,并将其无缝地集成到自动化的 DevOps 流程中。

2GP 的核心优势在于它的模块化、自动化和可追溯性。通过将大型应用分解为更小的、独立的软件包,我们降低了维护成本,提高了开发速度,并实现了真正的代码复用。

最佳实践:

  • 模块化设计 (Modular Design)

    遵循单一职责原则。让每个软件包都专注于一个明确的业务功能或技术能力。例如,一个包负责处理与外部系统的集成,另一个包提供一套通用的 Lightning Web Components,还有一个包实现核心的业务逻辑。

  • 版本控制策略 (Version Control Strategy)

    采用成熟的 Git 分支策略,如 GitFlow。使用功能分支进行新开发,将发布分支用于创建软件包版本,并使用主分支作为已发布代码的稳定记录。这为并行开发和版本管理提供了清晰的框架。

  • 自动化一切 (Automate Everything)

    构建一个完整的 CI/CD 流水线。当代码合并到特定分支时,自动触发脚本来:1) 创建一个新的 Scratch Org;2) 推送源代码;3) 运行所有 Apex 测试;4) 如果测试通过,则创建一个新的软件包版本;5) 将新版本自动安装到一个用于测试的 Sandbox 中。

  • 依赖清晰化 (Clarify Dependencies)

    sfdx-project.json 中明确并锁定所有依赖关系的版本。这可以避免“在我的机器上能用”的问题,并确保构建过程是可重复和确定性的。

  • 全面的测试 (Comprehensive Testing)

    不要仅仅依赖 Apex 单元测试。在 CI/CD 流程中集成自动化测试,包括集成测试(测试包与包之间的交互)和端到端的功能测试,以确保每个软件包版本的质量。

通过遵循这些原则和实践,你可以充分利用 2GP 的强大功能,为你的客户和用户交付更稳定、更可靠、更易于维护的 Salesforce 应用程序。

评论

此博客中的热门博文

Salesforce 登录取证:深入解析用户访问监控与安全

Salesforce Experience Cloud 技术深度解析:构建社区站点 (Community Sites)

精通 Salesforce Email Studio:咨询顾问指南之 AMPscript 与数据扩展实现动态个性化邮件