【第二世代管理パッケージ(2GP)】 拡張パッケージの作成手順 (連動関係の指定方法)

前回の投稿からだいぶ時間が立ってしまいましたが、2GP連載の第5弾の記事になります。

今回は、第二世代管理パッケージ(以下、2GP)で拡張パッケージを作成する方法について解説します。

はじめに

Summer ’23からはパッケージマネージャに2GPの採用を検討するような注意も表示されるようになりました。今から新規に拡張パッケージを作る場合、ベースとなる基本管理パッケージ(以下基本パッケージ)が1GPであっても、作成するパッケージは2GPを採用した方が良さそうです。この記事は、これまで1GPでパッケージを開発してきた開発者がその拡張パッケージを2GPで作成する場合の不安を解消することを目的としています。もちろん基本パッケージが2GPで、新規に拡張パッケージを開発するケースにも役立つ内容となっています。

拡張パッケージの開発の進め方は1GPと2GPで異なります。1GPでは拡張パッケージのパッケージ開発組織に基本パッケージをインストールして拡張機能の開発を進めていました。2GPでは、パッケージ開発組織は使用せずスクラッチ組織に依存する基本パッケージをインストールして拡張機能の開発をします。

また、パッケージバージョンを作成するプロセスも2GPでは異なります。2GPではパッケージバージョンの作成を開始すると、パッケージ作成専用のスクラッチ組織が一時的に作成され、そこでパッケージバージョンを作成する処理が実行されます。拡張パッケージの場合は、まずその組織に依存する基本パッケージが自動でインストールされ、それから拡張パッケージ用のソースがpushされるといった処理が内部で実行されています。

検証したところ2GPの連動関係を使用したパッケージ開発にはいくつかつまづきやすいポイントがありました。以下、2GPの連動関係を利用した拡張パッケージの開発手順と、各プロセスでの考慮事項を解説していきます。

2GPの連動関係

2GPでは実際のところ「拡張パッケージ」という用語は出てきません。ここでは、2GPの「連動関係」と翻訳されている「dependencies」を指定したパッケージを「拡張パッケージ」として捉えます。つまり、依存されるパッケージとそれに依存するパッケージは、1GPで言うところの基本パッケージと拡張パッケージの関係となり、例えば以下の図では「拡張パッケージA」と「拡張パッケージB」は「基本パッケージ」に依存している拡張パッケージということになります。

第二世代管理パッケージ間の連動関係の作成 | Salesforce DX 開発者ガイド | Salesforce Developers

依存関係の指定方法

1GPでは、拡張パッケージはパッケージ開発組織に基本パッケージをインストールし、基本パッケージに含まれるコンポーネントを参照するApexなどのコンポーネントを含めてアップロードすると自動で関連付けられるという仕様でした。

拡張を構築するには、基本パッケージをインストールし、その基本パッケージへの連動関係を自分のパッケージに組み込みます。拡張属性が自動的に有効になります。

管理パッケージへの拡張の公開

2GPは、拡張パッケージ側で依存する基本パッケージを明示的に指定します。依存関係はプロジェクト設定ファイルであるsfdx-projects.jsondependenciesにパッケージバージョンを指定します。

"dependencies": [
    {
        "package": "04tXXXXXXXXXXXXX"
    }
],

dependenciesに指定するパッケージバージョンは1GP、2GPのどちらでも指定可能です。また、バージョンの指定方法にはパッケージ名とバージョン番号で指定することも可能です。詳しくは「パッケージ、パッケージバージョン作成の手順」で解説します。

2GPで追加された機能

2GPの連動関係は本来、各パッケージをモジュール化し、それを組み合わせて提供することを意図しています。この記事では扱いませんが、以下のような機能も追加されています。

  • 依存するパッケージは複数指定可能
  • 名前空間内でApexのメソッドを共有することができる

とはいえ、AppExchangeアプリの場合は、複雑な依存関係を持つ構成にしてしまうと導入、メンテナンス、運用面でコストが掛かってしまいそうです。そのため、この記事で紹介する拡張パッケージを開発するために連動関係を利用するというのがAppExchange向けのアプリでは主なユースケースとなり、これらの機能を活用する機会はあまりなさそうです。

参考: NamespaceAccessible アノテーション | Apex 開発者ガイド | Salesforce Developers

拡張パッケージの開発手順

ここでは、2GPの拡張パッケージに入れる機能を開発する流れについて解説していきます。

開発時の流れは以下のとおりです。

  1. 開発用のスクラッチ組織を作成する
  2. 作成したスクラッチ組織に依存するパッケージ バージョンをインストールする
  3. スクラッチ組織で拡張機能を開発をする
  4. スクラッチ組織上でメタデータを作成した場合はpullしてsfdxプロジェクトに反映する

以下、実際のsfdxコマンドなど手順を詳しく解説していきます。

拡張機能 開発の手順

例として弊社パッケージMatchingMapを拡張するパッケージMatchingMapReportsを開発するケースで手順を解説していきます。

0. 名前空間の取得など前提条件の準備

基本パッケージが1GPの場合は、拡張パッケージに同じ名前空間を使用できませんので、拡張パッケージ用に名前空間組織を作成し、名前空間を取得する必要があります。

1GP と 2GP で名前空間が共有されている場合は、これらのパッケージを同じ組織にインストールできません。

名前空間の競合の回避 | Salesforce DX 開発者ガイド | Salesforce Developers

基本パッケージが2GPの場合は同一名前空間でも問題ありません。名前空間は必要に応じて用意してください。

以下、管理パッケージ開発用のsfdxプロジェクトのセットアップが済んでいるところからの解説になります。名前空間の取得も含め、管理パッケージ開発用のプロジェクトの作成手順については、「【第二世代管理パッケージ(2GP)】開発のはじめかた」の「2GP 開発に必要な Salesforce 組織の準備」と「2GP パッケージの作成手順」を参考に準備してください。

1. 開発用スクラッチ組織を作成する

まずは拡張機能を開発するためのスクラッチ組織を作成します。エイリアスなどは任意のものを指定します。

$ sfdx org create scratch -f config/project-scratch-def.json -c -a ext-dev-scratch -d 30

2. 依存するパッケージをインストールする

作成したスクラッチ組織に依存するパッケージのバージョンをインストールします。

例えば、依存するパッケージとしてMatchingMapのバージョンv1.50 パッケージバージョンID「04tXXXXXXXXXXXX」をインストールする場合は以下のコマンドを実行します。

$ sfdx package install -p 04tXXXXXXXXXXXX -w 15

このときインストールするパッケージバージョンは、後述のdependenciesに指定するパッケージバージョンと同じものである必要があります。しかし、package installに指定するバージョンはdependenciesに指定していない任意のバージョンをインストールできてしまいます。そのため、開発者が正しいバージョンをインストールして開発を進められるようにセットアップスクリプトを用意しておくなど工夫をすると良さそうです。

また非公式ですが、dependenciesを見て依存パッケージをインストールするsfdxプラグインを作っている人たちも居るようです。他にもあるかもしれませんので、こういったプラグインを活用するのも良さそうですね。

3. 開発用スクラッチ組織で拡張機能を開発する

拡張パッケージで実装する機能を実装します。

4.パッケージ作成に必要なメタデータをpull

2GPではプロジェクトのソースコードから直接パッケージバージョンが作成されます。スクラッチ組織上でメタデータを追加した場合には、pullしてsfdxプロジェクトのソースに追加しておきます。

$ sfdx project retrieve start

※元々はforce:source:pullでしたが、現在はproject retrieve startが推奨されています。

拡張機能の開発の流れは以上です。

考慮事項

手順2.でスクラッチ組織に、依存する基本パッケージをインストールしますが、必須機能などパッケージにインストール要件がある場合、それらの要件をクリアするようにスクラッチ組織定義ファイルを作成しておく必要があります。

例えば、コミュニティの機能をパッケージ内で使用している場合、スクラッチ組織のfeaturesに「Communities」を指定しておく必要があります。

"features": ["Communities"],

スクラッチ組織定義ファイルの作成方法については、独自のスクラッチ組織定義ファイルを作成する | Salesforce DX 開発者ガイド | Salesforce Developers を参照ください。

拡張パッケージのパッケージバージョンを作成する

開発が終わったら、以下の流れでパッケージおよびパッケージバージョンを作成します。

  1. パッケージを作成する
  2. パッケージ設定を行う
  3. パッケージバージョンを作成する
  4. リリース(promote)する

はじめに、パッケージバージョン作成のプロセスを確認したあと、実際にパッケージをアップロードするまでの流れを追っていきます。

パッケージバージョン作成時に実行される処理

パッケージバージョンを作成するためにはsfdx package version createコマンドを実行します。このコマンドは内部的には以下のような流れでアップロードが実行されます。

パッケージの作成を開始すると以下の処理が実行されている様子です。

  1. アップロード用のスクラッチ組織を作成
  2. 1.のスクラッチ組織にdependenciesに指定した依存パッケージをインストール
  3. 1.のスクラッチ組織に拡張パッケージのプロジェクトのソースをpush
  4. パッケージをアップロード後、パッケージのアーカイブが作成される
  5. 4. で作成されたパッケージをAppExchangeにアップロード

ポイントは、上記の2.で依存するパッケージがコマンド内部で作成されたスクラッチ組織にインストール可能であることです。例えば、パッケージのインストールに必須機能があった場合、その機能を有効化しておくといったスクラッチ組織の設定が必要になります。これは機能開発用のスクラッチ組織のセットアップと同様です。

インストール時に作られるスクラッチ組織の定義ファイルはdefinitionFileで指定することができます。基本的には開発時に使用したスクラッチ組織定義ファイルを指定すればOKです。

パッケージ、パッケージバージョン作成の手順

実際にsfdxコマンドでパッケージを作成する流れを見ていきます。

1. パッケージを作成する

以下のコマンドを実行してパッケージを作成します。

$ sfdx package create --name MatchingMapReports --package-type Managed --path force-app --target-dev-hub <DevHub組織ユーザ ユーザ名またはそのエイリアス>

ここではパッケージ名に「MatchingMapReports」を指定しています。

パラメータ--name、および、--pathはプロジェクトの構成に応じて指定してください。また、--target-devhubパラメータについては、デフォルトのDev Hub組織がすでに設定されている場合は省略可能です。

※ 次のdependenciesを追加するためには、それを指定するpackageDirectoriespackageversionNumberの指定も必要になるため先にパッケージを作成しておきます。

2. パッケージ設定を行う

パッケージバージョン作成前に、パッケージの依存関係の設定を行います。
sfdx-project.json を開き、dependenciesを以下のように追加し、依存するパッケージのバージョンIDを指定します。

依存するパッケージのインストールに機能要件がある場合は、definitionFileにスクラッチ組織定義ファイルを指定します。

{
  "packageDirectories": [
    {
      "path": "force-app",
      "dependencies": [
        {
          "package": "04tXXXXXXXXXXXX"
        }
      ],
      "definitionFile": "config/project-scratch-def.json",
      "package": "MatchingMapReports",
      "versionName": "ver 0.1",
      "versionNumber": "0.1.0.NEXT",
      "versionDescription": ""
    }
  ],

上記ではバージョンIDを直接指定していますが、バージョンIDのままでは実際にどのバージョンかがわりにくいので以下のようにpackageAliasesにエイリアスを指定するのがおすすめです。

{
  ...
    {
      "dependencies": [
        {
          "package": "MatchingMap@1.50.0"
        }
      ],
  ...
  "packageAliases": {
    "MatchingMap@1.50.0": "04tXXXXXXXXXXXX",
    ....

packageには、パッケージ名とバージョン番号の組み合わせも指定可能です。ただし、依存するパッケージが1GPの場合、バージョン番号のしくみが2GPとは異なるため使用できません。

3. パッケージバージョンを作成する

以下のコマンドを実行して、パッケージ名「MatchingMapReports」のパッケージバージョンを作成します。

$ sfdx package version create --package MatchingMapReports --wait 10 --code-coverage --installation-key-bypass
Version create.... Create version status: Success
Successfully created the package version [08c5h000000GnKRAA0]. Subscriber Package Version Id: 04t5h000000J6uKAAS

コマンド実行時には、内部的に「パッケージバージョン作成時に実行される処理」の手順が実行されていると考えられます。

パッケージバージョンの作成に成功するとパッケージバージョンID、上記の場合は「04t5h000000J6uKAAS」が表示されます。そのまま、リリースする場合はこちらのIDを指定します。

4. リリース(promote)する

作成したパッケージバージョンのIDを指定してpromoteコマンドを実行し、リリースします。

$ sfdx package version promote --package <パッケージバージョンId>

以上で、任意の組織に作成した拡張パッケージをインストールできるようになります。

トラブルシューティング

基本パッケージのインストールに失敗

前述の通りパッケージバージョンを作成するプロセスではdependenciesに指定されたパッケージがパッケージ作成用のスクラッチ組織にインストールされます。

実際に、「個人取引先」の機能を使用するパッケージをdependenciesに指定し、definitionFileは指定しなかった場合を試してみたところ、以下のエラーが発生しました。

Version create.... Create version status: Error
Error (1): Multiple errors occurred: 
(1) パッケージ連動関係のインストール中にエラーが発生しました。ID 04t5h000000Mr0J: null
(2) パッケージ連動関係のインストール中にエラーが発生しました。ID 04t5h000000Mr0J: null

エラーメッセージから原因を読み取れないのが難点ですが、このエラーが出た場合は、definitionFileが指定されているか?指定されている場合は、スクラッチ組織定義ファイルのfeaturesに必要な機能が設定されているかを確認しましょう。

基本パッケージのバージョンを挙げたらメタデータの依存関係が壊れた

dependenciesに指定する基本パッケージのバージョンを上げる場合に、新しいバージョンで参照するコンポーネントが削除されているとパッケージバージョンの作成に失敗します。

以下の例は、新しいバージョンで削除されたオブジェクト「Project__c」の項目「Status__c」をApexクラスで参照していたケースのエラーです。

Version create.... Create version status: Error
Error (1): Multiple errors occurred: 
(1) TestUsingBasePackageComponents: SELECT Id, Name, hrendohtest__Status__c FROM hrendohtest__Project__c
                             ^
ERROR at Row:2:Column:30
No such column 'hrendohtest__Status__c' on entity 'hrendohtest__Project__c'. If you are attempting to use a custom field, be sure to append the '__c' after the custom field name. Please reference your WSDL or the describe call for the appropriate names.

開発中に古いバージョンをインストールしていた場合にのみ発生するエラーなのでかなりレアケースかとは思います。しかし、sfdxには開発用のスクラッチ組織へインストールするバージョンとdependenciesに指定されているバージョンを一致させるしくみがありません。そこで、package.jsonにスクラッチ組織をセットアップするnpmスクリプトを用意して、その中でdependenciesからパッバージョンを読み込んで基本パッケージをインストールするようにすると良さそうです。

基本パッケージのバージョンダウンはできない

当然ですが、依存する基本パッケージはすでに指定しているバージョンより古いものは指定できません。

$ sfdx package version create --package MatchingMapReports --versionnumber=0.1.2.1 --wait 10 --code-coverage --installation-key
-bypass
Version create.... Create version status: Error
Error (1): Multiple errors occurred: 
(1) パッチバージョンを作成できません。パッケージ連動関係バージョン 04t10000000SLXt は、上位パッケージ連動関係バージョンとは異なるメジャーまたはマイナーパッケージバージョンです。このパッチとパッケージ両方の上位に対して、sfdx-project.json ファイルで同じメジャーおよびマイナー連動関係バージョンを指定してください。パッケージバージョン番号は major.minor.patch.build の形式で設定してください。

登録者組織へインストールする際の注意点

登録者組織へのインストールする際の考慮事項は、おおむね1GPの場合と同じです。

ここでは、1GP、2GPに関わらず拡張パッケージを登録者組織にインストールする際の考慮事項をまとめています。

依存するパッケージは先にインストール

事前に必要な依存するパッケージバージョンをインストールしておく必要があります。

依存するパッケージがインストールされていない組織に、拡張パッケージをインストールすると以下のようなエラーが発生します。

Error (1): Encountered errors installing the package! Installation errors: 
1) 無効なアップグレードです。, Details: インストールしようとしているパッケージは、パッケージ「Sample2gpAppMainPkg」、バージョン「0.4」と連動しています。パッケージ「Sample2gpAppExPkg」をインストールする前にパッケージ「Sample2gpAppMainPkg」を対象組織にインストールしてください。

依存するパッケージは正しいバージョンをインストール

登録者組織にインストールされている基本パッケージのバージョンはdependenciesに指定されているバージョンかより新しい必要があります。

古いバージョンがインストールされている組織に拡張パッケージをインストールすると以下のようなエラーが発生します。

Error (1): Encountered errors installing the package! Installation errors: 
1) 無効なアップグレードです。, Details: インストールしようとしているパッケージは、パッケージ「Sample2gpAppMainPkg」、バージョン「0.4」と連動しています。新しいパッケージをインストールする前に対象組織のパッケージをバージョン「0.4」にアップグレードしてください。

削除したメタデータへの参照

組織にインストールされている基本パッケージのバージョンが、dependenciesに指定されているバージョンかより新しい場合は、拡張パッケージのインストールは成功します。

しかし、基本パッケージの新しいバージョンで削除されたコンポーネントを拡張パッケージ側で参照している場合、インストールすることができません。

Error (1): Encountered errors installing the package! Installation errors: 
1) (MM_FilterFieldDef) In field: field - no CustomField named matchingmap__MM_FilterFieldDef__c.matchingmap__FormatDefaultValue__c found, Details: MM_FilterFieldDef: In field: field - no CustomField named matchingmap__MM_FilterFieldDef__c.matchingmap__FormatDefaultValue__c found
2) (MatchingmapExpansion/MM_FilterFieldDefListReport) invalid report type, Details: MatchingmapExpansion/MM_FilterFieldDefListReport: invalid report type

基本パッケージのアップグレードに関する考慮事項

拡張パッケージがインストールされている登録者組織の基本パッケージをアップグレードする際の注意事項はあまりありません。たとえ、拡張パッケージから参照されている項目が基本パッケージから削除されたとしても登録者組織では自動で削除されないためアップグレードへの影響はありません。

ただし、削除済みマークがついている項目は拡張パッケージで参照されたままとなるため登録者組織で明示的に削除することはできません。AppExchangeパッケージ内の項目は組織の項目数制限の対象外となりますが、削除済みとなった項目はパッケージ外として扱われるため、登録者組織の項目数を消費してしまう可能性があります(未検証です)。

まとめ

以上、2GPで拡張パッケージを開発する手順と注意点について解説してきました。

2GPでは、拡張パッケージは連動関係 dependencies の仕組みを使用します。とくに1GPと異なる点は、2GPではパッケージ開発組織にあたるパッケージバージョン作成用のスクラッチ組織が都度作成され、依存する基本パッケージも都度インストールされる点です。ただし、これだけ留意すれば、1GPと同様に開発を進めることができます。バージョン作成時以外の注意点もほぼ1GPと同じです。

この記事を一読していただければ、基本パッケージが1GPのケースでも拡張パッケージに2GPを採用する不安が払拭できたのではないかと思います。Summer ’23からはパッケージマネージャに2GPの採用を検討するような注意が表示されるようになりました。この記事が、2GP採用検討の際の参考になれば幸いです。