experience-ui-bundle-2gp-deploy について
既存プロジェクトとビルド可能な UI Bundle を持つ開発者向けに、Salesforce 2GP のパッケージ化と、別の組織へのインストール、アップグレード、アンインストールを案内するリファレンススキルです。全工程を一律に進めるのではなく、依頼された作業に対応する手順を扱います。
主な機能
- 01managed または unlocked の選択
- 02既存バンドルのパッケージ準備
- 032GP パッケージとバージョンの作成
- 04購読組織でのインストール、更新、削除
- 05パッケージ作成と導入の問題調査
活用シーン
- 01既存 UI Bundle の 2GP 化
- 02既存パッケージの新バージョン作成
- 03購読組織へのパッケージ導入
- 04パッケージ作成や更新の失敗診断
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ forcedotcom/ sf-skills --skill experience-ui-bundle-2gp-deployインストール後、Claude Code で/experience-ui-bundle-2gp-deployを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
assets/CustomApplication.app-meta.xmlPermissionSet.permissionset-meta.xmlscripts/find-bundle-package-dir.sh詳細
既存の UI バンドルをパッケージ化する(2GP)
現在のプロジェクトにすでに存在する UI バンドル
(<packageDir>/uiBundles/<name>/ 配下。<packageDir> は sfdx-project.json に記載されたパッケージディレクトリで、通常は force-app/main/default です)を、第 2 世代パッケージ(2GP)として配布し、別の組織にインストール/アップグレード/アンインストールする方法です。
これは参照用の情報であり、上から順に実行する手順書ではありません。 ユーザーはすでにプロジェクトと、ビルド済み(またはビルド可能な)バンドルを持っています。ユーザーの意図を読み取り、該当する部分だけを適用してください。
質問されたことだけに回答してください。 必要な部分のコマンド、そのコマンドの対象となる組織、そして実際に関係する注意点だけを提示してください。それ以外の部分を繰り返したり、フレーバーの表を再説明したり、ユーザーが単一の手順について尋ねているのにビルド→作成→インストール→昇格の全手順を再掲したりしないでください。デバッグの質問には修正方法だけを回答し、パッケージ化のチュートリアルは不要です。インストールの質問には sf package install の行とサブスクライバーと Dev Hub の違いだけを回答し、Part 1 と Part 2 は不要です。ここでは、簡潔さが正確さにつながります。
| ユーザーの目的 | 参照先 |
|---|---|
| 管理パッケージとアンロックパッケージのどちらにするか決める | フレーバーを選ぶ |
| バンドルをパッケージ化可能にする/CustomApplication を設定する | Part 1 |
| パッケージまたは新しいバージョンを作成する | Part 2 |
| インストール/アップグレード/アンインストール/昇格する | Part 3 |
| 失敗をデバッグする | Part 4 + トラブルシューティング |
このスキルはパッケージ化と組織間配布(sf package …)を対象とします。バンドルを単一の組織にソースデプロイするだけの場合(sf project deploy …)は、代わりに experience-ui-bundle-deploy を使用してください。ここでは sf project generate や sf template generate ui-bundle を実行しないでください。プロジェクトとバンドルはすでに存在します。
MyReactApp / force-app / force-app/main/default はプレースホルダーです。以下に記載されている箇所はすべて、ユーザーの実際のバンドル名と <packageDir> に置き換えてください。<packageDir> は決定的に解決してください。複数パッケージのプロジェクトで [0] を推測してはいけません。次を使用します。
packageDir="$(scripts/find-bundle-package-dir.sh <bundleName>)" # walks packageDirectories; picks the entry whose tree contains uiBundles/<bundleName>/
Step 0 — 組織を確認する(組織に触れる前に実行)
デフォルトの組織だと決めつけないでください。ユーザーに尋ねるか、sf org list を読み取り、使用する組織を改めて確認してください。
- Dev Hub(
devhub)— パッケージの作成、バージョンのビルド、ソースのデプロイを行う組織です。必ず必要です。 - サブスクライバー(
subscriber)— インストール先の組織です。インストール/アップグレード/アンインストールの場合のみ必要です。
sf org list # connected orgs + default Dev Hub
sf org list --json | jq -r '.result.nonScratchOrgs[]?.alias'
ルール:
- 作成のみのタスク(パッケージまたはバージョン)→ Dev Hub が 1 つあれば十分です。サブスクライバーについて尋ねないでください。
- インストールのタスク → 両方を確認し、どちらがどちらかも確認してください。Dev Hub に誤ってインストールすることは、よくある厄介なミスです。
以下では、devhub / subscriber を実際のエイリアスに置き換えてください。
ID の凡例(パッケージ化): 0Ho… パッケージ · 04t… インストール可能なバージョン(SubscriberPackageVersionId) · 05i… Package2Version · 08c… バージョン作成リクエスト · 0Hf… インストールリクエスト · 06y… アンインストールリクエスト。
ID の凡例(実行時。サブスクライバーで発生した問題のデバッグに有用): 9YE… UI Bundle レコード · 9YF… UIBundleApplication 接続レコード · 02u… CustomApplication / TabSet · 0Zu… ManagedContentSpace(ワークスペース) · 0ap… ManagedContentChannel(WEB_APP)。インストール後に App Launcher のタイルが表示されない場合、ほとんどの場合、これらのいずれかが欠落しているか、正しくプロビジョニングされていません。
前提条件 — 誰もが忘れがちな 2GP の切り替え
2GP を利用するには、Dev Hub で手動で切り替える設定が必要です。この設定は、CLI コマンドでもメタデータのデプロイでも変更できません。設定 → Dev Hubで、次の両方を有効にします。
- Dev Hub を有効化
- Unlocked Packages and Second-Generation Managed Packages を有効化 ← 本当のゲートです。
#2 が有効になるまで、sf package create は NOT_FOUND を返し、Package2 のクエリはすべて sObject type 'Package2' is not supported を返します。CLI で回避する方法はありません。設定を切り替えてください。開始前に確認します。
# clean "0 records" = 2GP ON; "sObject type 'Package2' is not supported" = toggle OFF
sf data query --target-org devhub --use-tooling-api --query "SELECT Id FROM Package2 LIMIT 1"
sf org display --target-org devhub --json | jq '.result.isDevHub'
フレーバーを選択する
3 種類すべてが 2GP(同じ sf package CLI を使用)です。作成前に選択してください。名前空間、インストール時のバンドル名、共存方法がこれによって決まります。
| Managed | Unlocked — namespaced | Unlocked — org-dependent | |
|---|---|---|---|
| 名前空間 | 必須 | 必須 | なし(空の "") |
| ソースの公開範囲 | 非公開(IP 保護) | 公開/編集可能 | 公開/編集可能 |
| インストール時の名前 | ns__Name | ns__Name | Name のみ(フラット) |
| 同じ名前のローカルバンドルとの共存 | 可能(名前空間でフィルタリング) | 可能(名前空間でフィルタリング) | 不可 — 競合します |
| アップグレード時の動作 | クリーンな置き換え(ロック済み) | 置き換え、サブスクライバーによる編集を上書き | 置き換え、サブスクライバーによる編集を上書き |
| アップグレード失敗時のロールバックリスク | あり | あり | なし |
| 一般的な用途 | ISV/AppExchange での配布 | 組織に依存しない共有、ソースを公開 | パッケージがターゲット組織にすでに存在するメタデータに依存する場合 |
名前空間付きのフレーバー(managed、unlocked-namespaced)には、登録済みで、この Dev Hub にリンクされた名前空間が必要です(アプリケーションランチャー → Namespace Registries)。登録済みの名前空間がない場合は、org-dependent unlocked を使用してください。こちらは名前空間が不要です。
リンクの仕組み(名前空間 ⇄ Dev Hub)
名前空間は、それを所有する別個の Developer Edition(DE)組織に存在します。Dev Hub はリンクされた登録を通じて、その名前空間を借用します。具体的には次のとおりです。
- DE 組織にサインアップし、その組織に名前空間を登録します(設定 → パッケージマネージャー → 名前空間の登録)。
- Dev Hub で、アプリケーションランチャー → Namespace Registries → 名前空間をリンクを選択し、DE 組織の認証情報でログインして、この Dev Hub に名前空間をリンクします。
sfdx-project.jsonのnamespaceに、リンクした名前空間のスラッグを設定します。ここで指定した値がターゲットの Dev Hub にリンクされていない場合、sf package version createは名前空間エラーで失敗します(トラブルシューティングを参照)。
1 つの DE 組織に複数の名前空間を持たせることができ、1 つの Dev Hub から複数の DE 組織をリンクできます。そのため、1 つの Dev Hub で複数の名前空間を使ってパッケージをビルドできます。名前空間はバージョン作成時に固定され、ビルドされたバージョン内のすべての UI Bundle 行に引き継がれます。後から変更することはできません。
実行時モデル — フレーバーが重要な理由
通常の質問に答えるために、これを説明する必要はありません。ユーザーから「なぜ」と聞かれたときに参照してください。たとえば、なぜマネージドではソースが非表示になるのか、なぜ名前空間付きインストールでは ns__Name になるのか、なぜ一部の URL の形式が異なるのか、ロック解除版をアップグレードしたら編集内容が消えたのはなぜか、といった質問です。
- オリジンの分離。 インストール済みの UI バンドルはそれぞれ、
salesforce.comのコア UI とは異なる*.salesforce.app上の独自のオリジンからレンダリングされます。階層は次のとおりです。salesforce.com— ファーストパーティのコア UI*.salesforce.app— AFS ホストのセカンドパーティバンドル(名前空間なし)<ns>.salesforce.app— サードパーティ/名前空間付き(マネージドおよび名前空間付きロック解除版) 名前空間ごとに独自のサブドメインが割り当てられるため、異なるパッケージの 2 つのバンドルが、クロスオリジンの影響を受けずに共存できます。
- IP 保護はマネージドパッケージのみの特性です。 マネージドパッケージの場合、サブスクライバー組織では
getSourceZip()が null を返します。コンパイル済みのdist/は不透明なコンテンツとして保存され、返されることはありません。ロック解除版(名前空間付き、または組織依存)の場合、配信されるバイナリはサブスクライバーが完全に読み取れます。 - インストールのセマンティクス。 マネージドおよび名前空間付きロック解除版は
ns__Nameとしてインストールされ、同名のローカルバンドルと共存できます。組織依存のロック解除版には名前空間がなく、裸のNameとしてインストールされるため、同じ開発者名のローカルバンドルと衝突します。 - 差分アップグレード。 新しい
04t…に対してsf package installを実行すると、プラットフォームは、受信した各dist/アセットのコンテンツインデックスハッシュを、すでに保存されているものと比較し、ハッシュが変わっていないアセットをスキップします。1 つのバンドルだけを変更するパッチでは、そのバンドルの変更されたファイルのみが書き換えられます。開発者所有のアーティファクト(dist/、ui-bundle.json、ISV ベース権限セット)は置き換えられます。サブスクライバー所有の状態(サブスクライバーが作成した権限セット、カスタムメタデータ、プロビジョニング済みドメイン)は保持されます。 - キルスイッチ。 Setup → Security → Multi-Framework Domains → プロビジョニング済みドメインを無効化します。直ちに 404 になります。メタデータはインストールされたままで、無効化は取り消し可能です。
パート 1 — 既存のバンドルをパッケージ化できる状態にする
プロジェクトをパッケージ化して組織内で使用できるよう準備します。リクエストに必要な変更のみ適用してください。
1a. sfdx-project.json に API バージョンと名前空間を設定する
ここで設定する namespace によって、ビルドできるフレーバーが決まるため(上の表を参照)、慎重に設定してください。安全なデフォルト値はありません。<ns> はユーザーが実際に登録している名前空間に置き換えます。組織依存の場合は "" を使用します。
# namespaced (managed / unlocked-namespaced): <ns> MUST be registered & linked to this Dev Hub
node -e "const fs=require('fs'),f='sfdx-project.json',j=JSON.parse(fs.readFileSync(f)); j.sourceApiVersion='68.0'; j.namespace='<ns>'; fs.writeFileSync(f,JSON.stringify(j,null,2))"
# org-dependent unlocked: no namespace
node -e "const fs=require('fs'),f='sfdx-project.json',j=JSON.parse(fs.readFileSync(f)); j.sourceApiVersion='68.0'; j.namespace=''; fs.writeFileSync(f,JSON.stringify(j,null,2))"
cat sfdx-project.json # confirm namespace + sourceApiVersion before packaging
- マネージド/名前空間付きロック解除版 →
namespace= 登録済みのリンクされた名前空間。 - 組織依存のロック解除版 →
namespaceは""のままにします。 - この Dev Hub に登録されていない名前空間を設定すると、後でビルドに失敗します(トラブルシューティングを参照)。
1b. バンドルをビルドする — パッケージ化の前に dist/ が必要です
cd force-app/main/default/uiBundles/MyReactApp # the real bundle dir
npm install --no-audit --no-fund
npm run build
cd -
dist/ が存在する前にパッケージ化またはデプロイすると、アプリはインストールされますが レンダリング結果は空白になります。バンドルにはビルド済みアセットが含まれます。必ず先にビルドしてください。
1c. CustomApplication を設定する(バンドルを Salesforce アプリとして起動できるようにする必要がある場合のみ)
バンドルがすでに別の方法で参照されている場合(FlexiPage、Experience Cloud サイトへの埋め込みなど)は、この手順をスキップしてください。それ以外の場合は、<SKILL_DIR>/assets/CustomApplication.app-meta.xml(<SKILL_DIR> はこのスキル自体のディレクトリの絶対パス)を読み込み、MyReactApp をすべて実際のバンドルの開発者名に置き換え、結果をユーザーのプロジェクトの <packageDir>/applications/ に書き込んでください。<uiBundle> にはバンドルの開発者名を指定します。同じパッケージ内ではプレフィックスは不要です。名前空間をまたぐ場合は、名前空間ありなら ns__Name、名前空間なしなら c__Name として解決されます。
App Launcher タイルが実際に参照するのは、次の3つのフィールドです。正しく設定すれば、インストールしたサブスクライバーに壊れたタイルが表示されることはありません。
<uiType>Lightning</uiType>— App Launcher で表示するために必須です<navType>Standard</navType>— 標準のナビゲーションコンテナーです<formFactors>Large</formFactors>— デスクトップのフォームファクターです(検証はインストール時のみ行われるため、値がない場合や誤っている場合でもデプロイは成功しますが、タイルは表示されません)
mkdir -p force-app/main/default/applications
# then write the substituted template to:
# force-app/main/default/applications/<BundleName>.app-meta.xml
1d. ソースを Dev Hub にデプロイする(package create の前にメタデータを用意するため)
sf project deploy start --source-dir force-app --target-org devhub --api-version 68.0 --wait 30
1e. 権限セットでアプリの表示を許可する(1c で CustomApplication を追加し、手動で Setup をクリックせずにアプリへアクセスできるようにする場合のみ)
<SKILL_DIR>/assets/PermissionSet.permissionset-meta.xml を読み込み、MyReactApp を実際のバンドル名に置き換え(<application> とラベルの両方)、結果をユーザーのプロジェクトに書き込んでから、デプロイして割り当てます。
mkdir -p force-app/main/default/permissionsets
# write the substituted template to:
# force-app/main/default/permissionsets/<BundleName>_Access.permissionset-meta.xml
sf project deploy start --source-dir force-app/main/default/permissionsets/MyReactApp_Access.permissionset-meta.xml --target-org devhub --api-version 68.0 --wait 30
sf org assign permset --name MyReactApp_Access --target-org devhub
パート 2 — パッケージを作成する(Dev Hub のみ)
サブスクライバー組織は関与しません。sf package create は一度だけ実行し(0Ho… コンテナーを登録します)、その後、インストール可能な 04t… バージョンを繰り返しビルドします。上で選んだフレーバーを指定してください。
# managed
sf package create --name MyReactApp --package-type Managed --path force-app --target-dev-hub devhub
# unlocked, namespaced (namespace comes from sfdx-project.json)
sf package create --name MyReactApp --package-type Unlocked --path force-app --target-dev-hub devhub
# unlocked, org-dependent (no namespace)
sf package create --name MyReactApp --package-type Unlocked --org-dependent --path force-app --target-dev-hub devhub
次に、バージョンをビルドします。
sf package version create --package MyReactApp --installation-key-bypass --wait 20 --target-dev-hub devhub
# a specific/patch version instead:
sf package version create --package MyReactApp --version-number 1.0.1 --wait 20 --target-dev-hub devhub
--version-number 1.0.0.NEXT はビルド番号を自動的に増分します。固定の 1.0.1 はその番号を指定します。--installation-key-bypass を指定すると、保護されていないバージョン(インストールキーなしでインストール可能)がビルドされます。指定せずに --installation-key <key> を渡すと、インストールが制限されます。
堅牢なバージョン作成(Dev Hub のキューが遅くても処理を継続できます)
ビルドがキュー内で待機している間に --wait がタイムアウトすると、要求ハンドルが失われることがあります。非同期で送信し、08c… ID を取得してから状態を確認してください。
REQ=$(sf package version create --package MyReactApp --installation-key-bypass \
--skip-validation --target-dev-hub devhub --json | jq -r '.result.Id')
echo "request: $REQ"
while :; do
J=$(sf package version create report -i "$REQ" --target-dev-hub devhub --json)
ST=$(echo "$J" | jq -r '.result[0].Status'); echo "status: $ST"
case "$ST" in
Success) echo "$J" | jq -r '.result[0].SubscriberPackageVersionId'; break;;
Error) echo "$J" | jq -r '.result[0].Error[]? // "build failed"'; break;;
esac
sleep 30
done
--skip-validation を指定すると処理は速くなりますが、ベータバージョンが作成されます(プロモートできず、ベータ版からベータ版へのアップグレードもできません。パート 3 を参照)。リリース可能なビルドにするには、このオプションを外してください。キューに入ったビルドは、いつでも次のコマンドで再開できます。
sf package version create report -i 08c… --target-dev-hub devhub
パート 3 — インストール/アップグレード/アンインストール/プロモート
最初にサブスクライバーのエイリアスを確認してください(ステップ 0)。 ここでの操作はすべてサブスクライバーに対して実行されます。ただし、プロモートは Dev Hub で実行されます。
# fresh install
sf package install --package 04t… --target-org subscriber --wait 10
# add --installation-key <key> if the version was built with one
# add --publish-wait 10 to wait for the version to finish publishing
# upgrade (newer version over the old)
sf package install --package 04t…v2 --target-org subscriber --upgrade-type Mixed --wait 10
# --upgrade-type: Mixed (default) | DeprecateOnly | Delete (destructive — care)
# uninstall
sf package uninstall --package 04t… --target-org subscriber --wait 20
# promote a managed version to released/immutable — runs on the DEV HUB, irreversible
sf package version promote --package 04t… --target-dev-hub devhub
ベータ版からベータ版へのアップグレードはできません。 ベータ版 v0.1 に対して --skip-validation を指定したベータ版 v0.2 をインストールすると、"Cannot upgrade beta package." というエラーになります。v0.1(管理パッケージ)をプロモートするか、先に v0.1 をアンインストールしてから v0.2 をインストールしてください。
アンロックパッケージのアップグレードでは、サブスクライバーによるバンドルへの編集内容が上書きされます。 Org-dependent パッケージでは、アップグレード失敗時にロールバックされません。名前空間付きのフレーバーではロールバックされます。
堅牢なインストール(インストールが実際に完了したことを確認する)
sf package install --wait は、要求がまだ IN_PROGRESS の間に終了コード 0 を返すことがあります。これは誤った成功判定です。確認してください。
sf package install --package 04t… --target-org subscriber --wait 20 --no-prompt
sf package installed list --target-org subscriber --json \
| jq -r '.result[]? | select(.SubscriberPackageVersionId=="04t…") | .SubscriberPackageVersionId'
何も表示されない場合、サーバー側で処理中です。失敗と判断する前に、数分おいてから sf package installed list で状態を確認してください。
パート 4 — デバッグ/確認
ほとんどは読み取り専用です。失敗の原因を診断したり、状態を確認したりする場合に使用してください。
# Dev Hub state
sf org display --target-org devhub --json | jq '{isDevHub:.result.isDevHub, user:.result.username, instance:.result.instanceUrl, api:.result.apiVersion}'
# Is 2GP on? (the #1 root cause)
sf data query --target-org devhub --use-tooling-api --query "SELECT Id, Name, NamespacePrefix, ContainerOptions FROM Package2"
# Packages & versions on the Dev Hub
sf package list --target-dev-hub devhub
sf package version list --packages MyReactApp --target-dev-hub devhub --verbose
# Version-create failures — status + Error[]
sf package version create list --target-dev-hub devhub
sf package version create report -i 08c… --target-dev-hub devhub
sf data query --target-org devhub --use-tooling-api \
--query "SELECT Id, Status, Package2Id, Error FROM Package2VersionCreateRequest ORDER BY CreatedDate DESC LIMIT 5"
# A version's details
sf package version report --package 04t… --target-dev-hub devhub
# What's installed in the subscriber
sf package installed list --target-org subscriber --json \
| jq -r '.result[]? | "\(.SubscriberPackageName) \(.SubscriberPackageVersionNumber) \(.SubscriberPackageVersionId)"'
# Install/uninstall stuck IN_PROGRESS
sf package install report --request-id 0Hf… --target-org subscriber
sf package uninstall report --request-id 06y… --target-org subscriber
# Deploy failures (before you can even package)
sf project deploy start --source-dir force-app --target-org devhub --dry-run --wait 30
sf project deploy report --target-org devhub
# Bundle renders blank — confirm built assets shipped
ls -la force-app/main/default/uiBundles/MyReactApp/dist
トラブルシューティング
| 症状 | 原因/対処方法 |
|---|---|
sObject type 'Package2' is not supported | 2GP の切り替えが OFF です。Setup → Dev Hub で「Unlocked & Second-Gen Managed Packages」を有効にしてください(手動での操作が必要で、CLI での対処方法はありません)。 |
sf package create → NOT_FOUND | 同じく、2GP がプロビジョニングされていません。切り替えを有効にして、再認証してください。 |
有効化後も isDevHub: false/null と表示される | CLI のログイン情報がキャッシュされています。再認証してください。キャッシュされたフラグではなく、Package2 のクエリと package create の結果を信頼してください。 |
version create がハングする/--wait がタイムアウトする | ビルドがキューに入っています。非同期で送信してから version create report -i 08c… で状態を確認し、後で同じ ID を使って再開してください。 |
install --wait が終了コード 0 で終了するのに、アプリが見つからない | サーバー側ではまだ IN_PROGRESS です。sf package installed list で確認し、数分間ポーリングしてください。 |
| 「Cannot upgrade beta package」 | ベータ版からベータ版へはアップグレードできません。v0.1(管理パッケージ)を昇格するか、先にアンインストールしてから v0.2 をインストールしてください。 |
| 管理パッケージ/名前空間付きビルドで名前空間エラーが発生する | 名前空間がこの Dev Hub に登録/リンクされていません(App Launcher → Namespace Registries)。または、組織依存のアンロック済みパッケージ(名前空間なし)に切り替えてください。 |
| アプリをインストールできるが、画面が空白になる | デプロイ/パッケージ化の前にバンドルがビルドされていません。npm run build を実行し、dist/ を確認してから再デプロイし、バージョンを再ビルドしてください。 |
| 間違った組織にインストールされた | ステップ 0 で誤ったエイリアスが確認されています。sf org list を再確認してください。subscriber と devhub は異なります。 |
| 組織依存バンドルがローカルのバンドルと衝突する | どちらもプレフィックスなし(null-prefix)の名前を使用しています。名前空間付きの形式を使用するか、名前を変更してください。 |
補足
- まず組織を確認してください。 Dev Hub は常に必要です。subscriber が必要なのはインストール/アップグレード/ アンインストールの場合のみです。作成のみのタスクでは subscriber を要求しないでください。
- 全工程を実行する場合の順序: バンドルをビルド → ソースをデプロイ →
package create(初回のみ) →package version create(リリースごと) →install→promote(管理パッケージのみ)。 - Salesforce のパッケージ化に関する社内の質問は、#packaging が公式の問い合わせ先です。
- 外部の公式ドキュメント(詳しい情報について):
- Second-Generation Managed Packaging Developer Guide — https://developer.salesforce.com/docs/atlas.en-us.pkg2_dev.meta/pkg2_dev/sfdx_dev_dev2gp.htm (管理パッケージ/AppExchange 形式:ワークフロー、コンポーネント、配布、プッシュアップグレード、1GP から 2GP への移行に関する不足点)。
- アンロック済みパッケージも同じ
sf packageCLI を使用します。アンロック済みの名前空間付き形式と組織依存形式については、同じガイドの「Unlocked Packages」セクションを参照してください。