---
description: Deploy solutions, PCF controls, and web resources to Dataverse using PAC CLI
tags: [deploy, dataverse, power-platform, pac-cli, pcf, solutions]
techStack: [dataverse, pcf-framework, power-platform]
appliesTo: ["**/Solutions/**", "**/pcf/**", "deploy to dataverse", "pac pcf push"]
alwaysApply: false
---
# Dataverse Deploy
> **Category**: Operations
> **Last Updated**: January 19, 2026
> **Primary Guide**: [`docs/guides/PCF-DEPLOYMENT-GUIDE.md`](../../../docs/guides/PCF-DEPLOYMENT-GUIDE.md)
---
## Critical Rules
**These rules are MANDATORY. See [PCF-DEPLOYMENT-GUIDE.md](../../../docs/guides/PCF-DEPLOYMENT-GUIDE.md) for full details.**
### MUST:
- ✅ **MUST** use unmanaged solution unless explicitly told to use managed (ADR-022)
- ✅ **MUST** use Dataverse publisher `Spaarke` with prefix `sprk_`
- ✅ **MUST** rebuild fresh every deployment (`npm run build:prod`)
- ✅ **MUST** copy ALL 3 files to Solution folder (bundle.js, ControlManifest.xml, styles.css)
- ✅ **MUST** update version in ALL 5 locations
- ✅ **MUST** include `.js` and `.css` entries in `[Content_Types].xml`
- ✅ **MUST** use `pack.ps1` script (creates forward slashes in ZIP)
- ✅ **MUST** disable/restore CPM around PAC commands
### 🚨 CRITICAL - Control Version is the Cache Key:
- ✅ **MUST** update `ControlManifest.Input.xml` version FIRST - this is what Dataverse uses to detect updates
- ✅ **MUST** increment the control version for EVERY deployment, not just the solution version
- ⚠️ If you only update `solution.xml` but not `ControlManifest.Input.xml`, Dataverse will NOT update the control
### NEVER:
- ❌ **NEVER** use managed solution unless explicitly told - unmanaged is the default
- ❌ **NEVER** use or create a new publisher - always use `Spaarke` (`sprk_`)
- ❌ **NEVER** reuse old solution ZIPs - always pack fresh
- ❌ **NEVER** use `Compress-Archive` - creates backslashes, breaks import
- ❌ **NEVER** skip copying files - stale bundles cause silent failures
### ⚠️ AVOID (but valid fallback):
- ⚠️ **AVOID** `pac pcf push` for production - rebuilds in dev mode, larger bundles
- ✅ **BUT** use `pac pcf push --publisher-prefix sprk` when solution imports empty (see Troubleshooting)
---
## Deployment Workflow
**Follow [PCF-DEPLOYMENT-GUIDE.md](../../../docs/guides/PCF-DEPLOYMENT-GUIDE.md) for complete details.**
### Step 1: Build Fresh
```bash
cd src/client/pcf/{ControlName}
rm -rf out/ bin/
npm run build:prod
# Verify size (~200-400KB, NOT 8MB)
ls -la out/controls/control/bundle.js
```
### Step 2: Update Version (5 Locations)
| # | File | Update |
|---|------|--------|
| 1 | `control/ControlManifest.Input.xml` | `version="X.Y.Z"` |
| 2 | `control/{Component}.tsx` | UI version footer |
| 3 | `Solution/solution.xml` | `X.Y.Z` |
| 4 | `Solution/Controls/.../ControlManifest.xml` | `version="X.Y.Z"` |
| 5 | `Solution/pack.ps1` | `$version = "X.Y.Z"` |
### Step 3: Copy Fresh Build to Solution
```bash
cp out/controls/control/bundle.js \
out/controls/control/ControlManifest.xml \
out/controls/control/styles.css \
Solution/Controls/sprk_Spaarke.Controls.{ControlName}/
```
### Step 4: Pack and Import
```bash
# Disable CPM
mv /c/code_files/spaarke-wt-ai-rag-pipeline/Directory.Packages.props{,.disabled}
# Pack (creates fresh ZIP with forward slashes)
cd Solution && powershell -ExecutionPolicy Bypass -File pack.ps1
# Import
pac solution import --path bin/{SolutionName}_vX.Y.Z.zip --publish-changes
# Restore CPM
mv /c/code_files/spaarke-wt-ai-rag-pipeline/Directory.Packages.props{.disabled,}
```
### Step 5: Verify
```bash
pac solution list | grep -i "{SolutionName}"
```
Hard refresh browser (`Ctrl+Shift+R`) and verify version footer.
> **⚠️ Why avoid `pac pcf push` for production?** It rebuilds in development mode, ignoring production optimizations. Bundle size increases from ~300KB to 8MB. Tree-shaking is lost. **However**, it is a valid fallback when `pac solution pack` imports an empty solution (see "Solution Imports But Is Empty" in Troubleshooting).
---
## 🚨 Dataverse Control Caching (READ THIS)
**Dataverse caches PCF controls aggressively.** If your deployment seems to succeed but the browser still shows old behavior, the cache wasn't busted.
### Root Cause
Dataverse determines whether to update a control based on the **control manifest version** (`ControlManifest.Input.xml`), NOT just the solution version. If you:
- ✅ Update `solution.xml` version
- ❌ Forget to update `ControlManifest.Input.xml` version
...then Dataverse sees the same control version and **silently keeps the old bundle**.
### The Fix: Version Order Matters
**Always update versions in this order:**
1. **FIRST**: `control/ControlManifest.Input.xml` → `version="X.Y.Z"` (THIS IS THE KEY)
2. Then rebuild (this propagates to `out/controls/control/ControlManifest.xml`)
3. Copy all 3 files to Solution folder
4. Update `Solution/solution.xml` → `X.Y.Z`
5. Update `Solution/pack.ps1` → `$version = "X.Y.Z"`
### Nuclear Option: Delete and Reimport
If you've deployed but the control is still cached:
```bash
# Delete the solution completely from Dataverse
pac solution delete --solution-name {SolutionName}
# Reimport fresh
pac solution import --path bin/{SolutionName}_vX.Y.Z.zip --publish-changes
# Verify
pac solution list | grep -i "{SolutionName}"
```
Then hard refresh browser (`Ctrl+Shift+R`).
### Symptoms of Caching Issue
| Symptom | Likely Cause |
|---------|--------------|
| `pac solution import` succeeds but UI unchanged | Control version not incremented |
| `pac solution list` shows new version but old behavior | Control manifest version mismatch |
| Hard refresh doesn't help | Need delete + reimport |
| Same control works in one browser but not another | Browser cache - try incognito |
---
## Decision Tree: Which Workflow?
```
Is the PCF embedded in a Custom Page?
├── YES → See "Custom Page Deployment" section in guide
└── NO → Use "Deployment Workflow" above (build → copy → pack.ps1 → import)
```
**Primary Guide:** [`docs/guides/PCF-DEPLOYMENT-GUIDE.md`](../../../docs/guides/PCF-DEPLOYMENT-GUIDE.md) - Complete workflow with version management and troubleshooting.
---
## Purpose
Deploy Dataverse components using PAC CLI following the [PCF-DEPLOYMENT-GUIDE.md](../../../docs/guides/PCF-DEPLOYMENT-GUIDE.md). This skill handles authentication, Central Package Management conflicts, and solution import issues.
---
## Best Practices
| Practice | Implementation |
|----------|----------------|
| **Always use unmanaged** | Never export/pack as managed unless user explicitly requests |
| **Always use Spaarke publisher** | Never create a new publisher - use `Spaarke` (`sprk_`) |
| **Always build fresh** | Run `npm run build:prod`, never reuse old artifacts |
| **Always use pack.ps1** | Never use `Compress-Archive` (creates backslashes) |
| **Version footer** | Every PCF MUST display `vX.Y.Z • Built YYYY-MM-DD` in the UI |
| **Version bumping** | Increment version in ALL 5 locations |
| **Verify deployment** | ALWAYS run `pac solution list` after import to confirm version |
| **Use React 16 APIs** | Dataverse provides React 16.14.0 - see ADR-022 |
### Key Guidance
- **Prefer pack.ps1 workflow** for production - build → copy files → pack.ps1 → import
- **Use `pac pcf push` as fallback** when solution imports empty (Customizations.xml issue)
- **Version Locations:** Update ALL 5: (1) ControlManifest.Input.xml, (2) UI footer, (3) Solution.xml, (4) extracted ControlManifest.xml, (5) pack.ps1
- **Full Guide:** See [`docs/guides/PCF-DEPLOYMENT-GUIDE.md`](../../../docs/guides/PCF-DEPLOYMENT-GUIDE.md) for complete workflow
---
## Prerequisites Check
Before ANY deployment operation:
```bash
# 1. Check PAC CLI is installed
pac --version
# 2. Check authentication status
pac auth list
# 3. Verify active connection points to correct environment
# Look for: Active = *, Environment URL matches expected target
```
### Expected Output
```
Index Active Kind Name Cloud Type Environment Environment Url
[1] UNIVERSAL dev Public User HIPC DEV 2 https://hipc-dev-2.crm.dynamics.com/
[2] * UNIVERSAL prod Public User SPAARKE DEV 1 https://spaarkedev1.crm.dynamics.com/
```
### If No Active Auth
```bash
# Create new authentication
pac auth create --environment "https://YOUR-ENV.crm.dynamics.com"
# Or select existing profile
pac auth select --index 1
```
---
## Additional Scenarios
### Scenario 1: PCF Control Deployment
**Use the "Deployment Workflow" section above.** Follow [PCF-DEPLOYMENT-GUIDE.md](../../../docs/guides/PCF-DEPLOYMENT-GUIDE.md) for the complete workflow:
1. Build fresh (`npm run build:prod`)
2. Update version in ALL 5 locations
3. Copy ALL 3 files to Solution folder
4. Pack with `pack.ps1` (NOT `Compress-Archive`)
5. Import with `pac solution import`
6. Verify with `pac solution list`
---
### Scenario 1b: PCF Control with Platform Libraries (Large Controls)
**Use when**: PCF bundle exceeds 5MB due to bundled React/Fluent UI.
If your bundle is > 1MB, you're likely bundling React and Fluent UI. Use **platform libraries** so Dataverse provides these at runtime.
#### Check Bundle Size
```bash
ls -la out/controls/*/bundle.js
# Should be 300-500KB with platform libraries, 5MB+ without
```
#### Fix ControlManifest.Input.xml
Add `` elements:
```xml
```
#### Create featureconfig.json (CRITICAL)
pcf-scripts requires a feature flag to externalize ReactDOM:
```json
{
"pcfReactPlatformLibraries": "on"
}
```
Without this file, React is externalized but ReactDOM is still bundled, causing React version mismatch errors at runtime.
#### Enable Custom Webpack for Icon Tree-Shaking (CRITICAL for large bundles)
If your bundle is still large (>500KB) after adding platform libraries, `@fluentui/react-icons` is likely not tree-shaking. The full icon library is ~6.8MB.
**Solution**: Enable custom webpack with `sideEffects: false` for icons:
1. **Update featureconfig.json** (add `pcfAllowCustomWebpack`):
```json
{
"pcfReactPlatformLibraries": "on",
"pcfAllowCustomWebpack": "on"
}
```
2. **Create webpack.config.js** in control root:
```javascript
// Custom webpack configuration for PCF
// Enables tree-shaking for @fluentui/react-icons
module.exports = {
optimization: {
usedExports: true,
sideEffects: true,
innerGraph: true,
providedExports: true
},
module: {
rules: [
{
// Mark @fluentui/react-icons as side-effect-free
test: /[\\/]node_modules[\\/]@fluentui[\\/]react-icons[\\/]/,
sideEffects: false
}
]
}
};
```
**Result**: Bundle size typically drops from 5-9MB to 200-400KB.
> **⚠️ NOTE**: `pac pcf push` rebuilds in development mode, ignoring these optimizations. Use the pack.ps1 workflow instead to preserve production build.
#### Fix package.json
Move React/Fluent to `devDependencies` (type-checking only):
```json
{
"devDependencies": {
"@types/react": "^16.14.0",
"@types/react-dom": "^16.9.0",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"@fluentui/react-components": "^9.46.0"
}
}
```
> **Note**: Use React 16 types to match the platform runtime. See [ADR-022](../../adr/ADR-022-pcf-platform-libraries.md).
**Remove** from `dependencies`: any `react`, `react-dom`, or `@fluentui/react-*` packages (keep in devDependencies only).
> **Full details**: See [`docs/guides/PCF-DEPLOYMENT-GUIDE.md`](../../../docs/guides/PCF-DEPLOYMENT-GUIDE.md)
---
### Scenario 1c: PCF Control in Custom Page (COMPLEX)
**Use when**: PCF control is embedded in a Canvas App Custom Page.
> **⚠️ WARNING**: This is the most complex deployment scenario. When a PCF is used inside a Custom Page, THREE version locations must stay synchronized.
**See detailed guide**: [`docs/guides/PCF-DEPLOYMENT-GUIDE.md`](../../../docs/guides/PCF-DEPLOYMENT-GUIDE.md) (Custom Page section)
#### Quick Summary
| Location | Purpose | Updated By |
|----------|---------|------------|
| **Dataverse Registry** | Master version | `pac pcf push` or Solution Import |
| **Solution Controls Folder** | Exported artifact | Manual copy |
| **Canvas App Embedded** | Runtime bundle | Manual copy + `pac canvas pack` |
#### The Problem
When you open a Custom Page in Power Apps Studio, it may **downgrade** your PCF if the Dataverse Registry has an older version.
#### Key Rules
1. **ALWAYS update Dataverse Registry FIRST** before opening Power Apps Studio
2. **ALWAYS copy bundle to BOTH locations** (Controls folder AND Canvas App embedded)
3. **NEVER open Power Apps Studio** until all three locations are synchronized
---
### Scenario 1d: PCF Production Release (Full Solution Workflow)
**Use when**: Deploying a PCF update with proper version tracking for production.
> **⚠️ CRITICAL**: `pac pcf push` does NOT update your named solution's version. Use this workflow for production releases.
**See detailed guide**: [`docs/guides/PCF-DEPLOYMENT-GUIDE.md`](../../../docs/guides/PCF-DEPLOYMENT-GUIDE.md)
#### Why This Workflow?
| Method | Updates Dataverse Control | Updates Named Solution Version | Best For |
|--------|--------------------------|-------------------------------|----------|
| `pac pcf push` | ✅ Yes | ❌ No (creates temp solution) | Dev testing |
| Solution Import | ✅ Yes | ✅ Yes | Production releases |
#### Quick Steps
```bash
# 1. Build
cd src/client/pcf/{ControlName}
npm run build:prod
# 2. Update version in 4 locations (manual)
# 3. Copy bundle to solution folder
cp out/controls/control/bundle.js \
infrastructure/dataverse/solutions/{Solution}/Controls/{namespace}.{ControlName}/
# 4. Pack and import
pac solution pack --zipfile Solution_vX.Y.Z.zip --folder {Solution}
pac solution import --path Solution_vX.Y.Z.zip --publish-changes
# 5. Verify
pac solution list | grep -i "{SolutionName}"
```
---
### Scenario 2: Solution Export
**Use when**: Backing up or extracting a solution for modification.
```bash
# List available solutions
pac solution list
# Export unmanaged solution (ALWAYS use unmanaged)
pac solution export --name "{SolutionName}" --path "./{SolutionName}.zip" --managed false
```
> **⚠️ NEVER export as managed** unless the user explicitly requests it. Managed solutions have caused issues in past projects.
---
### Scenario 3: Solution Import
**Use when**: Deploying a solution package to an environment.
```bash
# Import and publish in one step
pac solution import --path "./{SolutionName}.zip" --publish-changes
# Force import (overwrites conflicts)
pac solution import --path "./{SolutionName}.zip" --force-overwrite --publish-changes
```
#### Post-Import Verification
```bash
# Check solution was imported
pac solution list | grep -i "{SolutionName}"
# If not auto-published, publish manually
pac solution publish
```
---
### Scenario 4: Web Resource Deployment
**Use when**: Deploying JavaScript, CSS, or image files.
Include web resources in a solution and use Scenario 3.
```bash
# Export solution containing web resources
pac solution export --name "SpaarkeCore" --path "./SpaarkeCore.zip" --managed false
# Extract, modify, repack, import
unzip SpaarkeCore.zip -d SpaarkeCore_extracted
# ... modify files in WebResources folder ...
pac solution pack --zipfile SpaarkeCore_modified.zip --folder SpaarkeCore_extracted
pac solution import --path SpaarkeCore_modified.zip --publish-changes
```
---
### Scenario 5: Publish Customizations
**Use when**: Making customizations visible to users.
```bash
# Publish all customizations
pac solution publish
# Or use publish-all for everything
pac solution publish-all
```
---
## Conventions
### Publisher
**Always use `Spaarke` publisher with prefix `sprk_`** - NEVER create a new publisher.
| Setting | Value |
|---------|-------|
| Unique Name | `Spaarke` |
| Prefix | `sprk` |
| Option Value Prefix | `65949` |
**Naming examples:**
- PCF controls: `sprk_Spaarke.Controls.{ControlName}`
- Web resources: `sprk_FileName.js`
- Entities: `sprk_entityname`
- Fields: `sprk_fieldname`
### Working Directories
| Component Type | Directory |
|----------------|-----------|
| PCF Controls | `src/client/pcf/{ControlName}` |
| Web Resources JS | `src/client/webresources/js/` |
| Ribbon XML | `infrastructure/dataverse/ribbon/` |
| Solutions | `infrastructure/dataverse/solutions/` |
---
## Error Handling
| Error | Cause | Solution |
|-------|-------|----------|
| `No active authentication profile` | Not logged in | Run `pac auth create --environment "URL"` |
| `NU1008: Projects that use central package version management` | Directory.Packages.props conflict | Disable CPM before PAC commands |
| `Unable to remove directory "obj\Debug\Metadata"` | File lock during cleanup | **Harmless** - import the packed solution directly |
| `Solution not found` | Wrong solution name | Run `pac solution list` to find exact name |
| `Publisher not found` | Wrong publisher prefix | Use `--publisher-prefix sprk` |
| `Import failed: Dependency not found` | Missing dependent solution | Import dependencies first |
| `File exceeds 5MB limit` | React/Fluent bundled | Use platform libraries (Scenario 1b) |
| PCF version regresses in Custom Page | Registry has older version | Update registry FIRST (Scenario 1c) |
| `PowerAppsToolsTemp` solutions appear | Created by `pac pcf push` | Delete after deployment if needed |
| `Cannot create property '_updatedFibers'` | Using React 18 APIs with React 16 runtime | Use `ReactDOM.render()`, not `createRoot()` - see ADR-022 |
| `createRoot is not a function` | Importing from `react-dom/client` | Import from `react-dom` instead |
| Solution zip not created | pack.ps1 failed | Check pack.ps1 script exists and run manually |
| `Orphaned component blocking deployment` | Namespace changed or old controls exist | Delete orphaned controls via Web API (see below) |
| `CustomControls Source File styles.css does not exist` | styles.css not copied to solution folder | Copy ALL 3 files to Solution folder |
| Solution import succeeds but UI shows old behavior | Control manifest version not updated | **Update ControlManifest.Input.xml version FIRST**, rebuild, then deploy |
| Deployment seems stuck on old version | Dataverse control cache | Delete solution with `pac solution delete`, then reimport fresh |
| **Solution imports but shows 0 components** | Empty `Customizations.xml` | Use `pac pcf push` fallback (see below) |
### 🚨 Solution Imports But Is Empty (0 Components)
**Symptoms:**
- `pac solution import` succeeds
- `pac solution list` shows solution with correct version
- But solution in portal shows "All: 0" - no components
- PCF control doesn't appear or doesn't update
**Root Cause:**
The `Customizations.xml` file in your Solution folder has **empty component sections**:
```xml
```
When you run `pac solution pack`, it packs exactly what's in `Customizations.xml`. If the component references are missing, you get an empty solution that imports but does nothing.
**Solution - Use `pac pcf push` as Fallback:**
```bash
cd src/client/pcf/{ControlName}
# pac pcf push generates proper Customizations.xml automatically
pac pcf push --publisher-prefix sprk
# If you get file lock error during cleanup, ignore it - solution is already packed
# Import the generated solution
pac solution import --path "out/PowerAppsTools_sprk/bin/Debug/PowerAppsTools_sprk.zip" --publish-changes
# Verify
pac solution list | grep -i "{ControlName}"
```
**Why This Works:**
`pac pcf push` generates a proper `Customizations.xml` with component references:
```xml
sprk_Spaarke.Controls.UniversalDocumentUpload
/Controls/sprk_Spaarke.Controls.UniversalDocumentUpload/ControlManifest.xml
```
**Prevention:**
After using `pac pcf push` successfully:
1. Examine the generated `Customizations.xml` and update your Solution folder's version to match the structure
2. **IMPORTANT**: `pac pcf push` bypasses the Solution folder entirely - it builds from source and deploys directly to Dataverse. **You must still copy the fresh bundle.js and ControlManifest.xml to your Solution folder** to keep it in sync for future `pac solution pack` deployments.
```bash
# After pac pcf push, sync Solution folder:
cp out/controls/control/bundle.js \
out/controls/control/ControlManifest.xml \
Solution/src/WebResources/sprk_Spaarke.Controls.{ControlName}/
```
This prevents the Solution folder from becoming stale and ensures future solution imports work correctly.
### Orphaned Control Cleanup
When namespace changes (e.g., `Spaarke.PCF` → `Spaarke.Controls`) or old deployments exist, orphaned controls can block new deployments.
**Symptoms:**
- Deployment fails with duplicate component errors
- Multiple versions of same control in solution list
- "Component with same name already exists" errors
**Solution - Delete via Web API:**
```bash
# 1. Find the orphaned control's ID
# Use Dataverse Web API or Advanced Find
# 2. Delete using PAC CLI or Web API
pac org fetch --filter "customcontrolid eq 'GUID-HERE'"
# 3. Or use Power Platform Admin Center:
# - Go to Environments → Your Environment → Settings → Solutions
# - Find and delete orphaned components
```
**Prevention:**
- Always use consistent namespace (e.g., `Spaarke.Controls`)
- Delete old solutions before changing namespace
- Use `pac solution delete` to cleanly remove old solutions
---
## Quick Reference Commands
```bash
# Authentication
pac auth list # Show all auth profiles
pac auth create --environment "URL" # Create new profile
pac auth select --index N # Switch profile
# Solutions
pac solution list # List all solutions
pac solution export --name X --path Y # Export solution (add --managed false)
pac solution import --path Y # Import solution
pac solution publish # Publish customizations
# PCF Controls - Use pack.ps1 workflow (see Deployment Workflow above)
npm run build:prod # Build control
powershell -File Solution/pack.ps1 # Pack solution (NOT Compress-Archive)
pac solution import --path bin/X.zip # Import solution
# Troubleshooting
pac org who # Show current org info
pac solution check --path Y # Validate solution before import
```
---
## Related Skills
- `ribbon-edit` - Ribbon customizations use solution export/import workflow
- `spaarke-conventions` - Naming conventions for all Dataverse components
- `adr-aware` - ADR-006 governs PCF control patterns, ADR-022 governs React version
- `ci-cd` - CI/CD pipeline status and automated deployment workflows
---
## CI/CD Integration
### Automated Plugin Deployment via GitHub Actions
Plugin deployments can be automated via the `deploy-staging.yml` workflow:
| Workflow | Trigger | What It Deploys |
|----------|---------|-----------------|
| `deploy-staging.yml` | Auto (after CI passes on master) or Manual | Dataverse plugins via PAC CLI |
### Workflow Plugin Deployment
The `deploy-plugins` job in `deploy-staging.yml`:
1. Downloads build artifacts from CI
2. Authenticates with Power Platform using service principal
3. Deploys plugin assembly via PAC CLI
```yaml
pac auth create --url $POWER_PLATFORM_URL --applicationId $CLIENT_ID --clientSecret $SECRET
pac plugin push --path ./artifacts/publish/plugins/Spaarke.Plugins.dll
```
### When to Use Manual vs Automated
| Scenario | Use |
|----------|-----|
| Plugin code changes merged to master | Automated (`deploy-staging.yml`) |
| PCF control iterative development | Manual (this skill - Quick Dev Deploy) |
| Production solution release | Manual (this skill - Scenario 1d) |
| Custom Page updates | Manual (this skill - Scenario 1c) |
| Emergency hotfix | Manual (this skill) |
### Required Secrets for Automated Deployment
| Secret | Purpose |
|--------|---------|
| `POWER_PLATFORM_URL` | Dataverse environment URL |
| `POWER_PLATFORM_CLIENT_ID` | Service principal app ID |
| `POWER_PLATFORM_CLIENT_SECRET` | Service principal secret |
### Monitor Automated Deployments
```powershell
# View staging deployment status
gh run list --workflow=deploy-staging.yml
# View specific deployment run
gh run view {run-id}
# Check deploy-plugins job
gh run view {run-id} --log --job=deploy-plugins
```
### Manual Trigger of Plugin Deployment
```powershell
# Trigger staging deployment with plugins
gh workflow run deploy-staging.yml -f deploy_plugins=true
```
## Related ADRs
| ADR | Relevance |
|-----|-----------|
| [ADR-006](.claude/adr/ADR-006-pcf-over-webresources.md) | PCF over legacy webresources |
| [ADR-022](.claude/adr/ADR-022-pcf-platform-libraries.md) | React 16 compatibility (CRITICAL) |
| [ADR-021](.claude/adr/ADR-021-fluent-design-system.md) | Fluent UI v9 design system |
---
## Resources
| Resource | Purpose |
|----------|---------|
| [`docs/guides/PCF-DEPLOYMENT-GUIDE.md`](../../../docs/guides/PCF-DEPLOYMENT-GUIDE.md) | **Primary guide** - Consolidated PCF deployment workflow |
---
## Tips for AI
**🚨 CRITICAL: Control Manifest Version is the Cache Key**
When deploying PCF updates, the #1 cause of "deployment succeeded but nothing changed" is forgetting to update the control manifest version. Follow this exact order:
1. **FIRST**: Update `control/ControlManifest.Input.xml` version attribute
2. **THEN**: Rebuild with `npm run build:prod` (or `pcf-scripts build --buildMode production`)
3. **THEN**: Copy ALL 3 files to Solution folder
4. **THEN**: Update `solution.xml` and `pack.ps1` versions
5. **THEN**: Pack and import
**If the user reports the control isn't updating after deployment:**
1. Check if `ControlManifest.Input.xml` version was incremented
2. If not, increment it, rebuild, and redeploy
3. If still stuck, use the nuclear option: `pac solution delete` then reimport
**Never assume** that updating `solution.xml` alone will bust the cache. The control manifest version is what Dataverse checks.
---
**🚨 CRITICAL: Solution Imports But Shows 0 Components**
If `pac solution pack` + `pac solution import` succeeds but the solution is empty in the portal:
1. **Root cause**: `Customizations.xml` has empty `` section
2. **Fix**: Use `pac pcf push --publisher-prefix sprk` as fallback
3. **Why it works**: `pac pcf push` auto-generates proper component references
4. **File lock error during cleanup is harmless** - solution is already packed, import it directly
5. **CRITICAL**: After `pac pcf push`, **sync the Solution folder** - `pac pcf push` bypasses it entirely, leaving it stale:
```bash
cp out/controls/control/bundle.js out/controls/control/ControlManifest.xml \
Solution/src/WebResources/sprk_Spaarke.Controls.{ControlName}/
```
6. **Prevention**: Copy the generated `Customizations.xml` structure to your Solution folder for future `pac solution pack` deployments