Skip to content

feat: unified CLI with profile-based authentication#28

Merged
joshsmithxrm merged 91 commits intomainfrom
feature/unified-cli-auth
Dec 29, 2025
Merged

feat: unified CLI with profile-based authentication#28
joshsmithxrm merged 91 commits intomainfrom
feature/unified-cli-auth

Conversation

@joshsmithxrm
Copy link
Owner

@joshsmithxrm joshsmithxrm commented Dec 29, 2025

Summary

Major release introducing a unified CLI (ppds) with profile-based authentication, replacing the former ppds-migrate tool. This PR also introduces the new PPDS.Auth package for reusable authentication across the ecosystem.

New Packages

Package Purpose
PPDS.Auth Authentication profiles with encrypted credential storage
PPDS.Cli Unified CLI tool (replaces PPDS.Migration.Cli)

Key Features

  • Profile-based authentication: Create auth profiles once, use everywhere
  • 10 authentication methods: Interactive, device code, client secret, certificates, managed identity, GitHub OIDC, Azure DevOps OIDC, username/password
  • Environment discovery: Global Discovery Service integration for easy environment selection
  • Multi-profile pooling: Use multiple Application Users for high-throughput scenarios
  • DOP-based parallelism: Server-recommended parallelism instead of adaptive rate control

Command Structure

ppds
├── auth      Authentication profile management
├── env       Environment discovery and selection  
├── data      Data operations (export, import, copy, analyze)
├── schema    Schema generation and entity listing
└── users     User mapping for cross-environment migrations

Breaking Changes

  • ppds-migrate command is now ppds
  • Authentication flags (--auth env) replaced with profile-based auth (ppds auth create)
  • Adaptive rate presets removed in favor of DOP-based parallelism

Architectural Decisions

Versioning

Package Version Notes
PPDS.Plugins 1.1.1 MinVer addition
PPDS.Dataverse 1.0.0-beta.1 DOP-based parallelism
PPDS.Migration 1.0.0-beta.1 CLI separated
PPDS.Auth 1.0.0-beta.1 New package
PPDS.Cli 1.0.0-beta.1 New package

Test plan

  • Build passes: dotnet build
  • Tests pass: dotnet test
  • CLI installs: dotnet tool install --global PPDS.Cli --version 1.0.0-beta.1
  • Auth flow: ppds auth create --name test
  • Environment discovery: ppds env list
  • Data export: ppds data export --schema schema.xml --output data.zip
  • Data import: ppds data import --data data.zip

🤖 Generated with Claude Code

joshsmithxrm and others added 30 commits December 27, 2025 17:37
Phase 1 of unified CLI implementation. Creates shared authentication
infrastructure for PAC CLI-compatible auth management.

New PPDS.Auth package includes:
- Profile storage with platform-specific encryption (DPAPI on Windows)
- Cloud environment support (Public, UsGov, UsGovHigh, UsGovDod, China)
- DeviceCode credential provider with MSAL token caching
- ClientSecret credential provider for service principals
- ServiceClientFactory for creating authenticated ServiceClients
- Multi-profile pooling support with environment validation

Also adds:
- ADR-0008 documenting unified CLI architecture decision
- UNIFIED_CLI_SPEC.md with full command reference
- UNIFIED_CLI_PROGRESS.md for implementation tracking

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Phase 2 of unified CLI implementation:

- Rename PPDS.Migration.Cli to PPDS.Cli (ppds-migrate -> ppds)
- Restructure commands into groups: auth, data, schema, users
- Move data commands to Commands/Data/ folder
- Rename MigrateCommand to CopyCommand (ppds data copy)
- Implement all auth commands:
  - ppds auth create: Create profile with device code auth
  - ppds auth list: List all profiles (text/JSON)
  - ppds auth select: Select active profile
  - ppds auth delete: Delete profile with confirmation
  - ppds auth update: Re-authenticate existing profile
  - ppds auth name: Rename a profile
  - ppds auth clear: Delete all profiles and credentials
  - ppds auth who: Show current active profile
- Update all namespaces and test files
- All 792 tests passing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Phase 3 of unified CLI implementation:

- Create Discovery infrastructure in PPDS.Auth:
  - DiscoveredEnvironment model for GDS results
  - IGlobalDiscoveryService interface
  - GlobalDiscoveryService using ServiceClient.DiscoverOnlineOrganizationsAsync
  - EnvironmentResolver for matching by name/URL/ID
  - AmbiguousMatchException for multiple matches
- Add env commands to CLI:
  - ppds env list: Discover and list environments via GDS
  - ppds env select: Resolve environment and bind to profile
  - ppds env who: Show current profile and environment
- Add EnvironmentId property to EnvironmentInfo
- Support JSON output for all env commands
- All 792 tests passing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Phase 4 of unified CLI implementation:

- Add CertificateFileCredentialProvider for PFX/P12 file auth
- Add CertificateStoreCredentialProvider for Windows cert store auth
- Add ManagedIdentityCredentialProvider for Azure managed identity
- Update CredentialProviderFactory with new providers
- Update auth create command with new options:
  - --application-id: App registration client ID
  - --client-secret: Client secret for service principal
  - --certificate-path: Path to PFX/P12 certificate file
  - --certificate-password: Certificate file password
  - --certificate-thumbprint: Windows cert store thumbprint
  - --managed-identity: Use Azure managed identity
- Auto-detect auth method from provided options
- Validation for required fields per auth method
- Platform checks for Windows-only features
- All 792 tests passing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Wire up all CLI commands to use PPDS.Auth profile system:
- Add --profile and --environment options to all data commands
- Add --source-env/--target-env for copy and users commands
- Create ProfileServiceFactory for profile-to-connection resolution
- Create ProfileConnectionSourceAdapter bridging Auth to Dataverse

Remove legacy auth infrastructure (pre-v1, no backwards compat):
- Delete AuthMode, AuthResolver, DeviceCodeTokenProvider
- Remove global --url and --auth options from Program.cs
- Simplify ServiceFactory to just progress reporter utilities

Commands now work seamlessly with active profile:
  ppds data export --schema schema.xml -o data.zip
  ppds data import --data data.zip --profile myprofile
  ppds data copy --schema schema.xml --source-env Dev --target-env QA

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add InteractiveBrowserCredentialProvider using MSAL's AcquireTokenInteractive:
- Opens system browser for authentication (like PAC CLI)
- Auto-detects headless environments (SSH, CI, containers, no display)
- Falls back to device code flow when browser unavailable

Update CredentialProviderFactory to use browser auth by default for
DeviceCode auth method profiles, with automatic fallback.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Phase 6 - Pooling Support:
- Multi-profile parsing already implemented (--profile app1,app2,app3)
- Environment validation ensures all profiles target same environment
- Updated help text to document pooling for high-throughput operations

Phase 7 - Polish:
- All auth methods working (browser, device code, secret, cert, managed identity)
- Browser auth is default with device code fallback for headless
- Complete command documentation

The unified CLI is now feature-complete for v1:
- ppds auth (create, list, select, delete, clear, show, who)
- ppds env (list, select, who)
- ppds data (export, import, copy, analyze)
- ppds schema (generate, list)
- ppds users (generate)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove "like PAC CLI" comments from source code
- Fix DeviceCodeCredentialProvider description (not browser login)
- Update progress document decision rationales
- Comments now explain "why" not "what"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
env who:
- Actually connects to Dataverse and executes WhoAmI
- Shows user details (name, domain, email, user ID, business unit)
- Shows org details (name, unique name, org ID, URL)

auth who:
- Shows auth type, cloud, tenant ID
- Shows user or app ID depending on auth method
- Shows environment details including unique name and environment ID

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use InteractiveBrowserCredentialProvider when available (not SSH/CI)
- Fall back to DeviceCodeCredentialProvider in headless environments
- Add --force flag to auth update to clear cached credentials
- Update help text for --device-code option

Note: This is WIP - token cache mapping to profiles still needs work.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Key changes:
- auth create always prompts (forceInteractive=true), never reuses cached tokens
- auth update is now metadata-only (name changes), no re-authentication
- Silent auth looks up accounts by username stored in profile, not first account
- ICredentialProvider now exposes TenantId and ObjectId from auth result
- Interactive browser uses Prompt.SelectAccount for fresh auth

This aligns with pac auth create/update behavior:
- pac auth create: always prompts
- pac auth update: updates name/environment metadata only
- Token refresh is handled automatically by MSAL

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add AuthMethod.InteractiveBrowser enum value (default for desktop)
- DetermineAuthMethod returns InteractiveBrowser when browser available
- InteractiveBrowserCredentialProvider now returns correct AuthMethod
- Remove confirmation prompt from auth clear (just clears immediately)
- Profile now correctly shows InteractiveBrowser instead of DeviceCode

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Change from verbose card format to table format matching pac auth list:
- Columns: Index, Active, Method, Name, User, Cloud, Environment, Environment Url
- Dynamic column widths based on content
- Active profile highlighted in green

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use --index option (required) instead of positional argument
- Add --environment option to set default environment
- Matches pac auth update interface: --index, --name, --environment

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use --environment option (required) instead of positional argument
- Match pac output format:
  - "Connected as <user>"
  - "Looking for environment '<env>'"
  - "Validating connection..."
  - "Connected to... <friendly name>"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Comprehensive alignment of ppds auth commands with pac CLI patterns:

Phase 1 - Command Interface:
- auth select: positional -> --index/-i, --name/-n options
- auth delete: positional -> --index/-i, --name/-n options
- auth name: positional -> --index/-i, --name/-n options (both required)

Phase 2 - Auth Create Options:
- Renamed to camelCase: --deviceCode, --applicationId, --clientSecret,
  --certificateDiskPath, --certificatePassword, --managedIdentity
- Added short aliases: -dc, -id, -cs, -cdp, -cp, -mi, -env, -ci
- Added --username/-un, --password/-p for UsernamePassword auth
- Added --githubFederated/-ghf, --azureDevOpsFederated/-adof (preview)

Phase 3 - Auth Who Enhancement:
- Added "Connected as X" header matching pac format
- Added Method and Type columns (TokenCacheType detection)
- Aligned field formatting with pac output

Phase 4 - Connected Header:
- Created ConsoleHeader utility for consistent headers
- Applied "Connected as X to Y" to env list/select/who commands

Phase 5 - Polish:
- Added 'org' command as alias for 'env'
- Created ErrorOutput utility for version/docs in errors
- Updated auth clear message to match pac

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
pac auth delete does not require confirmation, so neither should ppds.
Removed --force/-f option and confirmation prompt.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Added -ct alias for --certificateThumbprint (following pac pattern: -cdp, -cp, -ct)
- Fixed auth create to properly resolve environment via GlobalDiscoveryService
  when specified by name/partial match instead of storing the name as URL
- Now shows Environment URL in auth create output

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Added missing PAC-equivalent fields to auth who:
- Entra ID Object Id (stored during auth)
- Authority (derived from cloud/tenant)
- Environment Id
- Environment Type (Sandbox, Production, etc.)
- Environment Geo (region)
- Organization Id
- Organization Unique Name

Updated auth create and env select to store additional
environment metadata from GlobalDiscoveryService.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds JWT token parsing to extract additional user/tenant information
that PAC CLI displays in 'auth who' output:
- Token Expires On (from MSAL AuthenticationResult.ExpiresOn)
- Tenant Country (from JWT 'tenant_ctry' claim)
- PUID (from JWT 'puid' claim)
- User Country/Region (from JWT 'ctry' claim)

Implementation details:
- Add System.IdentityModel.Tokens.Jwt NuGet package
- Add AccessToken property to ICredentialProvider interface
- Create JwtClaimsParser helper to decode and extract claims
- Add new fields to AuthProfile for persistent storage
- Update auth create to extract claims after authentication
- Update auth who text and JSON output to display new fields

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The SDK's Microsoft.Xrm.Sdk.Organization.OrganizationType enum values are:
- 0 = Customer (Production)
- 5 = CustomerTest (Sandbox)
- 12 = Default
- 13 = Developer
- 14 = Trial
- 15 = Teams

Previous mapping incorrectly assumed 0=Production, 1=Sandbox, 2=Developer.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix "Token Expires On" -> "Token Expires" in auth who
- Add Environment ID and User Email to env who output
- Update header format: "Connected as X" then "Connected to... Y" on separate lines
- Add "Organization Information" header in env who to match pac

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds support for extracting claims from MSAL's ClaimsPrincipal (ID token)
in addition to parsing the access token JWT. This enables extracting
user-specific claims that may be in the ID token but not the access token.

Note: tenant_ctry and ctry claims require either:
- Optional claims configured in the app registration, or
- Windows Account Manager (WAM) broker authentication

Since we use Microsoft's public client ID and browser auth, these claims
are not currently available. PUID extraction works correctly.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove TenantCountry and UserCountry fields since they require Windows
Account Manager (WAM) which we don't support. Also removes:
- Debug code (PPDS_DEBUG_CLAIMS environment variable)
- "Validating environment connection" step (was only for country claims)
- Unused static import

Keeps working functionality:
- PUID extraction from JWT claims
- TokenExpiresOn
- All other auth who/env who fields

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…mands

Replace inconsistent 'Connecting to Dataverse using...' messages with
standardized PAC CLI-aligned headers showing identity and environment.

Changes:
- Add ResolvedConnectionInfo class to capture profile and env after connection
- Add ConsoleHeader.WriteConnectedAs(connectionInfo) overload
- Add ConsoleHeader.WriteConnectedAsLabeled() for source/target scenarios
- Update SchemaCommand, ImportCommand, ExportCommand, CopyCommand, UsersCommand
- Add Profile and EnvironmentUrl properties to ProfileConnectionSource
- All headers suppressed in JSON output mode

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixed ConsoleProgressReporter outputting "[Export] : 0/0" lines when
receiving progress events with Message but no Entity during the
data writing phase.

Changes:
- Handle message-only events (like "Writing output file...") separately
- Skip entity progress format when Entity is null/empty
- Messages now display properly instead of as malformed progress lines

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
joshsmithxrm and others added 11 commits December 29, 2025 00:47
- Add EstimatedRemaining to ProgressEventArgs for per-entity ETA display
- Add CreatedCount/UpdatedCount to MigrationResult and EntityImportResult
- Pass ETA through from ProgressSnapshot in TieredImporter adapter
- Aggregate created/updated counts from entity results for upsert operations
- Display ETA in progress line: "| ETA: m:ss" (or h:mm:ss for hour+)
- Display breakdown in completion summary for upsert mode only
- Add FormatEta helper to correctly handle hour+ durations

Progress output now shows:
  [+00:00:12.345] [Import] contact (Tier 2): 5,000/10,000 (50%) @ 1,234 rec/s | ETA: 4:30

Completion summary for upsert shows:
  Import succeeded.
      42,366 record(s) in 00:00:35 (1,210.5 rec/s)
          Created: 40,000 | Updated: 2,366
      0 Error(s)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Rename --include-system-fields to --include-audit-fields
- Use IsCustomizable metadata instead of hardcoded field list
- Audit fields (7): createdon, createdby, createdonbehalfby,
  modifiedon, modifiedby, modifiedonbehalfby, overriddencreatedon
- Add exceptions for BPF fields (processid, stageid, entityimageid)
- Handle Virtual attributes: only include Image/MultiSelectPicklist
- Remove --exclude-patterns option (overkill)
- Remove --include-files stub (not implemented)
- Non-customizable system fields now auto-excluded by metadata

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Relationships are always needed for dependency analysis and M2M
support. No valid use case for excluding them.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add --entity option to show detailed field metadata for a specific entity:
- Display field properties: type, custom, customizable, create/update validity
- Show filter result (Include/Audit/Exclude) matching schema generate behavior
- Support --include-audit-fields to preview audit field inclusion
- JSON output via --json flag with full details and summary

Also remove obsolete tests for removed --include-files option.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…zable check

Add IsNonMigratableSystemField check for system bookkeeping fields that are
marked IsCustomizable=true but serve no purpose in data migration:
- timezoneruleversionnumber
- utcconversiontimezonecode
- importsequencenumber

These fields exist on every entity and contain system-managed values,
not business data. Updated both DataverseSchemaGenerator and CLI display.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…8.0.0 to 10.0.1

---
updated-dependencies:
- dependency-name: Microsoft.Extensions.Options.ConfigurationExtensions
  dependency-version: 10.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
…tensions.Msal

Bumps Microsoft.Identity.Client from 4.68.0 to 4.79.2
Bumps Microsoft.Identity.Client.Extensions.Msal from 4.68.0 to 4.79.2

---
updated-dependencies:
- dependency-name: Microsoft.Identity.Client
  dependency-version: 4.79.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: Microsoft.Identity.Client.Extensions.Msal
  dependency-version: 4.79.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
---
updated-dependencies:
- dependency-name: MinVer
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: MinVer
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: MinVer
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
---
updated-dependencies:
- dependency-name: System.Security.Cryptography.Pkcs
  dependency-version: 10.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
---
updated-dependencies:
- dependency-name: xunit.runner.visualstudio
  dependency-version: 3.1.5
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: xunit.runner.visualstudio
  dependency-version: 3.1.5
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: xunit.runner.visualstudio
  dependency-version: 3.1.5
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
- Update root README with new package table (PPDS.Auth, PPDS.Cli)
- Rewrite CLI README with actual ppds command structure
- Update CLAUDE.md with correct project structure, namespaces, ADR refs
- Rename ADR-0008 to ADR-0007 (fix numbering gap)
- Delete obsolete spec files (UNIFIED_CLI_SPEC, UNIFIED_CLI_PROGRESS)
- Create CHANGELOG for PPDS.Auth (1.0.0-beta.1)
- Create CHANGELOG for PPDS.Cli (1.0.0-beta.1)
- Update PPDS.Dataverse CHANGELOG (1.0.0-beta.1, DOP-based parallelism)
- Update PPDS.Migration CHANGELOG (1.0.0-beta.1, remove CLI section)
- Update PPDS.Plugins CHANGELOG (1.1.1, MinVer addition)
- Replace AdaptiveRate section in Dataverse README with DOP-based

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings December 29, 2025 09:15
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a unified CLI (ppds) with profile-based authentication, replacing the former ppds-migrate tool, and introduces the new PPDS.Auth package for reusable authentication. The architecture shifts from adaptive rate control to DOP-based parallelism using server-recommended values from the x-ms-dop-hint header.

Key Changes:

  • Unified CLI tool replacing ppds-migrate with profile-based authentication
  • DOP-based parallelism using server's RecommendedDegreesOfParallelism instead of adaptive rate control
  • Schema generation improvements with metadata-driven field filtering

Reviewed changes

Copilot reviewed 136 out of 141 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
tests/PPDS.Dataverse.Tests/Progress/ProgressSnapshotTests.cs New test file for ProgressSnapshot computed properties and edge cases
tests/PPDS.Dataverse.Tests/Pooling/PoolSizingTests.cs Updated to test DOP-based pool sizing instead of per-connection sizing
tests/PPDS.Dataverse.Tests/Pooling/PoolExhaustedExceptionTests.cs New test file for pool exhaustion exception handling
tests/PPDS.Dataverse.Tests/Pooling/DataverseConnectionPoolTests.cs Updated to remove adaptive rate controller dependency
src/PPDS.Migration/Schema/DataverseSchemaGenerator.cs Enhanced field filtering with metadata-driven approach
src/PPDS.Migration/Import/TieredImporter.cs Added schema mismatch detection and field validity checking
src/PPDS.Dataverse/Pooling/DataverseConnectionPool.cs Refactored to use DOP-based sizing and removed adaptive rate control
src/PPDS.Dataverse/BulkOperations/BulkOperationExecutor.cs Replaced adaptive rate control with DOP-based parallelism
src/PPDS.Cli/Program.cs New unified CLI entry point

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@gemini-code-assist
Copy link

Summary of Changes

Hello @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 represents a significant architectural shift, introducing a unified command-line interface for Power Platform development. The core purpose is to streamline user experience, enhance authentication flexibility, and optimize data operations through a more robust and adaptive parallelism model. By centralizing CLI functionality and leveraging profile-based authentication, developers can manage their Dataverse interactions more efficiently and securely, particularly in high-throughput and CI/CD scenarios.

Highlights

  • Unified CLI: The ppds-migrate tool has been replaced by a new unified CLI named ppds, consolidating all Dataverse operations under a single command structure. This includes authentication, environment management, data operations (export, import, copy, analyze), schema generation, and user mapping.
  • Profile-Based Authentication: A new PPDS.Auth package introduces profile-based authentication, allowing users to create and manage authentication profiles with encrypted credential storage. This supports 10 different authentication methods, including interactive browser, device code, client secret, certificates, managed identity, and OIDC for GitHub and Azure DevOps.
  • DOP-Based Parallelism: The dataverse connection pool now utilizes server-recommended Degrees of Parallelism (DOP) based on the x-ms-dop-hint header, replacing the previous adaptive rate control mechanism. This ensures optimal throughput while preventing throttling, with scaling achieved by adding multiple Application Users.
  • Breaking Changes: Existing users will need to update their commands from ppds-migrate to ppds. Authentication flags (--auth env) are replaced by the new profile-based authentication system (ppds auth create). Adaptive rate presets have been removed in favor of DOP-based parallelism.
  • Schema Generation Enhancements: The schema generation now includes metadata-driven field filtering, allowing for more precise control over which fields are included based on their validity for create/update operations and customizability. A new SchemaMismatchException is introduced for clearer error reporting during import when target columns are missing.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@github-advanced-security github-advanced-security bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This is a major and impressive refactoring that introduces a unified CLI, a shared authentication package, and a more robust DOP-based parallelism model. The new architecture is a significant improvement in terms of extensibility, user experience, and reliability. The profile-based authentication is well-implemented with support for numerous methods, and the new CLI command structure is logical and follows industry best practices. The detailed error reporting with actionable suggestions is a fantastic addition. I have two medium-severity comments regarding security: one about an empty catch block that could hide certificate store issues, and another about the use of simple obfuscation for credentials on non-Windows platforms, suggesting a move to platform-native secure storage in the future. Overall, this is an excellent pull request that greatly enhances the capabilities and quality of the SDK.

joshsmithxrm and others added 6 commits December 29, 2025 03:23
- PPDS.Plugins: net462 only (plugins run exclusively in Dataverse sandbox)
- PPDS.Cli: net8.0;net9.0;net10.0 (supports all current runtimes)
- PPDS.Dataverse: net8.0;net9.0;net10.0
- PPDS.Migration: net8.0;net9.0;net10.0
- PPDS.Auth: net8.0;net9.0;net10.0

This ensures CLI can be installed by users with any current .NET runtime,
and removes misleading modern TFMs from Plugins (which can only execute
on .NET Framework 4.6.2 in the Dataverse sandbox).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@joshsmithxrm
Copy link
Owner Author

Re: Gemini Code Assist review

Thanks for the thorough review. Addressing the two medium-severity items:

  1. Empty catch block in certificate store - Fixed. Added warning output to CertificateFileCredentialProvider.StoreCertificateTemporarily() so certificate store issues are now visible to users.

  2. Credential obfuscation on non-Windows - Acknowledged. This is a known limitation documented in ADR-0007. Platform-native secrets (libsecret/keychain) would add dependencies. Current XOR obfuscation is acceptable for developer workstations; CI/CD uses environment variables or OIDC which don't store credentials locally. Future enhancement tracked.

joshsmithxrm and others added 2 commits December 29, 2025 11:37
- Fix FormatEta bug: use eta.Minutes instead of TotalMinutes for sub-hour display
- Update auth comment to be method-agnostic (not MSAL-specific)
- Add warning output for certificate store errors instead of silent catch

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add Auth-v* and Cli-v* tag triggers to publish-nuget.yml
- Add PPDS.Auth and PPDS.Cli package handlers
- Remove deleted PPDS.Migration.Cli reference
- Update all workflows to use .NET 8.0, 9.0, 10.0 SDKs
- Add net9.0 target to test projects to match source projects

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@joshsmithxrm joshsmithxrm merged commit 3e1289b into main Dec 29, 2025
5 checks passed
@joshsmithxrm joshsmithxrm deleted the feature/unified-cli-auth branch December 29, 2025 18:09
joshsmithxrm added a commit that referenced this pull request Jan 8, 2026
docs: comprehensive documentation audit and cleanup
joshsmithxrm added a commit that referenced this pull request Feb 8, 2026
…ing bug

Port _cachedResultUrl fix from InteractiveBrowserCredentialProvider (PR #515)
to DeviceCodeCredentialProvider. Without URL tracking, a token obtained for
globaldisco was incorrectly reused for the target environment URL, causing
"Server Error, no error report generated from server".

Fix CredentialProviderFactory to map AuthMethod.DeviceCode directly to
DeviceCodeCredentialProvider instead of routing through CreateInteractiveProvider
which overrode the user's explicit choice with InteractiveBrowserCredentialProvider.

Both bugs introduced in 3e1289b (Dec 29, 2025), PR #28.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants