Experience Ui Bundle 2gp Deploy

作者 forcedotcome5164d94d751无许可证1K 个星标收录于 2026年10月8日更新于 2026年10月8日仓库昨天更新

MUST activate when the user wants to package, distribute, or install/upgrade/uninstall/promote a UI Bundle as a Salesforce second-generation (2GP) package (project may contain uiBundles/ or sfdx-project.json for packaging tasks; install/upgrade tasks may lack local bundle files). Handles making a bundle packageable, choosing managed/unlocked flavor, creating package/version with sf package, installing/upgrading in another org, and debugging failures. TRIGGER on packaging, 2GP, managed package, unlocked package, AppExchange, package version, sf package, sf package install, sf package upgrade, install a package, upgrade a package, cross-org distribution. DO NOT TRIGGER for plain source deploy to one org (use experience-ui-bundle-deploy) or scaffolding a new bundle (use experience-ui-bundle-project-generate). Apply piecemeal — do ONLY the part asked.

包含脚本DevOps & Cloud
AI 生成的概览

将现有的 Salesforce UI Bundle 打包为第二代(2GP)包,并完成安装、升级或提升版本。

功能
指导将现有的 Salesforce UI Bundle 打包为第二代包:选择托管、非托管带命名空间或依赖组织的非托管版本类型,使 Bundle 可打包,使用 sf CLI 创建包和版本,并在不同组织间安装、升级、卸载或提升版本。还涵盖故障排查,例如未开启 2GP 开关、命名空间错误、渲染空白和安装卡住。附带一个从 sfdx-project.json 解析 Bundle 包目录的 shell 脚本,以及 CustomApplication 和权限集元数据模板。
适用场景
当 Salesforce 项目中已存在 UI Bundle,且用户希望将其作为 2GP 包进行打包、分发、安装、升级、卸载或提升版本时使用。不适用于向单个组织进行普通源代码部署,也不适用于搭建新的 Bundle。
运行要求
Salesforce CLI(sf)2.0 或更高版本、Node.js 18+、npm 7+ 和 jq 1.6+;需要启用第二代打包开关的 Dev Hub,安装、升级或卸载还需要订阅方组织。带命名空间的版本类型需要已注册并关联到 Dev Hub 的命名空间。附带可执行 shell 脚本和元数据模板,并需要访问 Salesforce 组织的网络连接。

Package an existing UI Bundle (2GP)

How to take a UI Bundle that already exists in the current project (under <packageDir>/uiBundles/<name>/, where <packageDir> is the package directory from sfdx-project.json — commonly force-app/main/default) and ship it as a second-generation package (2GP), then install / upgrade / uninstall it in another org.

This is reference knowledge, not a runbook to execute top-to-bottom. The user already has a project and a built (or buildable) bundle. Read their intent and apply only the matching part:

Answer only what was asked. Give the commands for the one part the user needs plus the org each targets and any genuine caveat — nothing else. Do not restate the other parts, re-explain the flavor table, or replay the full build→create→install→promote sequence when the user asked about a single step. A debug question wants the fix, not a packaging tutorial; an install question wants the sf package install line and the subscriber-vs-Dev-Hub distinction, not Part 1 and Part 2. Brevity is correctness here.

The user wants to…Go to
Decide managed vs. unlockedChoose a flavor
Make the bundle packageable / wire a CustomApplicationPart 1
Create the package or a new versionPart 2
Install / upgrade / uninstall / promotePart 3
Debug a failurePart 4 + Troubleshooting

This skill is for packaging and cross-org distribution (sf package …). For plain source deploy of a bundle into one org (sf project deploy …), use experience-ui-bundle-deploy instead. Never sf project generate or sf template generate ui-bundle here — the project and bundle exist. MyReactApp / force-app / force-app/main/default are placeholders; substitute the user's real bundle name and their <packageDir> everywhere they appear below. Resolve <packageDir> deterministically — never guess [0] in a multi-package project — with:

bash
packageDir="$(scripts/find-bundle-package-dir.sh <bundleName>)"   # walks packageDirectories; picks the entry whose tree contains uiBundles/<bundleName>/

Step 0 — Confirm the orgs (do this before touching any org)

Do not assume the default org. Ask the user, or read sf org list, then restate what you'll use:

  • Dev Hub (devhub) — where the package is created, versions are built, and source is deployed. Always required.
  • Subscriber (subscriber) — the org you install into. Only required for install / upgrade / uninstall.
bash
sf org list                                            # connected orgs + default Dev Hubsf org list --json | jq -r '.result.nonScratchOrgs[]?.alias'

Rules:

  • Create-only task (package or version) → one Dev Hub is enough; don't ask for a subscriber.
  • Install task → confirm both, and confirm which is which. Installing into the Dev Hub by mistake is a common, messy error.

Substitute the real aliases for devhub / subscriber everywhere below.

ID legend (packaging): 0Ho… package · 04t… installable version (SubscriberPackageVersionId) · 05i… Package2Version · 08c… version-create request · 0Hf… install request · 06y… uninstall request.

ID legend (runtime, useful when debugging a broken subscriber): 9YE… UI Bundle row · 9YF… UIBundleApplication junction · 02u… CustomApplication / TabSet · 0Zu… ManagedContentSpace (workspace) · 0ap… ManagedContentChannel (WEB_APP). A missing App Launcher tile after install almost always traces back to one of these being absent or misprovisioned.


Prerequisite — the 2GP toggle everyone forgets

2GP needs a manual Setup toggle on the Dev Hub that no CLI command or metadata deploy can flip. Setup → Dev Hub, both on:

  1. Enable Dev Hub, and
  2. Enable Unlocked Packages and Second-Generation Managed Packages ← the real gate.

Until #2 is on, sf package create returns NOT_FOUND and any Package2 query returns sObject type 'Package2' is not supported. There is no CLI workaround — flip the toggle. Verify before starting:

bash
# clean "0 records" = 2GP ON;  "sObject type 'Package2' is not supported" = toggle OFFsf 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'

Choose a flavor

All three are 2GP (same sf package CLI). Pick before creating — it drives the namespace, how the bundle is named on install, and coexistence.

ManagedUnlocked — namespacedUnlocked — org-dependent
Namespacerequiredrequirednone (empty "")
Source visibilityhidden (IP-protected)visible / editablevisible / editable
Installs asns__Namens__Namebare Name (flat)
Coexists with a local same-name bundleyes (ns-filtered)yes (ns-filtered)no — collides
Upgrade behaviorclean replace (locked)replace, overwrites subscriber editsreplace, overwrites subscriber edits
Rollback risk on failed upgradeyesyesnone
Typical useISV / AppExchange distributionorg-agnostic sharing, source openpackage depends on metadata already in the target org

Namespaced flavors (managed, unlocked-namespaced) need a namespace registered and linked to this Dev Hub (App Launcher → Namespace Registries). No registered namespace? Use org-dependent unlocked — it needs none.

How linking works (namespace ⇄ Dev Hub)

The namespace lives in a separate Developer Edition (DE) org that owns it; the Dev Hub borrows it via a linked registration. Concretely:

  1. Sign up a DE org and register a namespace on it (Setup → Package Manager → Namespace Registrations).
  2. In the Dev Hub, App Launcher → Namespace Registries → Link Namespace, log in with the DE org's credentials to link the namespace to this Dev Hub.
  3. Set namespace in sfdx-project.json to the linked namespace slug. If the value here isn't linked to the target Dev Hub, sf package version create fails with a namespace error (see Troubleshooting).

One DE org can carry multiple namespaces, and one Dev Hub can link multiple DE orgs — so a single Dev Hub can build packages under several namespaces. The namespace is locked in at version-create time and travels with every UI Bundle row inside the built version; you cannot change it later.


Runtime model — why the flavor matters

You do not have to explain this to answer a routine question. Reach for it when the user asks why: why managed hides source, why namespaced installs are ns__Name, why some URLs look different, or why an unlocked upgrade wiped their edits.

  • Origin isolation. Every installed UI Bundle renders from its own origin on *.salesforce.app, distinct from salesforce.com core UI. Tiers:
    • salesforce.com — 1st-party core UI
    • *.salesforce.app — 2nd-party AFS-hosted bundles (no namespace)
    • <ns>.salesforce.app — 3rd-party / namespaced (managed + unlocked-namespaced) Because each namespace gets its own subdomain, two bundles from different packages can coexist without cross-origin bleed.
  • IP protection is a managed-only property. For managed packages, getSourceZip() returns null in subscriber orgs — the compiled dist/ is stored as opaque content and never handed back. For unlocked (namespaced or org-dependent), the served binary is fully readable by the subscriber.
  • Install semantics. Managed and unlocked-namespaced install as ns__Name and can coexist with a local same-name bundle. Org-dependent unlocked has no namespace — it installs as bare Name and collides with a local bundle of the same developer name.
  • Delta upgrade. On sf package install of a newer 04t…, the platform compares content-index hashes of each incoming dist/ asset against what's already stored and skips any asset whose hash is unchanged — a patch that touches one bundle re-writes only that bundle's changed files. Developer-owned artifacts (dist/, ui-bundle.json, ISV base permission sets) are replaced; subscriber-owned state (subscriber-created permission sets, custom metadata, provisioned domain) is preserved.
  • Kill switch. Setup → Security → Multi-Framework Domains → disable a provisioned domain. Immediate 404; metadata stays installed; reversible.

Part 1 — make the existing bundle packageable

Prepare the project so it can be packaged and used in-org. Apply only what the request needs.

1a. Set API version + namespace in sfdx-project.json

The namespace here decides which flavor you can build (see table above), so set it deliberately — there is no safe default. Substitute the user's real registered namespace for <ns>; use "" for org-dependent.

bash
# namespaced (managed / unlocked-namespaced): <ns> MUST be registered & linked to this Dev Hubnode -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 namespacenode -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
  • Managed / namespaced-unlocked → namespace = a registered, linked namespace.
  • Org-dependent unlocked → leave namespace as "".
  • Setting a namespace that isn't registered to this Dev Hub fails the build later (see Troubleshooting).

1b. Build the bundle — dist/ must exist before packaging

bash
cd force-app/main/default/uiBundles/MyReactApp        # the real bundle dirnpm install --no-audit --no-fundnpm run buildcd -

Package or deploy before dist/ exists and the app installs but renders blank — the bundle ships with its built assets. Always build first.

1c. Wire a CustomApplication (only if the bundle must be launchable as a Salesforce app)

Skip this step when the bundle is already referenced another way (embedded in a FlexiPage, Experience Cloud site, etc.). Otherwise read <SKILL_DIR>/assets/CustomApplication.app-meta.xml (where <SKILL_DIR> is the absolute path to this skill's own directory), replace every MyReactApp with the real bundle developer name, and write the result to the user's project under <packageDir>/applications/. Author <uiBundle> with the bundle's developer name — inside the same package no prefix is needed; cross-namespace it resolves as ns__Name (namespaced) or c__Name (no namespace).

The three fields the App Launcher tile actually cares about — installed subscribers won't see a broken tile if they're set correctly:

  • <uiType>Lightning</uiType> — required for the App Launcher to render it
  • <navType>Standard</navType> — standard navigation container
  • <formFactors>Large</formFactors> — desktop form factor (validation is install-time only, so a missing/wrong value passes deploy but hides the tile)
bash
mkdir -p force-app/main/default/applications# then write the substituted template to:#   force-app/main/default/applications/<BundleName>.app-meta.xml

1d. Deploy source to the Dev Hub (so metadata exists before package create)

bash
sf project deploy start --source-dir force-app --target-org devhub --api-version 68.0 --wait 30

1e. Grant app visibility via a permission set (only if 1c added a CustomApplication and the app must be reachable without a manual Setup click)

Read <SKILL_DIR>/assets/PermissionSet.permissionset-meta.xml, replace MyReactApp with the real bundle name (both in <application> and the label), write the result into the user's project, then deploy and assign:

bash
mkdir -p force-app/main/default/permissionsets# write the substituted template to:#   force-app/main/default/permissionsets/<BundleName>_Access.permissionset-meta.xmlsf project deploy start --source-dir force-app/main/default/permissionsets/MyReactApp_Access.permissionset-meta.xml --target-org devhub --api-version 68.0 --wait 30sf org assign permset --name MyReactApp_Access --target-org devhub

Part 2 — create the package (Dev Hub only)

No subscriber org involved. sf package create runs once (registers the 0Ho… container); you build installable 04t… versions repeatedly after. Pick the one flavor you chose above:

bash
# managedsf 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

Then build a version:

bash
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 auto-bumps the build number; a fixed 1.0.1 pins it. --installation-key-bypass builds an unprotected version (no key to install); omit it and pass --installation-key <key> to gate installs.

Robust version-create (survives a slow Dev Hub queue)

--wait can time out while the build sits queued, losing the request handle. Submit async, capture the 08c… id, poll:

bash
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 30done

--skip-validation is faster but produces a beta version (can't be promoted, and beta can't upgrade beta — see Part 3). Drop it for a releasable build. Resume a queued build anytime: sf package version create report -i 08c… --target-dev-hub devhub


Part 3 — install / upgrade / uninstall / promote

Confirm the subscriber alias first (Step 0). Everything here hits the subscriber — except promote, which runs on the Dev Hub.

bash
# fresh installsf 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)
# uninstallsf package uninstall --package 04t… --target-org subscriber --wait 20
# promote a managed version to released/immutable — runs on the DEV HUB, irreversiblesf package version promote --package 04t… --target-dev-hub devhub

Beta can't upgrade beta. A --skip-validation (beta) v0.2 over a beta v0.1 fails with "Cannot upgrade beta package." Either promote v0.1 (managed) or uninstall v0.1 first, then install v0.2.

Unlocked upgrades overwrite subscriber edits to the bundle. Org-dependent has no rollback on a failed upgrade; namespaced flavors do.

Robust install (confirm it actually landed)

sf package install --wait can exit 0 while the request is still IN_PROGRESS — a false success. Verify:

bash
sf package install --package 04t… --target-org subscriber --wait 20 --no-promptsf package installed list --target-org subscriber --json \  | jq -r '.result[]? | select(.SubscriberPackageVersionId=="04t…") | .SubscriberPackageVersionId'

Prints nothing → still processing server-side; poll sf package installed list a few minutes before concluding it failed.


Part 4 — debug / inspect

Mostly read-only. Reach for these to diagnose a failure or inspect state.

bash
# Dev Hub statesf 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 Hubsf package list --target-dev-hub devhubsf package version list --packages MyReactApp --target-dev-hub devhub --verbose
# Version-create failures — status + Error[]sf package version create list --target-dev-hub devhubsf package version create report -i 08c… --target-dev-hub devhubsf 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 detailssf package version report --package 04t… --target-dev-hub devhub
# What's installed in the subscribersf package installed list --target-org subscriber --json \  | jq -r '.result[]? | "\(.SubscriberPackageName) \(.SubscriberPackageVersionNumber) \(.SubscriberPackageVersionId)"'
# Install/uninstall stuck IN_PROGRESSsf package install report   --request-id 0Hf… --target-org subscribersf 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 30sf project deploy report --target-org devhub
# Bundle renders blank — confirm built assets shippedls -la force-app/main/default/uiBundles/MyReactApp/dist

Troubleshooting

SymptomCause / fix
sObject type 'Package2' is not supported2GP toggle OFF — Setup → Dev Hub → enable "Unlocked & Second-Gen Managed Packages" (manual, no CLI fix).
sf package create → NOT_FOUNDSame — 2GP not provisioned. Enable toggle, re-auth.
isDevHub: false/null after enablingCached CLI login — re-auth. Trust the Package2 query + package create, not the cached flag.
version create hangs / --wait times outBuild queued. Use the async submit + version create report -i 08c… poll; resume later with the same id.
install --wait exits 0 but app missingStill IN_PROGRESS server-side. Confirm with sf package installed list; poll a few minutes.
"Cannot upgrade beta package"Beta can't upgrade beta. Promote v0.1 (managed) or uninstall it first, then install v0.2.
Namespace error on managed/namespaced buildNamespace not registered/linked to this Dev Hub (App Launcher → Namespace Registries), or switch to org-dependent unlocked (no namespace).
App installs but renders blankBundle not built before deploy/package — npm run build, confirm dist/, redeploy, rebuild the version.
Installed into the wrong orgWrong alias confirmed in Step 0. Re-check sf org list; subscriber ≠ devhub.
Org-dependent bundle collides with a local oneBoth use a bare (null-prefix) name. Use a namespaced flavor, or rename.

Notes

  • Confirm orgs first. Dev Hub always; subscriber only for install/upgrade/ uninstall. Don't ask for a subscriber on a create-only task.
  • Order for a full run: build bundle → deploy source → package create (once) → package version create (each release) → install → promote (managed only).
  • For internal Salesforce packaging questions, the authoritative channel is #packaging.
  • Authoritative external docs (for deeper reference):

来源与署名

来源:forcedotcom/sf-skills位于skills/experience-ui-bundle-2gp-deploy提交e5164d9

许可证: 无许可证

内容归原作者所有。SourceWeft 从公开仓库中收录这些内容。

举报或申请下架

更多来自 forcedotcom/sf-skills 的技能

Service Itsm Teams Itservice Configure

forcedotcom

Configure the "Set Up Salesforce IT Service" checklist for Microsoft Teams Employee Service (ITSM) — the employee side, covering app enablement, marketplace install guidance, user access assignment, and Digital Experience Site selection. Use this for: 'turn on Salesforce IT Service', 'set up IT Service on Teams', 'assign Teams for Employee permission set', 'give employees access to Teams for Employee Service', 'manage user access for Teams ITSM', 'grant users the permission sets needed for Teams Employee Service', 'select a digital experience site for Teams', 'install Salesforce IT Service app on Teams', or any request to complete the IT Service half of the Teams ITSM Go page checklist (including the Manage User Access step). DO NOT TRIGGER for the base Teams Salesforce Go page toggle or Azure/Entra app setup (service-itsm-teams-configure) or for the IT Desk/fulfiller half of the checklist (service-itsm-teams-itdesk-configure).

待分类1K昨天更新

Service Itsm Teams Coordinate

forcedotcom

End-to-end autopilot orchestrator for setting up Microsoft Teams integration in Salesforce Service Cloud ITSM — runs the whole flow (enable the Teams for Employee Service Go feature, register the Microsoft Entra app, populate Named Credentials, configure the IT Desk and IT Service checklists, turn on Swarming, and optionally embed the Agentforce agent) in one continuous pass, stopping only at the points a human must act. Use when the user asks to set up Microsoft Teams for ITSM end to end, 'set up teams for it service', 'do the whole teams itsm setup', 'configure microsoft teams for employee service', or wants a guided Teams ITSM walkthrough. Delegates each stage to a specialized child skill while driving the sequence itself. DO NOT TRIGGER when the user asks to enable Teams alone, configure just the IT Desk or IT Service checklist alone, or enable Swarming alone — delegate directly to the specific child skill in those cases.

待分类1K昨天更新

Service Itsm Teams Itdesk Configure

forcedotcom

Configure the "Set Up Salesforce IT Desk" checklist for Microsoft Teams Employee Service (ITSM) — the fulfiller/agent side, covering app enablement, marketplace install guidance, user access assignment, and Swarming collaboration-tool setup. Use this for: 'turn on Salesforce IT Desk', 'set up IT Desk on Teams', 'assign Teams for IT Desk permission set', 'set Teams as collaboration tool for swarming', 'install Salesforce IT Desk app on Teams', or any request to complete the IT Desk half of the Teams ITSM Go page checklist. DO NOT TRIGGER for the base Teams Salesforce Go page toggle or Azure/Entra app setup (service-itsm-teams-configure) or for the IT Service/employee half of the checklist (service-itsm-teams-itservice-configure).

待分类1K昨天更新

Service Itsm Teams Debug

forcedotcom

通过针对 Salesforce 组织运行通过/失败配置检查清单,诊断 Microsoft Teams 员工服务(ITSM)配置故障。

DevOps & Cloud1K昨天更新

Service Itsm Teams Employee Agent Configure

forcedotcom

Configure the embedded Agentforce Employee Agent so it replies inside the Microsoft Teams ITSM custom client ('Salesforce Employee Assist' / 'Ask AI Agent'). Use this for: 'set up employee agent in Teams', 'embed Agentforce agent in Teams', 'make the IT Service Employee Agent reply in Teams', 'Teams Ask AI Agent not responding', 'agent joins then leaves without replying', 'configure MIAW deployment for Teams employee agent', 'Teams embedded messaging agent setup'. Builds the whole stack headlessly (zero Setup-UI clicks): the Web messaging channel with User Verification ON, the Enhanced Chat User Verification Key Set (JWKS_URL) it requires, the Teams_AgentForce custom-client deployment, the routing flow to the agent, and the Agent Access permission set that lets the portal user reach the agent. DO NOT TRIGGER for enabling the Teams feature Salesforce Go page toggle (service-itsm-teams-configure) or for configuring notification preferences.

待分类1K昨天更新

Service Itsm Swarming Configure

forcedotcom

通过 Connect API 调用启用 Salesforce Swarming ITSM 功能,并将协作工具设为 Teams。

DevOps & Cloud1K昨天更新