Spring’21で、スクラッチ組織でしか利用できなかったソース追跡がDeveloper SandboxとDeveloper Pro Sandboxでも利用できるようになったので使ってみました。
ソース追跡を有効化
Sandboxでソース追跡を有効化するには、本番組織での設定が必要です。
本番組織での設定変更後に、作成または更新したSandboxでソース追跡ができるようになります。
(スクラッチ環境ではソース追跡の有効化は不要で、すぐに使えます。)
有効化の手順は、Salesforce DX 開発者ガイドで紹介されています。
sfdxコマンド
ソース追跡で使用する主なコマンドは以下の3つです。
-
force:source:status
リモート(Sandboxやスクラッチ環境)とローカルの変更を確認できます。
-r オプションを付けるとリモートのみ、-lオプションを付けるとローカルのみの変更を確認できます。
-
force:source:pull
force:source:statusで確認できるリモートの変更の全てを取り込みます。
-f オプションを付けると、リモートとローカルの変更が競合していてもリモートの変更で上書きします。
-
force:source:push
force:source:statusで確認できるローカルの変更の全てをリモートに適用します。
-f オプションを付けると、リモートとローカルの変更が競合していてもローカルの変更で上書きします。
ローカルで変更してみる
VS Codeで SFDCプロジェクトを作成し、ソース追跡が有効な組織に接続し、ローカルで「myclass01」クラスを作成しました。
>sfdx force:apex:class:create -n myclass01 -d force-app/main/default/classes target dir = XXXX\force-app\main\default\classes create force-app\main\default\classes\myclass01.cls create force-app\main\default\classes\myclass01.cls-meta.xml
プロジェクトには.clsファイルと.cls-meta.xmlが追加されています。
force:source:status
コマンドで変更を確認してみます。
>sfdx force:source:status === Source Status STATE FULL NAME TYPE PROJECT PATH ───────── ───────── ───────── ───────────────────────────────────────────────────── Local Add myclass01 ApexClass force-app\main\default\classes\myclass01.cls Local Add myclass01 ApexClass force-app\main\default\classes\myclass01.cls-meta.xml
ローカルで2ファイル追加したことを確認できました。
この変更を force:source:push
コマンドでリモートに反映してみます。
>sfdx force:source:push *** Deploying with SOAP *** Job ID | 0Af1m00000SLnB5CAL SOURCE PROGRESS | | 1/1 Components === Pushed Source STATE FULL NAME TYPE PROJECT PATH ───── ───────── ───────── ───────────────────────────────────────────────────── Add myclass01 ApexClass force-app\main\default\classes\myclass01.cls Add myclass01 ApexClass force-app\main\default\classes\myclass01.cls-meta.xml
無事デプロイできましたので、再度force:source:status
コマンドで変更を確認してみます。
>sfdx force:source:status === Source Status No results found
ローカルとリモートで差異がないことを確認できました。
リモートで変更してみる
次に、接続している組織の設定画面から「myclass01」を更新してみました。
force:source:status
コマンドで変更を確認してみます。
>sfdx force:source:status Source Status STATE FULL NAME TYPE PROJECT PATH ────────────── ───────── ───────── ───────────────────────────────────────────────────── Remote Changed myclass01 ApexClass force-app\main\default\classes\myclass01.cls Remote Changed myclass01 ApexClass force-app\main\default\classes\myclass01.cls-meta.xml
リモートで2ファイル変更されていることを確認できましたので、この変更をforce:source:pull
コマンドでローカルに取り込みます。
>sfdx force:source:pull === Pulled Source STATE FULL NAME TYPE PROJECT PATH ─────── ───────── ───────── ──────────────────────────────────────────── Changed myclass01 ApexClass force-app\main\default\classes\myclass01.cls
ローカルのmyclass01.cls に、リモートでの変更が反映され、force:source:statu
コマンドでも、ローカルとリモートで差異がないことを確認できました。
>sfdx force:source:status === Source Status No results found
ソース追跡ではそのほかのメタデータの変更も追跡できるので、項目の追加・削除を試してみました。
リモート側で項目を追加し、プロファイル別項目レベルセキュリティ設定やページレイアウトに追加したので、これらの変更をまとめて確認できました。
=== Source Status STATE FULL NAME TYPE PROJECT PATH ───────────── ────────────────────── ─────────── ──────────────────────────────────────────── Remote Add Admin Profile Remote Add Account.Field1__c CustomField Remote Add Account-Account Layout Layout
これらの変更をforce:source:pull
コマンドでローカルに取り込んだ後、リモート側で項目を削除すると、その削除も確認できました。項目追加時と同様に、項目削除でプロファイル別項目レベルセキュリティ設定やページレイアウトも変更されたことがわかります。
>sfdx force:source:status === Source Status STATE FULL NAME TYPE PROJECT PATH ────────────── ────────────────────── ─────────── ────────────────────────────────────────────────────────────────────── Remote Changed Admin Profile force-app\main\default\profiles\Admin.profile-meta.xml Remote Deleted Account.Field1__c CustomField force-app\main\default\objects\Account\fields\Field1__c.field-meta.xml Remote Changed Account-Account Layout Layout force-app\main\default\layouts\Account-Account Layout.layout-meta.xml
ソース追跡の裏側
force:source:status コマンドや force:source:push コマンド、force:source:pull コマンドを実行すると、.sfdx/orgs/<接続している組織にログインしているユーザ名>フォルダに MetadataTypeInfos.json と sourcePathInfos.json が生成(または更新)されます。
これらのファイルにソース追跡の情報が保持されているので、中身をのぞいてみます。
MetadataTypeInfos.json
MetadataTypeInfos.jsonには、メタデータの定義が含まれています。
MetadataTypeInfos.jsonはforce:source:status
コマンドを実行すると生成されましたので、
まずは、ローカルで「myclass0」クラスを生成後リモートにpushする前のものを見てみます。
新しく作成した環境で何の変更も加えていなかったので、serverMaxRevisionCounterは0で、メタデータの情報も空でした。
次は、ローカルでの変更を force:source:push
コマンドでリモートに反映したあとのものです。
sourceMembersに"ApexClass__myclass01"が追加され、serverRevisionCounterとlastRetrievedFromServerが同じ値で記載されています。
クラスを追加するとシステム管理者プロファイル「有効な Apex クラス」にも登録されるので、"Profile__Admin"の情報も追加されています。
次は、リモートでの「myclass0」変更後にforce:source:status
コマンドを実行したあとのものです。
serverRevisionCounterの値のみ増えており、ローカルとリモートで差異があることがわかります。 今回はリモート側で2回保存したので、serverRevisionCounterが2つ上がっています。内容は変更せずに保存する空更新でもリビジョンが1つ上がりました。
最後に、リモート側の変更をforce:source:pull
コマンドで取り込んだあとのものです。
MetadataTypeInfos.jsonは、force:source:pull
コマンド実行でも更新されます。
lastRetrievedFromServerの値が更新されており、ローカルとリモートのリビジョンが一致しました。
なお、maxrevision.jsonの情報は、SourceMemberオブジェクトで保持されおり、以下のクエリで情報を取得できました。
参考:Developers’ Blog : A Deep Dive Into Source Tracked Projects
>sfdx force:data:soql:query --query "SELECT ChangedBy, IsNameObsolete, MemberName, MemberType, RevisionCounter FROM SourceMember" --usetoolingapi CHANGEDBY ISNAMEOBSOLETE MEMBERNAME MEMBERTYPE REVISIONCOUNTER ─────────────── ────────────── ────────── ────────── ─────────────── 0050l000005rwfV false Admin Profile 1 0050l000005rwfV false myclass01 ApexClass 4 Total number of records retrieved: 2. Querying Data... done
sourcePathInfos.json
sourcePathInfos.jsonには、ローカルのファイル情報が含まれています。
まずは、ローカルで「myclass0」クラスを生成後リモートにpushする前のものを見てみます。
ローカルのプロジェクトフォルダ以下にある情報が記載されています。
次に、ローカルでの変更をforce:source:push
コマンドでリモートに反映したあとのものを見てみました。こちらは「myclass0」クラスに関する部分の差分を確認してみました。
クラス作成後内容を変更していないので、差分はstateが"n"から"u"に変わっただけで、modifiedTime、changeTime、contentHashは同じでした。
次は、リモートでの「myclass0」変更後にforce:source:status
コマンドを実行したあとのものです。
当然の結果ですが、ローカルファイルは更新されていないので、何も変わっていません。
最後に、リモート側の変更をforce:source:pull
コマンドで取り込んだあとのものです。
myclass01.clsが更新されたので、ファイルの情報が更新されています。
sourcePathInfos.jsonは、force:source:status
コマンド実行では更新されません。
このコマンドを実行すると、ローカルにあるファイルから現在のハッシュ値を計算し、sourcePathInfos.jsonで保持しているハッシュ値と比較して変更有無が判断されます。sourcePathInfos.jsonで持っているハッシュ値はリモートにpushした時の値なので、ローカルで変更し、リモートにpushすることなくローカルでもとの内容に戻すと、”変更なし”と判断されます。 リモート側で同様の操作をした場合は”変更あり”と判断されるので、注意が必要です。
最後に
ソース追跡では「どこがどう変更されたのか」はわからないので、内容の変更履歴を追跡するにはGitなどが必要です。
また、force:source:deploy
コマンド及びforce:source:retrieve
コマンドを使った場合は変更が追跡されませんので、注意が必要です。
一方、追跡対象を明示することなく(追跡できるものであれば)すべての変更を追跡できるのは便利だと感じました。
これまで「設定」-「設定変更履歴の参照」で組織の変更を確認していたようなケースでは、ソース追跡を使うことで簡単に変更を確認できるようになりますので、ぜひ使ってみてください。