diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..1f1aea1 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,54 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + groups: + actions: + patterns: + - actions/* + - DragoAnt/* + - NuGet/* + + - package-ecosystem: nuget + directory: / + schedule: + interval: weekly + groups: + microsoft: + patterns: + - Microsoft.* + - System.* + testing: + patterns: + - xunit* + - Verify* + - NSubstitute* + - FluentAssertions + - Bogus + - coverlet.* + benchmarks: + patterns: + - BenchmarkDotNet* + ignore: + # Versions after 32.0.0 require a paid maintenance subscription. + - dependency-name: Verify + versions: + - '>32.0.0' + - dependency-name: Verify.XunitV3 + versions: + - '>32.0.0' + # 8.x requires a commercial license. + - dependency-name: FluentAssertions + update-types: + - version-update:semver-major + # One version line per target framework (Directory.Packages.props); a major bump would break the older TFMs. + - dependency-name: Microsoft.Extensions.* + update-types: + - version-update:semver-major + + - package-ecosystem: gitsubmodule + directory: / + schedule: + interval: weekly diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 8f0c4d4..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: build - -on: - push: - branches: - - main - pull_request: - workflow_dispatch: - -jobs: - build: - name: 'Build and test' - runs-on: 'ubuntu-latest' - steps: - - name: 'Checkout' - uses: actions/checkout@v6 - with: - submodules: recursive - fetch-depth: 0 - - - name: 'Install dotnet' - uses: actions/setup-dotnet@v6 - with: - dotnet-version: | - 8.0.x - 9.0.x - 10.0.x - global-json-file: './global.json' - - - name: 'Restore packages' - run: dotnet restore - - - name: 'Build' - run: dotnet build -c Release --no-restore - - - name: 'Test' - run: dotnet test --solution DragoAnt.System.Text.Json.sln -c Release --no-build --no-progress --coverage diff --git a/.github/workflows/build_and_publish_nuget.yml b/.github/workflows/build_and_publish_nuget.yml deleted file mode 100644 index 948a139..0000000 --- a/.github/workflows/build_and_publish_nuget.yml +++ /dev/null @@ -1,49 +0,0 @@ -# This is a basic workflow to help you get started with Actions - -name: Build and publish to nuget.org - -# Controls when the workflow will run -on: - release: - types: - - published - -env: - PACKAGE_OUTPUT_DIRECTORY: '${{ github.workspace }}/output' - NUGET_SOURCE_URL: 'https://api.nuget.org/v3/index.json' - MSBUILD_GITHUB_PROPERTIES: "/p:GITHUB_RUN_ID=${{ github.run_id }} /p:GITHUB_RUN_NUMBER=${{ github.run_number }} '/p:GITHUB_REF_NAME=${{ github.ref_name }}' '/p:GITHUB_SHA=${{ github.sha }}' '/p:GITHUB_REPOSITORY=${{ github.repository }}'" - -jobs: - build_publish_nuget: - name: 'Build and publish nuget packages' - runs-on: 'ubuntu-latest' - steps: - - name: 'Checkout' - uses: actions/checkout@v6 - with: - submodules: recursive - fetch-depth: 0 - - - name: 'Install dotnet' - uses: actions/setup-dotnet@v4 - with: - dotnet-version: | - 8.0.x - 9.0.x - 10.0.x - global-json-file: "./global.json" - - - name: 'Restore packages' - run: dotnet restore ${{ env.MSBUILD_GITHUB_PROPERTIES }} - - - name: 'Build' - run: dotnet build -c Release --no-restore ${{ env.MSBUILD_GITHUB_PROPERTIES }} - - - name: 'Test' - run: dotnet test --solution DragoAnt.System.Text.Json.sln -c Release --no-build --no-progress - - - name: 'Pack' - run: dotnet pack -c Release --no-build -o ${{ env.PACKAGE_OUTPUT_DIRECTORY }} ${{ env.MSBUILD_GITHUB_PROPERTIES }} - - - name: 'Push packages' - run: dotnet nuget push ${{ env.PACKAGE_OUTPUT_DIRECTORY }}/*.nupkg -k ${{ secrets.NUGET_ORG_API_KEY }} -s ${{ env.NUGET_SOURCE_URL }} --skip-duplicate diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e6fe783 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,23 @@ +name: ci + +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + build: + name: Build and test + uses: DragoAnt/.github/.github/workflows/dotnet-build.yml@c9f20c11ebd297193a910d965d33bdf37c0d6c42 # main + with: + version: 0.0.0-ci.${{ github.run_number }} + coverage-threshold: 70 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..8ab2eef --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,99 @@ +name: release + +on: + release: + types: + - published + +permissions: + contents: read + +concurrency: + group: release-${{ github.event.release.tag_name }} + cancel-in-progress: false + +jobs: + version: + name: Version from tag + runs-on: ubuntu-24.04 + timeout-minutes: 5 + outputs: + version: ${{ steps.version.outputs.version }} + steps: + - name: Validate tag + id: version + env: + TAG: ${{ github.event.release.tag_name }} + PRERELEASE: ${{ github.event.release.prerelease }} + run: | + version="${TAG#[vV]}" + ident='(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)' + semver="^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-$ident(\.$ident)*)?$" + if ! [[ "$version" =~ $semver ]]; then + echo "::error::release tag '$TAG' is not a SemVer version (vMAJOR.MINOR.PATCH[-prerelease], no build metadata)" + exit 1 + fi + if [[ "$version" == 9999.* ]]; then + echo "::error::9999.x is the local development version and is never published" + exit 1 + fi + if [[ "$PRERELEASE" == "true" && "$version" != *-* ]]; then + echo "::error::the release is marked pre-release but '$TAG' has no -suffix" + exit 1 + fi + if [[ "$PRERELEASE" != "true" && "$version" == *-* ]]; then + echo "::error::'$TAG' is a pre-release version; tick 'Set as a pre-release' on the release" + exit 1 + fi + echo "version=$version" >> "$GITHUB_OUTPUT" + echo "Package version: $version" + + build: + name: Build, test, pack + needs: version + uses: DragoAnt/.github/.github/workflows/dotnet-build.yml@c9f20c11ebd297193a910d965d33bdf37c0d6c42 # main + with: + version: ${{ needs.version.outputs.version }} + pack: true + coverage-threshold: 70 + + publish: + name: Publish to nuget.org + needs: build + runs-on: ubuntu-24.04 + timeout-minutes: 10 + environment: + name: nuget + url: https://www.nuget.org/profiles/${{ vars.NUGET_USER || 'DragoAnt' }} + permissions: + id-token: write + steps: + - name: Download packages + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: ${{ needs.build.outputs.packages-artifact }} + path: packages + + - name: NuGet login (Trusted Publishing) + id: login + continue-on-error: true + uses: NuGet/login@8d196754b4036150537f80ac539e15c2f1028841 # v1.2.0 + with: + user: ${{ vars.NUGET_USER || 'DragoAnt' }} + + - name: Push + env: + OIDC_KEY: ${{ steps.login.outputs.NUGET_API_KEY }} + FALLBACK_KEY: ${{ secrets.NUGET_ORG_API_KEY }} + run: | + if [ -n "$OIDC_KEY" ]; then + key="$OIDC_KEY" + echo "Pushing with a short-lived nuget.org Trusted Publishing key." + elif [ -n "$FALLBACK_KEY" ]; then + key="$FALLBACK_KEY" + echo "::warning::nuget.org Trusted Publishing login failed, falling back to the NUGET_ORG_API_KEY secret. Add a Trusted Publishing policy on nuget.org (this repository, workflow file of this workflow, environment 'nuget') and delete the secret." + else + echo "::error::nuget.org Trusted Publishing login failed and no NUGET_ORG_API_KEY secret is available. Add a Trusted Publishing policy on nuget.org for this repository, this workflow file and environment 'nuget' (or set the NUGET_USER variable if the policy owner is not 'DragoAnt')." + exit 1 + fi + dotnet nuget push "packages/*.nupkg" --api-key "$key" --source https://api.nuget.org/v3/index.json --skip-duplicate