Skip to content
Back to skills

Dataverse Deploy

ASecurity

Deploy solutions, PCF controls, and web resources to Dataverse using PAC CLI

  • 666 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added February 8, 2026
developmentjavascriptgojavashellbashreactnodetestinggitapi

Works with

  • cli
  • api

Security analysis

A93/100
  • highPerforms destructive filesystem operations

Pro shows the line behind each finding and how to fix it

Scanned February 12, 2026

npx -y skills add majiayu000/claude-skill-registry --skill dataverse-deploy --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Dataverse Deploy?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Dataverse Deploy
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/majiayu000-dataverse-deploy/badge)](https://www.skillsdirectory.com/skills/majiayu000-dataverse-deploy)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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` | `<Version>X.Y.Z</Version>` |
| 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` → `<Version>X.Y.Z</Version>`
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 `<platform-library>` elements:

```xml
<resources>
  <code path="index.ts" order="1" />
  <!-- Host-provided: DO NOT bundle React/Fluent -->
  <platform-library name="React" version="16.14.0" />
  <platform-library name="Fluent" version="9.46.2" />
</resources>
```

#### 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
<CustomControls />
<WebResources />
```

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
<CustomControls>
  <CustomControl>
    <Name>sprk_Spaarke.Controls.UniversalDocumentUpload</Name>
    <FileName>/Controls/sprk_Spaarke.Controls.UniversalDocumentUpload/ControlManifest.xml</FileName>
  </CustomControl>
</CustomControls>
```

**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 `<CustomControls />` 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

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.