diff --git a/.github/workflows/01-versioning.yml b/.github/workflows/01-versioning.yml
index 50c5e91d..8cb38d33 100644
--- a/.github/workflows/01-versioning.yml
+++ b/.github/workflows/01-versioning.yml
@@ -15,13 +15,16 @@ jobs:
tag: ${{ steps.versioning.outputs.tag }}
version: ${{ steps.versioning.outputs.version }} # same as tag but without the -betaN part at the end, if present
steps:
+
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Necessary to fetch tags
+
- name: Install xmlstarlet
run: |
sudo apt-get update
sudo apt-get install -y xmlstarlet
+
- id: versioning
run: |
TAG=${GITHUB_REF#refs/tags/}
@@ -40,17 +43,10 @@ jobs:
echo "Error: Tag does not match the expected format 'X.Y.Z' or 'X.Y.Z-betaN'"
exit 1
fi
+
- name: Set Version in Directory.Build.props
run: xmlstarlet ed --inplace -u "/Project/PropertyGroup/Version" -v "${{ steps.versioning.outputs.version }}" Directory.Build.props
- - name: Set Version in VSIX manifest
- shell: pwsh
- run: |
- [xml]$manifest = Get-Content "src/EcoCode.Vsix/source.extension.vsixmanifest"
- $identity = $manifest.SelectSingleNode("//*[local-name()='Identity']")
- if ($identity -ne $null) {
- $identity.Version = "${{ steps.versioning.outputs.version }}"
- }
- $manifest.Save("src/EcoCode.Vsix/source.extension.vsixmanifest")
+
- name: Cache updated sources
uses: actions/cache/save@v4
with:
diff --git a/.github/workflows/02-build.yml b/.github/workflows/02-build.yml
index 176fac1a..79a06be4 100644
--- a/.github/workflows/02-build.yml
+++ b/.github/workflows/02-build.yml
@@ -19,10 +19,15 @@ jobs:
**/*
key: cache-${{ github.sha }}
enableCrossOsArchive: true
+
- uses: actions/setup-dotnet@v4
+
- run: dotnet restore NuGetOnly.slnf
+
- run: dotnet build NuGetOnly.slnf -c Release --no-restore
+
- run: dotnet pack NuGetOnly.slnf -c Release --no-build -o ./nupkg -p:Version=${{ inputs.tag }}
+
- uses: actions/cache/save@v4
with:
path: nupkg
@@ -40,33 +45,17 @@ jobs:
**/*
key: cache-${{ github.sha }}
enableCrossOsArchive: true
+
- uses: actions/setup-dotnet@v4
+
- run: dotnet restore ToolOnly.slnf
+
- run: dotnet build ToolOnly.slnf -c Release --no-restore
+
- run: dotnet pack ToolOnly.slnf -c Release --no-build -o ./tool -p:Version=${{ inputs.tag }}
+
- uses: actions/cache/save@v4
with:
path: tool
key: cache-${{ github.sha }}
enableCrossOsArchive: true
-
- build-vsix:
- runs-on: windows-latest
- steps:
- - name: Restore versioned sources
- uses: actions/cache/restore@v4
- with:
- path: |
- !**/.git
- **/*
- key: cache-${{ github.sha }}
- enableCrossOsArchive: true
- - uses: actions/setup-dotnet@v4
- - run: dotnet restore VsixOnly.slnf
- - uses: microsoft/setup-msbuild@v2
- - run: msbuild VsixOnly.slnf "-p:OutputPath=..\..\vsix;Configuration=Release"
- - uses: actions/cache/save@v4
- with:
- path: vsix
- key: cache-${{ github.sha }}
- enableCrossOsArchive: true
diff --git a/.github/workflows/03-publish.yml b/.github/workflows/03-publish.yml
index 2abb0db0..901db84a 100644
--- a/.github/workflows/03-publish.yml
+++ b/.github/workflows/03-publish.yml
@@ -6,14 +6,9 @@ on:
tag:
required: true
type: string
- version:
- required: true
- type: string
secrets:
nuget-api-key:
required: true
- vsmarketplace-api-key:
- required: true
jobs:
publish-nuget:
@@ -24,7 +19,9 @@ jobs:
path: nupkg
key: cache-${{ github.sha }}
enableCrossOsArchive: true
+
- run: echo "PACKAGE=$(find . -name 'EcoCode.${{ inputs.tag }}.nupkg' | head -n 1)" >> $GITHUB_ENV
+
- run: dotnet nuget push ${{ env.PACKAGE }} -k "${{ secrets.nuget-api-key }}" -s https://api.nuget.org/v3/index.json
publish-tool:
@@ -35,20 +32,7 @@ jobs:
path: tool
key: cache-${{ github.sha }}
enableCrossOsArchive: true
+
- run: echo "PACKAGE=$(find . -name 'EcoCode.Tool.${{ inputs.tag }}.nupkg' | head -n 1)" >> $GITHUB_ENV
+
- run: dotnet nuget push ${{ env.PACKAGE }} -k "${{ secrets.nuget-api-key }}" -s https://api.nuget.org/v3/index.json
-
- publish-vsix:
- if: ${{ inputs.tag == inputs.version }}
- runs-on: windows-latest
- steps:
- - uses: actions/cache/restore@v4
- with:
- path: vsix
- key: cache-${{ github.sha }}
- enableCrossOsArchive: true
- - uses: cezarypiatek/VsixPublisherAction@1.1
- with:
- extension-file: vsix/EcoCode.vsix
- publish-manifest-file: vsix/publishManifest.json
- personal-access-code: ${{ secrets.vsmarketplace-api-key }}
diff --git a/.github/workflows/04-release.yml b/.github/workflows/04-release.yml
index 06329c2c..5b79dcbb 100644
--- a/.github/workflows/04-release.yml
+++ b/.github/workflows/04-release.yml
@@ -19,16 +19,13 @@ jobs:
path: nupkg
key: cache-${{ github.sha }}
enableCrossOsArchive: true
+
- uses: actions/cache/restore@v4
with:
path: tool
key: cache-${{ github.sha }}
enableCrossOsArchive: true
- - uses: actions/cache/restore@v4
- with:
- path: vsix
- key: cache-${{ github.sha }}
- enableCrossOsArchive: true
+
- uses: softprops/action-gh-release@v2
with:
tag_name: ${{ inputs.tag }}
@@ -39,4 +36,3 @@ jobs:
files: |
nupkg/**/EcoCode.${{ inputs.tag }}.nupkg
tool/**/EcoCode.Tool.${{ inputs.tag }}.nupkg
- vsix/EcoCode.vsix
diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
index dc6b0815..e43f8398 100644
--- a/.github/workflows/build-and-test.yml
+++ b/.github/workflows/build-and-test.yml
@@ -18,21 +18,28 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
+
- uses: actions/setup-dotnet@v4
+
- run: dotnet restore TestsOnly.slnf
+
- run: dotnet build TestsOnly.slnf -c Release --no-restore
+
- run: dotnet test TestsOnly.slnf -c Release --no-build --logger "trx;LogFileName=test_results.trx" -p:CollectCoverage=true -p:Threshold=80 -p:CoverletOutputFormat='cobertura'
+
- uses: danielpalme/ReportGenerator-GitHub-Action@5
with:
reports: '/home/runner/work/ecoCode-csharp/ecoCode-csharp/src/EcoCode.Tests/coverage.cobertura.xml'
targetdir: 'CoverageReports'
historydir: 'CoverageHistory'
- reporttypes: 'HtmlInline;Cobertura'
+ reporttypes: 'HtmlInline;Cobertura'
+
- uses: actions/upload-artifact@v4
if: always() # Ensure this step runs even if the test step fails
with:
name: Test Results
path: '**/test_results.trx'
+
- uses: actions/upload-artifact@v4
with:
name: Coverage reports
diff --git a/.github/workflows/create-tag-release.yml b/.github/workflows/create-tag-release.yml
index 26a178e0..686600e0 100644
--- a/.github/workflows/create-tag-release.yml
+++ b/.github/workflows/create-tag-release.yml
@@ -28,10 +28,8 @@ jobs:
uses: ./.github/workflows/03-publish.yml
with:
tag: ${{ needs.versioning.outputs.tag }}
- version: ${{ needs.versioning.outputs.version }}
secrets:
nuget-api-key: ${{ secrets.NUGET_API_KEY }}
- vsmarketplace-api-key: ${{ secrets.VSMARKETPLACE_API_KEY }}
release:
needs: publish
diff --git a/Directory.Build.props b/Directory.Build.props
index a8623584..c2627047 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -15,6 +15,8 @@
en-US
true
+ latest
+ all
true
true
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 8a0f3b54..5867e6d3 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -21,7 +21,6 @@
-
@@ -30,4 +29,4 @@
-
\ No newline at end of file
+
diff --git a/README.md b/README.md
index 22995eeb..02e52b5b 100644
--- a/README.md
+++ b/README.md
@@ -15,9 +15,7 @@ _ecoCode_ is based on evolving catalogs of [good practices](https://github.com/g
There are several ways you can use the ecoCode C# analyzers in your .Net projects:
1. As a [NuGet package](#nugetPackage)
2. As a [.NET tool](#dotnetTool)
-3. As a [Visual Studio extension](#vsExtension)
-4. Coming soon : as a VS Code extension.
-5. As an [analyzer for SonarQube](#sonarQube).
+3. As an [analyzer for SonarQube](#sonarQube).
🧩 NuGet package
-----------------
@@ -39,15 +37,7 @@ Once installed, you can launch an analyzis on an existing codebase like this :
The file to analyze can be a .sln, a .slnx or a .csproj. The report format depends on it's required extension, the following are currently supported : .html, .json and .csv.
-Pre-requisite : .Net 8 SDK, with the goal of requiring the oldest .Net Core LTS from then on (ie. require .Net 10 in November 2026).
-
-🧩 Visual Studio extension
------------------
-The extension is available on the VS marketplace at this address : https://marketplace.visualstudio.com/items?itemName=greencodeinitiative.ecoCode, and can be installed in your Visual Studio instance like any extension, typically through the extension manager available in Visual Studio. Once installed, the ecoCode analyzers are automatically activated in your IDE/compilation process, and will list any applicable info/alert.
-
-🧩 VS Code extension
------------------
-Not available yet, stay tuned.
+Pre-requisite : .Net 8 SDK.
🧩 Analyzer for SonarQube
-----------------
@@ -73,10 +63,10 @@ EcoCode C# can use [SonarScanner for .Net](https://docs.sonarsource.com/sonarqub
|[EC92](https://github.com/green-code-initiative/ecoCode/blob/main/ecocode-rules-specifications/src/main/rules/EC92/csharp/EC92.asciidoc)|Use `Length` to test empty `strings`|⚠️|✔️|
|[EC93](https://github.com/green-code-initiative/ecoCode/blob/main/ecocode-rules-specifications/src/main/rules/EC93/csharp/EC93.asciidoc)|Return `Task` directly|ℹ️|✔️|
-🌿 Roslyn Rules
+🌿 Customized Roslyn Rules
-------------------
-EcoCode C# customizes the severity of the following Microsoft-produced Roslyn rules.
+EcoCode C# customizes the severity of the following native Roslyn rules.
|Id|Description|Old Severity|New Severity|
|--|-----------|:----------:|:----------:|
diff --git a/VsixOnly.slnf b/VsixOnly.slnf
deleted file mode 100644
index db4eab2d..00000000
--- a/VsixOnly.slnf
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "solution": {
- "path": "ecoCode-csharp.sln",
- "projects": [
- "src\\EcoCode.Core\\EcoCode.Core.csproj",
- "src\\EcoCode.Vsix\\EcoCode.Vsix.csproj"
- ]
- }
-}
diff --git a/ecoCode-csharp.sln b/ecoCode-csharp.sln
index 2fdf953a..c4521235 100644
--- a/ecoCode-csharp.sln
+++ b/ecoCode-csharp.sln
@@ -17,7 +17,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Filters", "Filters", "{2750
NuGetOnly.slnf = NuGetOnly.slnf
TestsOnly.slnf = TestsOnly.slnf
ToolOnly.slnf = ToolOnly.slnf
- VsixOnly.slnf = VsixOnly.slnf
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Assets", "Assets", "{375CBD0D-5754-352F-9D9D-ED72E2F1B0ED}"
@@ -50,8 +49,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EcoCode.Tests", "src\EcoCod
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EcoCode.Tool", "src\EcoCode.Tool\EcoCode.Tool.csproj", "{DBF60DEC-9C46-6FC0-EEE0-892F31353788}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EcoCode.Vsix", "src\EcoCode.Vsix\EcoCode.Vsix.csproj", "{A9DB5FED-AAF1-F128-AF8E-738F916D065F}"
-EndProject
Global
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F20F15FF-417E-42ED-962A-2CF00C3E4467}
@@ -77,10 +74,20 @@ Global
{DBF60DEC-9C46-6FC0-EEE0-892F31353788}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DBF60DEC-9C46-6FC0-EEE0-892F31353788}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DBF60DEC-9C46-6FC0-EEE0-892F31353788}.Release|Any CPU.Build.0 = Release|Any CPU
- {A9DB5FED-AAF1-F128-AF8E-738F916D065F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A9DB5FED-AAF1-F128-AF8E-738F916D065F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A9DB5FED-AAF1-F128-AF8E-738F916D065F}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A9DB5FED-AAF1-F128-AF8E-738F916D065F}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {6B1DB35C-47E6-C62F-BA1D-5846AF2B77D4} = {089100B1-113F-4E66-888A-E83F3999EAFD}
+ {27501C7C-A65B-40A8-464F-F047BAFE6F81} = {089100B1-113F-4E66-888A-E83F3999EAFD}
+ {375CBD0D-5754-352F-9D9D-ED72E2F1B0ED} = {089100B1-113F-4E66-888A-E83F3999EAFD}
+ EndGlobalSection
+EndGlobal
+}
+ EndGlobalSection
+EndGlobal
+916D065F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/EcoCode.Core/Analyzers/EC92.UseLengthToTestEmptyStrings.Fixer.cs b/src/EcoCode.Core/Analyzers/EC92.UseLengthToTestEmptyStrings.Fixer.cs
index f70dec0b..c8e7f37e 100644
--- a/src/EcoCode.Core/Analyzers/EC92.UseLengthToTestEmptyStrings.Fixer.cs
+++ b/src/EcoCode.Core/Analyzers/EC92.UseLengthToTestEmptyStrings.Fixer.cs
@@ -35,7 +35,7 @@ private static async Task ReplaceWithLengthCheckAsync(Document documen
{
var (left, right) = (binaryExpr.Left, binaryExpr.Right);
var stringExpr = left.IsEmptyStringLiteral() ? right : right.IsEmptyStringLiteral() ? left : null;
- return stringExpr is null ? document : await document.WithUpdatedRoot(binaryExpr, UpdateBinaryExpression(binaryExpr, stringExpr));
+ return stringExpr is null ? document : await document.WithUpdatedRoot(binaryExpr, UpdateBinaryExpression(binaryExpr, stringExpr)).ConfigureAwait(false);
static BinaryExpressionSyntax UpdateBinaryExpression(BinaryExpressionSyntax binaryExpr, ExpressionSyntax stringExpr) =>
SyntaxFactory.BinaryExpression(binaryExpr.Kind(),
diff --git a/src/EcoCode.Core/Analyzers/EC93.ReturnTaskDirectly.Fixer.cs b/src/EcoCode.Core/Analyzers/EC93.ReturnTaskDirectly.Fixer.cs
index 33a68d62..28aaecd0 100644
--- a/src/EcoCode.Core/Analyzers/EC93.ReturnTaskDirectly.Fixer.cs
+++ b/src/EcoCode.Core/Analyzers/EC93.ReturnTaskDirectly.Fixer.cs
@@ -84,7 +84,7 @@ private static async Task ReturnTaskDirectlyWithExpressionAsync(
return await document.WithUpdatedRoot(methodDecl, methodDecl
.WithModifiers(methodDecl.Modifiers.RemoveAt(asyncIndex))
- .WithExpressionBody(newBody));
+ .WithExpressionBody(newBody)).ConfigureAwait(false);
}
private static async Task ReturnTaskDirectlyWithBodyAwaitAsync(
@@ -104,7 +104,7 @@ private static async Task ReturnTaskDirectlyWithBodyAwaitAsync(
return await document.WithUpdatedRoot(methodDecl, methodDecl
.WithModifiers(methodDecl.Modifiers.RemoveAt(asyncIndex))
- .WithBody(newBody));
+ .WithBody(newBody)).ConfigureAwait(false);
}
private static async Task ReturnTaskDirectlyWithBodyReturnAwaitAsync(
@@ -123,6 +123,6 @@ private static async Task ReturnTaskDirectlyWithBodyReturnAwaitAsync(
return await document.WithUpdatedRoot(methodDecl, methodDecl
.WithModifiers(methodDecl.Modifiers.RemoveAt(asyncIndex))
- .WithBody(newBody));
+ .WithBody(newBody)).ConfigureAwait(false);
}
}
diff --git a/src/EcoCode.Core/EcoCode.Core.csproj b/src/EcoCode.Core/EcoCode.Core.csproj
index 3429e4fe..89231f9b 100644
--- a/src/EcoCode.Core/EcoCode.Core.csproj
+++ b/src/EcoCode.Core/EcoCode.Core.csproj
@@ -5,6 +5,8 @@
EcoCode.Core
true
true
+
+ ${NoWarn};CA1062
diff --git a/src/EcoCode.Tool/Commands/AnalyzeCommand.cs b/src/EcoCode.Tool/Commands/AnalyzeCommand.cs
index ae348374..ced65e88 100644
--- a/src/EcoCode.Tool/Commands/AnalyzeCommand.cs
+++ b/src/EcoCode.Tool/Commands/AnalyzeCommand.cs
@@ -30,7 +30,7 @@ public override async Task ExecuteAsync(CommandContext context, AnalyzeSett
using var workspace = MSBuildWorkspace.Create();
workspace.WorkspaceFailed += (sender, e) => Program.WriteLine(e.Diagnostic.Message, "red");
- var analysisService = await AnalysisService.CreateAsync(settings.SeverityLevel);
+ var analysisService = await AnalysisService.CreateAsync(settings.SeverityLevel).ConfigureAwait(false);
var diagnostics = new List();
@@ -68,7 +68,7 @@ public override async Task ExecuteAsync(CommandContext context, AnalyzeSett
await analysisService.AnalyzeProjectAsync(project, diagnostics).ConfigureAwait(false);
}
- await ReportService.GenerateReportAsync(diagnostics, settings.Output, settings.OutputType);
+ await ReportService.GenerateReportAsync(diagnostics, settings.Output, settings.OutputType).ConfigureAwait(false);
return 0;
}
diff --git a/src/EcoCode.Tool/Common/AdditionalFile.cs b/src/EcoCode.Tool/Common/AdditionalFile.cs
index 311a70d1..1a92e386 100644
--- a/src/EcoCode.Tool/Common/AdditionalFile.cs
+++ b/src/EcoCode.Tool/Common/AdditionalFile.cs
@@ -20,6 +20,6 @@ public static async Task LoadGlobalConfigAsync()
throw new FileNotFoundException($"Editor config file not found at {editorConfigPath}");
string fileContent = await File.ReadAllTextAsync(editorConfigPath).ConfigureAwait(false);
- return new AdditionalFile(GlobalConfigFile, fileContent.Replace("is_global = true", string.Empty));
+ return new AdditionalFile(GlobalConfigFile, fileContent.Replace("is_global = true", string.Empty, StringComparison.OrdinalIgnoreCase));
}
}
diff --git a/src/EcoCode.Tool/EcoCode.Tool.csproj b/src/EcoCode.Tool/EcoCode.Tool.csproj
index 13f887ce..4462a6b3 100644
--- a/src/EcoCode.Tool/EcoCode.Tool.csproj
+++ b/src/EcoCode.Tool/EcoCode.Tool.csproj
@@ -25,7 +25,7 @@
https://api.nuget.org/v3/index.json
- ${NoWarn};CA1812
+ ${NoWarn};CA1031;CA1812
diff --git a/src/EcoCode.Tool/Services/AnalysisService.cs b/src/EcoCode.Tool/Services/AnalysisService.cs
index 4af318d5..375b485c 100644
--- a/src/EcoCode.Tool/Services/AnalysisService.cs
+++ b/src/EcoCode.Tool/Services/AnalysisService.cs
@@ -21,7 +21,7 @@ private AnalysisService(ImmutableArray analyzers, AnalyzerOp
public static async Task CreateAsync(DiagnosticSeverity minSeverity)
{
- var globalConfig = await AdditionalFile.LoadGlobalConfigAsync();
+ var globalConfig = await AdditionalFile.LoadGlobalConfigAsync().ConfigureAwait(false);
var analyzers = ImmutableArray.CreateBuilder();
diff --git a/src/EcoCode.Tool/Services/ReportService.Csv.cs b/src/EcoCode.Tool/Services/ReportService.Csv.cs
index b9cb76d0..024a3f1d 100644
--- a/src/EcoCode.Tool/Services/ReportService.Csv.cs
+++ b/src/EcoCode.Tool/Services/ReportService.Csv.cs
@@ -8,10 +8,10 @@ private static class Csv
public static async Task WriteToStreamAsync(StreamWriter writer, List diagnostics)
{
- await writer.WriteLineAsync(Header);
+ await writer.WriteLineAsync(Header).ConfigureAwait(false);
foreach (var diag in diagnostics)
- await writer.WriteLineAsync($"{diag.Directory};{diag.File};{diag.Location};{diag.Severity};{diag.Code};{diag.Message}");
+ await writer.WriteLineAsync($"{diag.Directory};{diag.File};{diag.Location};{diag.Severity};{diag.Code};{diag.Message}").ConfigureAwait(false);
}
}
}
diff --git a/src/EcoCode.Tool/Services/ReportService.Html.cs b/src/EcoCode.Tool/Services/ReportService.Html.cs
index fc321fad..91cf6363 100644
--- a/src/EcoCode.Tool/Services/ReportService.Html.cs
+++ b/src/EcoCode.Tool/Services/ReportService.Html.cs
@@ -1,4 +1,7 @@
-namespace EcoCode.Tool.Services;
+using System.Globalization;
+using System.Text;
+
+namespace EcoCode.Tool.Services;
static partial class ReportService
{
@@ -32,7 +35,7 @@ private static class Html