持续集成 (CI) 是一种软件开发实践,开发人员在其中 定期将他们的代码更改集成到源代码存储库中。确保新代码 不引入错误,自动构建和测试在开发人员签入之前或之后运行 变化。
许多第三方 CI 工具可供您选择。Salesforce DX 轻松集成 添加到这些工具中,以便您可以为 Salesforce 设置持续集成 应用。
- 使用 CircleCI 进行持续集成 CircleCI
是一种常用的集成工具,它与您现有的版本控制系统集成,以将增量更新推送到您指定的环境。CircleCI 可以用作基于云的或本地工具。这些说明演示了如何使用 GitHub、CircleCI 和 Dev Hub 组织进行持续集成。 - 使用 Jenkins 进行持续集成
Jenkins 是一个开源的、可扩展的自动化服务器,用于实现持续集成和持续交付。您可以轻松地将 Salesforce DX 集成到 Jenkins 框架中,以针对临时组织自动测试 Salesforce 应用程序。 - 与 Travis CI 的持续集成
Travis CI 是一种基于云的持续集成 (CI) 服务,用于构建和测试 GitHub 上托管的软件项目。 - 组织开发模型
的示例 CI 存储库 通过从您选择的供应商处克隆示例存储库,快速开始使用 CI。每个存储库都有一个示例配置文件和一个包含分步信息的综合 README.md。 - 包开发模型
的示例 CI 存储库 通过克隆所选供应商的示例存储库,快速开始使用 CI。每个存储库都有一个示例配置文件和一个包含分步信息的综合 README.md。
使用 CircleCI 进行持续集成
CircleCI 是一种常用的集成工具,可与您现有的集成 版本控制系统,用于将增量更新推送到您指定的环境。CircleCI系列 可用作基于云的或本地工具。这些说明演示了如何使用 GitHub、CircleCI 和 Dev Hub 组织,用于持续集成。
- 为 CircleCI 配置环境
在集成现有 CircleCI 框架之前,请配置开发人员中心组织和 CircleCI 项目。 - 将 CircleCI 连接到您的 DevHub
授权 CircleCI 通过连接的应用程序将内容推送到您的 Dev Hub。
为 CircleCI 配置环境
在集成现有的 CircleCI 框架之前,请配置开发人员中心组织和 CircleCI 项目。
- 使用 CircleCI 设置 GitHub 存储库。您可以按照 CircleCI 网站上的注册步骤访问您的代码 在 GitHub 上。
- 安装 Salesforce CLI(如果您还没有)。
- 如果尚未操作,请遵循使用 JWT 流为 Dev Hub 组织授权组织。
- 加密服务器密钥。
- 首先,生成密钥和初始化向量 (iv) 以在本地加密 server.key 文件。CircleCI 使用 key 和 iv 来 在生成环境中解密服务器密钥。在包含 server.key 文件的目录中运行以下命令。 对于值,请输入您自己选择的单词 以创建唯一密钥。<passphrase>
openssl enc -aes-256-cbc -k <passphrase> -P -md sha1 -nosalt
键和 iv 值显示在输出中。key=****24B2 iv =****DA58
- 请注意 key 和 iv 值,稍后需要用到它们。
- 使用新生成的密钥加密 server.key 文件,然后 IV 值。在包含 server.key 文件的目录中运行以下命令,将 <key> 和 <iv> 替换为上一步中的值。
openssl enc -nosalt -aes-256-cbc -in server.key -out server.key.enc -base64 -K <key> -iv <iv>
注意key 和 iv 值仅使用一次,并且不要使用它们来加密超过 server.key 的值。虽然您可以重复使用此对来加密其他 事情,这样做被视为违反安全规定。每次在步骤 a 中运行命令时,都会生成新的键和 iv 值。在 换句话说,你不能重新生成同一对。如果丢失这些值,则必须 生成新的并再次加密。
- 首先,生成密钥和初始化向量 (iv) 以在本地加密 server.key 文件。CircleCI 使用 key 和 iv 来 在生成环境中解密服务器密钥。在包含 server.key 文件的目录中运行以下命令。 对于值,请输入您自己选择的单词 以创建唯一密钥。<passphrase>
接下来,将 server.key.enc 的密钥、iv 和内容存储为 CircleCI UI 中受保护的环境变量。这些值被认为是机密的,因此 采取适当的预防措施来保护它们。
将 CircleCI 连接到您的 DevHub
授权 CircleCI 通过连接的应用程序将内容推送到您的开发人员中心。
- 确保您已安装 Salesforce CLI。通过运行 sf version 进行检查,并确认您看到的是版本信息。如果你 未安装,请参阅安装 Salesforce CLI。
- 确认您可以从包含 server.key 文件的目录中执行基于 JWT 的授权。从包含 您的 server.key(替换 <your_consumer_key> 并在指示的位置<your_username>值)。
sf org login jwt --client-id <your_consumer_key> --jwt-key-file server.key --username <your_username> --set-default-dev-hub
- 使用页面顶部的 Fork 链接将 sfdx-circleci 存储库分叉到你的 GitHub 帐户中。
- 为此项目创建一个本地目录,并将分叉的存储库本地克隆到新的 目录。将 <git_username> 替换为你自己的 GitHub 用户名。
git clone https://github.com/<git_username>/sfdx-circleci.git
- 从基于 JWT 的授权连接的应用中检索生成的使用者密钥。从 设置,在“快速查找”框中,输入“应用”,然后选择“应用” 经理。在已连接旁边的行菜单中选择查看 应用程序。
- 在 CircleCI UI 中,您会看到一个名为 sfdx-circleci 的项目。在项目设置中,存储 名为 HUB_CONSUMER_KEY 的 CircleCI 环境变量中的使用者密钥。有关更多信息,请参阅 CircleCI 文档在 项目。
- 将用于访问 Dev Hub 的用户名存储在 CircleCI 环境变量中 使用 CircleCI UI 命名HUB_SFDX_USER。
- 将加密服务器密钥中的密钥和 iv 值存储在 CircleCI 环境变量中 分别命名为 DECRYPTION_KEY和 DECRYPTION_IV。完成环境变量的设置后,您的 项目屏幕如下图所示。
注意在包含 server.key 文件的目录中,使用命令删除 server.key。切勿在公共场所存放密钥或证书。rm server.key
你准备好了!现在,当您提交并推送更改时,您的更改将启动一个 CircleCI 构建。
为存储库做贡献
如果您发现任何问题或改进此存储库的机会,请修复它们!随意 为这个项目做贡献,分叉这个仓库,然后将 内容。进行更改后,通过发送拉取请求与社区共享这些更改。 有关参与的详细信息,请参阅如何发送拉取请求 到 GitHub 项目。
报告问题
如果您发现此演示中有任何问题无法修复,请随时在此存储库的问题部分报告。
使用 Jenkins 进行持续集成
Jenkins 是一个开源的、可扩展的自动化服务器,用于实现连续 集成和持续交付。您可以轻松地将 Salesforce DX 集成到 Jenkins 中 框架,用于针对临时组织自动测试 Salesforce 应用程序。
为了集成 Jenkins,我们假设:
- 您熟悉 Jenkins 的工作原理。您可以通过多种方式配置和使用 Jenkins。我们 专注于将 Salesforce DX 集成到 Jenkins 多分支管道中。
- 运行 Jenkins 服务器的计算机有权访问版本控制 系统和包含 Salesforce 应用程序的存储库。
- 为 Jenkins 配置环境
在将 Dev Hub 和临时组织集成到现有 Jenkins 框架之前,请配置 Jenkins 环境。我们的示例假定你使用的是包开发模型。 - Jenkinsfile 演练
示例 Jenkinsfile 演示如何将 Dev Hub 和临时组织集成到 Jenkins 作业中。此示例使用 Jenkins 多分支管道。每个 Jenkins 设置都是不同的。本演练介绍自动测试 Salesforce 应用程序的方法之一。本演练重点介绍了用于创建临时组织、上传代码和运行测试的 Salesforce CLI 命令。 - 示例 Jenkinsfile
Jenkinsfile 是一个文本文件,其中包含 Jenkins Pipeline 的定义。此 Jenkinsfile 展示了如何集成 Salesforce CLI 命令,以使用 scratch 组织自动测试 Salesforce 应用程序。
为 Jenkins 配置环境
在将 Dev Hub 和 Scratch 组织集成到现有的 Jenkins 框架之前, 配置 Jenkins 环境。我们的示例假定你正在包中工作 发展模式。
- 在开发人员中心组织中,按照基于 JWT 的授权流所述创建连接的应用。此步骤包括获取或创建私钥和数字证书。在保存连接的 应用程序。您需要使用者密钥来设置 Jenkins 环境。也有可用的 用于对数字证书进行签名的私钥文件。
- 在运行 Jenkins 服务器的计算机上,执行以下操作。
- 下载并安装 Salesforce CLI。
- 使用 Jenkins 管理员凭据将私钥文件存储为 Jenkins 机密文件 接口。记下新条目的 ID。稍后在 Jenkinsfile 中引用此凭据条目。
- 在 Jenkins 环境中设置以下变量。
- SF_USERNAME – Dev Hub 组织的用户名,例如 juliet.capulet@myenvhub.com。
- SF_INSTANCE_URL – 托管 Dev Hub 的 Salesforce 实例的登录 URL 组织。默认值为 https://login.salesforce.com。我们建议您更新 此值设置为 Dev Hub 组织的 My Domain 登录 URL。您可以找到一个组织的 “设置”中“我的域”页面上的“我的域”登录 URL。
- SF_CONSUMER_KEY – 创建连接后返回的使用者密钥 开发人员中心组织中的应用。
- SERVER_KEY_CREDENTALS_ID – 您的私钥文件的凭证 ID 存储在 Jenkins 管理员凭据界面中。
- PACKAGE_NAME – 包的名称,例如“我的包”。
- PACKAGE_VERSION-软件包的版本,以 04t 开头。
- TEST_LEVEL – 包的测试级别,例如 RunLocalTests。
- 设置您的 Salesforce DX 项目,以便您可以创建临时组织。
- (可选)将自定义工具插件安装到 Jenkins 控制台中,并创建一个自定义 引用 Salesforce CLI 的工具。Jenkins 演练假定您创建了一个 /usr/local/bin 目录中名为 toolbelt 的自定义工具,该目录是 Salesforce CLI 的安装目录。
Jenkinsfile 演练
示例 Jenkinsfile 展示了如何将 Dev Hub 和 Scratch 组织集成到 Jenkins 作业。此示例使用 Jenkins 多分支管道。每个 Jenkins 设置都是 不同。本演练介绍自动测试 Salesforce 的方法之一 应用。本演练重点介绍了用于创建临时组织的 Salesforce CLI 命令。 上传代码并运行测试。
本演练依赖于 sfdx-jenkins-package Jenkinsfile。我们假设你熟悉 Jenkinsfile、Jenkins Pipeline DSL 和 Groovy 编程语言的结构。这 演练演示如何使用 Salesforce CLI 实现 Jenkins 管道,以及 临时组织。有关所使用的命令,请参阅 CLI 命令参考。
此工作流与 Jenkinsfile 阶段最接近。
- 定义变量
- 查看源代码
- 将所有阶段包装在 withCredentials 命令中
- 将所有阶段包装在 withEnv 命令中
- 授权 Dev Hub 组织并创建临时组织
- 推送源并分配权限集
- 运行 Apex 测试
- 删除临时组织
- 创建程序包
- 创建临时组织并显示信息
- 安装包、运行单元测试和删除临时组织
定义变量
使用关键字定义所需的变量 通过 Salesforce CLI 命令。为每个变量分配相应的环境变量 您之前在 Jenkins 环境中设置。def
def SF_CONSUMER_KEY=env.SF_CONSUMER_KEY
def SERVER_KEY_CREDENTALS_ID=env.SERVER_KEY_CREDENTALS_ID
def TEST_LEVEL='RunLocalTests'
def PACKAGE_NAME='0Ho1U000000CaUzSAK'
def PACKAGE_VERSION
def SF_INSTANCE_URL = env.SF_INSTANCE_URL ?: "https://MyDomainName.my.salesforce.com"
定义变量,但不要设置其 价值。你稍后再做。SF_USERNAME
def SF_USERNAME
虽然不是必需的,但我们假设你使用 Jenkins 全局工具配置来 创建指向 CLI 安装目录。在 Jenkinsfile 中,使用 tool 命令将变量的值设置为此自定义工具。toolbelttoolbelt
def toolbelt = tool 'toolbelt'
现在,您可以使用 在 Jenkinsfile 中引用 Salesforce CLI 可执行文件。${toolbelt}/sf
查看源代码
在测试代码之前,请从版本控制中获取相应的版本或分支 系统 (VCS) 存储库。在此示例中,我们使用 Jenkins 命令。我们假设 Jenkins 管理员已经 配置环境以访问正确的 VCS 存储库并检出正确的 分支。checkout scm
stage('checkout source') {
// when running in multi-branch job, one must issue this command
checkout scm
}
将所有阶段包装在 withCredentials 命令中
您之前使用 凭据接口。因此,您必须使用 Jenkinsfile 正文中的命令来访问机密文件。该命令允许您命名凭据条目,然后 从凭据存储中提取,并通过变量提供给包含的代码。 使用时,将所有阶段放在其 代码块。withCredentialswithCredentialswithCredentials
此示例将 JWT 密钥文件的凭据 ID 存储在变量 中。您之前定义了 并将其设置为其 相应的环境变量。该命令从以下位置获取密钥文件的内容 凭据存储并将内容放置在临时位置。位置 存储在变量 中。你 将变量与命令一起使用以指定 私钥安全。SERVER_KEY_CREDENTALS_IDSERVER_KEY_CREDENTALS_IDwithCredentialsserver_key_fileserver_key_fileorg login jwt
withCredentials([file(credentialsId: SERVER_KEY_CREDENTALS_ID, variable: 'server_key_file')])
# all stages will go here
}
将所有阶段包装在 withEnv 命令中
运行 Jenkins 作业时,了解文件的存储位置会很有帮助。那里 是需要注意的两个主要目录:工作区目录和主页 目录。工作区目录对于每个作业都是唯一的,而主目录是 所有工作都一样。
该命令将 JWT 密钥文件存储在 作业期间的 Jenkins 工作区。但是,Salesforce CLI 命令将身份验证文件存储在主目录中;这些 身份验证文件在作业持续时间之外保留。withCredentialsauth
运行单个作业时,此设置不会出现问题,但在运行时可能会导致问题 多个作业。因此,如果使用同一个 Dev Hub 运行多个作业,会发生什么情况,或者 其他 Salesforce 用户?当 CLI 尝试以用户身份连接到开发人员中心时,你 经过身份验证时,无法刷新令牌。为什么?CLI 尝试使用 JWT 密钥 其他工作区中不再存在的文件,而不考虑当前作业。withCredentials
如果使用 设置主目录以匹配工作区目录,则每个作业的身份验证文件都是唯一的。 为每个作业创建唯一的身份验证文件也更安全,因为每个作业只能访问 它创建的身份验证文件。withEnv
使用 时,将所有阶段都放在其代码中 块withEnv
withEnv(["HOME=${env.WORKSPACE}"]) {
# all stages will go here
}
注意
如果不使用管道或在管道阶段之外运行命令,请添加主页 脚本的环境规范:。export HOME=$WORKSPACE
授权 Dev Hub 组织并创建临时组织
此示例使用两个 阶段:一个阶段用于授权开发人员中心组织,另一个阶段用于创建临时 组织。sfdx-jenkins-package
// -------------------------------------------------------------------------
// Authorize the Dev Hub org with JWT key and give it an alias.
// -------------------------------------------------------------------------
stage('Authorize DevHub') {
rc = command "${toolbelt}/sf org login jwt --instance-url ${SF_INSTANCE_URL} --client-id ${SF_CONSUMER_KEY} --username ${SF_USERNAME} --jwt-key-file ${server_key_file} --set-default-dev-hub --alias HubOrg"
if (rc != 0) {
error 'Salesforce dev hub org authorization failed.'
}
}
// -------------------------------------------------------------------------
// Create new scratch org to test your code.
// -------------------------------------------------------------------------
stage('Create Test Scratch Org') {
rc = command "${toolbelt}/sf org create scratch --target-dev-hub HubOrg --set-default --definition-file config/project-scratch-def.json --alias ciorg --wait 10 --duration-days 1"
if (rc != 0) {
error 'Salesforce test scratch org creation failed.'
}
}
用于授权开发人员中心组织。org login jwt
此步骤只需运行一次,但我们建议您将其添加到 Jenkinsfile 中,并在每次授权时进行授权 运行 Jenkins 作业。这样,您始终可以确保 Jenkins 作业不会中止 由于缺乏授权。授权多个通常没有什么坏处 时间,但请记住,临时组织版本的API调用限制仍然存在 适用。
使用命令的标志 提供有关你正在授权的开发人员中心组织的信息。的值 、 和 标志是 SF_CONSUMER_KEY、HubOrg 和 SF_INSTANCE_URL 环境变量 之前分别定义。标志的值是您在上一节中使用 命令。该标志指定此 HubOrg 是用于创建临时组织的默认 Dev Hub 组织。org login jwt–client-id–username–instance-url–jwt-key-fileserver_key_filewithCredentials–set-default-dev-hub
注意
最佳做法是为每个 Jenkins 作业提供唯一的身份验证文件 使用包装器。但这是可能的 改为在 Jenkins 计算机上授权 Dev Hub。优点是你的 在计算机上为运行的任何 Jenkins 作业集中设置身份验证。这 缺点是安全性:每个作业都可以访问所有经过身份验证的用户,无论 你希望他们或不愿意。withEnv
如果确实要在 Jenkins 上对 Dev Hub 进行身份验证If you do want to auth to auth to your Dev Hub on your Jenkins 机器,请按照下列步骤操作:
- 在 Jenkins 计算机上,以 Jenkins 用户身份,使用 任何命令。org login
- 在 Jenkinsfile 中,删除 、 、 和语句。withCredentialswithEnvorg login jwt
使用 CLI 命令创建 一个临时组织。在此示例中,CLI 命令使用 config/project-scratch-def.json 文件(相对于 项目目录)以创建临时组织。该标志将输出指定为 JSON 格式。该标志将新的临时组织设置为 违约。org create scratch–json–set-default
解析命令的 JSON 输出的 Groovy 代码提取自动生成的用户名 组织创建的一部分。使用此用户名,存储在 SF_USERNAME 变量中,用于 使用推送源、分配权限集等的 CLI 命令。org create scratch
推送源并分配权限集
让我们用元数据填充您的新临时组织。此示例使用命令部署源 到组织。源代码包括构成 Salesforce 的所有部分 application:Apex 类和测试类、权限集、布局、触发器、 自定义对象等。project deploy start
// -------------------------------------------------------------------------
// Push source to test scratch org.
// -------------------------------------------------------------------------
stage('Push To Test Scratch Org') {
rc = command "${toolbelt}/sf project deploy start --target-org ciorg"
if (rc != 0) {
error 'Salesforce push to test scratch org failed.'
}
}
回想一下包含输出的自动生成的用户名的 SF_USERNAME 变量 通过前面的命令 阶段。该代码使用此变量作为标志的参数,以指定 新的 Scratch 组织。org create scratch–target-org
该命令将部署所有 它在项目中找到的与 Salesforce 相关的文件。将 .forceignore 文件添加到存储库以列出 您不希望推送到组织的文件。project deploy start
运行 Apex 测试
现在,您的源代码和测试源代码已推送到临时组织,请运行命令以运行 Apex 测试。apex run test
// -------------------------------------------------------------------------
// Run unit tests in test scratch org.
// -------------------------------------------------------------------------
stage('Run Tests In Test Scratch Org') {
rc = command "${toolbelt}/sf apex run test --target-org ciorg --wait 10 --result-format tap --code-coverage --test-level ${TEST_LEVEL}"
if (rc != 0) {
error 'Salesforce unit test run in test scratch org failed.'
}
}
您可以为 CLI 指定各种标志 命令。在示例中:apex run test
- 该标志运行所有测试 在临时组织中,源自已安装的托管软件包的测试除外。 您还可以指定运行 仅本地测试,运行 仅某些 Apex 测试或套件,或运行组织中的所有测试。–test-level ${TEST_LEVEL}RunLocalTestsRunSpecifiedTestsRunAllTestsInOrg
- 该标志指定 命令输出采用 Test Anything Protocol (TAP) 格式。测试结果 写入文件仍采用 JUnit 和 JSON 格式。–result-format tap
- 该标志指定用户名 用于访问 Scratch 组织(SF_USERNAME 中的值)。–target-org ciorg
该命令将其测试结果写入 JUnit 格式。apex run test
删除临时组织
Salesforce 保留在临时文件后指定天数内删除临时组织的权利 已创建。您还可以在管道中创建一个阶段,用于显式删除 测试完成后的 scratch 组织。此清理可确保更好地管理您的 资源。org delete scratch
// -------------------------------------------------------------------------
// Delete package install scratch org.
// -------------------------------------------------------------------------
stage('Delete Package Install Scratch Org') {
rc = command "${toolbelt}/sf org delete scratch --target-org installorg --no-prompt"
if (rc != 0) {
error 'Salesforce package install scratch org deletion failed.'
}
}
创建程序包
现在,让我们创建一个包。如果您不熟悉包装,则可以将包装视为 使用元数据填充的容器。它包含一组相关功能, 自定义项和架构。您可以使用包将元数据从一个 Salesforce 组织移动到 另一个。创建包后,添加元数据并创建新的包版本。
// -------------------------------------------------------------------------
// Create package version.
// -------------------------------------------------------------------------
stage('Create Package Version') {
if (isUnix()) {
output = sh returnStdout: true, script: "${toolbelt}/sf package version create --package ${PACKAGE_NAME} --installation-key-bypass --wait 10 --json --target-dev-hub HubOrg"
} else {
output = bat(returnStdout: true, script: "${toolbelt}/sf package version create --package ${PACKAGE_NAME} --installation-key-bypass --wait 10 --json --target-dev-hub HubOrg").trim()
output = output.readLines().drop(1).join(" ")
}
// Wait 5 minutes for package replication.
sleep 300
def jsonSlurper = new JsonSlurperClassic()
def response = jsonSlurper.parseText(output)
PACKAGE_VERSION = response.result.SubscriberPackageVersionId
response = null
echo ${PACKAGE_VERSION}
}
创建临时组织并显示信息
还记得您之前创建临时组织的时候吗?现在让我们创建一个临时组织进行安装 您的包,并显示有关该暂存组织的信息。
// -------------------------------------------------------------------------
// Create new scratch org to install package to.
// -------------------------------------------------------------------------
stage('Create Package Install Scratch Org') {
rc = command "${toolbelt}/sf org create scratch --target-dev-hub HubOrg --set-default --definition-file config/project-scratch-def.json --alias installorg --wait 10 --duration-days 1"
if (rc != 0) {
error 'Salesforce package install scratch org creation failed.'
}
}
// -------------------------------------------------------------------------
// Display install scratch org info.
// -------------------------------------------------------------------------
stage('Display Install Scratch Org') {
rc = command "${toolbelt}/sf org display --target-org installorg"
if (rc != 0) {
error 'Salesforce install scratch org display failed.'
}
}
安装包、运行单元测试和删除临时组织
最后,在临时组织中安装软件包,运行单元测试,然后删除 scratch org. 就是这样!
// -------------------------------------------------------------------------
// Install package in scratch org.
// -------------------------------------------------------------------------
stage('Install Package In Scratch Org') {
rc = command "${toolbelt}/sf package install --package ${PACKAGE_VERSION} --target-org installorg --wait 10"
if (rc != 0) {
error 'Salesforce package install failed.'
}
}
// -------------------------------------------------------------------------
// Run unit tests in package install scratch org.
// -------------------------------------------------------------------------
stage('Run Tests In Package Install Scratch Org') {
rc = command "${toolbelt}/sf apex run test --target-org installorg --result-format tap --code-coverage --test-level ${TEST_LEVEL} --wait 10"
if (rc != 0) {
error 'Salesforce unit test run in pacakge install scratch org failed.'
}
}
// -------------------------------------------------------------------------
// Delete package install scratch org.
// -------------------------------------------------------------------------
stage('Delete Package Install Scratch Org') {
rc = command "${toolbelt}/sf org delete scratch --target-org installorg --no-prompt"
if (rc != 0) {
error 'Salesforce package install scratch org deletion failed.'
}
}
示例 Jenkinsfile
Jenkinsfile 是一个文本文件,其中包含 Jenkins 流水线的定义。此 Jenkinsfile 展示了如何集成 Salesforce CLI 命令以自动测试 Salesforce 使用 Scratch 组织的应用程序。
Jenkinsfile 演练主题使用此 sfdx-jenkins-package Jenkinsfile 作为示例。
#!groovy
import groovy.json.JsonSlurperClassic
node {
def SF_CONSUMER_KEY=env.SF_CONSUMER_KEY
def SF_USERNAME=env.SF_USERNAME
def SERVER_KEY_CREDENTALS_ID=env.SERVER_KEY_CREDENTALS_ID
def TEST_LEVEL='RunLocalTests'
def PACKAGE_NAME='0Ho1U000000CaUzSAK'
def PACKAGE_VERSION
def SF_INSTANCE_URL = env.SF_INSTANCE_URL ?: "https://login.salesforce.com"
def toolbelt = tool 'toolbelt'
// -------------------------------------------------------------------------
// Check out code from source control.
// -------------------------------------------------------------------------
stage('checkout source') {
checkout scm
}
// -------------------------------------------------------------------------
// Run all the enclosed stages with access to the Salesforce
// JWT key credentials.
// -------------------------------------------------------------------------
withEnv(["HOME=${env.WORKSPACE}"]) {
withCredentials([file(credentialsId: SERVER_KEY_CREDENTALS_ID, variable: 'server_key_file')]) {
// -------------------------------------------------------------------------
// Authorize the Dev Hub org with JWT key and give it an alias.
// -------------------------------------------------------------------------
stage('Authorize DevHub') {
rc = command "${toolbelt}/sf org login jwt --instance-url ${SF_INSTANCE_URL} --client-id ${SF_CONSUMER_KEY} --username ${SF_USERNAME} --jwt-key-file ${server_key_file} --set-default-dev-hub --alias HubOrg"
if (rc != 0) {
error 'Salesforce dev hub org authorization failed.'
}
}
// -------------------------------------------------------------------------
// Create new scratch org to test your code.
// -------------------------------------------------------------------------
stage('Create Test Scratch Org') {
rc = command "${toolbelt}/sf org create scratch --target-dev-hub HubOrg --set-default --definition-file config/project-scratch-def.json --alias ciorg --wait 10 --duration-days 1"
if (rc != 0) {
error 'Salesforce test scratch org creation failed.'
}
}
// -------------------------------------------------------------------------
// Display test scratch org info.
// -------------------------------------------------------------------------
stage('Display Test Scratch Org') {
rc = command "${toolbelt}/sf org display --target-org ciorg"
if (rc != 0) {
error 'Salesforce test scratch org display failed.'
}
}
// -------------------------------------------------------------------------
// Push source to test scratch org.
// -------------------------------------------------------------------------
stage('Push To Test Scratch Org') {
rc = command "${toolbelt}/sf project deploy start --target-org ciorg"
if (rc != 0) {
error 'Salesforce push to test scratch org failed.'
}
}
// -------------------------------------------------------------------------
// Run unit tests in test scratch org.
// -------------------------------------------------------------------------
stage('Run Tests In Test Scratch Org') {
rc = command "${toolbelt}/sf apex run test --target-org ciorg --wait 10 --result-format tap --code-coverage --test-level ${TEST_LEVEL}"
if (rc != 0) {
error 'Salesforce unit test run in test scratch org failed.'
}
}
// -------------------------------------------------------------------------
// Delete test scratch org.
// -------------------------------------------------------------------------
stage('Delete Test Scratch Org') {
rc = command "${toolbelt}/sf org delete scratch --target-org installorg --no-prompt"
if (rc != 0) {
error 'Salesforce test scratch org deletion failed.'
}
}
// -------------------------------------------------------------------------
// Create package version.
// -------------------------------------------------------------------------
stage('Create Package Version') {
if (isUnix()) {
output = sh returnStdout: true, script: "${toolbelt}/sf package version create --package ${PACKAGE_NAME} --installation-key-bypass --wait 10 --json --target-dev-hub HubOrg"
} else {
output = bat(returnStdout: true, script: "${toolbelt}/sf package version create --package ${PACKAGE_NAME} --installation-key-bypass --wait 10 --json --target-dev-hub HubOrg").trim()
output = output.readLines().drop(1).join(" ")
}
// Wait 5 minutes for package replication.
sleep 300
def jsonSlurper = new JsonSlurperClassic()
def response = jsonSlurper.parseText(output)
PACKAGE_VERSION = response.result.SubscriberPackageVersionId
response = null
echo ${PACKAGE_VERSION}
}
// -------------------------------------------------------------------------
// Create new scratch org to install package to.
// -------------------------------------------------------------------------
stage('Create Package Install Scratch Org') {
rc = command "${toolbelt}/sf org create scratch --target-dev-hub HubOrg --set-default --definition-file config/project-scratch-def.json --alias installorg --wait 10 --duration-days 1"
if (rc != 0) {
error 'Salesforce package install scratch org creation failed.'
}
}
// -------------------------------------------------------------------------
// Display install scratch org info.
// -------------------------------------------------------------------------
stage('Display Install Scratch Org') {
rc = command "${toolbelt}/sf org display --target-org installorg"
if (rc != 0) {
error 'Salesforce install scratch org display failed.'
}
}
// -------------------------------------------------------------------------
// Install package in scratch org.
// -------------------------------------------------------------------------
stage('Install Package In Scratch Org') {
rc = command "${toolbelt}/sf package install --package ${PACKAGE_VERSION} --target-org installorg --wait 10"
if (rc != 0) {
error 'Salesforce package install failed.'
}
}
// -------------------------------------------------------------------------
// Run unit tests in package install scratch org.
// -------------------------------------------------------------------------
stage('Run Tests In Package Install Scratch Org') {
rc = command "${toolbelt}/sf apex run test --target-org installorg --result-format tap --code-coverage --test-level ${TEST_LEVEL} --wait 10"
if (rc != 0) {
error 'Salesforce unit test run in pacakge install scratch org failed.'
}
}
// -------------------------------------------------------------------------
// Delete package install scratch org.
// -------------------------------------------------------------------------
stage('Delete Package Install Scratch Org') {
rc = command "${toolbelt}/sf org delete scratch --target-org installorg --no-prompt"
if (rc != 0) {
error 'Salesforce package install scratch org deletion failed.'
}
}
}
}
}
def command(script) {
if (isUnix()) {
return sh(returnStatus: true, script: script);
} else {
return bat(returnStatus: true, script: script);
}
}
与 Travis CI 的持续集成
Travis CI 是一种基于云的持续集成 (CI) 服务,用于构建和测试托管在 GitHub 上的软件项目。
有关设置 Travis CI 的帮助,请参阅:
- 组织开发模型的示例 Travis CI 存储库
- 包开发模型的示例 Travis CI 存储库
组织开发模型的示例 CI 存储库
通过从您选择的供应商处克隆示例存储库,快速开始使用 CI。 每个存储库都有一个示例配置文件和一个全面的 README.md 分步信息。
这些示例存储库支持组织开发模型。此模型使用 Salesforce CLI、 源代码管理系统和应用程序生命周期中的沙盒。要确定这是否 模型适合您,请完成组织开发模型模块,赢取徽章。
供应商 | 链接到 GitHub 存储库 |
---|---|
AppVeyor | https://github.com/forcedotcom/sfdx-appveyor-org |
竹 | https://github.com/forcedotcom/sfdx-bamboo-org |
位桶 | https://github.com/forcedotcom/sfdx-bitbucket-org |
CircleCI系列 | https://github.com/forcedotcom/sfdx-circleci-org |
GitLab的 | https://github.com/forcedotcom/sfdx-gitlab-org |
詹金斯 | https://github.com/forcedotcom/sfdx-jenkins-org |
特拉维斯CI | https://github.com/forcedotcom/sfdx-travisci-org |
包开发模型的示例 CI 存储库
通过从您选择的供应商处克隆示例存储库,快速开始使用 CI。 每个存储库都有一个示例配置文件和一个全面的 README.md 分步信息。
这些示例存储库支持包开发模型。此模型使用 Salesforce CLI、源代码控制系统、用于开发的临时组织以及用于测试和 分期。要确定此型号是否适合您,请前往并通过以下方式获得徽章 完成包开发模型模块。
供应商 | 链接到 GitHub 存储库 |
---|---|
AppVeyor | https://github.com/forcedotcom/sfdx-appveyor-package |
竹 | https://github.com/forcedotcom/sfdx-bamboo-package |
位桶 | https://github.com/forcedotcom/sfdx-bitbucket-package |
CircleCI系列 | https://github.com/forcedotcom/sfdx-circleci-package |
GitLab的 | https://github.com/forcedotcom/sfdx-gitlab-packageCI/CD 模板 Salesforce/Apex 应用程序:https://gitlab.com/sfdx/sfdx-cicd-template |
詹金斯 | https://github.com/forcedotcom/sfdx-jenkins-package |
特拉维斯CI | https://github.com/forcedotcom/sfdx-travisci-package |
Salesforce DX 疑难解答
以下是一些帮助您解决问题的提示。
- CLI 版本信息 使用这些命令可查看有关 Salesforce CLI 的版本信息
。 - 错误:未找到
默认开发中心 由于授权问题,当您尝试创建临时组织时,您会看到此错误。 - 组织授权失败后无法工作
有时,您尝试使用 Salesforce CLI 或 IDE 授权 Dev Hub 组织或临时组织,但未成功登录到该组织。端口对于杂散授权过程保持打开状态,并且您不能使用 CLI 或 IDE。若要继续,请手动结束该过程。 - 错误:使用者密钥已被获取
假设你在已创建连接应用的组织上运行。当您尝试将检索到的源部署到其他组织时,部署失败并显示错误。发生了什么事?project retrieve startThe consumer key is already taken
CLI 版本信息
使用以下命令可查看有关 Salesforce CLI 的版本信息。
sf plugins --core // Version of the CLI and all installed plug-ins
sf --version // CLI version
错误:未找到默认开发中心
当您尝试创建临时组织时,由于授权问题,您会看到此错误。
假设你使用该标志成功授权了开发人员中心组织。与组织关联的用户名是默认用户名 Dev Hub 用户名。然后,您可以在不使用该标志的情况下成功创建临时组织。但是,当您尝试创建一个临时组织时 再次使用相同的CLI命令时,您会收到以下错误:–set-default-dev-hub–target-dev-hub
Error (1): No default dev hub found. Use -v or --target-dev-hub to specify an environment.
发生了什么事?
答:您不再位于运行 authorization 命令的目录中。这 使用标志的目录很重要。–set-default-dev-hub
如果从项目目录的根目录运行 authorization 命令,则会在本地设置配置变量。价值 仅当从同一项目目录运行命令时才适用。如果更改为 不同的目录并运行,本地 默认 Dev Hub 组织的设置不再适用,并且出现错误。target-dev-huborg create scratch
通过执行下列操作之一来解决问题。
- 全局设置,以便可以从任何目录运行。target-dev-huborg create scratch
sf config set target-dev-hub=<devhubusername> --global
- 从同一项目目录运行,其中 你已授权开发人员中心组织。org create scratch
- 使用 标志 with 从任何目录运行它。–target-dev-huborg create scratch
sf target-dev-hub --definition-file <file> --target-dev-hub <devhubusername> --alias my-scratch-org
- 若要检查是否已全局或本地设置配置值,请使用此命令并检查 “位置”列。
sf config list
组织授权失败后无法工作
有时,您尝试使用 Salesforce CLI 或 一个 IDE,但您没有成功登录到组织。港口仍然对流浪者开放 授权过程,并且您不能使用 CLI 或 IDE。若要继续,请结束该过程 手动地。
macOS 或 Linux
要从 macOS 或 Linux 上失败的组织授权中恢复,请使用终端终止 在端口 1717 上运行的进程。
- 在终端中,运行:
lsof -i tcp:1717
- 在结果中,找到使用该端口的进程的 ID。
- 跑:
kill -9 <the process ID>
窗户
要从 Windows 上失败的组织授权中恢复,请使用任务管理器结束节点 过程。
- 按 Ctrl+Alt+Delete,然后单击“任务管理器”。
- 选择“进程”选项卡。
- 找到名为 的进程。Node注意如果您是 Node.js 开发人员,则可以使用此进程运行多个进程 名字。
- 选择要结束的进程,然后单击“结束” 过程。
错误:使用者密钥已被获取
假设您在一个组织上运行 你已在其中创建已连接的应用。当您尝试将检索到的源部署到 不同的组织,部署失败并显示错误。发生了什么事?
project retrieve startThe consumer key is already taken
连接的应用包括网站或应用用来标识自身的使用者密钥 Salesforce的。使用者密钥在整个 Salesforce 生态系统中必须是唯一的。什么时候 您尝试部署检索到的 (和未更改的) 源文件与 将应用连接到新组织时,由于使用者密钥重复,部署失败。
有几个选项可以解决此问题。
- 在部署源之前,从项目中删除连接的应用源文件 到新组织。因此,不会创建连接的应用。连接的应用程序 源文件的名称类似于 。force-app/main/default/connectedApps/MyConnApp.connectedApp-meta.xml
- 更新已连接应用的文件,并将元素的值更改为唯一值。这是 显示元素的示例连接应用文件的代码片段。<consumerKey><consumerKey>
<?xml version="1.0" encoding="UTF-8"?> <ConnectedApp xmlns="http://soap.sforce.com/2006/04/metadata"> <contactEmail>john@doecompany.com</contactEmail> <contactPhone>5556789</contactPhone> <label>MyConnApp</label> <oauthConfig> <callbackUrl>http://localhost:1717/OauthRedirect</callbackUrl> <consumerKey>3MVG9PG9sFc71i9n55UWbx2</consumerKey> <isAdminApproved>false</isAdminApproved> ...
Salesforce DX 的限制
以下是您在使用 Salesforce DX 时可能遇到的一些已知问题。
有关最新的已知问题,请访问 Trailblazer Community 的已知问题页面。
Salesforce 命令行界面
如果与客户端密码一起使用,则授权失败auth:web:login描述: 如果您使用客户端 ID 和客户端密码运行,则无法使用 Salesforce CLI 发出 命令发送到临时组织,因为授权文件不正确 创建。auth:web:login
解决方法: 在没有客户端的情况下使用基于 Web 的流程 ID 和客户端密钥,或使用基于 JWT 的流程向组织授权。 有关开发说明,请参阅《Salesforce DX 开发人员指南》中的授权 Hub 和 Scratch 组织授权方法。Windows Defender 暂停 CLI 安装描述:在 Windows 上安装 Salesforce CLI 时, 你会看到 Windows Defender 警告。此消息是预期的,因为我们 更新了安装程序的代码签名证书。

解决方法:若要忽略此消息,请单击“运行” 反正。无法使用 Salesforce CLI 导入记录类型描述:运行命令时,我们不支持 RecordType。data:tree:import
解决方法:没有。对 Windows 上 Shell 环境的有限支持描述: Salesforce CLI 在命令提示符 () 和 Powershell 上进行了测试。有已知的 Cygwin 和 Min-GW 环境中的问题,以及 Windows 子系统的问题 Linux (WSL)。将来可能会测试和支持这些环境 释放。现在,请改用受支持的 shell。cmd.exe
解决方法: 没有。该命令不 完成执行force:apex:test:run描述: 在某些情况下,该命令不会完成执行。例子 这些情况包括 Apex 测试或 Apex 测试中的编译错误 当另一个预编译正在进行时触发另一个预编译。force:apex:test:run
解决方法: 通过键入 control-C 停止命令执行。如果命令是 作为持续集成 (CI) 作业的一部分,请尝试设置环境 变量。SFDX_PRECOMPILE_DISABLE=true
Dev Hub 和 Scratch 组织
Salesforce CLI 有时无法识别 Scratch 组织 社区描述:有时(但并非在所有情况下)Salesforce CLI 不承认与社区一起创建临时组织 特征。您无法使用 CLI 打开临时组织,即使 暂存组织在开发人员中心中列出。
解决方法:你可以试试这个 解决方法,尽管它并不能在所有情况下解决问题。删除 暂存组织,然后使用 CLI 创建新的暂存组织。 删除和重新创建临时组织将计入您的每日临时记录 组织限制。拉取社区并部署社区时出错描述:发生此错误是因为临时组织没有 所需的访客许可证。
解决方法:在您的临时组织中 定义文件,如果指定了 Communities 功能,则还要指定 站点功能。
源管理
错误:删除自定义标签后未找到任何结果force:source:status描述:在暂存中删除自定义标签后,该命令将返回错误 组织。force:source:statusNo Results Found
解决方法:选项#1:如果您只有一个或两个划痕 组织,您可以通过其 生成的用户名,请使用此解决方法。在 你的 DX project/.sfdx/org 目录,仅删除 受影响的临时组织所在的文件夹。
选项#2:如果你有 与您的 DX 项目相关的几个临时组织,而您不知道 要删除哪个临时组织的本地数据,请使用此解决方法。删除 Your DX project/.sfdx/org 目录。此目录包含所有 与项目相关的临时组织。当您运行下一个 source-tracking 命令,用于此组织或其他临时组织 (, , 或 ),CLI 将重建 该组织的源跟踪信息。source:pushsource:pullsource:status
删除 目录(在选项 #1 或选项 #2 之后),再次运行。force:source:status错误:名为“Account.PersonAccount”的“RecordType”类型的实体不能是 发现描述: 尽管您可以在临时打开个人帐户 组织,将该功能添加到您的临时组织定义中,运行或导致错误,source:pushsource:pull
解决方法: 没有。force:source:convert 不会将安装后脚本添加到包 .xml描述:如果运行 ,package.xml 不包含帖子 安装脚本。force:source:convert解决方法:若要解决此问题,请选择以下选项之一 方法:
- 手动将元素添加到元数据目录中的 package.xml 中 产生<postInstallClass>force:source:convert
- 手动将元素添加到发布组织中的包中,或者 要将软件包部署到的组织。
必须在对象的元数据文件中手动启用源跟踪描述:如果您在标准或自定义设备上启用 Feed 跟踪 对象,然后运行 , Feed 跟踪未启用。force:source:pull
解决方法:在您的 Salesforce 中 DX项目,手动启用标准或自定义的Feed跟踪 对象在其元数据文件 (-meta.xml) 中添加 .<enableFeeds>true</enableFeeds>无法将查找筛选器推送到临时组织描述: 执行命令推送关系源时 字段,有时会收到以下错误:force:source:push
duplicate value found: <unknown> duplicates value on record with id: <unknown> at line num, col num.
解决方法: 没有。
部署
部署时编译可能会增加临时组织中的部署时间描述: 如果您的 Apex 代码部署时间很慢,您的临时组织可能会 有设置 设置为 。enableCompileOnDeploytrue解决方法:要将其关闭,请将其设置为 ( default) 或从 Scratch 组织中删除该设置 定义。
false
{
"orgName": "ekapner Company",
"edition": "Developer",
"features": [],
"settings": {
"lightningExperienceSettings": {
"enableS1DesktopEnabled": true
},
"apexSettings": {
"enableCompileOnDeploy": false
}
}
}
托管的第一代软件包
在临时组织中安装软件包时,不会执行任何测试描述: 如果将测试作为连续测试的一部分 集成过程中,当您在 Scratch 组织。
解决方法: 您可以在 软件包已安装。CLI 中用于托管包密码的新术语描述: 使用 CLI 将安装密钥添加到软件包版本或 要安装受密钥保护的软件包版本,密钥的参数名称为 。当您查看 Salesforce 用户界面中的托管包版本,相同的包 属性称为“密码”。在 API 中,对应的字段名称, “password”,保持不变。–installationkey
解决方法: 没有。
托管的第二代软件包
无法为托管软件包指定修补程序版本描述: 由四部分组成的软件包版本号包括一个补丁段,定义 作为 major.minor.patch.build。但是,您不能为 第二代托管包。如果设置了修补程序,则软件包创建将失败 包描述符中的数字。我们计划为 Winter ’20 版本中的托管包。
解决方法: 始终设置 版本号的补丁段,设置为 0。例如,1.2.0.1 是 有效,但 1.2.1.1 无效。受保护的自定义元数据和自定义设置对开发人员可见 临时组织(如果已安装的软件包共享命名空间)描述: 将机密存储在 使用受保护的自定义元数据或受保护的第二代包 自定义设置。您可以使用 相同的命名空间。但是,当您在临时组织中安装这些软件包时, 这些机密对于在 具有共享命名空间的 Scratch 组织。将来,我们可能会添加一个 “package-protected” 关键字来防止访问这些中的包机密 情况。
解决方法: 没有。
解锁套餐
受保护的自定义元数据和自定义设置对开发人员可见 临时组织(如果已安装的软件包共享命名空间)描述: 在解锁的密钥中存储密钥时要小心 使用受保护的自定义元数据或受保护的自定义设置的包。你 可以创建多个具有相同命名空间的解锁包。但是,当 您在临时组织中安装这些软件包,这些密钥对 在临时组织工作的任何开发人员,共享 命名空间。将来,我们可能会在 在这些情况下阻止访问包机密。
解决方法: 没有。