diff --git a/.github/scripts/validate-csharp-package.sh b/.github/scripts/validate-csharp-package.sh new file mode 100755 index 00000000..d255b703 --- /dev/null +++ b/.github/scripts/validate-csharp-package.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash + +set -euo pipefail + +repository_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd) +solution="$repository_root/csharp/Platform.Interfaces.sln" +test_project="$repository_root/csharp/Platform.Interfaces.Tests/Platform.Interfaces.Tests.csproj" +package_project="$repository_root/csharp/Platform.Interfaces/Platform.Interfaces.csproj" +package_output=$(mktemp -d) + +cleanup() { + rm -rf "$package_output" +} +trap cleanup EXIT + +dotnet restore "$solution" --nologo +dotnet build "$solution" --configuration Release --no-restore --no-incremental --nologo -warnaserror +dotnet test "$test_project" --configuration Release --framework net8 --no-build --no-restore --nologo +dotnet pack "$package_project" --configuration Release --no-build --no-restore \ + --output "$package_output" --nologo -warnaserror + +mapfile -t packages < <(find "$package_output" -maxdepth 1 -type f -name '*.nupkg' -print) +mapfile -t symbol_packages < <(find "$package_output" -maxdepth 1 -type f -name '*.snupkg' -print) + +if [[ ${#packages[@]} -ne 1 ]]; then + echo "Expected one .nupkg, found ${#packages[@]}." >&2 + exit 1 +fi + +if [[ ${#symbol_packages[@]} -ne 0 ]]; then + echo "Expected embedded symbols instead of a separate .snupkg." >&2 + exit 1 +fi + +for expected_file in README.md icon.png lib/net8.0/Platform.Interfaces.dll; do + if ! unzip -Z1 "${packages[0]}" | grep -Fxq "$expected_file"; then + echo "${packages[0]} is missing $expected_file." >&2 + exit 1 + fi +done + +echo "Validated ${packages[0]} with embedded symbols." diff --git a/.github/workflows/AutoMerge.yml b/.github/workflows/AutoMerge.yml index d5c5e0c3..71f387e3 100644 --- a/.github/workflows/AutoMerge.yml +++ b/.github/workflows/AutoMerge.yml @@ -5,9 +5,10 @@ on: jobs: auto-merge: + if: ${{ github.actor == 'dependabot[bot]' }} runs-on: ubuntu-latest + timeout-minutes: 5 steps: - - uses: actions/checkout@v2 - uses: ahmadnassri/action-dependabot-auto-merge@v2 with: target: minor diff --git a/.github/workflows/csharp.yml b/.github/workflows/csharp.yml index a5b414de..29f45feb 100644 --- a/.github/workflows/csharp.yml +++ b/.github/workflows/csharp.yml @@ -2,15 +2,26 @@ name: csharp on: push: - branches: main + branches: main paths: - 'csharp/**' + - 'README.md' + - '.github/scripts/validate-csharp-package.sh' - '.github/workflows/csharp.yml' + pull_request: + branches: main + paths: + - 'csharp/**' + - 'README.md' + - '.github/scripts/validate-csharp-package.sh' + - '.github/workflows/csharp.yml' + +permissions: + contents: read + env: - NUGETTOKEN: ${{ secrets.NUGET_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SCRIPTS_BASE_URL: https://raw.githubusercontent.com/linksplatform/Scripts/main/MultiProjectRepository - + defaults: run: working-directory: csharp @@ -18,100 +29,113 @@ defaults: jobs: test: runs-on: ubuntu-latest + timeout-minutes: 10 steps: - name: Setup .NET - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v6 with: dotnet-version: '8.0.x' - - uses: actions/checkout@v1 + - uses: actions/checkout@v7 with: submodules: true - - name: Test - run: | - dotnet test -c Release -f net8 + - name: Validate tests and NuGet package + run: ../.github/scripts/validate-csharp-package.sh + pushNuGetPackageToGitHubPackageRegistry: needs: test + if: ${{ github.event_name == 'push' }} runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + packages: write steps: - name: Setup .NET - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v6 with: dotnet-version: '8.0.x' - - uses: actions/checkout@v1 + - uses: actions/checkout@v7 with: submodules: true - name: Publish NuGet package to GitHub Package Registry + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - dotnet build -c Release - dotnet pack -c Release --include-symbols - dotnet nuget add source https://nuget.pkg.github.com/linksplatform/index.json --name GitHub --username linksplatform --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text - dotnet nuget push **/*.nupkg --source GitHub --skip-duplicate - pusnToNuget: - runs-on: ubuntu-latest + package_output="$RUNNER_TEMP/github-packages" + dotnet pack Platform.Interfaces/Platform.Interfaces.csproj -c Release --output "$package_output" -warnaserror + dotnet nuget add source https://nuget.pkg.github.com/linksplatform/index.json --name GitHub --username linksplatform --password "$GITHUB_TOKEN" --store-password-in-clear-text + dotnet nuget push "$package_output"/*.nupkg --source GitHub --skip-duplicate + + pushToNuget: needs: test + if: ${{ github.event_name == 'push' }} + runs-on: ubuntu-latest + timeout-minutes: 10 steps: - name: Setup .NET - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v6 with: dotnet-version: '8.0.x' - - uses: actions/checkout@v1 + - uses: actions/checkout@v7 with: submodules: true - - name: Read project information - run: | - export REPOSITORY_NAME=$(basename ${{ github.repository }}) - wget "$SCRIPTS_BASE_URL/read_csharp_package_info.sh" - bash ./read_csharp_package_info.sh - name: Publish NuGet package + env: + NUGETTOKEN: ${{ secrets.NUGET_TOKEN }} run: | - export REPOSITORY_NAME=$(basename ${{ github.repository }}) - wget "$SCRIPTS_BASE_URL/push-csharp-nuget.sh" - bash ./push-csharp-nuget.sh - publiseRelease: + package_output="$RUNNER_TEMP/nuget-packages" + dotnet pack Platform.Interfaces/Platform.Interfaces.csproj -c Release --output "$package_output" -warnaserror + dotnet nuget push "$package_output"/*.nupkg --source https://api.nuget.org/v3/index.json --api-key "$NUGETTOKEN" --skip-duplicate + + publishRelease: + needs: [pushNuGetPackageToGitHubPackageRegistry, pushToNuget] + if: ${{ github.event_name == 'push' }} runs-on: ubuntu-latest - needs: test + timeout-minutes: 10 + permissions: + contents: write steps: - name: Setup .NET - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v6 with: dotnet-version: '8.0.x' - - uses: actions/checkout@v1 + - uses: actions/checkout@v7 with: submodules: true - name: Read project information - if: ${{ github.event_name == 'push' }} run: | export REPOSITORY_NAME=$(basename ${{ github.repository }}) wget "$SCRIPTS_BASE_URL/read_csharp_package_info.sh" bash ./read_csharp_package_info.sh - name: Publish release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | export REPOSITORY_NAME=$(basename ${{ github.repository }}) + export CSHARP_PACKAGE_VERSION="$(> "$GITHUB_OUTPUT" echo "isCsFilesChanged: ${isCsFilesChanged}" + generatePdfWithCode: - runs-on: ubuntu-latest needs: [findChangedCsFiles] - if: ${{ needs.findChangedCsFiles.outputs.isCsFilesChanged == 'true' }} + if: ${{ github.event_name == 'push' && needs.findChangedCsFiles.outputs.isCsFilesChanged == 'true' }} + runs-on: ubuntu-latest + timeout-minutes: 30 steps: - name: Setup .NET - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v6 with: dotnet-version: '8.0.x' - - uses: actions/checkout@v1 + - uses: actions/checkout@v7 with: submodules: true - name: Generate PDF with code @@ -146,20 +172,26 @@ jobs: wget "$SCRIPTS_BASE_URL/format-csharp-document.sh" wget "$SCRIPTS_BASE_URL/generate-csharp-pdf.sh" bash ./generate-csharp-pdf.sh + publishDocumentation: - runs-on: ubuntu-latest needs: [findChangedCsFiles] - if: ${{ needs.findChangedCsFiles.outputs.isCsFilesChanged == 'true' }} + if: ${{ github.event_name == 'push' && needs.findChangedCsFiles.outputs.isCsFilesChanged == 'true' }} + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: write steps: - name: Setup .NET - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v6 with: dotnet-version: '8.0.x' - - uses: actions/checkout@v1 + - uses: actions/checkout@v7 with: submodules: true - name: Publish documentation to gh-pages branch + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | export REPOSITORY_NAME=$(basename ${{ github.repository }}) wget "$SCRIPTS_BASE_URL/docfx.json" diff --git a/.github/workflows/readme-badges.yml b/.github/workflows/readme-badges.yml index 250c511b..df4664d0 100644 --- a/.github/workflows/readme-badges.yml +++ b/.github/workflows/readme-badges.yml @@ -20,10 +20,10 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 with: - node-version: '20' + node-version: '24' - name: Test the badge check run: node --test .github/scripts/check-readme-badges.test.mjs - name: Check that README badges reference existing workflows diff --git a/csharp/Platform.Interfaces.Tests/InterfacesTests.cs b/csharp/Platform.Interfaces.Tests/InterfacesTests.cs index ecd9b4d1..212f4af2 100644 --- a/csharp/Platform.Interfaces.Tests/InterfacesTests.cs +++ b/csharp/Platform.Interfaces.Tests/InterfacesTests.cs @@ -1,7 +1,6 @@ -using Xunit; - -#pragma warning disable CS0168 // Variable is declared but never used -#pragma warning disable CS0219 // Variable is assigned but its value is never used +using System.IO; +using System.Reflection.PortableExecutable; +using Xunit; namespace Platform.Interfaces.Tests { @@ -10,16 +9,38 @@ public static class InterfacesTests [Fact] public static void BuildTest() { - ICounter c1 = null; - ICounter c2 = null; - ICriterionMatcher cm1 = null; - IFactory f1 = null; - IProperties p1 = null; - IProperty p2 = null; - IProvider p3 = null; - IProvider p4 = null; - ISetter s1 = null; - ISetter s2 = null; + ICounter? c1 = null; + ICounter? c2 = null; + ICriterionMatcher? cm1 = null; + IFactory? f1 = null; + IProperties? p1 = null; + IProperty? p2 = null; + IProvider? p3 = null; + IProvider? p4 = null; + ISetter? s1 = null; + ISetter? s2 = null; + + Assert.Null(c1); + Assert.Null(c2); + Assert.Null(cm1); + Assert.Null(f1); + Assert.Null(p1); + Assert.Null(p2); + Assert.Null(p3); + Assert.Null(p4); + Assert.Null(s1); + Assert.Null(s2); + } + + [Fact] + public static void AssemblyContainsEmbeddedPortablePdb() + { + using var assembly = File.OpenRead(typeof(ICounter<>).Assembly.Location); + using var peReader = new PEReader(assembly); + + Assert.Contains( + peReader.ReadDebugDirectory(), + entry => entry.Type == DebugDirectoryEntryType.EmbeddedPortablePdb); } } } diff --git a/csharp/Platform.Interfaces/Platform.Interfaces.csproj b/csharp/Platform.Interfaces/Platform.Interfaces.csproj index e4208009..375a9b54 100644 --- a/csharp/Platform.Interfaces/Platform.Interfaces.csproj +++ b/csharp/Platform.Interfaces/Platform.Interfaces.csproj @@ -4,13 +4,14 @@ LinksPlatform's Platform.Interfaces Class Library Konstantin Diachenko Platform.Interfaces - 0.5.1 + 0.5.2 Konstantin Diachenko net8 Platform.Interfaces Platform.Interfaces LinksPlatform;Interfaces;ICounter;ICriterionMatcher;IFactory;IProperties;IProperty;IProvider;ISetter - https://raw.githubusercontent.com/linksplatform/Documentation/18469f4d033ee9a5b7b84caab9c585acab2ac519/doc/Avatar-rainbow-icon-64x64.png + README.md + icon.png https://linksplatform.github.io/Interfaces Unlicense git @@ -20,11 +21,9 @@ true true true - true - snupkg embedded latest - Add embedded symbols support for GitHub Package Registry debugging. + Fix package publishing while preserving embedded symbols, eliminate C# workflow warnings, and validate NuGet artifacts before release. enable @@ -36,4 +35,9 @@ + + + + + diff --git a/csharp/Platform.Interfaces/icon.png b/csharp/Platform.Interfaces/icon.png new file mode 100644 index 00000000..c1ad0cac Binary files /dev/null and b/csharp/Platform.Interfaces/icon.png differ