Add backend snapshot source providers #1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| on: | |
| push: | |
| branches: [ main ] | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| env: | |
| DOTNET_VERSION: 10.0.x | |
| SOLUTION: ManagedCode.FeatureChecker.slnx | |
| PROJECT: ManagedCode.FeatureChecker/ManagedCode.FeatureChecker.csproj | |
| COVERAGE_THRESHOLD: 85 | |
| COVERAGE_FILE: ManagedCode.FeatureChecker.Tests/bin/Release/net10.0/TestResults/coverage.cobertura.xml | |
| jobs: | |
| build: | |
| name: Build, Test, and Pack | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.version.outputs.version }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v5 | |
| with: | |
| dotnet-version: ${{ env.DOTNET_VERSION }} | |
| - name: Resolve package version | |
| id: version | |
| shell: bash | |
| run: | | |
| version="$(grep -oPm1 '(?<=<Version>)[^<]+' Directory.Build.props)" | |
| if [[ -z "${version}" ]]; then | |
| echo "::error::Directory.Build.props must define <Version>." | |
| exit 1 | |
| fi | |
| echo "version=${version}" >> "${GITHUB_OUTPUT}" | |
| echo "Package version: ${version}" | |
| - name: Restore dependencies | |
| run: dotnet restore ${{ env.SOLUTION }} | |
| - name: Verify formatting | |
| run: dotnet format ${{ env.SOLUTION }} --verify-no-changes | |
| - name: Build | |
| run: dotnet build ${{ env.SOLUTION }} --configuration Release --no-restore | |
| - name: Analyze | |
| run: dotnet build ${{ env.SOLUTION }} --configuration Release --no-restore -p:RunAnalyzers=true | |
| - name: Test | |
| run: dotnet test --solution ${{ env.SOLUTION }} --configuration Release --no-build --verbosity normal | |
| - name: Integration tests | |
| run: dotnet test --solution ${{ env.SOLUTION }} --configuration Release --no-build --verbosity normal -- --treenode-filter "/*/*/*/*[Category=Integration]" | |
| - name: Coverage | |
| run: dotnet test --solution ${{ env.SOLUTION }} --configuration Release --no-build --verbosity normal -- --coverage --coverage-output coverage.cobertura.xml --coverage-output-format cobertura | |
| - name: Enforce coverage threshold | |
| shell: bash | |
| run: | | |
| if [[ ! -f "${COVERAGE_FILE}" ]]; then | |
| echo "::error::Coverage file not found: ${COVERAGE_FILE}" | |
| exit 1 | |
| fi | |
| line_rate="$(sed -n 's/.*line-rate="\([^"]*\)".*/\1/p' "${COVERAGE_FILE}" | head -n 1)" | |
| if [[ -z "${line_rate}" ]]; then | |
| echo "::error::Could not read line-rate from ${COVERAGE_FILE}" | |
| exit 1 | |
| fi | |
| awk -v rate="${line_rate}" -v threshold="${COVERAGE_THRESHOLD}" 'BEGIN { | |
| coverage = rate * 100 | |
| printf("Line coverage: %.2f%%\n", coverage) | |
| if (coverage + 0.000001 < threshold) { | |
| printf("Line coverage %.2f%% is below required %.2f%%\n", coverage, threshold) | |
| exit 1 | |
| } | |
| }' | |
| - name: Pack | |
| run: dotnet pack ${{ env.PROJECT }} --configuration Release --no-build --output artifacts | |
| - name: Upload package artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: nuget-packages | |
| path: | | |
| artifacts/*.nupkg | |
| artifacts/*.snupkg | |
| if-no-files-found: error | |
| retention-days: 5 | |
| - name: Upload test artifacts | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: test-results | |
| path: | | |
| **/TestResults/** | |
| if-no-files-found: ignore | |
| retention-days: 5 | |
| publish-nuget: | |
| name: Publish to NuGet | |
| needs: build | |
| runs-on: ubuntu-latest | |
| if: github.ref == 'refs/heads/main' | |
| outputs: | |
| published: ${{ steps.publish.outputs.published }} | |
| version: ${{ needs.build.outputs.version }} | |
| steps: | |
| - name: Download package artifacts | |
| uses: actions/download-artifact@v5 | |
| with: | |
| name: nuget-packages | |
| path: artifacts | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v5 | |
| with: | |
| dotnet-version: ${{ env.DOTNET_VERSION }} | |
| - name: Publish NuGet packages | |
| id: publish | |
| shell: bash | |
| env: | |
| NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} | |
| run: | | |
| if [[ -z "${NUGET_API_KEY}" ]]; then | |
| echo "::error::NUGET_API_KEY secret is required to publish packages." | |
| exit 1 | |
| fi | |
| shopt -s nullglob | |
| packages=(artifacts/*.nupkg) | |
| if [[ "${#packages[@]}" -eq 0 ]]; then | |
| echo "::error::No NuGet packages were downloaded." | |
| exit 1 | |
| fi | |
| published=false | |
| for package in "${packages[@]}"; do | |
| echo "Publishing ${package}..." | |
| output="$(dotnet nuget push "${package}" --api-key "${NUGET_API_KEY}" --source https://api.nuget.org/v3/index.json --skip-duplicate 2>&1)" | |
| exit_code=$? | |
| echo "${output}" | |
| if [[ "${exit_code}" -ne 0 ]]; then | |
| echo "::error::Failed to publish ${package}." | |
| exit "${exit_code}" | |
| fi | |
| if echo "${output}" | grep -Eiq 'your package was pushed|successfully published'; then | |
| published=true | |
| fi | |
| done | |
| echo "published=${published}" >> "${GITHUB_OUTPUT}" | |
| create-release: | |
| name: Create GitHub Release and Tag | |
| needs: publish-nuget | |
| runs-on: ubuntu-latest | |
| if: needs.publish-nuget.outputs.published == 'true' | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Download package artifacts | |
| uses: actions/download-artifact@v5 | |
| with: | |
| name: nuget-packages | |
| path: artifacts | |
| - name: Create and push tag | |
| shell: bash | |
| run: | | |
| version="${{ needs.publish-nuget.outputs.version }}" | |
| tag="v${version}" | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| if git rev-parse "${tag}" >/dev/null 2>&1; then | |
| echo "Tag ${tag} already exists." | |
| exit 0 | |
| fi | |
| git tag -a "${tag}" -m "Release ${tag}" | |
| git push origin "${tag}" | |
| - name: Generate release notes | |
| shell: bash | |
| run: | | |
| version="${{ needs.publish-nuget.outputs.version }}" | |
| tag="v${version}" | |
| previous_tag="$(git tag --sort=-version:refname | grep -v "^${tag}$" | head -n 1 || true)" | |
| { | |
| echo "# Release ${version}" | |
| echo | |
| echo "Released on $(date +'%Y-%m-%d')" | |
| echo | |
| if [[ -n "${previous_tag}" ]]; then | |
| echo "## Changes since ${previous_tag}" | |
| echo | |
| git log --pretty=format:'- %s (%h)' "${previous_tag}..HEAD" | |
| else | |
| echo "## Initial release" | |
| echo | |
| git log --pretty=format:'- %s (%h)' --max-count=20 | |
| fi | |
| echo | |
| echo | |
| echo "## NuGet Packages" | |
| echo | |
| for package in artifacts/*.nupkg; do | |
| package_name="$(basename "${package}" .nupkg)" | |
| package_id="${package_name%.${version}}" | |
| echo "- [${package_id} ${version}](https://www.nuget.org/packages/${package_id}/${version})" | |
| done | |
| } > release-notes.md | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: v${{ needs.publish-nuget.outputs.version }} | |
| name: v${{ needs.publish-nuget.outputs.version }} | |
| body_path: release-notes.md | |
| files: | | |
| artifacts/*.nupkg | |
| artifacts/*.snupkg |