feat: unified CLI with profile-based authentication#28
Conversation
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>
- 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>
There was a problem hiding this comment.
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-migratewith profile-based authentication - DOP-based parallelism using server's
RecommendedDegreesOfParallelisminstead 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.
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 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
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.
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.
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.
- 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>
|
Re: Gemini Code Assist review Thanks for the thorough review. Addressing the two medium-severity items:
|
- 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>
docs: comprehensive documentation audit and cleanup
…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>
Summary
Major release introducing a unified CLI (
ppds) with profile-based authentication, replacing the formerppds-migratetool. This PR also introduces the newPPDS.Authpackage for reusable authentication across the ecosystem.New Packages
PPDS.Migration.Cli)Key Features
Command Structure
Breaking Changes
ppds-migratecommand is nowppds--auth env) replaced with profile-based auth (ppds auth create)Architectural Decisions
Versioning
Test plan
dotnet builddotnet testdotnet tool install --global PPDS.Cli --version 1.0.0-beta.1ppds auth create --name testppds env listppds data export --schema schema.xml --output data.zipppds data import --data data.zip🤖 Generated with Claude Code