Salesforce + 2nd Generation Managed Packageで CI/CD は出来るのか?
Salesforceを使ったプロジェクトを最近久しぶりにやっています。その中でSalesforceの ISVパートナーになり開発したアプリケーションをAppExchange (Salesforce Store) で配布しようと考えています。その道程の中での気づきや学びを共有していきます。
結論から言うと CIもCD も出来るけど CD に関してはちょっとクセあり?
まずは、CDについて解説したいと思います。CIは次回以降解説します。
環境としては GitHub, Salesforce Developer Experience, と Cloud Build を使います。
さて、ソースコードの統合がCIによってうまくいくと今度は本番環境に対してデプロイを高度に自動化していくことになります。これがまさにCD (Continuous Deployment)でやる部分です。(Delivery までではなく、Deploymentまでがやりたいことです。)
管理パッケージをどうやってSalesforceでデプロイするか?
開発したコードを本番環境にデプロイするには、AppExchangeの場合だと、それをパッケージ化して、最新パッケージを顧客の環境にインストールすることによって実現します。
もちろんAppExchangeで配布しているアプリケーションなので、たくさんのSalesforce 組織に対してパッケージがインストールされている状態においてもスケーラブルに高度に制御して行きたいと思います。
2nd Gen Managed Package (第二世代管理パッケージ) について
Salesforceでパッケージを作るのは従来とても自動化出来るような作業じゃありませんでした。UI上から人間がコンポーネントを一つずつチェックするようなイメージです。ヒューマンエラーが発生して当たり前の世界です。これが今では第一世代管理パッケージと呼ばれる古い方法です。プロの開発者はもうこれを使うのをやめましょう。(キッパリ)
説明は省きますが、今回はCLIで自動化が可能な第二世代管理パッケージを使います。詳しくはこちら。
パッケージとバージョンのライフサイクル
実態としてソースコード (メタデータ)が含まれるのはバージョンの方です。パッケージはAppExchangeでリスティングする単位を表すのでただの箱です。パッケージに紐付く形で無数のバージョンがあり、その中で安定しているバージョンをリリース (Promote、昇格とも言う)して、本番環境にデプロイします。
ちなみにリリースされていないバージョンの事をBetaバージョンといいます。
パッケージとバージョンの作り方
前提条件:
DevHub組織が作られている (sfdx clientでデフォルトになっている)
名前空間組織が作られている
DevHub組織が名前空間組織を認識している
まずはパッケージを作ります。コマンド一発で出来ます。
sfdx force:package:create --name PACKAGE_NAME \
--packagetype Managed --path force-app/
0Ho から始まるパッケージIDが作成されたはずです。そのIDにAliasとしてsfdx-package.json に記録しておきましょう。
次にそのパッケージに紐づくバージョンを作ります。
VERSION_NUM=1.0.0
sfdx force:package:version:create --package "PACKAGE_NAME" \
--installationkeybypass \
--definitionfile config/project-scratch-def.json \
--codecoverage --versionname="ver $VERSION_NUM" \
--versionnumber=$VERSION_NUM.NEXT
ちなみに二回目以降にバージョンを作成をして、そのバージョンで既存のお客様をアップデートさせる場合はAncestor IDを sfdx-project.json で指定してあげる必要があります。sfdx コマンドのオプションとして指定が出来ないようなので、毎回 JSONファイルを編集する必要があるのが微妙ですね。特に自動化する時に。。。
{
"packageDirectories": [
{
"path": "force-app",
"default": true,
"package": "PACKAGE_NAME",
"ancestorId": "04t3h000004bchBAAQ"
}
],
"namespace": "YOUR_NAMESPACE",
"sfdcLoginUrl": "https://login.salesforce.com",
"sourceApiVersion": "48.0",
"packageAliases": {
"PACKAGE_NAME": "0Hoxxxxxxxxxxxxxxxx"
}
}
パッケージのバージョンを作成とインストール時の制約
この辺の制約は結構複雑です。調べ始めていて意味不明のエラーで何度も挫けそうになりました。あまり文献としてまとまっていなかったように思うので、ここにまとめます。あとに続く開発者の肥やしになればと・・・
パッケージのバージョンを作る際にバージョン番号を命名してあげることがあります。しかし、Git Tagの様にに任意の文字列でつけることが出来ません。
MAJOR.MINOR.PATCH.BUILD
で、バージョン番号は必ず構成されます。任意の文字列を名付ける事は出来なくて、ネーミングとアップグレードにはかなり厳密なルールがあります。Buildいらなくない? と思いましたが、Versionは一度作ると消せないので、あるのだと思います。
パッケージバージョン作成時の制約
網羅的なものではないと思いますが、パッケージを作成する際の制約事項をまとめました。
• Build バージョンは一つしかPromote (Release) することが出来ない。
例) 1.0.0.1 を Promoteしたら 1.0.0.2 は Promote 出来ない。
• Patch バージョンは、Security Reviewを通っていないと作れない。
例) 1.0.0.1 → 1.0.1.1 はSecurity Reviewをパスしないと作れない
• Build バージョンは同じAncestor IDを持っている必要がある
例) 1.0.0.1 と 1.0.0.2 は同じAncestor IDを指定しないと行けない
• 破壊的な変更がある場合は Ancestryを分けなければならない。
例) 1.0.0.1 → 1.1.0.1 の移行で DummyController.cls を削除した場合、1.1.0.1 のAncestor IDで1.0.0.1を指定することが出来ない。Ancestorがないバージョンを作らなければならない。
Security Reviewに通ってないとPatch バージョンが作れないので、デバッグしただけ、見た目変えただけ、とかでもMinor バージョンを上げないといけない・・・ということが発生します。これはかなりイケてないです。(semantic versioning)
あと第二世代管理パッケージではソースコードを消せません (破壊的な変更)。クラスやLightning Componentを作ったあとに削除して新しいバージョンを作ろうとするとエラーが出ます。詳しくはこちら
使わなくなったクラスや、Lightning Component、Lighting App、Connected Appなどが消せないとソースコードのボリュームが無駄に増えて非衛生的ですし、何よりもチームメイトが混乱してしまいます。苦肉の策ですが、削除が出来ない代わりにゴミ箱フォルダーをソースコードに作ってそこに入れてゴミコードであることの意思表示をしています。これ本当にどうにかして欲しいです。
パッケージバージョンを組織にインストールする際の制約
• Betaパッケージはアップグレード出来ない。
• バージョンはアップグレードのみ可能。
例) 1.0.1.2 から 1.0.0.1 には戻せない。
• 古いパッケージからアップグレードする際は、直系の子孫じゃないとアップグレードが出来ない。
3つ目がものすごくややこしくて鬼門です。ここを次に丁寧に説明します。ちなみに「直系の子孫」は、僕が勝手に作った言葉です。
直系の子孫の定義
Major / Minor バージョンのリリースを上位パッケージ (Ancestor ID) で数珠つなぎにして一直線に並べたもの
と考えればわかりやすいと思います。例えばこの図の様なバージョン構成です。
1人で、1機能ずつ開発したらこんな感じになると思います。この場合、上から下の方向であれば全ての組み合わせでアップグレードが出来ます。
-----------------
なお、直系の子孫かどうかの判断に Patch / Build バージョンは関係ありません。
例えばこの様な場合は、1.0.1.1 も 1.0.2.1も直系とみなされます。よって、例えば 1.0.1.1 から 1.1.0.1アップグレードすることは可能です。
-----------------
さて、多くの開発者にとって混乱を招きやすいのはこのパターンだと思います。
0.2.0.1 を上位に持つ系統が2つ出来ました。(今まで伝統的に長男が家業を継いで来た一族である世代で長女も暖簾分けしてもらって同時に継いだイメージ?)
ただしこの場合、系統をまたいだアップグレードは出来ません。例えば 1.3.0.1 から 1.4.0.1 はアップグレード出来ません。アップグレードするならばアンインストールして再インストールしか方法がありません。
-----------------
応用ですが、以下の様な2つ系統があり、なおかつパッチバージョンがある場合のことも想定してみます。 1.2.1.1 から 1.4.0.1 はアップグレードは出来ませんが、 1.2.1.1 から 1.3.0.1 はアップグレード可能です。
Salesforce の Branch != Git の Branch
このようにツリー形式でバージョニングする事をSalesforceではBranchと呼びます。
名前で誤解をしていたので理解するのにとても苦労したのですがSalesforce の Branch は Merge が出来ません。なので一度系統が分かれてしまったら二度と元に戻ることが出来ません。元に戻すにはパッケージをアンインストールして、最新のバージョンを再インストールするしか方法がありません。
感想
複数の機能を同時に複数の組織で開発して、終わったらマージして一つのパッケージに・・・といったデプロイ手法を考えていたのですが、 2nd Generation Packagingは残念ながらこの様な開発は出来ないようです。これは正直期待していたのでちょっと残念です。という点で今回は一番やりたかったCDが出来ませんでした。
まとめ (ベストプラクティス)
Git を使って複数の機能を複数人で作っている場合、Git で複数のfeature branchを使いながら開発をする事になると思います。Feature branchからパッケージバージョンを作る事はやめましょう。系統が複数できて、マスターにマージができなくなる自体が発生します。
パッケージバージョンを作るのは、必ずMaster Branchから作るべきです。例えばGitHubを使っている場合であれば Release の機能を使ってパッケージバージョンを作るのがもっとも良いと思います。
そうすればパッケージバージョンはGitHubのリリースと同期することになり、見通しが良くなります。Ancestryのツリーが一本になりAbandoned Packageがなくなり、全ての組織が最新パッケージにアップグレード出来るようになります。
大分長くなってしまったので、今回は制限についての正しい情報についての解説と出来なかった事とそれからの示唆をまとめた投稿になりました。
ぶっちゃけまだまだ課題はありますが、Salesforceも大分、昔に比べるとモダンな開発がしやすくなって来たと思います。
次回は、Cloud Buildを使いながら、どのようにデプロイしていくかを書こうと思います。
もしよかったらスキとSalesforce開発者へ拡散お願いします。
この記事が気に入ったらサポートをしてみませんか?