Salesforce CLIのことがわかってきたので作成したスクラッチ組織にソースコードをデプロイする操作を試してみました。
スクラッチ組織の作成
『sf org create scratch』コマンドで作成します。ヘルプコマンドを見ていて思ったのですが、作成の際の管理者ユーザのユーザ名やメールアドレスは任意指定することも可能なようです。(パスワードは認証後に専用コマンドで設定する。)
※DEFINITION FILE OVERRIDE FLAGSのところで確認できます。
今回は次のようにフラグを指定して実行しました。
sf org create scratch -a demo-scratch1 -d -f config/project-scratch-def.json -v demo-product -y 7
参考までに定義ファイルはこんな感じ。(今回の作業内容的には特に重要ではないとは思いますが。)
スクラッチ組織の作成が成功してDevHub組織とスクラッチ組織の両方が認証された状態にしました。(エイリアスのscratchの綴りが誤りでscrachとなっていたのでこのあと修正。上の作成コマンドも修正。)
作成したスクラッチ組織の管理者ユーザのパスワード設定したい場合は次のようにコマンド実行します。
sf org generate password --length 12 -o demo-scratch1
Salesforce CLI上で認証できているので基本不要ですが、sf org logoutで認証解除してしまうとおそらくユーザIDとパスワードが不明な状態にしていたら再認証は不可と思われます。(パスワード設定してユーザIDと一緒にメモしておくのが安心。)
今回のデプロイ元の組織
デプロイをちょっと試すというよりは、実際の業務組織で利用するシーンと同じようにしたかったので、Apexクラスがけっこう存在する組織を使って試してみます。エラー→理由特定→再試行みたいな感じになる予定。
VSCodeの接続組織はデプロイ元の方です。--target-orgにスクラッチ組織を指定する形でデプロイ先を指定します。
使うのは『sf project deploy start』コマンド。多分これで行けると思います。
Trailheadにもそのように紹介されてます。
ヘルプコマンドによるとこのようなフラグ指定が可能なようです。
デプロイ先はスクラッチ組織になるので次のように指定します。
sf project deploy start -d force-app -o demo-scratch1
(このコマンドで気になっているのですが、うっかり target-orgの指定を忘れたらデフォルト組織宛に処理が実行されちゃうのかが気になってます。すごい注意が必要なコマンドな気が..。)
実行したところデプロイ処理が開始されました。動いたということでコマンド自体は正しく実行できたようです。
途中からERRORのカウントが増えてきました。左側がコンパイルエラー。右側がテストクラスのエラーと思われます。
カスタム設定が存在しない。カスタムオブジェクトが存在しない的な内容でエラーになりました。
package.xmlをカスタマイズしてカスタムオブジェクトを追加していこうと思ったのですが、一旦取ってこれそうなメタデータをひとしきり取得する形で進めることにしました。
再度デプロイを実行したところ、エラーの数が11まで削減。
エラー内容はよくわからない感じになりました。
ひとまず、アプリケーションランチャーなどで標準アプリケーションが含まれているのは不適切でした。この辺は取得対象を絞っての対応が必要になります。
こんな感じでカスタムアプリケーションのみ取得するように調整。
次にen_USとかjaとかでエラーになっているのはtranslationsの部分と思います。手順がわるいのかこの方法ではデプロイできないのか確認できませんが、今回はデプロイ対象に含めないようにpackage.xmlから除外して対処しました。
標準オブジェクトを取得対象に含めているのもエラー理由っぽい。(やり方あると思いますが)。添付のように宣言していたところを標準オブジェクトの宣言は削除します。
※カスタム設定はカスタムオブジェクトメタデータで取得できました。
だいぶエラー解消したけどまだ残っている感じに進みました。
cafeとかactiveRSSってなんだっけと検索したらインストール済みパッケージ関係でした。package.xmlに含めてしまっていたので削除。
あと少し、 opportunity access levelっていうのが最後。
元組織でアクセス制限の設定試してたかな。と思ったらなんか変な設定をしていました。(何か検証しようとしたんだと思いますが、かなり謎設定になってた。)
共有設定側を削除してソースを取得し直して再実行。エラーは解決しませんでした。「Error Taiki field integrity exception: unknown」とユーザ名的な名前が入っているのが気になったのでそちらをキーに調査したところ、ロールが原因でした。カスタマーコミュニーティユーザのロールです。これは確かに移行デプロイできない情報でした。
探し方ですがキーワード検索するとメタデータの情報に引っかかってけっこう見つかります。
package.xmlでロールを全件取得しているのが修正ポイントです。
ここを * ではなく、ロール名指定で取得するようにすればOK。ですがロール移行するほどの内容ではないのでまるごと削除しちゃいます。
この修正を実施してデプロイを実行してみたところ、エラーなく処理が完了しました。
デプロイ処理がやけに短時間で終わったけどErrors:0ということはこれでスクラッチ組織に反映できたのかなとチェックしたところ、リリース状況のページで処理失敗の情報が...。
メタデータ的な整合性チェックはここまでのエラーメッセージのようにやってくれるみたいですが、それ以外でもデプロイエラーの原因になるものがあるみたいです。パッと思いつく部分でSalesforce SitesとかKnowledgeとかが含まれているのが可能性として思い当たりました。
スクラッチ組織の管理者ユーザにナレッジの権限を追加。ダメ元でデプロイを試してみたところ進展がありました。本当にナレッジが理由だったみたいでコンフリクトエラーの表示がでるようになりました。
エラーメッセージとこの「 --ignore-conflicts」コマンドを試してというメッセージがグレーで表示されていました。
細かいところは気にせずメッセージのとおりに試してみました。
sf project deploy start -d force-app -o demo-scratch1 --ignore-conflicts
結論としてはデプロイはうまくいきませんでした。ただ、一番最初のコンパイルエラー的な即時終了の失敗や致命的なエラーでサポートに問い合わせてくださいというエラーではなく、きちんとリリース処理が実行されていってのデプロイ失敗というところまで進めることができました。
エラーの内容はカスタムオブジェクトの項目が無いとかカスタムレポートタイプが云々とかページレイアウトやワークフローメールアラートが云々とかそんな感じです。
まとめ
可能な限りメタデータファイルを用意すればDeveloper Edtion組織上で設定や開発したものをスクラッチ組織に持っていけるかと思いましたが、そこまで簡単な話ではありませんでした。クラスやページなどのApexコードはもちろん、それ以外の設定情報もきちんと精査して定義ファイルに指定していく必要がありそうです。
スクラッチ組織はAppExchange開発とかパートナー向けという話を聞いてはいます(気がします)が、通常のSalesforce組織の開発でSandbox組織のかわりにするのは難しそうな感じでした。標準オブジェクトや標準項目などを絡めると無理そうな気がしていてカスタムオブジェクト中心に開発機能に絞ってようやくデプロイが可能そうと思います。
Sandbox組織のかわりを目指してpackage.xmlにあまりわかっていないメタデータ詰め込んで試していたのでそのあたりがもう少し改善できそうなのと、スクラッチ組織の定義ファイルの機能有効化周りを理解すれば一部エラーは解決できそうなものもありそうな気もしますが、どちらにしても試行錯誤が必要になるのかなと思いました。Developer Edtion組織のレベルでこんな感じだったので、運用組織でやると更に大変そうかなと思います。これまでどおりにSandbox組織で開発をした方が適切そうでした。
スクラッチ組織の開発は組織に依存しないようなパッケージ製品の開発の際にまっさらな組織で試したいという場面で利用するのが良さそうです。(スクラッチ組織の説明にもそう書いてあったなって改めて思いました。)
補足 - スクラッチ組織へのデプロイ操作について
一応、今回はデプロイ対象を広げてしまったのでデプロイうまく行かなったという結果で終わっちゃいますが、「sf project deploy start」コマンドの使い方自体は間違ってないと思います。Trailheadやその他の勉強用にちょっとApexコードを用意してスクラッチ組織にデプロイしたいときには、メタデータは必要最低限で指定して今回試したコマンドでデプロイできるかと思います。
おまけ - スクラッチ組織のデプロイに使った定義ファイル
pacage.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <Package xmlns="http://soap.sforce.com/2006/04/metadata"> <types> <members>*</members> <name>ActionLinkGroupTemplate</name> </types> <types> <members>*</members> <name>AIApplication</name> </types> <types> <members>*</members> <name>AIApplicationConfig</name> </types> <types> <members>*</members> <name>AnalyticSnapshot</name> </types> <types> <members>*</members> <name>AnimationRule</name> </types> <types> <members>*</members> <name>ApexClass</name> </types> <types> <members>*</members> <name>ApexComponent</name> </types> <types> <members>*</members> <name>ApexEmailNotifications</name> </types> <types> <members>*</members> <name>ApexPage</name> </types> <types> <members>*</members> <name>ApexTestSuite</name> </types> <types> <members>*</members> <name>ApexTrigger</name> </types> <types> <members>*</members> <name>AppMenu</name> </types> <types> <members>*</members> <name>ApprovalProcess</name> </types> <types> <members>*</members> <name>AssignmentRules</name> </types> <types> <members>*</members> <name>AuraDefinitionBundle</name> </types> <types> <members>*</members> <name>AuthProvider</name> </types> <types> <members>*</members> <name>AutoResponseRules</name> </types> <types> <members>*</members> <name>BlacklistedConsumer</name> </types> <types> <members>*</members> <name>BotVersion</name> </types> <types> <members>*</members> <name>BrandingSet</name> </types> <types> <members>*</members> <name>BriefcaseDefinition</name> </types> <types> <members>*</members> <name>CallCenter</name> </types> <types> <members>*</members> <name>CallCoachingMediaProvider</name> </types> <types> <members>*</members> <name>Certificate</name> </types> <types> <members>*</members> <name>ChatterExtension</name> </types> <types> <members>*</members> <name>CleanDataService</name> </types> <types> <members>*</members> <name>Community</name> </types> <types> <members>*</members> <name>ConnectedApp</name> </types> <types> <members>*</members> <name>ContentAsset</name> </types> <types> <members>*</members> <name>CorsWhitelistOrigin</name> </types> <types> <members>*</members> <name>CspTrustedSite</name> </types> <types> <members>TRAILBLAZER</members> <name>CustomApplication</name> </types> <types> <members>*</members> <name>CustomApplicationComponent</name> </types> <types> <members>*</members> <name>CustomFeedFilter</name> </types> <types> <members>*</members> <name>CustomHelpMenuSection</name> </types> <types> <members>*</members> <name>CustomLabels</name> </types> <types> <members>*</members> <name>CustomMetadata</name> </types> <types> <members>*</members> <name>CustomNotificationType</name> </types> <types> <members>*</members> <name>CustomObject</name> </types> <types> <members>*</members> <name>CustomObjectTranslation</name> </types> <types> <members>*</members> <name>CustomPageWebLink</name> </types> <types> <members>*</members> <name>CustomPermission</name> </types> <types> <members>*</members> <name>CustomSite</name> </types> <types> <members>*</members> <name>CustomTab</name> </types> <types> <members>*</members> <name>Dashboard</name> </types> <types> <members>*</members> <name>DataCategoryGroup</name> </types> <types> <members>*</members> <name>DataWeaveResource</name> </types> <types> <members>*</members> <name>DelegateGroup</name> </types> <types> <members>*</members> <name>Document</name> </types> <types> <members>*</members> <name>DuplicateRule</name> </types> <types> <members>*</members> <name>EclairGeoData</name> </types> <types> <members>*</members> <name>EmailServicesFunction</name> </types> <types> <members>*</members> <name>EmailTemplate</name> </types> <types> <members>*</members> <name>EmbeddedServiceBranding</name> </types> <types> <members>*</members> <name>EmbeddedServiceConfig</name> </types> <types> <members>*</members> <name>EmbeddedServiceFlowConfig</name> </types> <types> <members>*</members> <name>EmbeddedServiceMenuSettings</name> </types> <types> <members>*</members> <name>EntitlementProcess</name> </types> <types> <members>*</members> <name>EntitlementTemplate</name> </types> <types> <members>*</members> <name>EscalationRules</name> </types> <types> <members>*</members> <name>ExternalCredential</name> </types> <types> <members>*</members> <name>ExternalDataSource</name> </types> <types> <members>*</members> <name>ExternalServiceRegistration</name> </types> <types> <members>*</members> <name>FeatureParameterBoolean</name> </types> <types> <members>*</members> <name>FeatureParameterDate</name> </types> <types> <members>*</members> <name>FeatureParameterInteger</name> </types> <types> <members>*</members> <name>FieldRestrictionRule</name> </types> <types> <members>*</members> <name>FlexiPage</name> </types> <types> <members>*</members> <name>FlowCategory</name> </types> <types> <members>*</members> <name>FlowTest</name> </types> <types> <members>*</members> <name>GatewayProviderPaymentMethodType</name> </types> <types> <members>*</members> <name>GlobalValueSet</name> </types> <types> <members>*</members> <name>GlobalValueSetTranslation</name> </types> <types> <members>*</members> <name>Group</name> </types> <types> <members>*</members> <name>HomePageComponent</name> </types> <types> <members>*</members> <name>HomePageLayout</name> </types> <types> <members>*</members> <name>InboundNetworkConnection</name> </types> <types> <members>*</members> <name>IPAddressRange</name> </types> <types> <members>*</members> <name>Layout</name> </types> <types> <members>*</members> <name>Letterhead</name> </types> <types> <members>*</members> <name>LightningBolt</name> </types> <types> <members>*</members> <name>LightningComponentBundle</name> </types> <types> <members>*</members> <name>LightningExperienceTheme</name> </types> <types> <members>*</members> <name>LightningMessageChannel</name> </types> <types> <members>*</members> <name>LightningOnboardingConfig</name> </types> <types> <members>*</members> <name>ManagedContentType</name> </types> <types> <members>*</members> <name>MatchingRule</name> </types> <types> <members>*</members> <name>MilestoneType</name> </types> <types> <members>*</members> <name>MLDataDefinition</name> </types> <types> <members>*</members> <name>MLPredictionDefinition</name> </types> <types> <members>*</members> <name>MobileApplicationDetail</name> </types> <types> <members>*</members> <name>MutingPermissionSet</name> </types> <types> <members>*</members> <name>MyDomainDiscoverableLogin</name> </types> <types> <members>*</members> <name>NamedCredential</name> </types> <types> <members>*</members> <name>NotificationTypeConfig</name> </types> <types> <members>*</members> <name>OauthCustomScope</name> </types> <types> <members>*</members> <name>OutboundNetworkConnection</name> </types> <types> <members>*</members> <name>PathAssistant</name> </types> <types> <members>*</members> <name>PermissionSet</name> </types> <types> <members>*</members> <name>PermissionSetGroup</name> </types> <types> <members>*</members> <name>PlatformCachePartition</name> </types> <types> <members>*</members> <name>PlatformEventChannel</name> </types> <types> <members>*</members> <name>PlatformEventChannelMember</name> </types> <types> <members>*</members> <name>PlatformEventSubscriberConfig</name> </types> <types> <members>*</members> <name>PostTemplate</name> </types> <types> <members>*</members> <name>Profile</name> </types> <types> <members>*</members> <name>ProfilePasswordPolicy</name> </types> <types> <members>*</members> <name>ProfileSessionSetting</name> </types> <types> <members>*</members> <name>Prompt</name> </types> <types> <members>*</members> <name>Queue</name> </types> <types> <members>*</members> <name>QuickAction</name> </types> <types> <members>*</members> <name>RedirectWhitelistUrl</name> </types> <types> <members>*</members> <name>RecommendationStrategy</name> </types> <types> <members>*</members> <name>RecordActionDeployment</name> </types> <types> <members>*</members> <name>RemoteSiteSetting</name> </types> <types> <members>*</members> <name>Report</name> </types> <types> <members>*</members> <name>ReportType</name> </types> <types> <members>*</members> <name>SamlSsoConfig</name> </types> <types> <members>*</members> <name>SharingRules</name> </types> <types> <members>*</members> <name>SharingSet</name> </types> <types> <members>*</members> <name>SiteDotCom</name> </types> <types> <members>*</members> <name>StandardValueSet</name> </types> <types> <members>*</members> <name>StandardValueSetTranslation</name> </types> <types> <members>*</members> <name>StaticResource</name> </types> <types> <members>*</members> <name>SynonymDictionary</name> </types> <types> <members>*</members> <name>TopicsForObjects</name> </types> <types> <members>*</members> <name>TransactionSecurityPolicy</name> </types> <types> <members>*</members> <name>UserProvisioningConfig</name> </types> <types> <members>*</members> <name>Workflow</name> </types> <version>58.0</version> </Package>