mirror of
https://github.com/forcedotcom/afv-library.git
synced 2026-08-07 00:02:34 +08:00
244 lines
12 KiB
Markdown
244 lines
12 KiB
Markdown
---
|
|
name: experience-ui-bundle-deploy
|
|
description: "MUST activate when the project contains a uiBundles/*/src/ directory and the task involves deploying, pushing to an org, or post-deploy org setup. Use this skill to deploy a UI bundle app to a Salesforce org and run the full ordered setup: org authentication, pre-deploy build, metadata deploy, permission-set assignment, role assignment, Experience Cloud self-registration, seed-data import, and GraphQL schema fetch plus codegen. Activate when a uiBundles/ project also has files like *.network-meta.xml, org-setup.config.json, a data-plan.json in the data/ dir, or sfdx-project.json and the user mentions deploying, pushing, org setup, or post-deploy tasks. DO NOT TRIGGER when: creating a new UI bundle project from scratch (use experience-ui-bundle-project-generate); styling or editing pages in an existing app without deploying (use experience-ui-bundle-frontend-generate); adding a specific feature such as auth, search, or file upload without deploying (use the matching experience-ui-bundle-*-generate skill)."
|
|
metadata:
|
|
version: "1.1"
|
|
relatedSkills:
|
|
- "experience-ui-bundle-frontend-generate"
|
|
- "experience-ui-bundle-project-generate"
|
|
cliTools:
|
|
- tool: ["jq"]
|
|
semver: ">=1.6"
|
|
- tool: ["node"]
|
|
semver: ">=18.0.0"
|
|
- tool: ["npm"]
|
|
semver: ">=7.0.0"
|
|
- tool: ["sf"]
|
|
semver: ">=2.0.0"
|
|
minApiVersion: "66.0"
|
|
allowed-tools: Bash Read Write Edit
|
|
---
|
|
|
|
# Deploying a UI Bundle App
|
|
|
|
Deploy order is load-bearing: a step's output is the next step's precondition
|
|
(deploy before schema fetch; permissions before schema fetch; role/self-reg
|
|
before the schema the guest user must see). This is the canonical setup sequence,
|
|
ported from the reference `org-setup.mjs`. The `org-setup.mjs` line
|
|
citations in `references/` are port-provenance (why each rule exists) pointing at
|
|
that external reference script — not files shipped with this skill — so you don't
|
|
need to open them to run the steps.
|
|
|
|
Run each step in order. **Every optional step is presence-driven**: if its
|
|
convention file is absent, no-op cleanly and move on — do not fabricate config.
|
|
For the two destructive/expensive steps (self-registration, data import),
|
|
**ask the user before running**.
|
|
|
|
## Inputs to gather up front
|
|
|
|
Read these from the project; **ask the user** only for what's missing:
|
|
|
|
- **Target org** — alias/username for `--target-org`. Ask if not obvious.
|
|
- **Source root** — run `scripts/get-source-root.sh` to resolve the metadata
|
|
source dir from `sfdx-project.json` (`packageDirectories[0].path` + `/main/default`).
|
|
It exits non-zero if the project file is missing or malformed. Never hardcode
|
|
`force-app/main/default`.
|
|
- **`org-setup.config.json`** (optional) — drives permset assignment, role, and
|
|
self-registration. Absent keys mean "skip that step". **Exception:** if the
|
|
file is missing but `permissionsets/` has permsets to assign, don't silently
|
|
skip — scaffold the config or gather equivalent inputs (see step 4).
|
|
- **`data-plan.json`** (optional, in the project's `data/` dir) — presence enables the data step.
|
|
|
|
## Step 1 — Org authentication (always)
|
|
|
|
Unconditional precondition; cannot be skipped. If the org is already connected
|
|
(`sf org display --target-org <org> --json` succeeds), no-op. Otherwise:
|
|
|
|
```bash
|
|
sf org login web --alias <org>
|
|
```
|
|
|
|
A failed login aborts the whole setup before deploy.
|
|
|
|
## Step 2 — Pre-deploy UI bundle build
|
|
|
|
Build **every** UI bundle so `dist/` exists before metadata deploy (UI bundle
|
|
entities deploy the built output). For each bundle dir under `uiBundles/`:
|
|
|
|
```bash
|
|
npm install
|
|
npm run build
|
|
```
|
|
|
|
Run when deploying UI bundles and `dist/` is missing or source changed.
|
|
|
|
## Step 3 — Deploy metadata
|
|
|
|
If self-registration is configured:
|
|
|
|
1. **Deploy license pre-check first** (see `references/license-checks.md`) — it
|
|
blocks the deploy with a clear, license-naming message instead of a cryptic
|
|
failure.
|
|
2. **Add the self-reg profile to `networkMemberGroups`** on the local source —
|
|
apply **Edit A** of `assets/network-selfreg-xml-recipe.md`. This must happen
|
|
**before** this deploy so the profile ships as a recognised site member; do
|
|
NOT deploy the network file on its own here (this deploy ships it). Best-effort
|
|
and idempotent — skip if already a member.
|
|
|
|
Then deploy the whole project (all metadata) by pointing `--source-dir` at the
|
|
resolved source root:
|
|
|
|
```bash
|
|
sf project deploy start --source-dir <sourceRoot> --target-org <org>
|
|
```
|
|
|
|
`<sourceRoot>` is the value from `scripts/get-source-root.sh` (e.g.
|
|
`force-app/main/default`). Always pass `--source-dir`. Do NOT run bare
|
|
`sf project deploy start` with no path: that command relies on source-tracking to
|
|
decide what to deploy, and on an org without source-tracking (most non-scratch
|
|
orgs) it aborts with *"This org does not have source-tracking enabled … specify
|
|
the files or a manifest to deploy."* Passing `--source-dir` deploys the same full
|
|
set on both source-tracked and non-tracked orgs and never emits that hint. If the
|
|
deploy reports conflicts on a source-tracked org, re-run with `--ignore-conflicts`
|
|
— do NOT roll back or reduce the deployed set.
|
|
|
|
Do NOT hand-build a `package.xml`, assemble a `--metadata-dir` mdapi zip, or
|
|
otherwise convert to metadata-format — none of that is needed and it is not part
|
|
of this flow.
|
|
|
|
Timeout 180s. Must complete before permission assignment and schema fetch —
|
|
objects, fields, and permission sets appear in the org only after deploy.
|
|
|
|
## Step 4 — Assign permission sets
|
|
|
|
Discover permission sets under `<packageDir>/main/default/permissionsets/`. If
|
|
none exist and none were passed explicitly, skip.
|
|
|
|
**If permsets exist but `org-setup.config.json` is missing, do NOT silently
|
|
skip.** A missing config makes every discovered permset resolve to `skip`, so
|
|
nothing gets assigned and the later GraphQL schema comes back incomplete (the
|
|
caller lacks FLS). Instead, help the user supply the assignments — either scaffold
|
|
`org-setup.config.json` from `assets/org-setup.config.template.json` or gather the
|
|
per-permset assignee inputs for a one-off run. Full schema + scaffolding flow:
|
|
`references/config-scaffold.md`. Confirm intent before writing the file or
|
|
assigning — don't fabricate assignees.
|
|
|
|
Otherwise assign each per its config assignee (`org-setup.config.json` →
|
|
`permsetAssignments`), where each assignee is one of `currentUser`, `guestUser`,
|
|
or `skip` (default `skip`):
|
|
|
|
```bash
|
|
sf org assign permset --name <permset> --target-org <org> [--on-behalf-of <guestUsername>]
|
|
```
|
|
|
|
- **currentUser** — omit `--on-behalf-of`.
|
|
- **guestUser** — resolve the site's guest username first (see the guest-user
|
|
section in `references/self-registration.md`). If the site can't be derived or
|
|
no guest user resolves, **skip that permset** and record the reason — don't
|
|
abort the others.
|
|
- Treat "Duplicate … PermissionSet" and "not found … target org" as skips, not
|
|
failures.
|
|
|
|
Required so GraphQL introspection returns the correct schema (the caller needs
|
|
FLS on custom fields).
|
|
|
|
## Step 5 — Assign role (config-gated)
|
|
|
|
Run only when `org-setup.config.json` has `role: { assignee: "currentUser",
|
|
roleName: "<UserRole>" }`. Assigning a role to the current user is what lets
|
|
Experience Cloud self-registration work. Idempotent — skip if the user already
|
|
has a role. Detail + exact queries: `references/role-assignment.md`.
|
|
|
|
## Step 6 — Enable self-registration (config-gated) — ask first
|
|
|
|
Run only when `org-setup.config.json` has
|
|
`selfRegistration: { selfRegProfile, accountName }`. **Ask the user before
|
|
running.** Sequence (full detail in `references/self-registration.md`):
|
|
|
|
1. **License pre-check** (soft skip) — if the org lacks a seat on the profile's
|
|
license, warn and skip; it is not a failure. See `references/license-checks.md`.
|
|
2. **Derive the site** — run `scripts/derive-site-name.sh`; it outputs the site
|
|
name (the base name of the single `*.network-meta.xml`) or exits non-zero when
|
|
zero or more than one exist (ambiguous — stop).
|
|
3. **Flip self-reg on + redeploy the network file** — apply **Edit B** of
|
|
`assets/network-selfreg-xml-recipe.md` (set `selfRegistration=true`, inject
|
|
`<selfRegProfile>`), then redeploy only that one file. Idempotent — skip both
|
|
if already enabled. (Edit A, the member-group add, already happened in step 3.)
|
|
4. **Create the Account + NetworkSelfRegistration** — apply
|
|
`assets/network-selfreg.apex` (idempotent; both are query-then-create; run 4a
|
|
and 4b as two separate `sf apex run` invocations).
|
|
|
|
## Step 7 — Data import (presence-driven) — ask first
|
|
|
|
Run `scripts/find-data-plan.sh` first. If it exits non-zero, **skip this step** —
|
|
do not prompt and do not error; just move on to step 8 (a brief "no data plan,
|
|
skipping data import" note is fine). There is nothing to import without a plan.
|
|
On success it prints the plan's path (it searches recursively, so both a
|
|
project-root `data/` and a `<packageDir>/main/default/data/` layout resolve).
|
|
|
|
When it exists: **always ask the user before importing or cleaning data** — it
|
|
deletes existing records first. Apply the verbatim templates; do not improvise
|
|
Apex:
|
|
|
|
1. Run `scripts/find-prep-script.sh`. If it succeeds, it prints the path of a
|
|
`prepare-import-unique-fields.js` that ships with the app — run that first; it
|
|
deduplicates re-runs by stamping stable unique keys on the record files.
|
|
Invoke it the way that copy expects (its interface varies —
|
|
see `references/data-import.md`). If it exits non-zero, there is no prep
|
|
script — skip to the clean step.
|
|
2. **Clean** in reverse plan order (children before parents) with
|
|
`assets/data-delete.apex`.
|
|
3. **Import** in forward plan order with `assets/data-import.apex`, resolving
|
|
`@referenceId` refs and batching by measured size.
|
|
|
|
Protocol, `@referenceId` resolution, measured batching, and the
|
|
`SETUP_RESULT_JSON` parse-and-hard-fail rule: `references/data-import.md`.
|
|
|
|
## Step 8 — GraphQL schema fetch + codegen
|
|
|
|
Run from the UI bundle directory, **after** deploy and permission assignment
|
|
(the schema reflects org state and the caller's FLS):
|
|
|
|
```bash
|
|
npm install
|
|
SF_TARGET_ORG=<org> npm run graphql:schema
|
|
npm run graphql:codegen
|
|
npm run build
|
|
```
|
|
|
|
Detail: `references/graphql.md`. Re-run schema fetch + codegen after every deploy
|
|
that changes objects, fields, or permissions.
|
|
|
|
## Done
|
|
|
|
Setup ends here — the 8 steps above are the complete sequence. Local dev preview
|
|
(`npm run dev:preview`) is a separate developer action, not part of setup; if the
|
|
user asks to preview the site, see `references/dev-preview.md`.
|
|
|
|
## Critical rules
|
|
|
|
- Deploy metadata **before** fetching schema — custom objects/fields appear only
|
|
after deploy.
|
|
- Assign permissions **before** schema fetch — the caller may lack FLS otherwise.
|
|
- Re-run schema fetch + codegen **after every** metadata deploy that changes
|
|
objects, fields, or permissions.
|
|
- Never silently skip permission assignment, self-registration, or data import —
|
|
either the convention file is present (run it, asking first for the destructive
|
|
ones) or it's absent (skip cleanly and say so).
|
|
- Discover the source path from `sfdx-project.json`; never hardcode
|
|
`force-app/main/default`.
|
|
- Apply the `assets/` Apex and XML templates **verbatim** — they encode
|
|
duplicate-rule bypass, `allOrNone=false` deletes, idempotency, and SOQL-safety
|
|
that are easy to get wrong by hand.
|
|
|
|
## Interaction order (summary)
|
|
|
|
1. Authenticate org
|
|
2. Build UI bundles (pre-deploy)
|
|
3. Deploy metadata (deploy-license gate if self-reg configured)
|
|
4. Assign permission sets (config-driven assignee)
|
|
5. Assign role (if configured)
|
|
6. Enable self-registration (if configured — ask first)
|
|
7. Import data (if data plan exists — ask first)
|
|
8. Fetch GraphQL schema + codegen + final build
|