Skip to content

Project file follow-ups, per-connection assembly reload, tests - #21

Merged
imranakram merged 14 commits into
masterfrom
fix/project-file-followups
Oct 25, 2026
Merged

imranakram merged 14 commits into
masterfrom
fix/project-file-followups

Conversation

@imranakram

@imranakram imranakram commented Oct 25, 2026 •

Copy link
Copy Markdown
Owner

Follow-ups to #19 (project files), plus tests, docs and release notes for the next release.

Fixes and changes

  • Opening a project and choosing "No" (remove existing) now disposes all watchers and forgets the persisted assembly, package and web resource watches. Before, the removed watchers kept deploying in the background.
  • Web resources are stored in the project file with their root folder and mappings, and restored on open: merged when the root folder matches, asked when it differs. Project files from the first version get a message instead.
  • Plugin assemblies are remembered per connection and reload on reconnect, like packages and web resources.
  • Plugin packages: if the saved id doesn't exist in the connected environment, the upload looks the package up by name and retries.
  • Packages in a project are skipped on environments without plugin package support, and wait for that check when a project is opened right after connecting.
  • Open... and Add > Plugin assembly need a connection.
  • WatchPluginFile.Dispose no longer throws when nothing was watched, and disposes its FileSystemWatcher.
  • Copilot review items from Added a "Project" where we can store and open #19: AddPluginAssembly uses its path parameter, Type enum renamed to WatchFileType, tooltip wording.

Tests

  • New XrmToolBox.AutoDeployer.Tests (NUnit, like PluginRegistration): 46 unit tests for project files and the sample templates, web resource settings merge, package lookup and web resource naming. CI runs them and uploads the results.
  • 3 integration tests (category Integration, local only) deploy through the plugin assembly and plugin package watchers, including the lookup by name. They're skipped without settings.
  • Tests/TestBench: test plugin assembly, plugin package, web resources, sample project templates, setup/cleanup scripts and a manual test plan. Connection string, publisher and prefix come from the gitignored Tests/testbench.local.json or environment variables; without a publisher the test bench creates its own.
  • To keep the helpers testable, project file reading, web resource merging and package lookup moved into small static classes (no behaviour change).

Docs and release

  • README: project files, per-connection settings, and a building and testing section linking to the test bench README.
  • Release notes in AutoDeployer.nuspec updated.

Housekeeping

How to test

  • CI: build, unit tests.
  • Locally: Tests/TestBench/README.md covers the environment setup, running the integration tests, and the manual test plan in XrmToolBox.

- Dispose all watchers when replacing them on project open, and forget
  persisted package/web resource watches so they don't come back
- Store web resource settings in the project file and restore them on open
- Skip plugin packages on environments that don't support them
- If a package upload fails because the package id doesn't exist in this
  environment, look it up by name and retry
- Require a connection before opening a project
- AddPluginAssembly uses its path parameter; rename Type enum to WatchFileType
- WatchPluginFile.Dispose no longer throws when nothing was watched, and
  disposes the FileSystemWatcher
- Fix Save button tooltip
- Watched plugin assembly paths are saved per connection and restored when
  connecting, so all watch types behave the same
- Removing an assembly (or replacing watchers from a project) forgets it too
- Adding a plugin assembly requires a connection
- Packages in a project opened while the plugin package support check is
  still running are added once the check completes, instead of being skipped
…or testing

No behaviour change: MainControl and WatchPluginPackageFile call the new static helpers, and the web resource naming helpers become internal so tests can reach them.
- XrmToolBox.AutoDeployer.Tests (NUnit, like PluginRegistration): project
  files, web resource settings merge, plugin package lookup and web
  resource naming
- Integration tests (category Integration, local only) deploy the test
  bench through the plugin assembly and plugin package watchers. They're
  skipped without a connection string (AUTODEPLOYER_TEST_CONNECTION or the
  gitignored Tests/connection.local.txt)
- Tests/TestBench: test plugin assembly, plugin package, web resources,
  sample .adproj files, Dataverse setup/cleanup scripts and a manual test plan
- Web resource roots written with forward slashes now count as the same folder
Same VSTest steps as PluginRegistration. The Integration category is filtered out, since those tests need a Dataverse environment.
The test bench README now walks through the app registration, application user, publisher and connection string, how to run the tests in Visual Studio and from the command line, and common errors. The main README describes project files and links to it. Setup-Dataverse.ps1 says so when the publisher is missing.
- Connection string, publisher and prefix come from Tests/testbench.local.json
  (gitignored, example in Tests/testbench.example.json), each overridable by
  AUTODEPLOYER_TEST_CONNECTION / _PUBLISHER / _PREFIX. Replaces
  Tests/connection.local.txt
- Setup-Dataverse.ps1 uses an existing publisher (checking its prefix) or
  creates it; without settings it creates its own AutoDeployTest / adt.
  Cleanup only removes a publisher the test bench created
- Sample projects are templates with placeholders for the test bench folder,
  prefix and package id; Setup writes filled-in copies to Projects/Local
- Integration tests take the package name from the prefix setting
…eference

- .github/dependabot.yml: weekly, grouped updates for the plugin and test
  projects' NuGet packages and for the workflow actions
- Remove the NuGet.Protocol reference: nothing uses it, and its HintPath
  pointed at the packages folder that went away with packages.config (#14)
- Tests: pin MscrmTools.Xrm.Connection to the plugin's version, which
  removes the MSB3277 version conflict warnings

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Unresolved restoration and deferred-watch handling can interrupt connection switching or violate project choices, while merge and XML-escaping issues affect saved configurations.

6 open issues
1 file not reviewed
  • XrmToolBox.AutoDeployer/MainControl.Designer.cs: Generated file
What changed in this PR

Extends the project-file support introduced in #19 with web-resource settings, per-connection assembly restoration, and watcher lifecycle fixes. Adds automated tests and a Dataverse test bench for validating deployment behavior.

Changes:

  • Restores and persists watches, handles package lookup by name, and adds connection and package-support guards.
  • Adds unit tests, local integration tests, sample projects, and setup/cleanup scripts; runs unit tests in CI.
  • Updates usage documentation, testing instructions, and release notes.
File Description
XrmToolBox.AutoDeployer/​XrmToolBox.AutoDeployer.csproj Includes new configuration and helper classes.
XrmToolBox.AutoDeployer/​WebResourcesManagerDialog.cs Exposes naming helpers to tests.
XrmToolBox.AutoDeployer/​WebResourceConfigMerger.cs Merges project web-resource settings.
XrmToolBox.AutoDeployer/​WatchPluginPackageFile.cs Retries missing package IDs by name.
XrmToolBox.AutoDeployer/​WatchPluginFile.cs Handles null watchers and releases resources.
XrmToolBox.AutoDeployer/​Properties/​AssemblyInfo.cs Grants tests access to internal members.
XrmToolBox.AutoDeployer/​ProjectFile.cs Extracts project XML loading and saving.
XrmToolBox.AutoDeployer/​Project.cs Adds web-resource settings and renames the enum.
XrmToolBox.AutoDeployer/​PluginPackageLookup.cs Adds package existence and name queries.
XrmToolBox.AutoDeployer/​PluginAssemblyWatchConfig.cs Stores watched assembly paths.
XrmToolBox.AutoDeployer/​MainControl.Designer.cs Clarifies the Save tooltip.
XrmToolBox.AutoDeployer/​MainControl.cs Implements restoration, replacement, merging, and guards.
XrmToolBox.AutoDeployer.sln Adds the test project.
Tests/​XrmToolBox.AutoDeployer.Tests/​XrmToolBox.AutoDeployer.Tests.csproj Configures NUnit tests and references.
Tests/​XrmToolBox.AutoDeployer.Tests/​WebResourceNamingTests.cs Tests naming, paths, and wildcard matching.
Tests/​XrmToolBox.AutoDeployer.Tests/​WebResourceConfigMergerTests.cs Tests web-resource merge behavior.
Tests/​XrmToolBox.AutoDeployer.Tests/​TestBench.cs Loads templates and local test settings.
Tests/​XrmToolBox.AutoDeployer.Tests/​Properties/​AssemblyInfo.cs Defines test assembly metadata.
Tests/​XrmToolBox.AutoDeployer.Tests/​ProjectFileTests.cs Tests serialization and sample projects.
Tests/​XrmToolBox.AutoDeployer.Tests/​PluginPackageLookupTests.cs Tests package lookup outcomes.
Tests/​XrmToolBox.AutoDeployer.Tests/​Integration/​WatcherIntegrationTests.cs Exercises real assembly and package deployments.
Tests/​XrmToolBox.AutoDeployer.Tests/​Integration/​DataverseFixture.cs Provides integration setup and deployment helpers.
Tests/​XrmToolBox.AutoDeployer.Tests/​Fakes/​FakeOrganizationService.cs Supplies a query-recording service fake.
Tests/​TestBench/​WebResources/​styles/​test.css Adds a stylesheet deployment sample.
Tests/​TestBench/​WebResources/​scripts/​test.js Adds a script deployment sample.
Tests/​TestBench/​WebResources/​pages/​test.html Adds a page deployment sample.
Tests/​TestBench/​Scripts/​Setup-Dataverse.ps1 Registers components and generates local projects.
Tests/​TestBench/​Scripts/​Dataverse.ps1 Shares settings and Web API access.
Tests/​TestBench/​Scripts/​Cleanup-Dataverse.ps1 Removes test components and generated projects.
Tests/​TestBench/​Scripts/​Check-Dataverse.ps1 Reports component modification times.
Tests/​TestBench/​Scripts/​Bump.ps1 Changes artifacts to trigger deployment.
Tests/​TestBench/​README.md Documents setup, execution, and manual checks.
Tests/​TestBench/​Projects/​AutoDeployTest.adproj Provides a complete project template.
Tests/​TestBench/​Projects/​AutoDeployTest-WrongPackageId.adproj Exercises package lookup fallback.
Tests/​TestBench/​Projects/​AutoDeployTest-OtherRoot.adproj Exercises root-folder conflicts.
Tests/​TestBench/​Projects/​AutoDeployTest-Legacy.adproj Exercises legacy project handling.
Tests/​TestBench/​PluginPackage/​PluginBase.cs Supplies package plugin infrastructure.
Tests/​TestBench/​PluginPackage/​Plugin1.cs Adds the sample package plugin.
Tests/​TestBench/​PluginPackage/​AutoDeployTest.Package.csproj Configures the test plugin package.
Tests/​TestBench/​PluginPackage/​.gitignore Excludes package build artifacts.
Tests/​TestBench/​PluginAssembly/​TestPlugin.cs Adds a minimal assembly deployment sample.
Tests/​TestBench/​PluginAssembly/​AutoDeployTest.Plugins.csproj Configures the classic test assembly.
Tests/​testbench.example.json Provides a local settings template.
README.md Documents projects, persistence, and testing.
AutoDeployer.nuspec Updates release notes.
.gitignore Excludes local credentials and generated projects.
.github/​workflows/​build.yml Runs unit tests and uploads results.

Review effort: Balanced

Comment thread XrmToolBox.AutoDeployer/MainControl.cs Resolved
Comment thread XrmToolBox.AutoDeployer/MainControl.cs Resolved
Comment thread XrmToolBox.AutoDeployer/MainControl.cs Resolved
Copilot AI balanced review requested due to automatic review settings October 25, 2026 14:11
…conflicts

- Sample projects: XML-escape the test bench folder and prefix when filling
  in the templates (Setup-Dataverse.ps1 and the tests), so a folder like
  C:\R&D still gives valid files
- Packages from projects opened while the support check runs are appended
  instead of replaced, and cleared when the watchers are replaced
- A saved plugin assembly that can't be watched (deleted or moved DLL) is
  skipped and reported, instead of aborting the connection switch; adding
  one that fails shows the error and isn't saved
- Web resource settings with the same root folder but a different prefix are
  treated as a conflict and ask before replacing, since the manager rebuilds
  every CRM name from one prefix

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Unresolved watcher restoration and connection-state handling defects can lose intended watches or restore unwanted ones.

No open issues 🎉

6 previously missed issues

In code that hasn't changed since last review

Medium severity Escape clone paths before inserting into XML

Tests/​TestBench/​Scripts/​Setup-Dataverse.ps1:105

The clone path is inserted directly into XML text. A valid Windows path such as C:\src\R&D\Tests\TestBench makes every generated project malformed because the ampersand is not escaped, so none of the ready-to-open samples can be loaded. XML-escape the replacement path before inserting it, or populate the XML elements through an XML API.

Medium severity XML-escape repository paths in sample projects

Tests/​XrmToolBox.AutoDeployer.Tests/​TestBench.cs:34

The sample-loading helper also substitutes an unescaped filesystem path into XML. When the repository is cloned beneath a directory containing &, such as C:\src\R&D, the sample-project unit tests fail during deserialization even though that clone path is valid. Escape Folder as XML text before replacement so it round-trips to the original path.

Medium severity Preserve or clear queued packages when replacing watches

XrmToolBox.AutoDeployer/​MainControl.cs:194

While the support check is pending, opening another project overwrites previously queued packages even when existing watches are kept. Replacing watches with a package-free project instead leaves the old queue intact, so those packages reappear when the check completes. Include queued packages in the keep/replace prompt, accumulate them when keeping, and clear them in RemoveAllWatchers when replacing.

Medium severity Skip failed assembly watcher restores

XrmToolBox.AutoDeployer/​MainControl.cs:684

Automatic reload now constructs every saved assembly watcher during UpdateConnection, but WatchPluginFile reads the DLL immediately and throws if it has been deleted, moved, or cleaned from the build output. One stale saved path therefore aborts connection initialization before package support and web-resource watches are restored. Handle reload failures per assembly, report the affected path, and continue restoring the other watches.

Medium severity Prevent duplicate watches after package identity resolution

XrmToolBox.AutoDeployer/​WatchPluginPackageFile.cs:323

After this resolved ID is persisted, reopening the original cross-environment project with existing watches kept creates a duplicate: AddPluginPackage only matches the old ID saved in the project. Both watches then upload the same file to the same package. Reconcile resolved package identity during import, or retain a stable source identity so reopening the project does not create another watch.

Medium severity Prevent duplicate resource names during project merging

XrmToolBox.AutoDeployer/​WebResourceConfigMerger.cs:38

Merging projects with the same root can add different local files that target the same CrmName, creating watchers that overwrite one Dataverse resource. This bypasses the duplicate-name validation in WebResourcesManagerDialog.cs:410–413. Preserve that constraint during merging by retaining the existing mapping or asking the user to resolve the conflict.

6 resolved since last review
1 file not reviewed
  • XrmToolBox.AutoDeployer/MainControl.Designer.cs: Generated file

Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings October 25, 2026 14:18
- Opening a project matches a package watch by name and file too, so a watch
  whose id was resolved by name isn't added a second time
- Merging web resource settings skips a mapping whose CRM name another file
  already deploys to, like the manager's own validation

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Unresolved connection-state and watch-merging issues can drop intended watches or create duplicate deployments.

1 open issue
1 previously missed issue

In code that hasn't changed since last review

Medium severity Preserve active watches when merging duplicate mappings

XrmToolBox.AutoDeployer/​WebResourceConfigMerger.cs:50

The manager retains unchecked scan results in Mappings, so an existing entry with IsActive = false causes an incoming active mapping for the same file to be discarded here. Opening a project with “Keep existing” then silently omits that project's watch, since RefreshWebResourceWatchers skips inactive mappings. Preserve an active watch from either configuration when merging duplicate paths.

1 file not reviewed
  • XrmToolBox.AutoDeployer/MainControl.Designer.cs: Generated file

Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings October 25, 2026 14:24
…pping

When the connection already maps the same file but doesn't watch it (the
manager keeps unchecked scan results), the merged mapping is watched if
either side watches it, instead of silently dropping the project's watch.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Unresolved watcher-lifetime and pending-package issues can cause deployment after removal, restore deleted watches, or lose queued project watches.

2 open issues
2 previously missed issues

In code that hasn't changed since last review

Medium severity Prompt before replacing projects with pending package watches

XrmToolBox.AutoDeployer/​MainControl.cs:196

When a project contains only packages and the support check is still pending, its watches exist only in _pendingProjectPackages, leaving the list empty. Opening another project then bypasses the keep/replace prompt entirely, so the first project's queued packages cannot be replaced and are eventually added alongside the second project. Include a nonempty pending queue in the prompt condition so RemoveAllWatchers can discard it.

Medium severity Ignore support-check callbacks from earlier connections

XrmToolBox.AutoDeployer/​MainControl.cs:963

A support check started for connection A can finish after switching to B. Its callback clears B's pending flag and consumes B's queued project packages using A's result; if A reports unsupported, those packages are discarded even when B's own check later succeeds. Capture the service and a connection-generation token when starting the check, and ignore callbacks from an earlier generation before changing state or draining the queue.

1 resolved since last review
1 file not reviewed
  • XrmToolBox.AutoDeployer/MainControl.Designer.cs: Generated file

Review effort: Balanced

Comment thread XrmToolBox.AutoDeployer/MainControl.cs Resolved
Copilot AI balanced review requested due to automatic review settings October 25, 2026 14:32

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

…ort checks

- WatchPluginPackageFile doesn't save its package list once disposed, also
  when the save was already queued on the UI thread, so replaced watches
  don't come back on reconnect
- WatchPluginFile stops waiting for the DLL when disposed and doesn't upload,
  so a removed watch can't deploy to the connection it was created for
- Packages still waiting for the support check count for the keep/replace
  question when opening another project
- A support check that finishes after switching connection is ignored
Copilot AI balanced review requested due to automatic review settings October 25, 2026 14:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@imranakram
imranakram merged commit 7b0e141 into master Oct 25, 2026
1 check passed
@imranakram
imranakram deleted the fix/project-file-followups branch October 25, 2026 14:47
@imranakram imranakram mentioned this pull request Oct 26, 2026
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