无论你的项目用什么开发语言,都离不开部署这件事,今天我们就聊聊Salesforce中的部署方式。
我本人常用的部署方式有三种:更改集(Change Set)、Workbench、ANT(Force.com migration tool)
1、更改集(Change Set)
更改集应该是日常开发中最常用的部署方式,小规模开发或对象及字段的更改等,使用更改集部署比较方便快捷。但是,更改集仅用于在连接的组织中部署。
更改集有两种:
- 入站更改集
- 出站更改集
我们以开发环境和生产环境为例,开发环境的出站更改集进行上载,生产环境的入站更改集进行对上载的集合(或者理解为包)进行对应部署。
1.1、开发环境出站更改集的配置与上载
开发环境上载完成后,会受到提示。
1.2、生产环境入站更改集进行部署
生产环境进行验证和部署后,部署状态显示已成功。当然,在生产环境部署Apex代码时,要运行对应的测试类。由于更改集大家都很熟悉,这里就不过多赘述。
2、Workbench
先简单介绍一下Workbench:
Workbench是Salesforce官方提供的一个在线工具,用来帮助开发人员(包括集成)、管理员更轻松地与Salesforce实例交互、管理和测试。它提供了许多功能,包括API调用、数据查询、数据加载、元数据检查、日志分析等。
以下是Salesforce Workbench的主要特点:
1. **API调用**: Workbench允许用户直接在浏览器中进行Salesforce API的调用。这使得用户能够在不编写代码的情况下测试API端点、验证数据格式等。
2. **数据操作**: 用户可以使用Workbench执行各种数据操作,包括创建、读取、更新和删除记录。这对于在Salesforce中进行数据迁移、数据清理或数据验证非常有用。
3. **元数据检查**: Workbench允许用户检查Salesforce实例中的元数据,包括对象、字段、布局、工作流规则等。这对于了解实例配置、识别潜在问题或进行比较分析非常有帮助。
4. **日志分析**: 用户可以使用Workbench查看Salesforce实例中的日志,并分析执行过程中的事件和错误。这对于调试和优化代码或集成非常有用。
页面如下,链接地址请跳转
下面进行具体操作。
2.1、使用workbench从Salesforce中检索&获取元数据
步骤1:例如,我的环境中有个自定义对象:主环境测试对象(MainObj__c)。想通过workbench从Salesforce中检索&获取元数据,需要使用xml文件操作,package.xml内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<types>
<members>MainObj__c</members>
<name>CustomObject</name>
</types>
<version>58.0</version>
</Package>
步骤2:然后,操作workbench
步骤3:下载&解压后,文件夹结构如下:
上述【objects】📂打开后是下面文件:
MainObj__c对象的元数据(Metadata)
<?xml version="1.0" encoding="UTF-8"?>
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
<actionOverrides>
<actionName>Accept</actionName>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>Accept</actionName>
<formFactor>Large</formFactor>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>Accept</actionName>
<formFactor>Small</formFactor>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>CancelEdit</actionName>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>CancelEdit</actionName>
<formFactor>Large</formFactor>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>CancelEdit</actionName>
<formFactor>Small</formFactor>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>Clone</actionName>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>Clone</actionName>
<formFactor>Large</formFactor>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>Clone</actionName>
<formFactor>Small</formFactor>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>Delete</actionName>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>Delete</actionName>
<formFactor>Large</formFactor>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>Delete</actionName>
<formFactor>Small</formFactor>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>Edit</actionName>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>Edit</actionName>
<formFactor>Large</formFactor>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>Edit</actionName>
<formFactor>Small</formFactor>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>List</actionName>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>List</actionName>
<formFactor>Large</formFactor>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>List</actionName>
<formFactor>Small</formFactor>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>New</actionName>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>New</actionName>
<formFactor>Large</formFactor>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>New</actionName>
<formFactor>Small</formFactor>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>SaveEdit</actionName>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>SaveEdit</actionName>
<formFactor>Large</formFactor>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>SaveEdit</actionName>
<formFactor>Small</formFactor>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>Tab</actionName>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>Tab</actionName>
<formFactor>Large</formFactor>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>Tab</actionName>
<formFactor>Small</formFactor>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>View</actionName>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>View</actionName>
<formFactor>Large</formFactor>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>View</actionName>
<formFactor>Small</formFactor>
<type>Default</type>
</actionOverrides>
<allowInChatterGroups>true</allowInChatterGroups>
<compactLayoutAssignment>SYSTEM</compactLayoutAssignment>
<deploymentStatus>Deployed</deploymentStatus>
<enableActivities>true</enableActivities>
<enableBulkApi>true</enableBulkApi>
<enableFeeds>false</enableFeeds>
<enableHistory>true</enableHistory>
<enableLicensing>false</enableLicensing>
<enableReports>true</enableReports>
<enableSearch>true</enableSearch>
<enableSharing>true</enableSharing>
<enableStreamingApi>true</enableStreamingApi>
<externalSharingModel>Private</externalSharingModel>
<fields>
<fullName>ExternalObj__c</fullName>
<externalId>false</externalId>
<label>测试用外部对象</label>
<length>255</length>
<referenceTo>ExternalObj_c__x</referenceTo>
<relationshipLabel>主环境测试对象</relationshipLabel>
<relationshipName>ExternalObj0wgJ</relationshipName>
<trackHistory>false</trackHistory>
<trackTrending>false</trackTrending>
<type>ExternalLookup</type>
</fields>
<fields>
<fullName>WorkbenchField__c</fullName>
<externalId>false</externalId>
<label>Workbench测试字段</label>
<length>255</length>
<required>false</required>
<trackHistory>false</trackHistory>
<trackTrending>false</trackTrending>
<type>Text</type>
<unique>false</unique>
</fields>
<label>主环境测试对象</label>
<listViews>
<fullName>All</fullName>
<filterScope>Everything</filterScope>
<label>全部</label>
</listViews>
<nameField>
<label>主环境测试对象名称</label>
<trackHistory>false</trackHistory>
<type>Text</type>
</nameField>
<pluralLabel>主环境测试对象</pluralLabel>
<searchLayouts/>
<sharingModel>ReadWrite</sharingModel>
<visibility>Public</visibility>
</CustomObject>
当然,也可以用类似于下面的文件,取得对象中某一字段的具体数据:
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<types>
<members>MainObj__c.WorkbenchField__c</members>
<name>CustomField</name>
</types>
<version>58.0</version>
</Package>
2.2 使用workbench部署取元数据
2.2.1 从一个环境部署到另一个环境(部署对象、Apex等的操作方法一样)
步骤1:假设,主环境中有一个自定义对象:
步骤2:主环境创建软件包,并将上述自定义对象添加到软件包
步骤3:用主环境账号登录Workbench,导出上述自定义对象的元数据
步骤4:将步骤3导出的元数据下载,Logout workbench后,用外部环境再次登录workbench,导入外部环境
步骤5:去外部环境验证部署结果
2.2.2 非不同环境间部署,只操作一个环境的话,可以直接作成文件,然后直接通过workbench,部署到该环境中
步骤1:自创建部属用文件
<!-- package.xml文件 -->
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<types>
<members>WbDeployObj__c.WbField1__c</members>
<name>CustomField</name>
</types>
<version>58.0</version>
</Package>
<!-- Object_c.object文件 -->
<?xml version="1.0" encoding="UTF-8"?>
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
<fields>
<fullName>WbField1__c</fullName>
<encryptionScheme>None</encryptionScheme>
<externalId>false</externalId>
<label>WB单一追加字段</label>
<length>100</length>
<required>false</required>
<trackHistory>false</trackHistory>
<trackTrending>false</trackTrending>
<type>Text</type>
<unique>false</unique>
</fields>
</CustomObject>
步骤2:导入
步骤3:去环境中检验部署结果
3、ANT(Force.com migration tool)
ANT 迁移工具是一个命令行实用工具,用于从 Salesforce 实例检索、部署和删除元数据。它帮助我们在不同的 Salesforce 实例之间迁移元数据组件,例如 Apex Classes、Visualforce、SObject 等组件。
3.1 使用 ANT 迁移工具的优点
- 元数据备份:从 Salesforce 组织获取 XML 文件形式的元数据,并将其下载到本地计算机上。
- 删除组件:更改集不允许从目标组织中删除任何元数据组件(metadata component)。但是使用 ANT 迁移工具,可以从目标组织中删除组件。这可以使用 destructiveChanges.xml 文件来完成。
- 组件支持:某些组件不支持使用更改集进行迁移,但可以使用 ANT 迁移工具来迁移它们。
- 命令行支持:还可以使用一些特定命令来调用 API。
3.2 ANT 迁移工具和更改集之间的差异
ANT 迁移工具(ANT Migration Tool) | 更改集(Change Set) | |
用户界面 | ANT 迁移工具支持命令行界面,需要使用 Ant 脚本和 `build.xml` 文件来指定要取得/部署的元素。 | 更改集是基于 UI 的部署工具,可在 Salesforce 组织中的设置中找到。使用 Setup > Deploy > Outbound / Inbound ChangeSets。 |
删除组件 | 使用 ANT 迁移工具,可以从目标组织中删除组件 | 更改集不允许从目标组织中删除任何元数据组件。 |
自动化 | 支持通过在工具如 Jenkins 等中执行脚本来自动化部署。 | 仅支持在沙盒之间或沙盒与生产环境之间手动部署。 |
依赖关系 | 所有依赖关系都需要手动添加。 | 依赖关系可以通过单击轻松添加。 |
持续部署 | 适用于大型团队,支持连续部署。 | 适用于中等规模的团队,使用连接的组织。 |
不同组织 | 可以在不相关的 Salesforce 组织中部署组件。 | 更改集仅允许在相关的组织和沙盒中部署组件。 |
两者都可以用于几乎任何事情的部署。如果部署规模较小,会使用更改集;但对于较大的部署(以及大型项目的首次部署),我们更推荐Ant(迁移工具)。
原因:在更改集中,如果必须为某个对象部署 100 个字段,在上传到其他环境之前,我们必须单独选择要添加到变更改中的每个字段。考虑一下如果有 10-15 个这样的对象,这极其耗时且容易出错。通过Ant,我们要做的就是在XML中提及对象,所有字段都会自动部署。
另外,如果必须删除生产中的组件,可以选择通过 ANT(Destructive.XML) 来完成,而更改集中没有这样的选项。
最后,更改集仅用于在连接的组织中部署,而通过 ANT 您可以在任何组织中部署。
3.3 配置Ant迁移工具
步骤1:安装Java
建议使用 Java 版本 11 或更高版本(java -version)
步骤2:下载ANT工具(免安装)
Apache Ant - Binary Distributions
步骤3:下载Ant迁移工具(免安装)
Salesforce Developers
※ 注意,解压缩后,将[salesforce_ant_59.0]中的[ant-salesforce.jar]文件复制&粘贴到[apache-ant-1.10.14-bin]中\lib文件夹下
步骤4:设置路径(只设置用户变量即可)
ANT_HOME : ANT的位置.
JAVA_HOME : JDK的位置(where java)
Path : 设置Ant的位置(需要到bin文件夹)
配置以后,检查java和Ant的版本
步骤5:配置Build.xml文件和Build.properties文件
要设置与 salesforce 的连接,我们需要配置“ build.properties
# build.properties
#
# Specify the login credentials for the desired Salesforce organization
sf.username = 你的Salesforce用户名
sf.password = 你的密码
#sf.sessionId = <Insert your Salesforce session id here. Use this or username/password above. Cannot use both>
#sf.pkgName = <Insert comma separated package names to be retrieved>
#sf.zipFile = <Insert path of the zipfile to be retrieved>
#sf.metadataType = <Insert metadata type name for which listMetadata or bulkRetrieve operations are to be performed>
# Use 'https://login.salesforce.com' for production or developer edition (the default if not specified).
# Use 'https://test.salesforce.com for sandbox.
sf.serverurl = https://login.salesforce.com
sf.maxPoll = 20
# If your network requires an HTTP proxy, see http://ant.apache.org/manual/proxy.html for configuration.
#
<!-- build.xml文件-->
<project name="Sample usage of Salesforce Ant tasks" default="test" basedir="." xmlns:sf="antlib:com.salesforce">
<property file="build.properties"/>
<property environment="env"/>
<!-- Setting default value for username, password and session id properties to empty string
so unset values are treated as empty. Without this, ant expressions such as ${sf.username}
will be treated literally.
-->
<condition property="sf.username" value=""> <not> <isset property="sf.username"/> </not> </condition>
<condition property="sf.password" value=""> <not> <isset property="sf.password"/> </not> </condition>
<condition property="sf.sessionId" value=""> <not> <isset property="sf.sessionId"/> </not> </condition>
<taskdef resource="com/salesforce/antlib.xml" uri="antlib:com.salesforce">
<classpath>
<pathelement location="../ant-salesforce.jar" />
</classpath>
</taskdef>
<!-- Shows deploying code & running tests for code in directory -->
<target name="deployCode">
<!-- Upload the contents of the "codepkg" directory, running the tests for just 1 class -->
<sf:deploy username="${sf.username}" password="${sf.password}" sessionId="${sf.sessionId}" serverurl="${sf.serverurl}" maxPoll="${sf.maxPoll}" deployRoot="codepkg" testLevel="RunSpecifiedTests" rollbackOnError="true">
<!-- 这里必须要填!!!否则出现错误: You must provide at least one test to run -->
<runTest>AccountHandlerTest</runTest>
</sf:deploy>
</target>
<!-- Shows retrieving code; only succeeds if done after deployCode -->
<target name="retrieveCode">
<!-- Retrieve the contents listed in the file codepkg/package.xml into the codepkg directory -->
<sf:retrieve username="${sf.username}" password="${sf.password}" sessionId="${sf.sessionId}" serverurl="${sf.serverurl}" maxPoll="${sf.maxPoll}" retrieveTarget="codepkg" unpackaged="codepkg/package.xml"/>
</target>
<!-- 定义删除自定义对象的目标 -->
<target name="deleteCustomObject">
<sf:deploy username="${sf.username}" password="${sf.password}" serverurl="${sf.serverurl}" maxPoll="${sf.maxPoll}" deployRoot="removecodepkg"/>
</target>
</project>
步骤6:创建(已经存在的话就修改)对应位置的Package.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<types>
<members>*</members>
<name>ApexClass</name>
</types>
<types>
<members>*</members>
<name>ApexTrigger</name>
</types>
<version>59.0</version>
</Package>
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>MyPkg</fullName>
<types>
<members>*</members>
<name>CustomObject</name>
</types>
<types>
<members>*</members>
<name>StandardObject</name>
</types>
<version>59.0</version>
</Package>
3.4 通过Ant迁移工具(Ant Migration Tool)取代码
步骤1:先进到salesforce_ant的sample目录下
步骤2:通过[ant retrieveCode]命令取代码
3.5 通过Ant迁移工具部署代码
步骤1:先进到salesforce_ant的sample目录下
步骤2:通过[ant deployCode]命令部署代码
出现上述错误的原因是:Apex没有测试类,导致覆盖率不够。追加测试类后,部署成功,如下:
假设,只想修改&部署AccountHandler.cls这一本代码,那么,本地修改后,并只保留如下文件,执行上述命令部署即可
3.6 使用Ant迁移工具删除组件
要删除组件,请使用与部署组件相同的过程,但还要包含一个名为destructiveChanges.xml 的删除清单文件,并在此清单中列出要删除的组件
destructiveChanges.xml源文件:
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>codepkg</fullName>
<types>
<members>SampleDeployClass</members>
<members>SampleFailingTestClass</members>
<name>ApexClass</name>
</types>
<types>
<members>SampleAccountTrigger</members>
<name>ApexTrigger</name>
</types>
<version>59.0</version>
</Package>
例如,我们要删除环境中一个自定义对象:
这时,destructiveChanges.xml的内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<types>
<members>AntDelObj__c</members>
<name>CustomObject</name>
</types>
<version>59.0</version>
</Package>
与destructiveChanges.xml同目录下package.xml的内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<version>59.0</version>
</Package>
运行[ant deleteCustomObject]即可删除此自定义对象
还有一些其他的部署方式,如SalesforceDX、CI/CD Pipeline或者其他一些第三方工具,这里就不一一介绍了。
总结:部署方式,还是要根据项目的实际情况来进行选择,确保安全且便捷。
Copyright © 乔木船长
个人主页:乔木船长
欢迎转发点评和指正!