Skip to content

Keep installed FieldWorks independent of dev registry paths (LT-22840) - #1183

Open
johnml1135 wants to merge 9 commits into
mainfrom
fix/LT-22840-dev-registry
Open

johnml1135 wants to merge 9 commits into
mainfrom
fix/LT-22840-dev-registry

Conversation

@johnml1135

@johnml1135 johnml1135 commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Fixes LT-22840.

Problem

Developer tooling wrote per-user overrides under HKCU\SOFTWARE\SIL: FieldWorks\9\RootCodeDir, RootDataDir, ProjectsDir, Icu<ver>DataDir, plus the silfw:// handler and the SilEncConverters40 and EncodingConverterRepository keys. The installed app reads HKCU before HKLM, so an installed FLEx could load code, data and ICU from a dev worktree. Installed 9.3.10 then failed to create AiAnalysisExportListener while the Hermit Crab trace fix was being validated, and silfw:// links launched the dev exe.

Design

Rule: a dev run never writes shared (HKCU) state, and a run never trusts a leftover HKCU path that points into a source tree.

A run is a "dev run" when FwDirectoryFinder.FindDevDistFiles finds a DistFiles folder with a FieldWorks.sln in an ancestor of the running assembly (already on main since #1093). Otherwise it is installed. A "source-tree path" is a path under a directory that contains FieldWorks.sln (FwDirectoryFinder.IsSourceTreePath).

Precedence (first match wins):

Item Dev run Installed run
Code, data dirs running tree's DistFiles > HKCU > HKLM > default HKCU (ignored if source-tree path) > HKLM > default
ICU data dir ICU_DATA env > running tree's DistFiles\Icu<ver> > HKCU > HKLM ICU_DATA env > HKCU > HKLM; a registry dir that does not exist or is a source-tree path is skipped
Projects dir in-memory choice for this session > HKCU ProjectsDir > HKLM > <DataDir>\Projects HKCU ProjectsDir > HKLM > <DataDir>\Projects

Projects are deliberately shared between dev and installed runs so developers see the same projects. ProjectsDir is NOT filtered by the source-tree rule. The only change is write side: a dev run keeps a projects-folder change in memory only (ProjectsDirectory setter) and never writes or deletes HKCU. An installed run still persists it as before.

What changed:

  • Writers removed: the unused setRegistryValues/setKeysInHKCU MSBuild targets (no callers in the repo) are deleted; the test initializer no longer writes RootCodeDir/RootDataDir; the launch-preflight skill script Resolve-FieldWorksDevRegistry.ps1 is now read-only, also with -Force (it just runs the repair script with -WhatIf).
  • Readers hardened: FwDirectoryFinder.GetDirectory and the Paratext plugin finder skip HKCU RootCodeDir/RootDataDir under a source tree and fall back to HKLM/default; Convert.cs ignores a source-tree v8 RootCodeDir; FwUtils.InitializeIcu goes through ChooseIcuDataDir and ignores a missing or source-tree Icu*DataDir. The dev ICU probe now also requires FieldWorks.sln.

Production vs dev-only impact

Production (installed) code paths changed: the source-tree filters (no effect unless an installed FLEx sits beside a FieldWorks.sln), skipping a nonexistent registry ICU directory in InitializeIcu (previously set ICU_DATA to a missing folder), and the Convert.cs v8 RootCodeDir check, which also catches path exceptions. Everything else is dev-only (build targets, test initializer, skill scripts, and the in-memory projects setter, which is active only in a source-tree run).

Repair script (Build/Repair-DeveloperRegistry.ps1)

Removes existing HKCU values that point into a source tree: RootCodeDir, RootDataDir, Icu*DataDir, SilEncConverters40\RootDir/DeveloperPluginDir, EncodingConverterRepository\Registry, and the silfw handler key if its exe is in a source tree. It keeps values outside source trees and keeps ProjectsDir, which dev and installed runs share.

# close FieldWorks first
.\Build\Repair-DeveloperRegistry.ps1 -WhatIf   # preview only
.\Build\Repair-DeveloperRegistry.ps1           # apply

Supports -WhatIf/-Confirm. It cannot identify a checkout whose FieldWorks.sln was deleted or moved.

Gotchas

  • Installed 9.3.10 needs the repair script once. The shipped binary has the old reader, which this PR cannot change; only builds after this one ignore source-tree paths.
  • Older branches can re-pollute. A dev build or test run from a branch without this change still writes these keys. Re-run the script after using one.
  • A checkout with its FieldWorks.sln removed is not recognised as a source tree.
  • Dev runs no longer remember a projects folder chosen in the UI across launches (process only); a projects folder already set in HKCU (including one inside a dev tree) is still used by both. An older dev build may have left HKCU ProjectsDir pointing into a worktree; installed FLEx follows it, as before.
  • Separate ICU dev-probe initialisation-order issue LT-22768 is not addressed.

Tests

  • FwDirectoryFinderTests (dummy registry): reader fallback matrix including a tree with no DistFiles; non-source user override wins; test initializer writes nothing; ProjectsDirectory set from a source-tree run does not persist; both run orders (dev then installed, installed then dev); a text scan checks the build scripts write no registry keys; a source-tree HKCU ProjectsDir is honoured by both dev and installed runs.
  • IcuDataDirChoiceTests: tree first, missing/source-tree keys skipped, HKLM fallback.
  • ParatextLexiconPluginDirectoryFinderTests: code/data fallback, source-tree ProjectsDir honoured, installed values win; on a temporary HKCU subkey, deleted in TearDown.
  • Build/Repair-DeveloperRegistry.Tests.ps1: 22 checks (keeps ProjectsDir) on PowerShell 5.1 and 7 (mocked registry): selective cleanup, preview, malformed/non-source values, missing keys, silfw/EC keys, read-only preflight.
  • Final run: FwUtilsTests 474 passed, 0 failed (476 discovered, 2 not executed); FwParatextLexiconPluginTests 38 passed, 0 failed; Pester file 22 checks passed. Not run: full FieldWorks suite, installer packaging, Convert.cs in isolation (no tests).

Manual verification

  1. Installed after dev: seed HKCU RootCodeDir/RootDataDir/Icu<ver>DataDir with worktree paths, run -WhatIf (it lists them), run it for real, then launch installed FLEx; it loads from Program Files and your projects list is unchanged.
  2. Dev after installed: run installed FLEx once, snapshot HKCU\SOFTWARE\SIL, launch Output\Debug\FieldWorks.exe from two worktrees; each uses its own DistFiles and ICU, and the HKCU snapshot diff is empty (including after changing the projects folder in the dev run).
  3. Clean user hive: launch each of dev and installed with no keys; dev uses the tree, installed uses HKLM/default.

This change is Reviewable

Dev launches wrote per-user overrides that made installed FLEx load
incompatible configuration from a worktree. Stop the code/data and ICU
writes, and keep the launch registry preflight read-only.

Ignore per-user source-tree code/data paths in installed directory lookup.
Provide an explicit selective repair with a WhatIf preview, preserving
ProjectsDir and overrides outside identifiable source trees.

Verified failing lookup regressions before the fix, full FwUtilsTests,
and registry repair/preflight checks on PowerShell 5.1 and 7.
@github-actions

Copy link
Copy Markdown

NUnit Tests

    1 files  ± 0      1 suites  ±0   10m 35s ⏱️ +13s
6 610 tests +15  6 525 ✅ +15  85 💤 ±0  0 ❌ ±0 
6 619 runs  +15  6 534 ✅ +15  85 💤 ±0  0 ❌ ±0 

Results for commit e477a76. ± Comparison against base commit a90754c.

@codecov-commenter

codecov-commenter commented Oct 13, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 61.53846% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 39.49%. Comparing base (a90754c) to head (e477a76).

Files with missing lines Patch % Lines
Src/Common/FwUtils/FwDirectoryFinder.cs 61.53% 6 Missing and 4 partials ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1183   +/-   ##
=======================================
  Coverage   39.49%   39.49%           
=======================================
  Files        1537     1537           
  Lines      354392   354417   +25     
  Branches    41058    41064    +6     
=======================================
+ Hits       139974   139985   +11     
- Misses     185044   185056   +12     
- Partials    29374    29376    +2     
Files with missing lines Coverage Δ
Src/Common/FwUtils/FwDirectoryFinder.cs 69.29% <61.53%> (-0.58%) ⬇️

... and 3 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

johnml1135 and others added 7 commits October 19, 2026 10:06
Delete the setRegistryValues and setKeysInHKCU targets; nothing in the
build calls them. They wrote ProjectsDir, SilEncConverters40,
EncodingConverterRepository and the silfw:// handler into HKCU, which
sent an installed FieldWorks to the dev tree. Dev code and data already
resolve from the running tree, so no dev run needs those keys.

ProjectsDirectory no longer persists when running from a source tree:
the choice lasts for the process. The reader and
Repair-DeveloperRegistry.ps1 now also drop a source-tree ProjectsDir.

Tests cover both run orders, the no-persist rule and a static check
that the build scripts write no registry keys.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
InitializeIcu now prefers the running source tree's ICU data, and
ignores a registry Icu*DataDir that is missing or inside a source tree.
A stale or foreign key can no longer redirect a dev build, or an
installed one.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ParatextLexiconPluginDirectoryFinder falls back to the machine value
when the per-user code, data or projects path is inside a source tree.
Convert.cs does the same for RootCodeDir, with a local check so it
stays independent of the FieldWorks dlls.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Repair-DeveloperRegistry.ps1 now removes SilEncConverters40 values,
the EncodingConverterRepository value and the silfw:// handler when
they point into a source tree. The handler goes as a whole key and
only through ShouldProcess. Installed values are kept.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ProjectsDirectory documents that a source-tree run keeps the value in
memory and that null does not delete a real HKCU value. Convert.cs
catches the path and security exceptions its source-tree check can
raise.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Developers want the same projects in both builds, so ProjectsDir is no
longer filtered by the source-tree rule in FwDirectoryFinder or the
Paratext finder, and Repair-DeveloperRegistry.ps1 keeps it. A dev run
still never writes it: the setter keeps the choice in memory.

Root*, Icu* and the converter keys stay filtered. Tests now expect a
source-tree ProjectsDir to be honoured by dev and installed runs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Restore the em dash and drop the stray blank line in
launch-or-attach.md, note that the repair keeps ProjectsDir, and
indent Resolve-FieldWorksDevRegistry.ps1 with tabs like other scripts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@johnml1135

Copy link
Copy Markdown
Contributor Author

Update: removed the unused setRegistryValues/setKeysInHKCU targets, which wrote RootCodeDir/RootDataDir/ProjectsDir, ICU, EC and silfw:// keys to HKCU. A source-tree run keeps a changed projects folder in memory only and never writes HKCU; ProjectsDir is still shared with installed FLEx and is not filtered, so existing dev-tree project folders keep working. InitializeIcu prefers the running tree and ignores missing or source-tree registry ICU dirs. The Paratext plugin finder skips source-tree HKCU RootCodeDir/RootDataDir, and Convert.cs skips a source-tree v8 RootCodeDir. Repair-DeveloperRegistry.ps1 also clears the source-tree SilEncConverters40, EncodingConverterRepository and silfw:// keys, and keeps ProjectsDir. Both run orders are tested (FwUtilsTests 474, Paratext plugin tests 38, repair script 22 checks, all passing); installed 9.3.10 still needs the script once.

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