Skip to content

Stop NOTA0001 and NOTA0002 reporting files nobody can fix - #14

Merged
henrikottesorensen merged 1 commit into
Notalib:mainfrom
henrikottesorensen:claude/skip-foreign-files-in-encoding-check
Aug 18, 2026
Merged

Stop NOTA0001 and NOTA0002 reporting files nobody can fix#14
henrikottesorensen merged 1 commit into
Notalib:mainfrom
henrikottesorensen:claude/skip-foreign-files-in-encoding-check

Conversation

@henrikottesorensen

Copy link
Copy Markdown
Collaborator

Follow-on to #13. NOTA0002 fires on every test project that installs Microsoft.NET.Test.Sdk, naming a file the consumer did not write and cannot fix.

What happens

Microsoft.NET.Test.Sdk contributes its generated test entry point to @(Compile), and that file carries a UTF-8 byte order mark:

~/.nuget/packages/microsoft.net.test.sdk/18.8.1/build/net8.0/Microsoft.NET.Test.Sdk.Program.cs(1,1):
  warning NOTA0002: File begins with a UTF-8 byte order mark. Re-save it as UTF-8 without one, ...

It lives in the read-only NuGet cache. It is shared by every project on the machine, and restored the moment it is touched. There is no re-saving it.

Why this is worse than a missing check

The only way to a clean build was NotaValidateSourceEncoding=false — which switches off NOTA0001 as well. So a rule written to catch corruption was talking people into disabling the only guard against it. NotaAllowUtf8Bom=true keeps NOTA0001, but silences the mark check everywhere rather than in the one place it is wrong.

The advice in the message made it worse: it points at tools/de-bom.sh, which would have someone rewriting their NuGet cache.

The change

The task skips anything under the NuGet package root or the intermediate output path — sources compiled into the project that the project does not own.

Two details that are deliberate:

  • The roots are passed in as items, not matched by wildcard. Remove="$(NuGetPackageRoot)**" would glob the entire package cache on every build.
  • NuGetPackageRoot is only passed when it has a value. It is empty in some restore-less contexts, and an empty string is a prefix of every path — it would skip every file and check nothing, silently.

obj carries no mark today. It is included because generated files are rewritten on the next build, so a report on one cannot be acted on either.

Why the suite missed it

The verification project is not a test project, so it never installed the SDK that carries the file. Every check passed while every consuming test project warned.

verify-package.sh now references Microsoft.NET.Test.Sdk in its throwaway consumer — for what it drags in, not anything it does — and asserts that NOTA000x names no file under the cache. Confirmed against the unfixed targets, where it fails:

The encoding check reported a file the consumer cannot fix:
/Users/.../.nuget/packages/microsoft.net.test.sdk/18.8.1/build/net8.0/Microsoft.NET.Test.Sdk.Program.cs(1,1): warning NOTA0002: ...
It lives in the NuGet cache. Skip it rather than asking anyone to re-save it.

With the fix, all three pass:

verify.sh:             All 8 rules reported.
verify-encoding.sh:    All source files are valid UTF-8 without a byte order mark, or BOM-marked UTF-16.
verify-package.sh:     All rules survive packaging.

Real violations still report — checked with a marked file and a Windows-1252 file in a consuming project, NOTA0002 and NOTA0001 respectively.

Release

Worth releasing promptly: as it stands, anyone on 2.2.2 with a test project sees this, and the obvious reaction is to switch the encoding check off entirely.

🤖 Generated with Claude Code

Microsoft.NET.Test.Sdk contributes a source file of its own to @(Compile) -
Microsoft.NET.Test.Sdk.Program.cs, the generated test entry point - and it
carries a UTF-8 byte order mark. It lives in the read-only NuGet cache. So
NOTA0002 fired on every test project that installed this package, naming a file
the consumer did not write, cannot re-save, and which is restored again the
moment it is touched.

That is worse than a missing check. The only escape was
NotaValidateSourceEncoding=false, which switches off NOTA0001 as well - so a rule
against corruption was talking people into turning off the one guard against it.
NotaAllowUtf8Bom=true would have kept NOTA0001, but silences the mark check
everywhere rather than in the one place it is wrong.

The task now skips anything under the NuGet package root or the intermediate
output path. Both are the same idea: sources compiled into the project that the
project does not own. Generated files under obj carry no mark today, and are
included because they are rewritten on the next build, so a report on one is
noise that cannot be acted on either. The roots are passed in as items rather
than matched by wildcard - globbing the package cache to exclude it would read
every file in it - and NuGetPackageRoot is only passed when it has a value, since
an empty root is a prefix of every path and would skip the entire check.

Not caught before because the verification project is not a test project and so
never installed the SDK that carries the file. It does now, in the throwaway
consumer verify-package.sh builds, referenced for what it drags in rather than
anything it does - and the script asserts NOTA000x names no file under the cache.
Confirmed against the unfixed targets, where it fails with the file named.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@henrikottesorensen
henrikottesorensen merged commit 967f230 into Notalib:main Aug 18, 2026
1 check passed
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