feat: add ppds plugins command group for plugin registration management#57
feat: add ppds plugins command group for plugin registration management#57joshsmithxrm merged 8 commits intomainfrom
Conversation
Implements #39 - Plugin registration management commands: - `ppds plugins extract` - Extract [PluginStep]/[PluginImage] attributes from assembly or nupkg - `ppds plugins deploy` - Deploy registrations to Dataverse with --solution, --clean, --what-if - `ppds plugins diff` - Compare config vs environment state, detect drift - `ppds plugins list` - List registered plugins in environment - `ppds plugins clean` - Remove orphaned registrations not in config Key implementation details: - Uses MetadataLoadContext for safe, read-only assembly reflection - Supports both classic assemblies (.dll) and NuGet plugin packages (.nupkg) - SDK-only Dataverse operations via IOrganizationService - JSON output for all commands (--json flag for tool integration) - NuGet extractor scans all DLLs to find those with plugin attributes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Plugin commands (diff, deploy, list, clean) now correctly get ServiceClient from IDataverseConnectionPool instead of trying to resolve it directly from DI container. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Adds support for filtering by plugin packages (NuGet) in addition to classic assemblies (DLL): - `--assembly, -a` filters pluginassembly entities - `--package, -pkg` filters pluginpackage entities When no filter is specified, both assemblies and packages are listed. Specifying one filter excludes the other. Also fixes the package → type query to correctly traverse: pluginpackage → pluginassembly (via packageid) → plugintype 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…ins list Step registration now supports: - deployment: ServerOnly (default), Offline, or Both - runAsUser: CallingUser (default) or systemuser GUID - description: step documentation - asyncAutoDelete: auto-delete async jobs on success List command changes: - Package hierarchy: Package → Assembly → Type → Step → Image - New JSON output model with PackageOutput containing Assemblies - Console output shows non-default step options on one line - Summary includes types/images with proper pluralization 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extract command changes: - Add --solution (-s) option to set solution on initial extract - Add --force (-f) option to skip merge and overwrite - When file exists, merge preserves deployment settings: - Assembly: solution, unknown fields - Step: deployment, runAsUser, description, asyncAutoDelete - All levels: ExtensionData for unknown JSON properties Config models now include [JsonExtensionData] to preserve unknown properties during round-trip serialization, enabling forward compatibility with future schema changes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Document all plugin command features: - Full step registration fields (deployment, runAsUser, etc.) - Extract merge behavior and --solution/--force options - List command hierarchy and filtering enhancements - Connection pooling for improved performance 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ration BREAKING CHANGE: SecureConfiguration removed from PluginStepAttribute. Secrets should never be committed to source control. Use environment variables, Azure Key Vault, or configure via Plugin Registration Tool. Added to PluginStepAttribute: - Description: documents what the step does - AsyncAutoDelete: auto-delete async jobs on success Updated: - AssemblyExtractor reads new attributes - Tests updated for new properties - PPDS.Plugins v2.0.0 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Summary of ChangesHello @joshsmithxrm, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces a comprehensive plugins command group to the CLI, enabling a code-first workflow for Dataverse plugin registration. The new commands (extract, deploy, diff, list, clean) are well-structured and cover the entire lifecycle of plugin management from source code attributes. The changes also include a necessary breaking change to PluginStepAttribute to improve security by removing SecureConfiguration. The implementation is robust, leveraging MetadataLoadContext for safe assembly inspection and providing features like merging configurations and what-if modes. My review focuses on a few key areas for performance optimization in the deploy and clean commands, and a best-practice suggestion for asynchronous operations in the PluginRegistrationService. All comments align with best practices and do not contradict any established rules.
There was a problem hiding this comment.
CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
There was a problem hiding this comment.
Pull request overview
This PR adds a comprehensive ppds plugins command group to enable code-first plugin deployment workflows for Dataverse, supporting both classic assemblies and NuGet packages. The implementation includes extraction of plugin metadata from attributes, deployment to environments, drift detection, listing, and cleanup of orphaned registrations.
Key Changes
- Breaking change in PPDS.Plugins v2.0.0: Removes
SecureConfigurationproperty fromPluginStepAttribute(security risk), addsDescriptionandAsyncAutoDeleteproperties - PPDS.Cli v1.0.0-beta.2: Adds complete plugin command group with 5 subcommands (extract, deploy, diff, list, clean)
- Metadata extraction: Uses
MetadataLoadContextfor safe assembly reflection without loading dependencies into current AppDomain
Reviewed changes
Copilot reviewed 23 out of 23 changed files in this pull request and generated 16 comments.
Show a summary per file
| File | Description |
|---|---|
| src/PPDS.Plugins/Attributes/PluginStepAttribute.cs | Removes SecureConfiguration, adds Description and AsyncAutoDelete properties |
| src/PPDS.Plugins/CHANGELOG.md | Documents breaking v2.0.0 release |
| tests/PPDS.Plugins.Tests/PluginStepAttributeTests.cs | Updates tests for removed SecureConfiguration property |
| src/PPDS.Cli/Program.cs | Registers new PluginsCommandGroup |
| src/PPDS.Cli/PPDS.Cli.csproj | Adds PPDS.Plugins reference and System.Reflection.MetadataLoadContext dependency |
| src/PPDS.Cli/Plugins/Models/PluginRegistrationConfig.cs | Defines JSON config models with JsonExtensionData for forward compatibility |
| src/PPDS.Cli/Plugins/Extraction/AssemblyExtractor.cs | Extracts plugin attributes from assemblies using MetadataLoadContext |
| src/PPDS.Cli/Plugins/Extraction/NupkgExtractor.cs | Extracts plugin metadata from NuGet packages |
| src/PPDS.Cli/Plugins/Registration/PluginRegistrationService.cs | Service for querying and managing plugin registrations in Dataverse |
| src/PPDS.Cli/Commands/Plugins/PluginsCommandGroup.cs | Command group definition with shared options |
| src/PPDS.Cli/Commands/Plugins/ExtractCommand.cs | Extracts attributes to JSON with merge support |
| src/PPDS.Cli/Commands/Plugins/DeployCommand.cs | Deploys registrations to Dataverse with upsert logic |
| src/PPDS.Cli/Commands/Plugins/DiffCommand.cs | Detects drift between config and environment |
| src/PPDS.Cli/Commands/Plugins/ListCommand.cs | Lists plugin hierarchy from environment |
| src/PPDS.Cli/Commands/Plugins/CleanCommand.cs | Removes orphaned registrations |
| src/PPDS.Cli/CHANGELOG.md | Documents beta.2 release with plugin features |
| tests/PPDS.Cli.Tests/Plugins/Models/PluginRegistrationConfigTests.cs | Tests for config model serialization |
| tests/PPDS.Cli.Tests/Commands/Plugins/*.cs | Unit tests for command structure and parsing |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Performance improvements: - DeployCommand: Use Dictionary for O(1) orphan step lookup instead of O(n*m) re-querying - CleanCommand: Calculate remaining step count in memory instead of re-querying - PluginRegistrationService: Use native IOrganizationServiceAsync2 when available Bug fixes: - DeployCommand: Skip image query in what-if mode for new steps (stepId doesn't exist) - ExtractCommand: Remove redundant !json check - CleanCommand: Remove unused configuredTypeNames variable - DiffCommand: Remove unused existingTypeMap variable Code quality: - AssemblyExtractor: Use LINQ FirstOrDefault for cleaner StepId lookup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…tch qs security vulnerability (#57) * chore(deps): bump actions/upload-artifact from 5 to 6 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](actions/upload-artifact@v5...v6) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * fix(deps): patch qs security vulnerability Ran npm audit fix to update qs from 6.14.0 to 6.14.1. --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Josh Smith <6895577+joshsmithxrm@users.noreply.github.com>
- Add Connection References + Environment Variables to sidebar Tools (#56, #57) - Standardize command titles: "Open Data Explorer", "Open Plugin Traces" (#51) - Remove text-transform: uppercase from 5 panel CSS files for Title Case headers (#10) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add Connection References + Environment Variables to sidebar Tools (#56, #57) - Standardize command titles: "Open Data Explorer", "Open Plugin Traces" (#51) - Remove text-transform: uppercase from 5 panel CSS files for Title Case headers (#10) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cdp): use pwsh Expand-Archive for VSIX extraction on Windows bsdtar interprets the C: drive prefix as a remote host, breaking VSIX extraction in --vsix mode. Use PowerShell's Expand-Archive on Windows; keep tar on other platforms. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * plan: extension UX audit fixes — 28 findings across 10 phases Side-by-side comparison of legacy v0.3.4 vs new extension produced 55 findings. After triage: 28 fixes, 22 keep-new-behavior, 5 deferred. Organized into 10 phases (1 cross-cutting + 9 per-panel) for parallel agent execution. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(ext): Phase 1 — cross-cutting infrastructure fixes - Add Connection References + Environment Variables to sidebar Tools (#56, #57) - Standardize command titles: "Open Data Explorer", "Open Plugin Traces" (#51) - Remove text-transform: uppercase from 5 panel CSS files for Title Case headers (#10) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(ext): Phases 2-9 — UX audit fixes across all 8 panels Solutions (#1,#2,#3,#5): Include Managed toggle, sort controls, Visible/API Managed fields, Maker Portal button, isVisible/isApiManaged end-to-end C# support. Import Jobs (#7,#8,#9): Search bar, Operation Context column (end-to-end C#→TS), record count with filtered status. Plugin Traces (#11,#12,#17,#18,#55): CRITICAL Trace Level dropdown fix, Maker Portal URL fix (Dynamics 365 classic), status text labels, record count, search bar. Web Resources (#19): Restored Created By and Created On columns. Connection References (#23,#24,#25,#28,#55): Expandable flow/connection detail with chevron toggle, ISO timestamp formatting, search bar, status badge improvements (Unknown/Unbound), Sync Settings button. Environment Variables (#29,#31,#55): Modified On column restored, search bar, Sync Settings button. Metadata Browser (#37): Custom Only filter toggle for entities. Data Explorer (#41,#42): Clear button, Import button (file dialog). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ext): review fixes — colSpan bug and command injection - Connection References: fix colCount from wrong heuristic (8) to correct value (7) matching actual column count - CDP tool: sanitize paths for PowerShell injection (escape single quotes); use execFileSync for tar on Unix to avoid shell interpretation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style(ext): extract inline styles to CSS classes per Gemini review Move search input and sort select inline styles to dedicated .toolbar-search and .toolbar-select CSS classes across 4 panels (Connection Refs, Env Variables, Import Jobs, Solutions). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ext): add missing webview entry points to knip config The 6 non-query/solutions webview panels are esbuild entry points but were missing from knip.json, causing false-positive unused-file reports. Also un-export internal-only types from shared modules. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ext): solution filter GUID bug and raw ISO timestamps - SolutionFilter shared component used uniqueName as option value instead of GUID id, causing "solutionId must be a valid GUID" error on Web Resources (and silently wrong on Conn Refs / Env Vars) - Import Jobs and Web Resources rendered raw ISO timestamps; add formatDateTime helper matching Plugin Traces / Conn Refs pattern Found by QA blind verification agents. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ext): review fixes — escaping, shell safety, URL normalization - Solutions panel: escape rootComponentBehavior and boolean ternaries before innerHTML insertion (S1 compliance) - Connection References: escape literal "Unbound" for consistent escaping discipline - CDP tool: use execFileSync instead of execSync for PowerShell VSIX extraction (S2 compliance — no shell: true) - Plugin Traces: strip trailing slash from environment URL before Maker Portal link construction Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ext): escape formatDate output in solutions detail card Wrap formatDate() calls in escapeHtml() for consistent escaping discipline — formatDate can return raw ISO string on parse failure. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
Adds
ppds pluginscommand group for managing Dataverse plugin registrations via CLI, enabling code-first plugin deployment workflows.New Commands
ppds plugins extract- Extract[PluginStep]/[PluginImage]attributes from assembly to JSON configppds plugins deploy- Deploy plugin registrations to Dataverseppds plugins diff- Compare config against environment, detect driftppds plugins list- List registered plugins with full hierarchyppds plugins clean- Remove orphaned registrationsKey Features
extractpreserves deployment settings (solution, runAsUser, etc.)--solutionoption on extract and deployPPDS.Plugins v2.0.0 (Breaking)
Description,AsyncAutoDeleteproperties toPluginStepAttributeSecureConfiguration(security risk - secrets in source control)PPDS.Cli v1.0.0-beta.2
deployment,runAsUser,description,asyncAutoDelete--solution,--force--package,--assembly[JsonExtensionData]for forward compatibilityTest plan
ppds plugins extract -i <dll> --solution <name>ppds plugins listshows hierarchyppds plugins deploy --what-if🤖 Generated with Claude Code