diff --git a/.editorconfig b/.editorconfig
index 2b1039ae..3baf9fec 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -23,6 +23,55 @@ indent_size = 8
[*.{cs,csx,cake}]
charset = utf-8-bom
+# Visual Basic files
+[*.{vb,vbx}]
+end_of_line = crlf
+charset = utf-8-bom
+
+# XML project files
+[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj,props,targets}]
+indent_size = 2
+
+# XML config files
+[*.{ruleset,config,nuspec,resx,vsixmanifest,vsct,runsettings}]
+indent_size = 2
+
+# Other XML files
+[*.{xml,svg}]
+indent_size = 2
+
+# JSON files
+[*.{json,jsonc}]
+indent_size = 2
+
+# YAML files
+[*.{yml,yaml}]
+indent_size = 2
+
+# Shell scripts
+[*.{in,sh}]
+indent_size = 2
+
+# Windows batch files
+[*.{bat,cmd}]
+end_of_line = crlf
+
+# Markdown files
+[*.{md,markdown}]
+indent_size = 2
+trim_trailing_whitespace = false
+
+# InnoSetup files
+[*.iss]
+end_of_line = crlf
+indent_size = 2
+
+################################################################################
+# STATIC ANALYSIS CONFIGURATION
+################################################################################
+
+[*.{cs,csx,cake}]
+
#### .NET Coding Conventions ####
dotnet_style_operator_placement_when_wrapping = beginning_of_line:suggestion
@@ -241,47 +290,261 @@ dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
-### *** END - C# files ***
-
-# Visual Basic files
-[*.{vb,vbx}]
-end_of_line = crlf
-charset = utf-8-bom
-
-# XML project files
-[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj,props,targets}]
-indent_size = 2
-
-# XML config files
-[*.{ruleset,config,nuspec,resx,vsixmanifest,vsct,runsettings}]
-indent_size = 2
-
-# Other XML files
-[*.{xml,svg}]
-indent_size = 2
-
-# JSON files
-[*.{json,jsonc}]
-indent_size = 2
-
-# YAML files
-[*.{yml,yaml}]
-indent_size = 2
-
-# Shell scripts
-[*.{in,sh}]
-indent_size = 2
-
-# Windows batch files
-[*.{bat,cmd}]
-end_of_line = crlf
-
-# Markdown files
-[*.{md,markdown}]
-indent_size = 2
-trim_trailing_whitespace = false
-
-# InnoSetup files
-[*.iss]
-end_of_line = crlf
-indent_size = 2
+### Miscellaneous code analysis configuration
+
+# Relaxed rules related to IDisposable implementation - https://blog.stephencleary.com/2009/08/how-to-implement-idisposable-and.html
+dotnet_diagnostic.CA1063.severity = none # CA1063: Implement IDisposable correctly
+dotnet_diagnostic.CA1816.severity = none # CA1816: Call GC.SuppressFinalize correctly
+
+# Disable CA1508 (false positive when a value is set inside a catch block)
+dotnet_diagnostic.CA1508.severity = none # CA1508: Avoid dead conditional code
+
+# Rules stricter than the default
+dotnet_diagnostic.CA1001.severity = warning # CA1001: Types that own disposable fields should be disposable
+dotnet_diagnostic.CA1047.severity = warning # CA1047: Do not declare protected members in sealed types
+dotnet_diagnostic.CA1070.severity = warning # CA1070: Do not declare event fields as virtual
+dotnet_diagnostic.CA2000.severity = warning # CA2000: Dispose objects before losing scope
+dotnet_diagnostic.IDE0043.severity = error # IDE0043: Format string contains invalid placeholder
+dotnet_diagnostic.IDE0076.severity = warning # IDE0076: Remove invalid global 'SuppressMessageAttribute'
+dotnet_diagnostic.IDE0077.severity = warning # IDE0077: Avoid legacy format target in global 'SuppressMessageAttribute'
+
+# Miscellaneous relaxations
+dotnet_diagnostic.CA1000.severity = none # CA1000: Do not declare static members on generic types
+dotnet_diagnostic.CA1032.severity = suggestion # CA1032: Implement standard exception constructors
+dotnet_diagnostic.CA1303.severity = none # CA1303: Do not pass literals as localized parameters
+
+# Miscellaneous configuration
+dotnet_code_quality.CA1303.use_naming_heuristic = false # Don't raise CA1303 just because a parameter name contains Text, Message, or Caption.
+dotnet_code_quality.CA1062.exclude_extension_method_this_parameter = true # Extension methods: null check may be skipped on "this" parameter.
+dotnet_code_quality.CA1062.null_check_validation_methods = System.String.IsNullOrEmpty|System.String.IsNullOrWhiteSpace
+dotnet_code_quality.dispose_ownership_transfer_at_constructor = true # Disposables passed to a constructor must be disposed by the constructed object.
+
+### StyleCop Analyzers rules
+
+# StyleCop Analyzers - Special rules
+dotnet_diagnostic.SA0001.severity = warning # XML comment analysis disabled
+dotnet_diagnostic.SA0002.severity = warning # Invalid settings file
+
+# StyleCop Analyzers - Spacing rules
+dotnet_diagnostic.SA1000.severity = warning # Keywords should be spaced correctly
+dotnet_diagnostic.SA1001.severity = warning # Commas should be spaced correctly
+dotnet_diagnostic.SA1002.severity = warning # Semicolons should be spaced correctly
+dotnet_diagnostic.SA1003.severity = warning # Symbols should be spaced correctly
+dotnet_diagnostic.SA1004.severity = warning # Documentation lines should begin with single space
+dotnet_diagnostic.SA1005.severity = warning # Single line comments should begin with single space
+dotnet_diagnostic.SA1006.severity = warning # Preprocessor keywords should not be preceded by space
+dotnet_diagnostic.SA1007.severity = warning # Operator keyword should be followed by space
+dotnet_diagnostic.SA1008.severity = warning # Opening parenthesis should be spaced correctly
+dotnet_diagnostic.SA1009.severity = warning # Closing parenthesis should be spaced correctly
+dotnet_diagnostic.SA1010.severity = warning # Opening square brackets should be spaced correctly
+dotnet_diagnostic.SA1011.severity = warning # Closing square brackets should be spaced correctly
+dotnet_diagnostic.SA1012.severity = warning # Opening braces should be spaced correctly
+dotnet_diagnostic.SA1013.severity = warning # Closing braces should be spaced correctly
+dotnet_diagnostic.SA1014.severity = warning # Opening generic brackets should be spaced correctly
+dotnet_diagnostic.SA1015.severity = warning # Closing generic brackets should be spaced correctly
+dotnet_diagnostic.SA1016.severity = warning # Opening attribute brackets should be spaced correctly
+dotnet_diagnostic.SA1017.severity = warning # Closing attribute brackets should be spaced correctly
+dotnet_diagnostic.SA1018.severity = warning # Nullable type symbols should be spaced correctly
+dotnet_diagnostic.SA1019.severity = warning # Member access symbols should be spaced correctly
+dotnet_diagnostic.SA1020.severity = warning # Increment decrement symbols should be spaced correctly
+dotnet_diagnostic.SA1021.severity = warning # Negative signs should be spaced correctly
+dotnet_diagnostic.SA1022.severity = warning # Positive signs should be spaced correctly
+dotnet_diagnostic.SA1023.severity = warning # Dereference and access of symbols should be spaced correctly
+dotnet_diagnostic.SA1024.severity = warning # Colons should be spaced correctly
+dotnet_diagnostic.SA1025.severity = warning # Code should not contain multiple whitespace in a row
+dotnet_diagnostic.SA1026.severity = warning # Code should not contain space after new or stackalloc keyword in implicitly typed array allocation
+dotnet_diagnostic.SA1027.severity = warning # Use tabs correctly
+dotnet_diagnostic.SA1028.severity = warning # Code should not contain trailing whitespace
+
+# StyleCop Analyzers - Readability rules
+dotnet_diagnostic.SA1100.severity = warning # Do not prefix calls with base unless local implementation exists
+dotnet_diagnostic.SA1101.severity = none # Prefix local calls with this
+dotnet_diagnostic.SX1101.severity = warning # Do not prefix local calls with this
+dotnet_diagnostic.SA1102.severity = warning # Query clause should follow previous clause
+dotnet_diagnostic.SA1103.severity = warning # Query clauses should be on separate lines or all on one line
+dotnet_diagnostic.SA1104.severity = warning # Query clause should begin on new line when previous clause spans multiple lines
+dotnet_diagnostic.SA1105.severity = warning # Query clauses spanning multiple lines should begin on own line
+dotnet_diagnostic.SA1106.severity = warning # Code should not contain empty statements
+dotnet_diagnostic.SA1107.severity = warning # Code should not contain multiple statements on one line
+dotnet_diagnostic.SA1108.severity = warning # Block statements should not contain embedded comments
+dotnet_diagnostic.SA1109.severity = warning # Block statements should not contain embedded regions
+dotnet_diagnostic.SA1110.severity = warning # Opening parenthesis or bracket should be on declaration line
+dotnet_diagnostic.SA1111.severity = warning # Closing parenthesis should be on line of last parameter
+dotnet_diagnostic.SA1112.severity = warning # Closing parenthesis should be on line of opening parenthesis
+dotnet_diagnostic.SA1113.severity = warning # Comma should be on the same line as previous parameter
+dotnet_diagnostic.SA1114.severity = warning # Parameter list should follow declaration
+dotnet_diagnostic.SA1115.severity = warning # Parameter should follow comma
+dotnet_diagnostic.SA1116.severity = warning # Split parameters should start on line after declaration
+dotnet_diagnostic.SA1117.severity = warning # Parameters should be on same line or separate lines
+dotnet_diagnostic.SA1118.severity = warning # Parameter should not span multiple lines
+dotnet_diagnostic.SA1120.severity = warning # Comments should contain text
+dotnet_diagnostic.SA1121.severity = warning # Use built-in type alias
+dotnet_diagnostic.SA1122.severity = warning # Use string.Empty for empty strings
+dotnet_diagnostic.SA1123.severity = warning # Do not place regions within elements
+dotnet_diagnostic.SA1124.severity = warning # Do not use regions
+dotnet_diagnostic.SA1125.severity = warning # Use shorthand for nullable types
+dotnet_diagnostic.SA1126.severity = warning # Prefix calls correctly
+dotnet_diagnostic.SA1127.severity = warning # Generic type constraints should be on their own line
+dotnet_diagnostic.SA1128.severity = warning # Put constructor initializers on their own line
+dotnet_diagnostic.SA1129.severity = warning # Do not use default value type constructor
+dotnet_diagnostic.SA1130.severity = warning # Use lambda syntax
+dotnet_diagnostic.SA1131.severity = warning # Use readable conditions
+dotnet_diagnostic.SA1132.severity = warning # Do not combine fields
+dotnet_diagnostic.SA1133.severity = warning # Do not combine attributes
+dotnet_diagnostic.SA1134.severity = warning # Attributes should not share line
+dotnet_diagnostic.SA1135.severity = warning # Using directives should be qualified
+dotnet_diagnostic.SA1136.severity = warning # Enum values should be on separate lines
+dotnet_diagnostic.SA1137.severity = warning # Elements should have the same indentation
+dotnet_diagnostic.SA1139.severity = warning # Use literal suffix notation instead of casting
+
+# StyleCop Analyzers - Ordering rules
+dotnet_diagnostic.SA1200.severity = warning # Using directives should be placed correctly
+dotnet_diagnostic.SA1201.severity = warning # Elements should appear in the correct order
+dotnet_diagnostic.SA1202.severity = warning # Elements should be ordered by access
+dotnet_diagnostic.SA1203.severity = warning # Constants should appear before fields
+dotnet_diagnostic.SA1204.severity = warning # Static elements should appear before instance elements
+dotnet_diagnostic.SA1205.severity = none # Partial elements should declare access
+dotnet_diagnostic.SA1206.severity = warning # Declaration keywords should follow order
+dotnet_diagnostic.SA1207.severity = warning # Protected should come before internal
+dotnet_diagnostic.SA1208.severity = warning # System using directives should be placed before other using directives
+dotnet_diagnostic.SA1209.severity = warning # Using alias directives should be placed after other using directives
+dotnet_diagnostic.SA1210.severity = warning # Using directives should be ordered alphabetically by namespace
+dotnet_diagnostic.SA1211.severity = warning # Using alias directives should be ordered alphabetically by alias name
+dotnet_diagnostic.SA1212.severity = warning # Property accessors should follow order
+dotnet_diagnostic.SA1213.severity = warning # Event accessors should follow order
+dotnet_diagnostic.SA1214.severity = warning # Readonly fields should appear before non-readonly fields
+dotnet_diagnostic.SA1216.severity = warning # Using static directives should be placed at the correct location
+dotnet_diagnostic.SA1217.severity = warning # Using static directives should be ordered alphabetically
+
+# StyleCop Analyzers - Naming rules
+dotnet_diagnostic.SA1300.severity = warning # Element should begin with upper-case letter
+dotnet_diagnostic.SA1301.severity = warning # Element should begin with lower-case letter
+dotnet_diagnostic.SA1302.severity = warning # Interface names should begin with I
+dotnet_diagnostic.SA1303.severity = warning # Const field names should begin with upper-case letter
+dotnet_diagnostic.SA1304.severity = warning # Non-private readonly fields should begin with upper-case letter
+dotnet_diagnostic.SA1305.severity = none # Field names should not use Hungarian notation
+dotnet_diagnostic.SA1306.severity = warning # Field names should begin with lower-case letter
+dotnet_diagnostic.SA1307.severity = warning # Accessible fields should begin with upper-case letter
+dotnet_diagnostic.SA1308.severity = warning # Variable names should not be prefixed
+dotnet_diagnostic.SA1309.severity = none # Private instance field names should not begin with underscore
+dotnet_diagnostic.SX1309.severity = warning # Private instance field names should begin with underscore
+dotnet_diagnostic.SX1309S.severity = none # Static field names should begin with underscore
+dotnet_diagnostic.SA1310.severity = warning # Field names should not contain underscore
+dotnet_diagnostic.SA1311.severity = warning # Static readonly fields should begin with upper-case letter
+dotnet_diagnostic.SA1312.severity = warning # Variable names should begin with lower-case letter
+dotnet_diagnostic.SA1313.severity = warning # Parameter names should begin with lower-case letter
+dotnet_diagnostic.SA1314.severity = warning # Type parameter names should begin with T
+
+# StyleCop Analyzers - Maintainability rules
+dotnet_diagnostic.SA1119.severity = warning # Statement should not use unnecessary parenthesis
+dotnet_diagnostic.SA1400.severity = warning # Access modifier should be declared
+dotnet_diagnostic.SA1401.severity = warning # Fields should be private
+dotnet_diagnostic.SA1402.severity = warning # File may only contain a single type
+dotnet_diagnostic.SA1403.severity = warning # File may only contain a single namespace
+dotnet_diagnostic.SA1404.severity = warning # Code analysis suppression should have justification
+dotnet_diagnostic.SA1405.severity = warning # Debug.Assert should provide message text
+dotnet_diagnostic.SA1406.severity = warning # Debug.Fail should provide message text
+dotnet_diagnostic.SA1407.severity = none # Arithmetic expressions should declare precedence
+dotnet_diagnostic.SA1408.severity = warning # Conditional expressions should declare precedence
+dotnet_diagnostic.SA1409.severity = none # Remove unnecessary code
+dotnet_diagnostic.SA1410.severity = warning # Remove delegate parenthesis when possible
+dotnet_diagnostic.SA1411.severity = warning # Attribute constructor should not use unnecessary parenthesis
+dotnet_diagnostic.SA1412.severity = warning # Store files as UTF-8 with byte order mark
+dotnet_diagnostic.SA1413.severity = warning # Use trailing comma in multi-line initializers
+
+# StyleCop Analyzers - Layout rules
+dotnet_diagnostic.SA1500.severity = warning # Braces for multi-line statements should not share line
+dotnet_diagnostic.SA1501.severity = warning # Statement should not be on a single line
+dotnet_diagnostic.SA1502.severity = warning # Element should not be on a single line
+dotnet_diagnostic.SA1503.severity = warning # Braces should not be omitted
+dotnet_diagnostic.SA1504.severity = warning # All accessors should be single-line or multi-line
+dotnet_diagnostic.SA1505.severity = warning # Opening braces should not be followed by blank line
+dotnet_diagnostic.SA1506.severity = warning # Element documentation headers should not be followed by blank line
+dotnet_diagnostic.SA1507.severity = warning # Code should not contain multiple blank lines in a row
+dotnet_diagnostic.SA1508.severity = warning # Closing braces should not be preceded by blank line
+dotnet_diagnostic.SA1509.severity = warning # Opening braces should not be preceded by blank line
+dotnet_diagnostic.SA1510.severity = warning # Chained statement blocks should not be preceded by blank line
+dotnet_diagnostic.SA1511.severity = warning # While-do footer should not be preceded by blank line
+dotnet_diagnostic.SA1512.severity = warning # Single-line comments should not be followed by blank line
+dotnet_diagnostic.SA1513.severity = warning # Closing brace should be followed by blank line
+dotnet_diagnostic.SA1514.severity = warning # Element documentation header should be preceded by blank line
+dotnet_diagnostic.SA1515.severity = warning # Single-line comment should be preceded by blank line
+dotnet_diagnostic.SA1516.severity = none # Elements should be separated by blank line
+dotnet_diagnostic.SA1517.severity = warning # Code should not contain blank lines at start of file
+dotnet_diagnostic.SA1518.severity = warning # Use line endings correctly at end of file
+dotnet_diagnostic.SA1519.severity = warning # Braces should not be omitted from multi-line child statement
+dotnet_diagnostic.SA1520.severity = warning # Use braces consistently
+
+# StyleCop Analyzers - Documentation rules
+dotnet_diagnostic.SA1600.severity = warning # Elements should be documented
+dotnet_diagnostic.SA1601.severity = none # Partial elements should be documented
+dotnet_diagnostic.SA1602.severity = warning # Enumeration items should be documented
+dotnet_diagnostic.SA1603.severity = warning # Documentation should contain valid XML
+dotnet_diagnostic.SA1604.severity = warning # Element documentation should have summary
+dotnet_diagnostic.SA1605.severity = warning # Partial element documentation should have summary
+dotnet_diagnostic.SA1606.severity = warning # Element documentation should have summary text
+dotnet_diagnostic.SA1607.severity = warning # Partial element documentation should have summary text
+dotnet_diagnostic.SA1608.severity = warning # Element documentation should not have default summary
+dotnet_diagnostic.SA1609.severity = none # Property documentation should have value
+dotnet_diagnostic.SA1610.severity = none # Property documentation should have value text
+dotnet_diagnostic.SA1611.severity = warning # Element parameters should be documented
+dotnet_diagnostic.SA1612.severity = warning # Element parameter documentation should match element parameters
+dotnet_diagnostic.SA1613.severity = warning # Element parameter documentation should declare parameter name
+dotnet_diagnostic.SA1614.severity = warning # Element parameter documentation should have text
+dotnet_diagnostic.SA1615.severity = warning # Element return value should be documented
+dotnet_diagnostic.SA1616.severity = warning # Element return value documentation should have text
+dotnet_diagnostic.SA1617.severity = warning # Void return value should not be documented
+dotnet_diagnostic.SA1618.severity = warning # Generic type parameters should be documented
+dotnet_diagnostic.SA1619.severity = warning # Generic type parameters should be documented partial class
+dotnet_diagnostic.SA1620.severity = warning # Generic type parameter documentation should match type parameters
+dotnet_diagnostic.SA1621.severity = warning # Generic type parameter documentation should declare parameter name
+dotnet_diagnostic.SA1622.severity = warning # Generic type parameter documentation should have text
+dotnet_diagnostic.SA1623.severity = warning # Property summary documentation should match accessors
+dotnet_diagnostic.SA1624.severity = warning # Property summary documentation should omit accessor with restricted access
+dotnet_diagnostic.SA1625.severity = warning # Element documentation should not be copied and pasted
+dotnet_diagnostic.SA1626.severity = warning # Single-line comments should not use documentation style slashes
+dotnet_diagnostic.SA1627.severity = warning # Documentation text should not be empty
+dotnet_diagnostic.SA1628.severity = warning # Documentation text should begin with a capital letter
+dotnet_diagnostic.SA1629.severity = warning # Documentation text should end with a period
+dotnet_diagnostic.SA1630.severity = warning # Documentation text should contain whitespace
+dotnet_diagnostic.SA1631.severity = none # Documentation should meet character percentage
+dotnet_diagnostic.SA1632.severity = none # Documentation text should meet minimum character length
+dotnet_diagnostic.SA1633.severity = warning # File should have header
+dotnet_diagnostic.SA1634.severity = warning # File header should show copyright
+dotnet_diagnostic.SA1635.severity = warning # File header should have copyright text
+dotnet_diagnostic.SA1636.severity = warning # File header copyright text should match
+dotnet_diagnostic.SA1637.severity = warning # File header should contain file name
+dotnet_diagnostic.SA1638.severity = warning # File header file name documentation should match file name
+dotnet_diagnostic.SA1639.severity = warning # File header should have summary
+dotnet_diagnostic.SA1640.severity = warning # File header should have valid company text
+dotnet_diagnostic.SA1641.severity = warning # File header company name text should match
+dotnet_diagnostic.SA1642.severity = warning # Constructor summary documentation should begin with standard text
+dotnet_diagnostic.SA1643.severity = warning # Destructor summary documentation should begin with standard text
+dotnet_diagnostic.SA1644.severity = warning # Documentation headers should not contain blank lines
+dotnet_diagnostic.SA1645.severity = warning # Included documentation file does not exist
+dotnet_diagnostic.SA1646.severity = warning # Included documentation XPath does not exist
+dotnet_diagnostic.SA1647.severity = warning # Include node does not contain valid file and path
+dotnet_diagnostic.SA1648.severity = warning # Inheritdoc should be used with inheriting class
+dotnet_diagnostic.SA1649.severity = warning # File name should match first type name
+dotnet_diagnostic.SA1650.severity = warning # Element documentation should be spelled correctly
+dotnet_diagnostic.SA1651.severity = warning # Do not use placeholder elements
+
+# Suppress some annoying IDE messages
+dotnet_diagnostic.IDE0040.severity = none # Accessibility modifiers required - has confusing rules and is a dupe of SA1400.
+dotnet_diagnostic.IDE0079.severity = none # Remove unnecessary suppression - apparently VS (as of v17.3.0) deems way too much stuff "unnecessary".
+dotnet_diagnostic.IDE0290.severity = none # Use primary constructor - No. Just no.
+
+# Solution-specific overrides
+
+# Some types and memberts are unused but present for completeness and/or future use, or instantiated via DI.
+# We don't need to flag every unused type or member: sprinkling PublicAPI and/or UsedImplicitly attributes around would be mostly noise.
+resharper_class_never_instantiated_global_highlighting = none
+resharper_unused_type_global_highlighting = none
+resharper_unused_member_global_highlighting = none
+resharper_member_can_be_private_global_highlighting = none
+
+# Given the type of C# projects (compiled MSBuild tasks, CLI tool) we don't need to optimize logging with LoggerMessage delegates (CA1848),
+# nor do we need CA1873 to complain that "argument evaluation may be expensive when logging is disabled".
+# Disabling these rules globally helps keep code clean and simple without needing to sprinkle pragmas or suppression attributes around the codebase.
+dotnet_diagnostic.CA1848.severity = none
+dotnet_diagnostic.CA1873.severity = none
diff --git a/.globalconfig b/.globalconfig
deleted file mode 100644
index d4788a63..00000000
--- a/.globalconfig
+++ /dev/null
@@ -1,262 +0,0 @@
-# Global AnalyzerConfig file
-# See https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/configuration-files#global-analyzerconfig
-
-is_global = true
-
-### Miscellaneous code analysis configuration
-
-# Relaxed rules related to IDisposable implementation - https://blog.stephencleary.com/2009/08/how-to-implement-idisposable-and.html
-dotnet_diagnostic.CA1063.severity = none # CA1063: Implement IDisposable correctly
-dotnet_diagnostic.CA1816.severity = none # CA1816: Call GC.SuppressFinalize correctly
-
-# Disable CA1508 (false positive when a value is set inside a catch block)
-dotnet_diagnostic.CA1508.severity = none # CA1508: Avoid dead conditional code
-
-# Rules stricter than the default
-dotnet_diagnostic.CA1001.severity = warning # CA1001: Types that own disposable fields should be disposable
-dotnet_diagnostic.CA1047.severity = warning # CA1047: Do not declare protected members in sealed types
-dotnet_diagnostic.CA1070.severity = warning # CA1070: Do not declare event fields as virtual
-dotnet_diagnostic.CA2000.severity = warning # CA2000: Dispose objects before losing scope
-dotnet_diagnostic.IDE0043.severity = error # IDE0043: Format string contains invalid placeholder
-dotnet_diagnostic.IDE0076.severity = warning # IDE0076: Remove invalid global 'SuppressMessageAttribute'
-dotnet_diagnostic.IDE0077.severity = warning # IDE0077: Avoid legacy format target in global 'SuppressMessageAttribute'
-
-# Miscellaneous relaxations
-dotnet_diagnostic.CA1000.severity = none # CA1000: Do not declare static members on generic types
-dotnet_diagnostic.CA1032.severity = suggestion # CA1032: Implement standard exception constructors
-dotnet_diagnostic.CA1303.severity = none # CA1303: Do not pass literals as localized parameters
-
-# Miscellaneous configuration
-dotnet_code_quality.CA1303.use_naming_heuristic = false # Don't raise CA1303 just because a parameter name contains Text, Message, or Caption.
-dotnet_code_quality.CA1062.exclude_extension_method_this_parameter = true # Extension methods: null check may be skipped on "this" parameter.
-dotnet_code_quality.CA1062.null_check_validation_methods = System.String.IsNullOrEmpty|System.String.IsNullOrWhiteSpace
-dotnet_code_quality.dispose_ownership_transfer_at_constructor = true # Disposables passed to a constructor must be disposed by the constructed object.
-
-### StyleCop Analyzers rules
-
-# StyleCop Analyzers - Special rules
-dotnet_diagnostic.SA0001.severity = warning # XML comment analysis disabled
-dotnet_diagnostic.SA0002.severity = warning # Invalid settings file
-
-# StyleCop Analyzers - Spacing rules
-dotnet_diagnostic.SA1000.severity = warning # Keywords should be spaced correctly
-dotnet_diagnostic.SA1001.severity = warning # Commas should be spaced correctly
-dotnet_diagnostic.SA1002.severity = warning # Semicolons should be spaced correctly
-dotnet_diagnostic.SA1003.severity = warning # Symbols should be spaced correctly
-dotnet_diagnostic.SA1004.severity = warning # Documentation lines should begin with single space
-dotnet_diagnostic.SA1005.severity = warning # Single line comments should begin with single space
-dotnet_diagnostic.SA1006.severity = warning # Preprocessor keywords should not be preceded by space
-dotnet_diagnostic.SA1007.severity = warning # Operator keyword should be followed by space
-dotnet_diagnostic.SA1008.severity = warning # Opening parenthesis should be spaced correctly
-dotnet_diagnostic.SA1009.severity = warning # Closing parenthesis should be spaced correctly
-dotnet_diagnostic.SA1010.severity = warning # Opening square brackets should be spaced correctly
-dotnet_diagnostic.SA1011.severity = warning # Closing square brackets should be spaced correctly
-dotnet_diagnostic.SA1012.severity = warning # Opening braces should be spaced correctly
-dotnet_diagnostic.SA1013.severity = warning # Closing braces should be spaced correctly
-dotnet_diagnostic.SA1014.severity = warning # Opening generic brackets should be spaced correctly
-dotnet_diagnostic.SA1015.severity = warning # Closing generic brackets should be spaced correctly
-dotnet_diagnostic.SA1016.severity = warning # Opening attribute brackets should be spaced correctly
-dotnet_diagnostic.SA1017.severity = warning # Closing attribute brackets should be spaced correctly
-dotnet_diagnostic.SA1018.severity = warning # Nullable type symbols should be spaced correctly
-dotnet_diagnostic.SA1019.severity = warning # Member access symbols should be spaced correctly
-dotnet_diagnostic.SA1020.severity = warning # Increment decrement symbols should be spaced correctly
-dotnet_diagnostic.SA1021.severity = warning # Negative signs should be spaced correctly
-dotnet_diagnostic.SA1022.severity = warning # Positive signs should be spaced correctly
-dotnet_diagnostic.SA1023.severity = warning # Dereference and access of symbols should be spaced correctly
-dotnet_diagnostic.SA1024.severity = warning # Colons should be spaced correctly
-dotnet_diagnostic.SA1025.severity = warning # Code should not contain multiple whitespace in a row
-dotnet_diagnostic.SA1026.severity = warning # Code should not contain space after new or stackalloc keyword in implicitly typed array allocation
-dotnet_diagnostic.SA1027.severity = warning # Use tabs correctly
-dotnet_diagnostic.SA1028.severity = warning # Code should not contain trailing whitespace
-
-# StyleCop Analyzers - Readability rules
-dotnet_diagnostic.SA1100.severity = warning # Do not prefix calls with base unless local implementation exists
-dotnet_diagnostic.SA1101.severity = none # Prefix local calls with this
-dotnet_diagnostic.SX1101.severity = warning # Do not prefix local calls with this
-dotnet_diagnostic.SA1102.severity = warning # Query clause should follow previous clause
-dotnet_diagnostic.SA1103.severity = warning # Query clauses should be on separate lines or all on one line
-dotnet_diagnostic.SA1104.severity = warning # Query clause should begin on new line when previous clause spans multiple lines
-dotnet_diagnostic.SA1105.severity = warning # Query clauses spanning multiple lines should begin on own line
-dotnet_diagnostic.SA1106.severity = warning # Code should not contain empty statements
-dotnet_diagnostic.SA1107.severity = warning # Code should not contain multiple statements on one line
-dotnet_diagnostic.SA1108.severity = warning # Block statements should not contain embedded comments
-dotnet_diagnostic.SA1109.severity = warning # Block statements should not contain embedded regions
-dotnet_diagnostic.SA1110.severity = warning # Opening parenthesis or bracket should be on declaration line
-dotnet_diagnostic.SA1111.severity = warning # Closing parenthesis should be on line of last parameter
-dotnet_diagnostic.SA1112.severity = warning # Closing parenthesis should be on line of opening parenthesis
-dotnet_diagnostic.SA1113.severity = warning # Comma should be on the same line as previous parameter
-dotnet_diagnostic.SA1114.severity = warning # Parameter list should follow declaration
-dotnet_diagnostic.SA1115.severity = warning # Parameter should follow comma
-dotnet_diagnostic.SA1116.severity = warning # Split parameters should start on line after declaration
-dotnet_diagnostic.SA1117.severity = warning # Parameters should be on same line or separate lines
-dotnet_diagnostic.SA1118.severity = warning # Parameter should not span multiple lines
-dotnet_diagnostic.SA1120.severity = warning # Comments should contain text
-dotnet_diagnostic.SA1121.severity = warning # Use built-in type alias
-dotnet_diagnostic.SA1122.severity = warning # Use string.Empty for empty strings
-dotnet_diagnostic.SA1123.severity = warning # Do not place regions within elements
-dotnet_diagnostic.SA1124.severity = warning # Do not use regions
-dotnet_diagnostic.SA1125.severity = warning # Use shorthand for nullable types
-dotnet_diagnostic.SA1126.severity = warning # Prefix calls correctly
-dotnet_diagnostic.SA1127.severity = warning # Generic type constraints should be on their own line
-dotnet_diagnostic.SA1128.severity = warning # Put constructor initializers on their own line
-dotnet_diagnostic.SA1129.severity = warning # Do not use default value type constructor
-dotnet_diagnostic.SA1130.severity = warning # Use lambda syntax
-dotnet_diagnostic.SA1131.severity = warning # Use readable conditions
-dotnet_diagnostic.SA1132.severity = warning # Do not combine fields
-dotnet_diagnostic.SA1133.severity = warning # Do not combine attributes
-dotnet_diagnostic.SA1134.severity = warning # Attributes should not share line
-dotnet_diagnostic.SA1135.severity = warning # Using directives should be qualified
-dotnet_diagnostic.SA1136.severity = warning # Enum values should be on separate lines
-dotnet_diagnostic.SA1137.severity = warning # Elements should have the same indentation
-dotnet_diagnostic.SA1139.severity = warning # Use literal suffix notation instead of casting
-
-# StyleCop Analyzers - Ordering rules
-dotnet_diagnostic.SA1200.severity = warning # Using directives should be placed correctly
-dotnet_diagnostic.SA1201.severity = warning # Elements should appear in the correct order
-dotnet_diagnostic.SA1202.severity = warning # Elements should be ordered by access
-dotnet_diagnostic.SA1203.severity = warning # Constants should appear before fields
-dotnet_diagnostic.SA1204.severity = warning # Static elements should appear before instance elements
-dotnet_diagnostic.SA1205.severity = none # Partial elements should declare access
-dotnet_diagnostic.SA1206.severity = warning # Declaration keywords should follow order
-dotnet_diagnostic.SA1207.severity = warning # Protected should come before internal
-dotnet_diagnostic.SA1208.severity = warning # System using directives should be placed before other using directives
-dotnet_diagnostic.SA1209.severity = warning # Using alias directives should be placed after other using directives
-dotnet_diagnostic.SA1210.severity = warning # Using directives should be ordered alphabetically by namespace
-dotnet_diagnostic.SA1211.severity = warning # Using alias directives should be ordered alphabetically by alias name
-dotnet_diagnostic.SA1212.severity = warning # Property accessors should follow order
-dotnet_diagnostic.SA1213.severity = warning # Event accessors should follow order
-dotnet_diagnostic.SA1214.severity = warning # Readonly fields should appear before non-readonly fields
-dotnet_diagnostic.SA1216.severity = warning # Using static directives should be placed at the correct location
-dotnet_diagnostic.SA1217.severity = warning # Using static directives should be ordered alphabetically
-
-# StyleCop Analyzers - Naming rules
-dotnet_diagnostic.SA1300.severity = warning # Element should begin with upper-case letter
-dotnet_diagnostic.SA1301.severity = warning # Element should begin with lower-case letter
-dotnet_diagnostic.SA1302.severity = warning # Interface names should begin with I
-dotnet_diagnostic.SA1303.severity = warning # Const field names should begin with upper-case letter
-dotnet_diagnostic.SA1304.severity = warning # Non-private readonly fields should begin with upper-case letter
-dotnet_diagnostic.SA1305.severity = none # Field names should not use Hungarian notation
-dotnet_diagnostic.SA1306.severity = warning # Field names should begin with lower-case letter
-dotnet_diagnostic.SA1307.severity = warning # Accessible fields should begin with upper-case letter
-dotnet_diagnostic.SA1308.severity = warning # Variable names should not be prefixed
-dotnet_diagnostic.SA1309.severity = none # Private instance field names should not begin with underscore
-dotnet_diagnostic.SX1309.severity = warning # Private instance field names should begin with underscore
-dotnet_diagnostic.SX1309S.severity = none # Static field names should begin with underscore
-dotnet_diagnostic.SA1310.severity = warning # Field names should not contain underscore
-dotnet_diagnostic.SA1311.severity = warning # Static readonly fields should begin with upper-case letter
-dotnet_diagnostic.SA1312.severity = warning # Variable names should begin with lower-case letter
-dotnet_diagnostic.SA1313.severity = warning # Parameter names should begin with lower-case letter
-dotnet_diagnostic.SA1314.severity = warning # Type parameter names should begin with T
-
-# StyleCop Analyzers - Maintainability rules
-dotnet_diagnostic.SA1119.severity = warning # Statement should not use unnecessary parenthesis
-dotnet_diagnostic.SA1400.severity = warning # Access modifier should be declared
-dotnet_diagnostic.SA1401.severity = warning # Fields should be private
-dotnet_diagnostic.SA1402.severity = warning # File may only contain a single type
-dotnet_diagnostic.SA1403.severity = warning # File may only contain a single namespace
-dotnet_diagnostic.SA1404.severity = warning # Code analysis suppression should have justification
-dotnet_diagnostic.SA1405.severity = warning # Debug.Assert should provide message text
-dotnet_diagnostic.SA1406.severity = warning # Debug.Fail should provide message text
-dotnet_diagnostic.SA1407.severity = none # Arithmetic expressions should declare precedence
-dotnet_diagnostic.SA1408.severity = warning # Conditional expressions should declare precedence
-dotnet_diagnostic.SA1409.severity = none # Remove unnecessary code
-dotnet_diagnostic.SA1410.severity = warning # Remove delegate parenthesis when possible
-dotnet_diagnostic.SA1411.severity = warning # Attribute constructor should not use unnecessary parenthesis
-dotnet_diagnostic.SA1412.severity = warning # Store files as UTF-8 with byte order mark
-dotnet_diagnostic.SA1413.severity = warning # Use trailing comma in multi-line initializers
-
-# StyleCop Analyzers - Layout rules
-dotnet_diagnostic.SA1500.severity = warning # Braces for multi-line statements should not share line
-dotnet_diagnostic.SA1501.severity = warning # Statement should not be on a single line
-dotnet_diagnostic.SA1502.severity = warning # Element should not be on a single line
-dotnet_diagnostic.SA1503.severity = warning # Braces should not be omitted
-dotnet_diagnostic.SA1504.severity = warning # All accessors should be single-line or multi-line
-dotnet_diagnostic.SA1505.severity = warning # Opening braces should not be followed by blank line
-dotnet_diagnostic.SA1506.severity = warning # Element documentation headers should not be followed by blank line
-dotnet_diagnostic.SA1507.severity = warning # Code should not contain multiple blank lines in a row
-dotnet_diagnostic.SA1508.severity = warning # Closing braces should not be preceded by blank line
-dotnet_diagnostic.SA1509.severity = warning # Opening braces should not be preceded by blank line
-dotnet_diagnostic.SA1510.severity = warning # Chained statement blocks should not be preceded by blank line
-dotnet_diagnostic.SA1511.severity = warning # While-do footer should not be preceded by blank line
-dotnet_diagnostic.SA1512.severity = warning # Single-line comments should not be followed by blank line
-dotnet_diagnostic.SA1513.severity = warning # Closing brace should be followed by blank line
-dotnet_diagnostic.SA1514.severity = warning # Element documentation header should be preceded by blank line
-dotnet_diagnostic.SA1515.severity = warning # Single-line comment should be preceded by blank line
-dotnet_diagnostic.SA1516.severity = none # Elements should be separated by blank line
-dotnet_diagnostic.SA1517.severity = warning # Code should not contain blank lines at start of file
-dotnet_diagnostic.SA1518.severity = warning # Use line endings correctly at end of file
-dotnet_diagnostic.SA1519.severity = warning # Braces should not be omitted from multi-line child statement
-dotnet_diagnostic.SA1520.severity = warning # Use braces consistently
-
-# StyleCop Analyzers - Documentation rules
-dotnet_diagnostic.SA1600.severity = warning # Elements should be documented
-dotnet_diagnostic.SA1601.severity = none # Partial elements should be documented
-dotnet_diagnostic.SA1602.severity = warning # Enumeration items should be documented
-dotnet_diagnostic.SA1603.severity = warning # Documentation should contain valid XML
-dotnet_diagnostic.SA1604.severity = warning # Element documentation should have summary
-dotnet_diagnostic.SA1605.severity = warning # Partial element documentation should have summary
-dotnet_diagnostic.SA1606.severity = warning # Element documentation should have summary text
-dotnet_diagnostic.SA1607.severity = warning # Partial element documentation should have summary text
-dotnet_diagnostic.SA1608.severity = warning # Element documentation should not have default summary
-dotnet_diagnostic.SA1609.severity = none # Property documentation should have value
-dotnet_diagnostic.SA1610.severity = none # Property documentation should have value text
-dotnet_diagnostic.SA1611.severity = warning # Element parameters should be documented
-dotnet_diagnostic.SA1612.severity = warning # Element parameter documentation should match element parameters
-dotnet_diagnostic.SA1613.severity = warning # Element parameter documentation should declare parameter name
-dotnet_diagnostic.SA1614.severity = warning # Element parameter documentation should have text
-dotnet_diagnostic.SA1615.severity = warning # Element return value should be documented
-dotnet_diagnostic.SA1616.severity = warning # Element return value documentation should have text
-dotnet_diagnostic.SA1617.severity = warning # Void return value should not be documented
-dotnet_diagnostic.SA1618.severity = warning # Generic type parameters should be documented
-dotnet_diagnostic.SA1619.severity = warning # Generic type parameters should be documented partial class
-dotnet_diagnostic.SA1620.severity = warning # Generic type parameter documentation should match type parameters
-dotnet_diagnostic.SA1621.severity = warning # Generic type parameter documentation should declare parameter name
-dotnet_diagnostic.SA1622.severity = warning # Generic type parameter documentation should have text
-dotnet_diagnostic.SA1623.severity = warning # Property summary documentation should match accessors
-dotnet_diagnostic.SA1624.severity = warning # Property summary documentation should omit accessor with restricted access
-dotnet_diagnostic.SA1625.severity = warning # Element documentation should not be copied and pasted
-dotnet_diagnostic.SA1626.severity = warning # Single-line comments should not use documentation style slashes
-dotnet_diagnostic.SA1627.severity = warning # Documentation text should not be empty
-dotnet_diagnostic.SA1628.severity = warning # Documentation text should begin with a capital letter
-dotnet_diagnostic.SA1629.severity = warning # Documentation text should end with a period
-dotnet_diagnostic.SA1630.severity = warning # Documentation text should contain whitespace
-dotnet_diagnostic.SA1631.severity = none # Documentation should meet character percentage
-dotnet_diagnostic.SA1632.severity = none # Documentation text should meet minimum character length
-dotnet_diagnostic.SA1633.severity = warning # File should have header
-dotnet_diagnostic.SA1634.severity = warning # File header should show copyright
-dotnet_diagnostic.SA1635.severity = warning # File header should have copyright text
-dotnet_diagnostic.SA1636.severity = warning # File header copyright text should match
-dotnet_diagnostic.SA1637.severity = warning # File header should contain file name
-dotnet_diagnostic.SA1638.severity = warning # File header file name documentation should match file name
-dotnet_diagnostic.SA1639.severity = warning # File header should have summary
-dotnet_diagnostic.SA1640.severity = warning # File header should have valid company text
-dotnet_diagnostic.SA1641.severity = warning # File header company name text should match
-dotnet_diagnostic.SA1642.severity = warning # Constructor summary documentation should begin with standard text
-dotnet_diagnostic.SA1643.severity = warning # Destructor summary documentation should begin with standard text
-dotnet_diagnostic.SA1644.severity = warning # Documentation headers should not contain blank lines
-dotnet_diagnostic.SA1645.severity = warning # Included documentation file does not exist
-dotnet_diagnostic.SA1646.severity = warning # Included documentation XPath does not exist
-dotnet_diagnostic.SA1647.severity = warning # Include node does not contain valid file and path
-dotnet_diagnostic.SA1648.severity = warning # Inheritdoc should be used with inheriting class
-dotnet_diagnostic.SA1649.severity = warning # File name should match first type name
-dotnet_diagnostic.SA1650.severity = warning # Element documentation should be spelled correctly
-dotnet_diagnostic.SA1651.severity = warning # Do not use placeholder elements
-
-# Suppress some annoying IDE messages
-dotnet_diagnostic.IDE0040.severity = none # Accessibility modifiers required - has confusing rules and is a dupe of SA1400.
-dotnet_diagnostic.IDE0079.severity = none # Remove unnecessary suppression - apparently VS (as of v17.3.0) deems way too much stuff "unnecessary".
-dotnet_diagnostic.IDE0290.severity = none # Use primary constructor - No. Just no.
-
-# Solution-specific overrides
-
-# This project generates no library packages; still, public types are used via reflection and/or DI, so "unused" public types are not a concern.
-resharper_class_never_instantiated_global_highlighting = none
-
-# Some methods are unused but present for completeness and/or future use; "unused" members are not a concern.
-resharper_unused_member_global_highlighting = none
-
-# Given the type of C# projects (compiled MSBuild tasks, CLI tool) we don't need to optimize logging with LoggerMessage delegates (CA1848),
-# nor do we need CA1873 to complain that "argument evaluation may be expensive when logging is disabled".
-# Disabling these rules globally helps keep code clean and simple without needing to sprinkle pragmas or suppression attributes around the codebase.
-dotnet_diagnostic.CA1848.severity = none
-dotnet_diagnostic.CA1873.severity = none
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 55ed183f..158dfa8b 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -74,7 +74,8 @@
// Configure file associations to languages (e.g. "*.extension": "html"). These have precedence over the default associations of the languages installed.
"files.associations": {
".globalconfig": "editorconfig",
- "*.md": "markdown"
+ "*.md": "markdown",
+ "*.sarif": "json",
},
// The default character set encoding to use when reading and writing files.
"files.encoding": "utf8",
diff --git a/Buildvana.slnx.DotSettings b/Buildvana.slnx.DotSettings
index 785161b7..7e87876f 100644
--- a/Buildvana.slnx.DotSettings
+++ b/Buildvana.slnx.DotSettings
@@ -2,6 +2,7 @@
None
NotCompiledCode
True
+ SOLUTION
DoShow
DoShow
DoShow
@@ -424,6 +425,7 @@
WARNING
DO_NOT_SHOW
WARNING
+ ShowAndRun
MergeVsActionsIntoResharperMenu
Experimental
LF
@@ -487,6 +489,7 @@
<Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"><ElementKinds><Kind Name="NAMESPACE" /><Kind Name="CLASS" /><Kind Name="STRUCT" /><Kind Name="ENUM" /><Kind Name="DELEGATE" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /></Policy>
<Configurator><ConnectList /></Configurator>
True
+ True
True
True
True
@@ -494,6 +497,7 @@
True
System.CodeDom.Compiler.GeneratedCodeAttribute
<data><AttributeFilter ClassMask="System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute" IsEnabled="True" /><AttributeFilter ClassMask="System.CodeDom.Compiler.GeneratedCodeAttribute" IsEnabled="True" /></data>
+ MergeVsActionsIntoResharperMenu
True
True
True
diff --git a/Common.targets b/Common.targets
new file mode 100644
index 00000000..2992475b
--- /dev/null
+++ b/Common.targets
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/schemas/buildvana.schema.json b/schemas/buildvana.schema.json
index b879f663..55becb7b 100644
--- a/schemas/buildvana.schema.json
+++ b/schemas/buildvana.schema.json
@@ -95,7 +95,10 @@
"description": "Environment variables forwarded to \u0060dotnet\u0060, keyed by variable name.",
"type": "object",
"additionalProperties": {
- "type": "string"
+ "type": [
+ "string",
+ "null"
+ ]
}
}
},
diff --git a/src/Buildvana.Core.Abstractions/BuildFailedException.cs b/src/Buildvana.Core.Abstractions/BuildFailedException.cs
index d307967a..5bbbe523 100644
--- a/src/Buildvana.Core.Abstractions/BuildFailedException.cs
+++ b/src/Buildvana.Core.Abstractions/BuildFailedException.cs
@@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
-using System.Linq;
using System.Runtime.CompilerServices;
namespace Buildvana.Core;
diff --git a/src/Buildvana.Core.Abstractions/Buildvana.Core.Abstractions.csproj b/src/Buildvana.Core.Abstractions/Buildvana.Core.Abstractions.csproj
index d49630b7..4cbb56d1 100644
--- a/src/Buildvana.Core.Abstractions/Buildvana.Core.Abstractions.csproj
+++ b/src/Buildvana.Core.Abstractions/Buildvana.Core.Abstractions.csproj
@@ -8,8 +8,4 @@
false
-
-
-
-
diff --git a/src/Buildvana.Core.Abstractions/Process/IProcessRunner.cs b/src/Buildvana.Core.Abstractions/Process/IProcessRunner.cs
index 9f70ed4e..fefc15f2 100644
--- a/src/Buildvana.Core.Abstractions/Process/IProcessRunner.cs
+++ b/src/Buildvana.Core.Abstractions/Process/IProcessRunner.cs
@@ -18,8 +18,13 @@ public interface IProcessRunner
///
/// The path to (or name of) the executable to run.
/// The arguments to pass to .
- /// The working directory in which to run the process, or to inherit the current process's working directory.
- /// If (the default), a is thrown when the process exits with a non-zero exit code; if , the result is returned regardless of exit code.
+ /// Environment variables to apply on top of the inherited environment: each entry adds or
+ /// overrides a variable, and a value removes that variable from the child process. Pass
+ /// to run with the current process's environment unchanged.
+ /// The working directory in which to run the process, or
+ /// to inherit the current process's working directory.
+ /// If (the default), a is thrown when
+ /// the process exits with a non-zero exit code; if , the result is returned regardless of exit code.
/// An optional callback invoked once per line of standard output as it is produced.
/// The full output text is captured into the returned regardless.
/// An optional callback invoked once per line of standard error as it is produced.
@@ -29,6 +34,7 @@ public interface IProcessRunner
Task RunAsync(
string executable,
IEnumerable args,
+ IReadOnlyDictionary? environment = null,
string? workingDirectory = null,
bool throwOnNonZero = true,
Action? onStdout = null,
diff --git a/src/Buildvana.Core.Configuration/Buildvana.Core.Configuration.csproj b/src/Buildvana.Core.Configuration/Buildvana.Core.Configuration.csproj
index 0771c421..cb4d8da7 100644
--- a/src/Buildvana.Core.Configuration/Buildvana.Core.Configuration.csproj
+++ b/src/Buildvana.Core.Configuration/Buildvana.Core.Configuration.csproj
@@ -13,7 +13,6 @@
-
diff --git a/src/Buildvana.Core.Configuration/DotNetInvocationConfig.cs b/src/Buildvana.Core.Configuration/DotNetInvocationConfig.cs
index ea314293..9f009538 100644
--- a/src/Buildvana.Core.Configuration/DotNetInvocationConfig.cs
+++ b/src/Buildvana.Core.Configuration/DotNetInvocationConfig.cs
@@ -18,5 +18,5 @@ public sealed record DotNetInvocationConfig
/// Gets environment variables forwarded to dotnet, keyed by variable name.
///
[Description("Environment variables forwarded to `dotnet`, keyed by variable name.")]
- public IReadOnlyDictionary? Env { get; init; }
+ public IReadOnlyDictionary? Env { get; init; }
}
diff --git a/src/Buildvana.Core.HomeDirectory/HomeDirectoryDiscovery.cs b/src/Buildvana.Core.HomeDirectory/HomeDirectoryDiscovery.cs
index d2bb682b..44303aef 100644
--- a/src/Buildvana.Core.HomeDirectory/HomeDirectoryDiscovery.cs
+++ b/src/Buildvana.Core.HomeDirectory/HomeDirectoryDiscovery.cs
@@ -35,7 +35,7 @@ public static bool TryDiscover(string startDirectory, [MaybeNullWhen(false)] out
{
Guard.IsNotNullOrEmpty(startDirectory);
- string? current = Path.GetFullPath(startDirectory);
+ var current = Path.GetFullPath(startDirectory);
while (current is not null)
{
if (DirectoryContainsMarker(current))
diff --git a/src/Buildvana.Core.JsonSchema/Buildvana.Core.JsonSchema.csproj b/src/Buildvana.Core.JsonSchema/Buildvana.Core.JsonSchema.csproj
index 55565ce6..2ef1dd09 100644
--- a/src/Buildvana.Core.JsonSchema/Buildvana.Core.JsonSchema.csproj
+++ b/src/Buildvana.Core.JsonSchema/Buildvana.Core.JsonSchema.csproj
@@ -2,7 +2,7 @@
Buildvana JSON schema
- Generic JSON schema generation and validation, independent of any specific model. Reports validation failures via JsonSchemaValidationException.
+ Generic JSON schema generation and validation, independent of any specific model.
$(StandardTfm)
diff --git a/src/Buildvana.Core.JsonSchema/JsonNullableAttribute.cs b/src/Buildvana.Core.JsonSchema/JsonNullableAttribute.cs
index ca79375e..653c6c6c 100644
--- a/src/Buildvana.Core.JsonSchema/JsonNullableAttribute.cs
+++ b/src/Buildvana.Core.JsonSchema/JsonNullableAttribute.cs
@@ -14,6 +14,4 @@ namespace Buildvana.Core.JsonSchema;
/// "unset", and an explicit null is disallowed by the generated schema.
///
[AttributeUsage(AttributeTargets.Property)]
-public sealed class JsonNullableAttribute : Attribute
-{
-}
+public sealed class JsonNullableAttribute : Attribute;
diff --git a/src/Buildvana.Core.JsonSchema/JsonSchemaGenerator.cs b/src/Buildvana.Core.JsonSchema/JsonSchemaGenerator.cs
index f75a36e1..afaac43b 100644
--- a/src/Buildvana.Core.JsonSchema/JsonSchemaGenerator.cs
+++ b/src/Buildvana.Core.JsonSchema/JsonSchemaGenerator.cs
@@ -20,6 +20,13 @@ namespace Buildvana.Core.JsonSchema;
///
/// The same should drive both generation and deserialization, so the
/// schema always describes exactly what the deserializer accepts.
+/// marks every reference-type dictionary value and collection element
+/// nullable regardless of how the model annotates it, so the generator reconciles that against the declared
+/// nullability read from the owning property or field via . This requires
+/// a member to read the annotations from: when the type being described is itself a dictionary or
+/// collection (so its values or elements have no owning member), their declared nullability cannot be
+/// recovered and the nullability emitted by the exporter is kept as-is. Wrap such a type in a containing
+/// object property to control the nullability of its values or elements.
///
public static class JsonSchemaGenerator
{
@@ -44,7 +51,11 @@ public static JsonNode Generate(Type type, JsonSerializerOptions options)
ArgumentNullException.ThrowIfNull(type);
ArgumentNullException.ThrowIfNull(options);
- var exporterOptions = new JsonSchemaExporterOptions { TransformSchemaNode = TransformSchemaNode };
+ var nullabilityContext = new NullabilityInfoContext();
+ var exporterOptions = new JsonSchemaExporterOptions
+ {
+ TransformSchemaNode = (context, schema) => TransformSchemaNode(context, schema, nullabilityContext),
+ };
var schema = options.GetJsonSchemaAsNode(type, exporterOptions);
// Declare the dialect and (optionally) a title so editors recognize and label the document.
@@ -60,7 +71,10 @@ public static JsonNode Generate(Type type, JsonSerializerOptions options)
return schema;
}
- private static JsonNode TransformSchemaNode(JsonSchemaExporterContext context, JsonNode schema)
+ private static JsonNode TransformSchemaNode(
+ JsonSchemaExporterContext context,
+ JsonNode schema,
+ NullabilityInfoContext nullabilityContext)
{
var attributeProvider = context.PropertyInfo is not null
? context.PropertyInfo.AttributeProvider
@@ -68,17 +82,32 @@ private static JsonNode TransformSchemaNode(JsonSchemaExporterContext context, J
schema = ApplyDescription(attributeProvider, schema);
- // Strip "null" wherever the exporter emits it, except where a property opts in with [JsonNullable]:
- // a nullable property otherwise means "unset" (an absent key), which needs no explicit null.
+ // Strip the "null" the exporter adds to a property's own type: a nullable property means "optional"
+ // (an absent key already expresses "unset"), so an explicit null is redundant unless the property
+ // opts in with [JsonNullable]. Value and element nodes skip this — their nullability is reconciled
+ // by the owning property below, because the exporter marks every reference-type value or element
+ // nullable regardless of how the model actually declares it.
+ var isValueOrElement = context.PropertyInfo is null && !context.Path.IsEmpty;
var keepNull = attributeProvider?.IsDefined(typeof(JsonNullableAttribute), inherit: true) ?? false;
- if (!keepNull && schema is JsonObject nullableSchema)
+ if (!isValueOrElement && !keepNull && schema is JsonObject ownSchema)
{
- RemoveNullFromType(nullableSchema);
- RemoveNullFromEnum(nullableSchema);
+ RemoveNullFromType(ownSchema);
+ RemoveNullFromEnum(ownSchema);
}
- // Close a dictionary to a fixed set of keys when the property carries [JsonAllowedKeys]. The exporter
- // runs this transform bottom-up, so the value schema cloned below is already null-stripped.
+ // Reconcile the nullability the exporter put on this property's values and elements with what the
+ // model actually declares (string vs string?), recursing through nested generics. This runs before
+ // ConstrainKeys so the keys it clones inherit the corrected value schema.
+ if (context.PropertyInfo?.AttributeProvider is MemberInfo member && schema is JsonObject propertySchema)
+ {
+ var nullability = CreateNullabilityInfo(nullabilityContext, member);
+ if (nullability is not null)
+ {
+ ReconcileValueNullability(propertySchema, nullability);
+ }
+ }
+
+ // Close a dictionary to a fixed set of keys when the property carries [JsonAllowedKeys].
if (TryGetAllowedKeys(attributeProvider, out var keys) && schema is JsonObject dictionarySchema)
{
ConstrainKeys(dictionarySchema, keys);
@@ -87,6 +116,65 @@ private static JsonNode TransformSchemaNode(JsonSchemaExporterContext context, J
return schema;
}
+ private static NullabilityInfo? CreateNullabilityInfo(NullabilityInfoContext context, MemberInfo member)
+ => member switch
+ {
+ PropertyInfo property => context.Create(property),
+ FieldInfo field => context.Create(field),
+ _ => null,
+ };
+
+ // Walks a property schema's value ("additionalProperties") and element ("items") subschemas alongside
+ // the matching nullability metadata, keeping "null" only where the model declares the value or element
+ // nullable. Recurses so nested generics (a dictionary of lists, say) are handled at every level.
+ private static void ReconcileValueNullability(JsonObject schema, NullabilityInfo nullability)
+ {
+ if (schema["additionalProperties"] is JsonObject valueSchema)
+ {
+ ApplyDeclaredNullability(valueSchema, GetValueNullability(nullability));
+ }
+
+ if (schema["items"] is JsonObject itemSchema)
+ {
+ ApplyDeclaredNullability(itemSchema, GetElementNullability(nullability));
+ }
+ }
+
+ private static void ApplyDeclaredNullability(JsonObject schema, NullabilityInfo? nullability)
+ {
+ if (nullability is null)
+ {
+ return;
+ }
+
+ if (nullability.ReadState != NullabilityState.Nullable)
+ {
+ RemoveNullFromType(schema);
+ RemoveNullFromEnum(schema);
+ }
+
+ ReconcileValueNullability(schema, nullability);
+ }
+
+ // The value type of a dictionary is its last generic argument (IReadOnlyDictionary).
+ private static NullabilityInfo? GetValueNullability(NullabilityInfo nullability)
+ {
+ var args = nullability.GenericTypeArguments;
+ return args.Length > 0 ? args[^1] : null;
+ }
+
+ // The element type is the array element, or the single generic argument of a collection.
+ private static NullabilityInfo? GetElementNullability(NullabilityInfo nullability)
+ {
+ if (nullability.ElementType is { } elementType)
+ {
+ return elementType;
+ }
+
+ var args = nullability.GenericTypeArguments;
+ return args.Length == 1 ? args[0] : null;
+ }
+
// Surfaces a [Description] (on the property, or on the type) as a schema "description" keyword.
// Adapted from the System.Text.Json schema-exporter documentation sample.
private static JsonNode ApplyDescription(ICustomAttributeProvider? attributeProvider, JsonNode schema)
diff --git a/src/Buildvana.Core.JsonSchema/JsonSchemaValidationException.cs b/src/Buildvana.Core.JsonSchema/JsonSchemaValidationException.cs
deleted file mode 100644
index beb7f965..00000000
--- a/src/Buildvana.Core.JsonSchema/JsonSchemaValidationException.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (C) Tenacom and Contributors. Licensed under the MIT license.
-// See the LICENSE file in the project root for full license information.
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Buildvana.Core.JsonSchema;
-
-///
-/// The exception thrown when a JSON value fails validation against a schema.
-///
-public sealed class JsonSchemaValidationException : Exception
-{
- ///
- /// Initializes a new instance of the class with no errors.
- ///
- public JsonSchemaValidationException()
- : this([])
- {
- }
-
- ///
- /// Initializes a new instance of the class with the specified message.
- ///
- /// A message describing the validation failure.
- public JsonSchemaValidationException(string message)
- : base(message)
- {
- Errors = [];
- }
-
- ///
- /// Initializes a new instance of the class with the specified
- /// message and inner exception.
- ///
- /// A message describing the validation failure.
- /// The exception that caused the current exception.
- public JsonSchemaValidationException(string message, Exception innerException)
- : base(message, innerException)
- {
- Errors = [];
- }
-
- ///
- /// Initializes a new instance of the class with the specified errors.
- ///
- /// The validation errors that caused the failure.
- public JsonSchemaValidationException(IReadOnlyList errors)
- : base(FormatMessage(errors))
- {
- Errors = errors;
- }
-
- ///
- /// Gets the validation errors that caused the failure.
- ///
- public IReadOnlyList Errors { get; }
-
- private static string FormatMessage(IReadOnlyList errors)
- {
- ArgumentNullException.ThrowIfNull(errors);
- return errors.Count switch
- {
- 0 => "JSON schema validation failed.",
- 1 => $"JSON schema validation failed: {errors[0]}",
- _ => "JSON schema validation failed:" + Environment.NewLine
- + string.Join(Environment.NewLine, errors.Select(static error => $" - {error}")),
- };
- }
-}
diff --git a/src/Buildvana.Core.JsonSchema/JsonSchemaValidator.cs b/src/Buildvana.Core.JsonSchema/JsonSchemaValidator.cs
index acd6607f..cfa1b4e2 100644
--- a/src/Buildvana.Core.JsonSchema/JsonSchemaValidator.cs
+++ b/src/Buildvana.Core.JsonSchema/JsonSchemaValidator.cs
@@ -96,21 +96,6 @@ public static IReadOnlyList Validate(
return located;
}
- ///
- /// Validates against and throws if it is invalid.
- ///
- /// The JSON value to validate. represents a JSON null.
- /// The schema to validate against.
- /// is invalid.
- public static void ValidateAndThrow(JsonNode? instance, JsonNode schema)
- {
- var errors = Validate(instance, schema);
- if (errors.Count > 0)
- {
- throw new JsonSchemaValidationException(errors);
- }
- }
-
private static void ValidateNode(
JsonNode? instance,
JsonNode schemaNode,
diff --git a/src/Buildvana.Core.Process/ProcessRunner.cs b/src/Buildvana.Core.Process/ProcessRunner.cs
index 5ad080af..4d14fd2f 100644
--- a/src/Buildvana.Core.Process/ProcessRunner.cs
+++ b/src/Buildvana.Core.Process/ProcessRunner.cs
@@ -25,6 +25,7 @@ public sealed class ProcessRunner : IProcessRunner
public async Task RunAsync(
string executable,
IEnumerable args,
+ IReadOnlyDictionary? environment = null,
string? workingDirectory = null,
bool throwOnNonZero = true,
Action? onStdout = null,
@@ -69,6 +70,11 @@ public async Task RunAsync(
.WithStandardErrorPipe(stderrPipe)
.WithValidation(CommandResultValidation.None);
+ if (environment is not null)
+ {
+ command = command.WithEnvironmentVariables(environment);
+ }
+
if (workingDirectory is not null)
{
command = command.WithWorkingDirectory(workingDirectory);
diff --git a/src/Buildvana.Sdk.SourceGenerators/Internal/AdditionalAssemblyInfoValues.cs b/src/Buildvana.Sdk.SourceGenerators/Internal/AdditionalAssemblyInfoValues.cs
index f034c97f..0c4c5573 100644
--- a/src/Buildvana.Sdk.SourceGenerators/Internal/AdditionalAssemblyInfoValues.cs
+++ b/src/Buildvana.Sdk.SourceGenerators/Internal/AdditionalAssemblyInfoValues.cs
@@ -3,4 +3,5 @@
namespace Buildvana.Sdk.SourceGenerators.Internal;
+// ReSharper disable once InconsistentNaming - CLSCompliant is the actual name of the attribute, and it's more readable to keep it as-is in this context.
internal readonly record struct AdditionalAssemblyInfoValues(bool? CLSCompliant, bool? ComVisible);
diff --git a/src/Buildvana.Sdk.Tasks/.editorconfig b/src/Buildvana.Sdk.Tasks/.editorconfig
new file mode 100644
index 00000000..e55cc8cb
--- /dev/null
+++ b/src/Buildvana.Sdk.Tasks/.editorconfig
@@ -0,0 +1,8 @@
+# Project-specific .editorconfig file
+
+root = false
+
+[*.cs]
+
+# Task properties are set by the build engine, not in our code. "Unused" getters are not a concern.
+resharper_auto_property_can_be_made_get_only_global_highlighting = none
diff --git a/src/Buildvana.Sdk.Tasks/Buildvana.Sdk.Tasks.csproj b/src/Buildvana.Sdk.Tasks/Buildvana.Sdk.Tasks.csproj
index a539a264..7e1a9e88 100644
--- a/src/Buildvana.Sdk.Tasks/Buildvana.Sdk.Tasks.csproj
+++ b/src/Buildvana.Sdk.Tasks/Buildvana.Sdk.Tasks.csproj
@@ -19,7 +19,6 @@
-
diff --git a/src/Buildvana.Sdk.Tasks/Internal/JetBrainsAnnotationsExporter.cs b/src/Buildvana.Sdk.Tasks/Internal/JetBrainsAnnotationsExporter.cs
index a05e0523..62830aea 100644
--- a/src/Buildvana.Sdk.Tasks/Internal/JetBrainsAnnotationsExporter.cs
+++ b/src/Buildvana.Sdk.Tasks/Internal/JetBrainsAnnotationsExporter.cs
@@ -124,7 +124,7 @@ private static IEnumerable GetTypeAndNested(INamedTypeSymbol t
private static bool IsExternallyVisible(INamedTypeSymbol type)
{
- for (INamedTypeSymbol? current = type; current is not null; current = current.ContainingType)
+ for (var current = type; current is not null; current = current.ContainingType)
{
if (!IsExternallyVisible(current.DeclaredAccessibility))
{
diff --git a/src/Buildvana.Sdk.Tasks/TaskLoggingHelperLogger.cs b/src/Buildvana.Sdk.Tasks/TaskLoggingHelperLogger.cs
index 0999fe72..20174f40 100644
--- a/src/Buildvana.Sdk.Tasks/TaskLoggingHelperLogger.cs
+++ b/src/Buildvana.Sdk.Tasks/TaskLoggingHelperLogger.cs
@@ -34,8 +34,7 @@ public IDisposable BeginScope(TState state)
public bool IsEnabled(LogLevel logLevel) => logLevel switch
{
- LogLevel.Trace => LogsMessagesOfImportance(MessageImportance.Low),
- LogLevel.Debug => LogsMessagesOfImportance(MessageImportance.Low),
+ LogLevel.Trace or LogLevel.Debug => LogsMessagesOfImportance(MessageImportance.Low),
LogLevel.Information => LogsMessagesOfImportance(MessageImportance.Normal),
LogLevel.Warning => true,
LogLevel.Error => true,
diff --git a/src/Buildvana.Sdk.Tasks/Undefined.cs b/src/Buildvana.Sdk.Tasks/Undefined.cs
index a9fe3c78..395439f0 100644
--- a/src/Buildvana.Sdk.Tasks/Undefined.cs
+++ b/src/Buildvana.Sdk.Tasks/Undefined.cs
@@ -9,12 +9,14 @@ namespace Buildvana.Sdk;
{
public static Undefined Value => default;
+ // ReSharper disable UnusedParameter.Global - Undefined is a unit type; all values are equal.
public static bool operator ==(Undefined first, Undefined second) => true;
public static bool operator !=(Undefined first, Undefined second) => false;
public bool Equals(Undefined other) => true;
+ // ReSharper restore UnusedParameter.Global
public override bool Equals(object? obj) => obj is Undefined;
public override int GetHashCode() => 0;
diff --git a/src/Buildvana.Tool/Buildvana.Tool.csproj b/src/Buildvana.Tool/Buildvana.Tool.csproj
index 33cf7a95..459d8662 100644
--- a/src/Buildvana.Tool/Buildvana.Tool.csproj
+++ b/src/Buildvana.Tool/Buildvana.Tool.csproj
@@ -34,7 +34,6 @@
-
diff --git a/src/Buildvana.Tool/Services/DotNetService.InvocationKind.cs b/src/Buildvana.Tool/Services/DotNetService.InvocationKind.cs
index b72f1068..86018278 100644
--- a/src/Buildvana.Tool/Services/DotNetService.InvocationKind.cs
+++ b/src/Buildvana.Tool/Services/DotNetService.InvocationKind.cs
@@ -14,6 +14,7 @@ private enum InvocationKind
///
/// An informational invocation: the user is interested in the output, but `dotnet` does not accept the `--verbosity` argument.
+ /// `dotnet` output is streamed at `Normal` verbosity.
///
Informational,
diff --git a/src/Buildvana.Tool/Services/DotNetService.cs b/src/Buildvana.Tool/Services/DotNetService.cs
index 86e6d76b..239e939c 100644
--- a/src/Buildvana.Tool/Services/DotNetService.cs
+++ b/src/Buildvana.Tool/Services/DotNetService.cs
@@ -82,7 +82,7 @@ public Task RestoreSolutionAsync(SolutionContext solution, IReadOnlyList
ContinuousIntegrationBuildArg(asMSBuildPassthrough: true),
];
- return RunDotNetAsync(args, InvocationKind.Normal, cancellationToken: cancellationToken);
+ return RunDotNetAsync(args, cancellationToken: cancellationToken);
}
///
@@ -110,7 +110,7 @@ public Task BuildSolutionAsync(SolutionContext solution, string configuration, I
ContinuousIntegrationBuildArg(asMSBuildPassthrough: true),
];
- return RunDotNetAsync(args, InvocationKind.Normal, cancellationToken: cancellationToken);
+ return RunDotNetAsync(args, cancellationToken: cancellationToken);
}
///
@@ -139,7 +139,7 @@ public async Task TestSolutionAsync(SolutionContext solution, string configurati
// bv-internal MSBuild evaluation: do not forward the user's arguments here, as they may be
// test-application options that `dotnet msbuild` would reject.
string[] probeArgs = ["msbuild", projectPath, "-nologo", "-getProperty:IsTestingPlatformApplication"];
- var probe = await RunDotNetAsync(probeArgs, InvocationKind.Internal, cancellationToken: cancellationToken).ConfigureAwait(false);
+ var probe = await RunDotNetAsync(probeArgs, null, InvocationKind.Internal, cancellationToken: cancellationToken).ConfigureAwait(false);
if (string.Equals(probe.StandardOutput.Trim(), "true", StringComparison.OrdinalIgnoreCase))
{
@@ -174,7 +174,7 @@ public async Task TestSolutionAsync(SolutionContext solution, string configurati
ContinuousIntegrationBuildArg(asMSBuildPassthrough: false),
];
- await RunDotNetAsync(args, InvocationKind.Normal, cancellationToken: cancellationToken).ConfigureAwait(false);
+ await RunDotNetAsync(args, cancellationToken: cancellationToken).ConfigureAwait(false);
}
///
@@ -204,7 +204,7 @@ public Task PackSolutionAsync(SolutionContext solution, string configuration, IR
ContinuousIntegrationBuildArg(asMSBuildPassthrough: true),
];
- return RunDotNetAsync(args, InvocationKind.Normal, cancellationToken: cancellationToken);
+ return RunDotNetAsync(args, cancellationToken: cancellationToken);
}
///
@@ -232,6 +232,9 @@ public async Task NuGetPushAllAsync(string artifactsPath, CancellationToken canc
{
_reporter.Detail($"Pushing {path} to {target.Source}...");
string[] args = [
+
+ // `dotnet nuget` has no verbosity option; use the global diagnostics flag when diagnostic output is enabled.
+ .. _reporter.IsVerbosityAtLeast(Verbosity.Diagnostic) ? ["-d"] : Array.Empty(),
"nuget",
"push",
path,
@@ -241,7 +244,7 @@ public async Task NuGetPushAllAsync(string artifactsPath, CancellationToken canc
target.ApiKey,
"--skip-duplicate",
];
- await _processRunner.RunAsync(DotNetMuxer, args, cancellationToken: cancellationToken).ConfigureAwait(false);
+ await RunDotNetAsync(args, invocationKind: InvocationKind.Informational, cancellationToken: cancellationToken).ConfigureAwait(false);
}
_reporter.Info($"Pushed {packages.Length} packages to {target.Source}.");
@@ -268,21 +271,23 @@ private string ContinuousIntegrationBuildArg(bool asMSBuildPassthrough)
/// A representing the ongoing operation, with a result describing child process outcome.
private Task RunDotNetAsync(
IEnumerable args,
- InvocationKind invocationKind,
+ IReadOnlyDictionary? environment = null,
+ InvocationKind invocationKind = InvocationKind.Normal,
CancellationToken cancellationToken = default)
{
- var (appendVerbosity, streamOutput) = invocationKind switch {
- InvocationKind.Normal => (AppendVerbosity: true, StreamOutput: true),
- InvocationKind.Informational => (AppendVerbosity: false, StreamOutput: true),
- InvocationKind.Internal => (AppendVerbosity: false, StreamOutput: false),
+ var (appendVerbosity, streamOutput, streamVerbosity) = invocationKind switch {
+ InvocationKind.Normal => (AppendVerbosity: true, StreamOutput: true, StreamVerbosity: null as Verbosity?),
+ InvocationKind.Informational => (AppendVerbosity: false, StreamOutput: true, StreamVerbosity: Verbosity.Normal),
+ InvocationKind.Internal => (AppendVerbosity: false, StreamOutput: false, StreamVerbosity: null),
_ => throw new UnreachableException(),
};
return _processRunner.RunAsync(
DotNetMuxer,
appendVerbosity ? args.Append($"--verbosity={_reporter.Verbosity}") : args,
- onStdout: streamOutput ? (x) => _reporter.ChildOutput(x, null) : null,
- onStderr: streamOutput ? (x) => _reporter.ChildError(x, null) : null,
+ environment: environment,
+ onStdout: streamOutput ? (x) => _reporter.ChildOutput(x, streamVerbosity) : null,
+ onStderr: streamOutput ? (x) => _reporter.ChildError(x, streamVerbosity) : null,
cancellationToken: cancellationToken);
}
}
diff --git a/src/Buildvana.Tool/Services/ServerAdapters/Internal/GitLab/GitLabServerAdapter.cs b/src/Buildvana.Tool/Services/ServerAdapters/Internal/GitLab/GitLabServerAdapter.cs
index 66877c6f..8c249280 100644
--- a/src/Buildvana.Tool/Services/ServerAdapters/Internal/GitLab/GitLabServerAdapter.cs
+++ b/src/Buildvana.Tool/Services/ServerAdapters/Internal/GitLab/GitLabServerAdapter.cs
@@ -14,7 +14,7 @@ namespace Buildvana.Tool.Services.ServerAdapters.Internal.GitLab;
///
internal sealed class GitLabServerAdapter : ServerAdapter
{
- internal GitLabServerAdapter()
+ private GitLabServerAdapter()
{
CIBotIdentity = new("GitLab CI", $"gitlab-ci@noreply.{Environment.GetEnvironmentVariable("CI_SERVER_HOST")}");
}
diff --git a/src/Buildvana.Tool/Services/ServerAdapters/ServerAdapter.cs b/src/Buildvana.Tool/Services/ServerAdapters/ServerAdapter.cs
index 3375f31e..463a1973 100644
--- a/src/Buildvana.Tool/Services/ServerAdapters/ServerAdapter.cs
+++ b/src/Buildvana.Tool/Services/ServerAdapters/ServerAdapter.cs
@@ -27,6 +27,7 @@ private protected ServerAdapter()
///
/// Gets the name of the remote repository's host.
///
+ // ReSharper disable once UnusedMemberInSuper.Global - We may need this property in the future
public abstract string HostName { get; }
///
@@ -101,6 +102,7 @@ public static ServerAdapter Create(IServiceProvider services)
/// The path to the file.
/// The SHA or reference to which the file belongs.
///
+ // ReSharper disable once UnusedMemberInSuper.Global - We may need this method in the future
public abstract Uri GetFileUrl(string path, string commitish);
///
diff --git a/tests/.editorconfig b/tests/.editorconfig
new file mode 100644
index 00000000..31adefbb
--- /dev/null
+++ b/tests/.editorconfig
@@ -0,0 +1,8 @@
+# Test-specific .editorconfig file
+
+root = false
+
+[*.cs]
+
+# Test methods can have compound or expression bodies, as appropriate for the test.
+resharper_arrange_method_or_operator_body_highlighting = none
diff --git a/tests/Buildvana.Core.Configuration.Tests/Buildvana.Core.Configuration.Tests.csproj b/tests/Buildvana.Core.Configuration.Tests/Buildvana.Core.Configuration.Tests.csproj
index 603da20a..35eff46e 100644
--- a/tests/Buildvana.Core.Configuration.Tests/Buildvana.Core.Configuration.Tests.csproj
+++ b/tests/Buildvana.Core.Configuration.Tests/Buildvana.Core.Configuration.Tests.csproj
@@ -3,8 +3,6 @@
Exe
$(StandardTfm)
- true
- true
false
true
diff --git a/tests/Buildvana.Core.JsonSchema.Tests/Buildvana.Core.JsonSchema.Tests.csproj b/tests/Buildvana.Core.JsonSchema.Tests/Buildvana.Core.JsonSchema.Tests.csproj
index 41b0f64a..5903bd00 100644
--- a/tests/Buildvana.Core.JsonSchema.Tests/Buildvana.Core.JsonSchema.Tests.csproj
+++ b/tests/Buildvana.Core.JsonSchema.Tests/Buildvana.Core.JsonSchema.Tests.csproj
@@ -3,8 +3,6 @@
Exe
$(StandardTfm)
- true
- true
false
true
diff --git a/tests/Buildvana.Core.JsonSchema.Tests/GeneratorSample.cs b/tests/Buildvana.Core.JsonSchema.Tests/GeneratorSample.cs
index 279e899a..d6aef857 100644
--- a/tests/Buildvana.Core.JsonSchema.Tests/GeneratorSample.cs
+++ b/tests/Buildvana.Core.JsonSchema.Tests/GeneratorSample.cs
@@ -22,4 +22,16 @@ internal sealed record GeneratorSample
[JsonAllowedKeys("alpha, beta")]
public IReadOnlyDictionary? Map { get; init; }
+
+ // Nullable dictionary value: the schema should keep "null" on the value type without any opt-in.
+ public IReadOnlyDictionary? Env { get; init; }
+
+ // Non-nullable dictionary value: the schema should strip the "null" the exporter adds.
+ public IReadOnlyDictionary? Vars { get; init; }
+
+ // Nullable array element: the schema should keep "null" on the item type without any opt-in.
+ public IReadOnlyList? Items { get; init; }
+
+ // Non-nullable array element: the schema should strip the "null" the exporter adds.
+ public IReadOnlyList? Tags { get; init; }
}
diff --git a/tests/Buildvana.Core.JsonSchema.Tests/JsonSchemaGeneratorTests.cs b/tests/Buildvana.Core.JsonSchema.Tests/JsonSchemaGeneratorTests.cs
index 28f0d7c0..df7d95a2 100644
--- a/tests/Buildvana.Core.JsonSchema.Tests/JsonSchemaGeneratorTests.cs
+++ b/tests/Buildvana.Core.JsonSchema.Tests/JsonSchemaGeneratorTests.cs
@@ -56,5 +56,37 @@ public async Task Generate_ConstrainsDictionaryToAllowedKeys()
await Assert.That((map["properties"] as JsonObject)!.Count).IsEqualTo(2);
}
+ [Test]
+ public async Task Generate_KeepsNullOnNullableDictionaryValue()
+ {
+ var type = Generate()["properties"]!["env"]!["additionalProperties"]!["type"];
+ await Assert.That(type is JsonArray).IsTrue();
+ await Assert.That(((JsonArray)type!).Count).IsEqualTo(2);
+ }
+
+ [Test]
+ public async Task Generate_StripsNullFromNonNullableDictionaryValue()
+ {
+ var type = Generate()["properties"]!["vars"]!["additionalProperties"]!["type"];
+ await Assert.That(type!.GetValueKind()).IsEqualTo(JsonValueKind.String);
+ await Assert.That(type.GetValue()).IsEqualTo("string");
+ }
+
+ [Test]
+ public async Task Generate_KeepsNullOnNullableArrayElement()
+ {
+ var type = Generate()["properties"]!["items"]!["items"]!["type"];
+ await Assert.That(type is JsonArray).IsTrue();
+ await Assert.That(((JsonArray)type!).Count).IsEqualTo(2);
+ }
+
+ [Test]
+ public async Task Generate_StripsNullFromNonNullableArrayElement()
+ {
+ var type = Generate()["properties"]!["tags"]!["items"]!["type"];
+ await Assert.That(type!.GetValueKind()).IsEqualTo(JsonValueKind.String);
+ await Assert.That(type.GetValue()).IsEqualTo("string");
+ }
+
private static JsonNode Generate() => JsonSchemaGenerator.Generate(Options);
}
diff --git a/tests/Buildvana.Core.JsonSchema.Tests/JsonSchemaValidatorTests.cs b/tests/Buildvana.Core.JsonSchema.Tests/JsonSchemaValidatorTests.cs
index 8514645b..ed45dcbf 100644
--- a/tests/Buildvana.Core.JsonSchema.Tests/JsonSchemaValidatorTests.cs
+++ b/tests/Buildvana.Core.JsonSchema.Tests/JsonSchemaValidatorTests.cs
@@ -1,7 +1,6 @@
// Copyright (C) Tenacom and Contributors. Licensed under the MIT license.
// See the LICENSE file in the project root for full license information.
-using System.Text;
using System.Text.Json.Nodes;
using Buildvana.Core.JsonSchema;
@@ -107,7 +106,7 @@ public async Task Validate_UnresolvableRef_Throws()
public async Task Validate_WithBytes_FillsLineAndColumn()
{
var schema = Schema("""{"type":"object","properties":{"name":{"type":"string"}}}""");
- var bytes = Encoding.UTF8.GetBytes("{\n \"name\": 42\n}");
+ var bytes = "{\n \"name\": 42\n}"u8;
var errors = JsonSchemaValidator.Validate(JsonNode.Parse(bytes), schema, bytes);
await Assert.That(errors.Count).IsEqualTo(1);
await Assert.That(errors[0].Line).IsEqualTo(2);
diff --git a/tests/Buildvana.Tool.Tests/Buildvana.Tool.Tests.csproj b/tests/Buildvana.Tool.Tests/Buildvana.Tool.Tests.csproj
index 8d0238bc..58cca09b 100644
--- a/tests/Buildvana.Tool.Tests/Buildvana.Tool.Tests.csproj
+++ b/tests/Buildvana.Tool.Tests/Buildvana.Tool.Tests.csproj
@@ -3,8 +3,6 @@
Exe
$(StandardTfm)
- true
- true
false
true