diff --git a/.github/workflows/dotnet-cd.yml b/.github/workflows/dotnet-cd.yml index d882900..8139e68 100644 --- a/.github/workflows/dotnet-cd.yml +++ b/.github/workflows/dotnet-cd.yml @@ -13,11 +13,40 @@ env: PACKAGE_NAME: nanotaboada/dotnet-samples-aspnetcore-webapi jobs: + test: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Set up .NET ${{ env.DOTNET_VERSION }} + uses: actions/setup-dotnet@v5.2.0 + with: + dotnet-version: ${{ env.DOTNET_VERSION }} + cache: true + cache-dependency-path: | + src/Dotnet.Samples.AspNetCore.WebApi/packages.lock.json + test/Dotnet.Samples.AspNetCore.WebApi.Tests/packages.lock.json + + - name: Restore dependencies + run: dotnet restore + + - name: Build projects (Release configuration) + run: dotnet build --configuration Release --no-restore + + - name: Run tests + run: dotnet test --configuration Release --no-build --verbosity normal + release: + needs: test runs-on: ubuntu-latest permissions: contents: write packages: write + id-token: write + attestations: write steps: - name: Checkout repository @@ -77,24 +106,6 @@ jobs: echo "📦 Release version: $SEMVER" echo "🏟️ Stadium name: $STADIUM" - - name: Set up .NET ${{ env.DOTNET_VERSION }} - uses: actions/setup-dotnet@v5.2.0 - with: - dotnet-version: ${{ env.DOTNET_VERSION }} - cache: true - cache-dependency-path: | - src/Dotnet.Samples.AspNetCore.WebApi/packages.lock.json - test/Dotnet.Samples.AspNetCore.WebApi.Tests/packages.lock.json - - - name: Restore dependencies - run: dotnet restore - - - name: Build projects (Release configuration) - run: dotnet build --configuration Release --no-restore - - - name: Run tests - run: dotnet test --configuration Release --no-build --verbosity normal - - name: Log in to GitHub Container Registry uses: docker/login-action@v4.1.0 with: @@ -106,12 +117,13 @@ jobs: uses: docker/setup-buildx-action@v4.0.0 - name: Build and push Docker image to GitHub Container Registry + id: push uses: docker/build-push-action@v7.0.0 with: context: . push: true - platforms: linux/amd64 - provenance: false + platforms: linux/amd64,linux/arm64 + provenance: mode=max cache-from: type=gha cache-to: type=gha,mode=max tags: | @@ -119,6 +131,13 @@ jobs: ghcr.io/${{ env.PACKAGE_NAME }}:${{ steps.version.outputs.semver }} ghcr.io/${{ env.PACKAGE_NAME }}:${{ steps.version.outputs.stadium }} + - name: Attest build provenance + uses: actions/attest-build-provenance@v4.1.0 + with: + subject-name: ghcr.io/${{ env.PACKAGE_NAME }} + subject-digest: ${{ steps.push.outputs.digest }} + push-to-registry: true + - name: Generate changelog id: changelog run: | @@ -127,15 +146,15 @@ jobs: if [ -z "$PREVIOUS_TAG" ]; then echo "📝 First release - no previous tag found" - CHANGELOG="Initial release" + CHANGELOG="No changes (first release)" else echo "📝 Generating changelog from $PREVIOUS_TAG to ${{ steps.version.outputs.tag_name }}" - CHANGELOG=$(git log --pretty=format:"- %s (%h)" ${PREVIOUS_TAG}..${{ steps.version.outputs.tag_name }}) - fi + CHANGELOG=$(git log --pretty=format:"- %s (%h)" --no-merges ${PREVIOUS_TAG}..${{ steps.version.outputs.tag_name }}) - # Write changelog to file - echo "$CHANGELOG" > changelog.txt - cat changelog.txt + if [ -z "$CHANGELOG" ]; then + CHANGELOG="No new changes since $PREVIOUS_TAG" + fi + fi # Set output for use in release body { @@ -167,6 +186,10 @@ jobs: docker pull ghcr.io/${{ env.PACKAGE_NAME }}:latest ``` + ## Changes + + ${{ steps.changelog.outputs.changelog }} + --- 📦 **Package:** [ghcr.io/${{ env.PACKAGE_NAME }}](https://github.com/${{ github.repository }}/pkgs/container/dotnet-samples-aspnetcore-webapi) diff --git a/CHANGELOG.md b/CHANGELOG.md index 276bc5d..0ba16c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,14 @@ This project uses famous football stadiums (A-Z) that hosted FIFA World Cup matc ### Added +- Extract `test` job from `release` in CD pipeline so tests run in isolation + before any publish step; add `linux/arm64` to build platforms; add + `id-token: write` and `attestations: write` permissions to `release`; set + `provenance: mode=max` and attest the image digest with + `actions/attest-build-provenance@v4.1.0` (`push-to-registry: true`); add + `--no-merges` to the changelog `git log` command; add empty changelog guard; + normalize first-release message to `"No changes (first release)"` (#465) + - Add `adr/0013-testing-strategy.md` documenting the decision to implement the full test pyramid as a deliberate educational choice (#421) - Add `test/.../Integration/PlayerWebApplicationTests.cs` with 14 HTTP-layer integration tests covering all player endpoints and `/health` via `WebApplicationFactory` backed by in-memory SQLite; includes `Utilities/TestAuthHandler.cs` to bypass `[Authorize]` on `GET /players/{id:Guid}`; expose `Program` to the test project via `public partial class Program {}` in `Program.cs`; add `Microsoft.AspNetCore.Mvc.Testing` to the test project (#421) - Add `test/.../Integration/PlayerRepositoryTests.cs` with 9 integration tests covering `Repository` (`GetAllAsync`, `FindByIdAsync`, `RemoveAsync`) and `PlayerRepository` (`FindBySquadNumberAsync`, `SquadNumberExistsAsync`); all tests use `DatabaseFakes.MigrateAsync()` on in-memory SQLite and are tagged `[Trait("Category", "Integration")]` (#461)