Salesforceサイトのメタデータ形式によるデプロイと、マニフェストファイル(package.xml)
この記事はSalesforce Advent Calendar 2023の8日目の投稿です。
最近気づいた、Salesforceサイトのデプロイには注意点が多数ある、という話をしてみます。
0. 前提
2023/12時点のSalesforce Platformの仕様に基づいています。
以下のSalesforce CLIバージョンと、git、gitに同梱されているgit bashを利用しています。
$ sf version
@salesforce/cli/2.19.8 win32-x64 node-v20.9.0
$ git version
git version 2.42.0.windows.2
1. はじめに
Salesforce CLIを利用してスクラッチ組織で開発を行う場合、様々なメタデータやソースコードのデプロイ・取得がコマンド一つで簡単に実施できます。
- 開発用PC→Scratch組織 :
$ sf project deploy start --target-org ${Scratch組織ユーザ名 or Alias}
- Scratch組織→開発用PC :
$ sf project retrieve start --target-org ${Scratch組織ユーザ名 or Alias}
ただし、ソース追跡のないSalesforce環境へソース形式のメタデータをMetadata APIを利用してデプロイする場合、現実的にはpackage.xmlによるデプロイを選択する必要があります。
ソース追跡のない組織へデプロイする際のpackage.xmlの必要性について
「Salesforce DX 開発者ガイド」を読むと次の記載がありますが、
ソース形式でメタデータをリリースするには、次の方法を使用します。
- リリースするコンポーネントをリストした package.xml を指定する
- メタデータコンポーネント名のカンマ区切りリストを指定する
- リリースするソースファイルパスのカンマ区切りリストを指定する
「コンポーネント名」や「ソースファイルパス」を指定してデプロイする場合は次の問題があり、何度もデプロイを行うことを考えると、package.xmlの管理を行うのが最善と考えられます。
- 変更セットでのデプロイ時と同じような手動操作が発生
- 本番環境やFull Sandbox等へのデプロイの度に、前回のリリースから今回のリリースまでに変更したリソースを漏れなくデプロイコマンドの引数に渡す必要があるため、自動デプロイとの相性が悪い
1.1. 表記について
公式な用語が不明だったため、以下2つの用語を導入します。
「スクラッチ組織へのPush」はソース形式メタデータのスクラッチ組織への転送を指し、コマンドで表現すると以下の操作を指します。
$ sf project deploy start \
--target-org ${Scratch組織ユーザ名 or Alias}
「package.xmlによるデプロイ」はマニフェストファイルの内容を参照したソース形式メタデータの任意の組織への転送を指し、コマンドで表現すると以下の操作となります。
$ sf project deploy start \
--mamifest ./package.xml \
--target-org ${任意の組織のユーザ名 or Alias}
2. package.xmlによるデプロイと、スクラッチ組織へのPushでデプロイ成否が変わりうる
スクラッチ組織で開発を行っていたのですが、スクラッチ組織へのPushと、package.xmlによるデプロイで、デプロイの成否が変わる事象に最近直面しました。
この時はSalesforce Sitesをソース形式のメタデータでデプロイする必要がありましたが、スクラッチ組織へのPushは成功するのに、package.xmlによるデプロイの場合だけエラーが発生していました。
package.xmlによるデプロイ時に発生したエラーの原因は2つありました。
2.1. package.xmlに設定が漏れていた
スクラッチ組織で開発中にリソースを追加した場合、ソース形式のメタデータはSalesforce CLIで適切に管理することは可能ですが、package.xmlの内容まで書き換えるわけではありません。
なお、アスタリスク指定を許容しているメタデータと、API参照名の直接指定が必要なメタデータの両方が存在するため、現状では特定の種類のメタデータを追加した場合のみpackage.xmlを修正する必要があります。
ということでSalesforceサイトのメタデータをデプロイ対象に含めるよう、以下のようにpackage.xmlに設定を追加する必要がありました。
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
(省略)
<types>
<members>CustomSite</members>
<name>*</name>
</types>
(省略)
<version>59.0</version>
</Package>
2.2. CLIの動作が異なっていた
Salesforceサイトのメタデータではレコードのデフォルト所有者の指定をユーザーIDベースで指定するのですが(以下ソース参照)、
- スクラッチ組織へのPush : メタデータに記載されているユーザIDは無視して、自動でレコード所有者やサイト管理者を指定
- package.xmlによるデプロイ : メタデータに記載されたレコード所有者やサイト管理者のユーザID(メールアドレス形式)に一致するユーザーが存在しない場合はエラー
というようにデプロイ時のチェックが異なっている模様でした。
これをスクラッチ組織での開発中に検知することは初見では困難なレベルで、ソース追跡なしのSandboxへpackage.xmlによるデプロイを行ってようやくわかる事象といえます。
Salesforceサイトのメタデータ例
以下はSalesforceサイトを作成後、sf project retrieve start
を実行しソース形式でダウンロードしたメタデータです。
-
siteGuestRecordDefaultOwner
(Salesforceサイトで作成されたデフォルトのレコード所有者) -
siteAdmin
(Salesforceサイトの管理者)
という属性にメールアドレス形式のユーザーIDが確認できます。
<?xml version="1.0" encoding="UTF-8"?>
<CustomSite xmlns="http://soap.sforce.com/2006/04/metadata">
<active>true</active>
<allowGuestPaymentsApi>false</allowGuestPaymentsApi>
<allowHomePage>false</allowHomePage>
<allowStandardAnswersPages>false</allowStandardAnswersPages>
<allowStandardIdeasPages>false</allowStandardIdeasPages>
<allowStandardLookups>false</allowStandardLookups>
<allowStandardPortalPages>true</allowStandardPortalPages>
<allowStandardSearch>false</allowStandardSearch>
<authorizationRequiredPage>Unauthorized</authorizationRequiredPage>
<bandwidthExceededPage>BandwidthExceeded</bandwidthExceededPage>
<browserXssProtection>true</browserXssProtection>
<cachePublicVisualforcePagesInProxyServers>true</cachePublicVisualforcePagesInProxyServers>
<clickjackProtectionLevel>SameOriginOnly</clickjackProtectionLevel>
<contentSniffingProtection>true</contentSniffingProtection>
<enableAuraRequests>true</enableAuraRequests>
<fileNotFoundPage>FileNotFound</fileNotFoundPage>
<genericErrorPage>Exception</genericErrorPage>
<inMaintenancePage>InMaintenance</inMaintenancePage>
<inactiveIndexPage>InMaintenance</inactiveIndexPage>
<indexPage>TestSite</indexPage>
<masterLabel>テストサイト</masterLabel>
<redirectToCustomDomain>true</redirectToCustomDomain>
<referrerPolicyOriginWhenCrossOrigin>true</referrerPolicyOriginWhenCrossOrigin>
<siteAdmin>test-rc9yfipkon8l@example.com</siteAdmin>
<siteGuestRecordDefaultOwner>test-rc9yfipkon8l@example.com</siteGuestRecordDefaultOwner>
<siteTemplate>SiteTemplate</siteTemplate>
<siteType>Visualforce</siteType>
</CustomSite>
3. Salesforceサイトのメタデータの管理方法について
上記サンプルコードを見ると、Salesforceサイトのメタデータには「ユーザーID」が直接書き込まれている(ハードコードと呼ばれる)ことが分かります。
SalesforceのユーザーIDはグローバルでユニークであることと、開発ライフサイクルで様々なSalesforce組織へのデプロイが必要であること(例えばスクラッチ組織、Sandbox、本番)を考えると、エラーなくSalesforceサイトをデプロイする際にこのハードコードされている「ユーザーID」が課題になります。
方法として取りうる案は以下の通りです。
- Salesforceサイトのメタデータは1ファイルで管理し、Sandbox/本番デプロイの度に、手動でIDを書き換える
- SalesforceサイトのメタデータをSandbox、本番組織の最低2ファイルで管理し、ユーザーID以外の差分が発生しない仕組みを構築
後者の仕組みをどう作るべきかを検討してみます。
3.1. スクラッチ組織へのデプロイ
これまでに述べた通り、スクラッチ組織へのPush時は、利用しているスクラッチ組織のSalesforceユーザーのIDを利用するので、エラーなくデプロイが可能です。
従って、SandboxのSalesforceサイトと、スクラッチ組織のSalesforceサイトのメタデータを分ける必要はなさそうですが、スクラッチ組織へのPush時は本来不要な本番組織用のSalesforceサイトもデプロイされてしまいます。
このような場合には.forceignoreファイルに本番組織用のメタデータのファイル名を記載することで、不要なSalesforceサイトがスクラッチ組織に作成されないように制御が可能です。
3.2. Sandboxへのデプロイ
スクラッチ組織の開発時と同じメタデータファイルを利用すれば、開発時と全く同じ設定を保持できるのであまり問題にならないと考えられます。
3.2. 本番組織へのデプロイ
こちらで問題になるのは、Sandbox用のメタデータファイルと、本番用のメタデータファイルでの更新漏れが発生しうることです。
もちろん、Merge Request(Pull Request)時に気づければ問題はないものの、見落としが発生する可能性はあるため、何らかの仕組みで検知することを考えてみます。
gitで、今チェックアウトされたブランチと、他のブランチとの差分を求める場合はgit diff
というコマンドがありますが、--name-only
というオプションを付けると、
$ git diff ${差分を比較したいブランチ名} --name-only
差分のあるファイル名だけが相対パスで返されます。
上記コマンドの結果に、本番組織用のメタデータファイルのファイル名が含まれるかどうかは
$ git diff ${差分を比較したいブランチ名} --name-only | grep ${本番組織用のメタデータファイル名}
というようにパイプ(|
)でgrepを実行するのですが、この際にファイル名が存在しないとコマンドは失敗ステータスを返します。
あとは、Salesforceサイトのメタデータに差分がある場合のみ、上記コマンドをCIのタイミングで走らせて、差分が漏れている場合だけCIをエラー応答させれば、一応のチェックは可能です。(詳細な実装はCIツールの仕様に依存するため省略)
4. まとめ
Salesforceのスクラッチ組織による開発は相当便利だと感じるものの、作成したメタデータをSandboxや本番組織へデプロイする際は様々な注意点があると感じます。
今後もこのような注意点に気づいたら、なるべく言語化していきたいと考えています。
Discussion