Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 105 additions & 40 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,57 +3,122 @@ name: Continuous Integration
on:
push:
branches:
- develop
- main
paths-ignore:
- 'README.md'
- 'docs/**'
- '.github/workflows/docs.yml'
- '.github/workflows/release.yml'
- "**.md"
- "renovate.json"
- ".github/ISSUE_TEMPLATE/**"
- ".github/PULL_REQUEST_TEMPLATE/**"
- ".github/workflows/docs.yml"
- ".github/workflows/release.yml"

pull_request:
paths-ignore:
- 'README.md'
- 'docs/**'
- '.github/workflows/docs.yml'
- '.github/workflows/release.yml'

- "**.md"
- "renovate.json"
- ".github/ISSUE_TEMPLATE/**"
- ".github/PULL_REQUEST_TEMPLATE/**"
- ".github/workflows/docs.yml"
- ".github/workflows/release.yml"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
unit-tests:
name: Unit Tests
test-and-analysis:
name: Test & Analysis
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_ORGANIZATION: ${{ secrets.SONAR_ORGANIZATION }}
SONAR_PROJECT_KEY: ${{ secrets.SONAR_PROJECT_KEY }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_ANALYSIS: true

steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
show-progress: false
- name: Install .NET SDK
uses: actions/setup-dotnet@v5
with:
global-json-file: global.json
- name: NuGet Cache
uses: actions/cache@v5
with:
path: ~/.nuget/packages
key: ${{ runner.os}}-nuget-${{ hashFiles('**/packages.lock.json') }}
restore-keys: ${{ runner.os }}-nuget
- name: Test
run: ./build.sh Test
- name: Publish Test Results
if: always()
uses: dorny/test-reporter@v2
with:
name: Test Results
path: .nuke/temp/TestResults/*.trx
reporter: dotnet-trx
path-replace-backslashes: true
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
show-progress: false

- name: Setup .NET
uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0
with:
global-json-file: global.json
cache: true
cache-dependency-path: '**/Directory.Packages.props'

- name: Restore dependencies
run: dotnet restore

- name: Install SonarCloud scanner
if: github.actor != 'dependabot[bot]' && github.actor != 'renovate[bot]'
run: dotnet tool update dotnet-sonarscanner --tool-path .sonar/scanner

- name: Begin SonarCloud analysis
if: github.actor != 'dependabot[bot]' && github.actor != 'renovate[bot]'
run: |
.sonar/scanner/dotnet-sonarscanner begin \
/k:"visus:AddressValidation" \
/o:"visus" \
/d:sonar.token="$SONAR_TOKEN" \
/d:sonar.host.url="https://sonarcloud.io" \
/d:sonar.cs.vscoveragexml.reportsPaths="**/TestResults/**/*.xml" \
/d:sonar.cs.vstest.reportsPaths="**/TestResults/**/*.trx"

- name: Build
run: dotnet build -c Release --no-restore

- name: Run tests
run: |
dotnet test -c Release --no-build --no-restore -- \
--coverage \
--coverage-output-format xml \
--report-trx

- name: Upload test results
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: test-results
path: "**/TestResults/*.trx"
retention-days: 1

- name: End SonarCloud analysis
if: github.actor != 'dependabot[bot]' && github.actor != 'renovate[bot]'
run: |
.sonar/scanner/dotnet-sonarscanner end \
/d:sonar.token="$SONAR_TOKEN"

publish-test-results:
name: Publish Test Results
runs-on: ubuntu-latest
needs: test-and-analysis
if: always()
timeout-minutes: 10
permissions:
contents: read
checks: write
pull-requests: write

steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 1
show-progress: false

- name: Download test results
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: test-results
path: test-results

- name: Publish test results
uses: dorny/test-reporter@df6247429542221bc30d46a036ee47af1102c451 # v2.7.0
with:
name: Test Results
path: test-results/**/*.trx
reporter: dotnet-trx
fail-on-error: false
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ public FedExAddressValidationClient(HttpClient httpClient, IOptions<FedExService
httpRequest.Headers.Add("x-customer-transaction-id", request.CustomerTransactionId);
}

httpRequest.Headers.Add("x-locale", _options.Value.Locale);
if ( !string.IsNullOrWhiteSpace(_options.Value.Locale) )
{
httpRequest.Headers.Add("x-locale", _options.Value.Locale);
}

httpRequest.Content = JsonContent.Create(request, ApiRequestJsonSerializerContext.Default.ApiRequest);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
namespace Visus.AddressValidation.Integration.FedEx.Configuration;

using System.ComponentModel.DataAnnotations;
using System.Globalization;
using AddressValidation.Abstractions;

/// <summary>
Expand Down Expand Up @@ -81,12 +80,12 @@ public sealed class FedExServiceOptions : IValidatableObject
/// used for address validation responses returned by the FedEx API.
/// </summary>
/// <remarks>
/// Defaults to <see cref="CultureInfo.CurrentCulture" />.<see cref="CultureInfo.Name" />
/// of the executing thread at the time the options object is
/// constructed. Use a value such as <c>en-US</c> or <c>fr-FR</c> to
/// When <see langword="null" /> (the default), the <c>x-locale</c>
/// request header is omitted and FedEx will use its own default locale.
/// Set to an IETF BCP 47 tag such as <c>en-US</c> or <c>fr-FR</c> to
/// request responses in a specific language and region.
/// </remarks>
public string Locale { get; set; } = CultureInfo.CurrentCulture.Name;
public string? Locale { get; set; }

/// <summary>
/// Performs cross-property validation on the options object.
Expand Down Expand Up @@ -115,7 +114,7 @@ public IEnumerable<ValidationResult> Validate(ValidationContext validationContex
[nameof(EndpointOverrideUri),]);
}

if ( !Constants.SupportedLocales.Contains(Locale) )
if ( Locale is not null && !Constants.SupportedLocales.Contains(Locale) )
{
yield return new ValidationResult(
$"'{Locale}' is not a supported IETF BCP 47 language tag.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace Visus.AddressValidation.Integration.FedEx.Configuration
public required string ClientSecret { get; set; }
public System.Uri EndpointBaseUri { get; }
public System.Uri? EndpointOverrideUri { get; set; }
public string Locale { get; set; }
public string? Locale { get; set; }
public System.Collections.Generic.IEnumerable<System.ComponentModel.DataAnnotations.ValidationResult> Validate(System.ComponentModel.DataAnnotations.ValidationContext validationContext) { }
}
[Microsoft.Extensions.Options.OptionsValidator]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[assembly: System.Resources.NeutralResourcesLanguage("en")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("DynamicProxyGenAssembly2")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Visus.AddressValidation.SourceGeneration.Tests")]
namespace Visus.AddressValidation.SourceGeneration
{
[Microsoft.CodeAnalysis.Generator]
public sealed class CustomResponseDataGenerator : Microsoft.CodeAnalysis.IIncrementalGenerator
{
public CustomResponseDataGenerator() { }
public void Initialize(Microsoft.CodeAnalysis.IncrementalGeneratorInitializationContext context) { }
}
}
25 changes: 25 additions & 0 deletions tests/Visus.AddressValidation.SourceGeneration.Tests/ApiTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
extern alias sourcegen;

namespace Visus.AddressValidation.SourceGeneration.Tests;

using System.Runtime.CompilerServices;
using PublicApiGenerator;

internal sealed class ApiTests
{
[Test]
[MethodImpl(MethodImplOptions.NoInlining)]
public async Task PublicApi_HasNoBreakingChanges_Async()

Check warning on line 12 in tests/Visus.AddressValidation.SourceGeneration.Tests/ApiTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Make method static (deprecated, use CA1822 instead)

See more on https://sonarcloud.io/project/issues?id=visus%3AAddressValidation&issues=AZ5U58b3XSDqH0AcEAhr&open=AZ5U58b3XSDqH0AcEAhr&pullRequest=243
{
string api = typeof(sourcegen::Visus.AddressValidation.SourceGeneration.CustomResponseDataGenerator).Assembly.GeneratePublicApi(new ApiGeneratorOptions
{
ExcludeAttributes =
[
"System.Reflection.AssemblyMetadataAttribute",
"System.Runtime.Versioning.TargetFrameworkAttribute",
],
});

await Verify(api).ConfigureAwait(false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Visus.AddressValidation.SourceGeneration\Visus.AddressValidation.SourceGeneration.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false"
Aliases="sourcegen"
SetTargetFramework="targetframework=netstandard2.0"/>
<ProjectReference Include="..\..\src\Visus.AddressValidation\Visus.AddressValidation.csproj"/>
</ItemGroup>
Expand Down
Loading