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
209 changes: 209 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
name: Build and Test
on:
push:
pull_request:
workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest
outputs:
semver: ${{ steps.gitversion.outputs.semVer }}
fullsemver: ${{ steps.gitversion.outputs.fullSemVer }}
nugetversion: ${{ steps.gitversion.outputs.fullSemVer }}

steps:
- name: Checkout code
uses: actions/checkout@v5
with:
fetch-depth: 0 # Required for GitVersion

- name: Install GitVersion
uses: gittools/actions/gitversion/setup@v4.1.0
with:
versionSpec: '6.x'

- name: Determine Version
uses: gittools/actions/gitversion/execute@v4.1.0
id: gitversion

- name: Display GitVersion outputs
run: |
echo "Version: ${{ steps.gitversion.outputs.semVer }}"
echo "AssemblyVersion: ${{ steps.gitversion.outputs.assemblySemVer }}"
echo "FileVersion: ${{ steps.gitversion.outputs.assemblySemFileVer }}"
echo "NuGet Version: ${{ steps.gitversion.outputs.fullSemVer }}"

- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: '8.0.x'

- name: Build project
working-directory: src
run: dotnet build --configuration Release /p:Version=${{ steps.gitversion.outputs.assemblySemVer }} /p:AssemblyVersion=${{ steps.gitversion.outputs.assemblySemVer }} /p:FileVersion=${{ steps.gitversion.outputs.assemblySemFileVer }} /p:PackageVersion=${{ steps.gitversion.outputs.fullSemVer }}

- name: Run tests with coverage
working-directory: src
run: dotnet test --configuration Release --collect:"XPlat Code Coverage" --results-directory ../coverage

- name: Generate coverage report
uses: danielpalme/ReportGenerator-GitHub-Action@5.4.16
with:
reports: 'coverage/**/coverage.cobertura.xml'
targetdir: 'coverage-report'
reporttypes: 'Html;Cobertura;TextSummary'

- name: Display Code Coverage Summary
run: |
echo "## 📊 Code Coverage Summary" >> $GITHUB_STEP_SUMMARY
if [ -f "coverage-report/Summary.txt" ]; then
echo '```' >> $GITHUB_STEP_SUMMARY
cat coverage-report/Summary.txt >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "### Build Log Coverage Summary:"
cat coverage-report/Summary.txt
else
echo "⚠️ Coverage summary file not found"
echo "⚠️ Coverage summary file not found" >> $GITHUB_STEP_SUMMARY
fi

- name: Generate Rich Release Notes
id: release_notes
uses: mikepenz/release-changelog-builder-action@v5.4.1
with:
configuration: |
{
"template": "## 🚀 Release ${{ steps.gitversion.outputs.semVer }}\n\n### 📅 Release Information\n- **Version**: ${{ steps.gitversion.outputs.semVer }}\n- **NuGet Version**: ${{ steps.gitversion.outputs.fullSemVer }}\n- **Build Date**: $(date -u +'%Y-%m-%d %H:%M:%S UTC')\n- **Commit**: ${{ github.sha }}\n\n#{{CHANGELOG}}\n\n### 📊 Statistics\n- **Total Changes**: #{{UNCATEGORIZED_COUNT}} commits\n- **Contributors**: #{{CONTRIBUTORS}}\n\n---\n*Generated automatically by GitHub Actions*",
"categories": [
{
"title": "## 🚀 Features",
"labels": ["feature", "enhancement", "feat"]
},
{
"title": "## 🐛 Bug Fixes",
"labels": ["bug", "fix", "bugfix"]
},
{
"title": "## 📚 Documentation",
"labels": ["documentation", "docs"]
},
{
"title": "## 🔧 Maintenance",
"labels": ["maintenance", "chore", "refactor"]
},
{
"title": "## ⚠️ Breaking Changes",
"labels": ["breaking", "breaking-change"]
}
],
"pr_template": "- #{{TITLE}} (#{{NUMBER}}) by @#{{AUTHOR}}",
"empty_template": "- #{{TITLE}} (#{{HASH}}) by @#{{AUTHOR}}",
"max_pull_requests": 200,
"max_back_track_time_days": 365
}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Save Release Notes to File
run: echo "${{ steps.release_notes.outputs.changelog }}" > release-notes.md

- name: Upload Release Notes as artifact
uses: actions/upload-artifact@v4
with:
name: release-notes
path: release-notes.md

- name: Create NuGet package
working-directory: src
run: dotnet pack --configuration Release --no-build /p:PackageVersion=${{ steps.gitversion.outputs.fullSemVer }} --output ../packages

- name: Upload NuGet package as artifact
uses: actions/upload-artifact@v4
with:
name: nuget-package
path: packages/*.nupkg

- name: Publish coverage report as artifact
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage-report/

publish-nuget:
runs-on: ubuntu-latest
needs: build
if: github.event_name == 'push' && github.ref_name == github.event.repository.default_branch
environment:
name: Publish
url: https://www.nuget.org/packages/TestStack.BDDfy/

steps:
- name: Download NuGet package
uses: actions/download-artifact@v5
with:
name: nuget-package
path: packages

- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: '8.0.x'

- name: Publish to NuGet
run: dotnet nuget push packages/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate

create-release-tag:
runs-on: ubuntu-latest
needs: [build, publish-nuget]
if: github.event_name == 'push' && github.ref_name == github.event.repository.default_branch
permissions:
contents: write

steps:
- name: Checkout code
uses: actions/checkout@v5
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Check if tag already exists
id: check_tag
run: |
TAG="v${{ needs.build.outputs.semver }}"
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "Tag $TAG already exists"
echo "tag_exists=true" >> $GITHUB_OUTPUT
else
echo "Creating new tag: $TAG"
echo "tag_exists=false" >> $GITHUB_OUTPUT
echo "new_tag=$TAG" >> $GITHUB_OUTPUT
fi

- name: Download Release Notes
if: steps.check_tag.outputs.tag_exists == 'false'
uses: actions/download-artifact@v5
with:
name: release-notes
path: .

- name: Create Git Tag and GitHub Release
if: steps.check_tag.outputs.tag_exists == 'false'
run: |
TAG="${{ steps.check_tag.outputs.new_tag }}"

# Configure git
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

# Create and push tag
git tag -a "$TAG" -m "Release $TAG"
git push origin "$TAG"

# Create GitHub release
gh release create "$TAG" \
--title "Release $TAG" \
--notes-file release-notes.md \
--generate-notes
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,5 @@ tools/Cake/
tools/GitReleaseNotes/
tools/gitversion.commandline/
artifacts/
*.code-workspace
**/coverage
2 changes: 1 addition & 1 deletion GitVersion.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
mode: ContinuousDelivery
next-version: 4.1.0
next-version: 8.0.0
branches: {}
ignore:
sha: []
4 changes: 4 additions & 0 deletions src/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[*.cs]

# Default severity for analyzer diagnostics with category 'Style'
dotnet_analyzer_diagnostic.category-Style.severity = none
5 changes: 5 additions & 0 deletions src/Directory.build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Project>
<PropertyGroup>
<TargetFrameworks>net8.0</TargetFrameworks>
</PropertyGroup>
</Project>
9 changes: 0 additions & 9 deletions src/NuGet.config

This file was deleted.

74 changes: 24 additions & 50 deletions src/Samples/TestStack.BDDfy.Samples/AssemblySetupFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,55 +33,36 @@ public AssemblySetupFixture()
}

[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public class AssemblyFixtureAttribute : Attribute
public class AssemblyFixtureAttribute(Type fixtureType): Attribute
{
public AssemblyFixtureAttribute(Type fixtureType)
{
FixtureType = fixtureType;
}

public Type FixtureType { get; private set; }
public Type FixtureType { get; private set; } = fixtureType;
}

public class XunitTestFrameworkWithAssemblyFixture : XunitTestFramework
public class XunitTestFrameworkWithAssemblyFixture(IMessageSink messageSink): XunitTestFramework(messageSink)
{
public XunitTestFrameworkWithAssemblyFixture(IMessageSink messageSink)
: base(messageSink)
{ }

protected override ITestFrameworkExecutor CreateExecutor(AssemblyName assemblyName)
=> new XunitTestFrameworkExecutorWithAssemblyFixture(assemblyName, SourceInformationProvider, DiagnosticMessageSink);
}

public class XunitTestFrameworkExecutorWithAssemblyFixture : XunitTestFrameworkExecutor
public class XunitTestFrameworkExecutorWithAssemblyFixture(AssemblyName assemblyName,
ISourceInformationProvider sourceInformationProvider, IMessageSink diagnosticMessageSink): XunitTestFrameworkExecutor(assemblyName, sourceInformationProvider, diagnosticMessageSink)
{
public XunitTestFrameworkExecutorWithAssemblyFixture(AssemblyName assemblyName,
ISourceInformationProvider sourceInformationProvider, IMessageSink diagnosticMessageSink)
: base(assemblyName, sourceInformationProvider, diagnosticMessageSink)
{
}

protected override async void RunTestCases(IEnumerable<IXunitTestCase> testCases,
IMessageSink executionMessageSink, ITestFrameworkExecutionOptions executionOptions)
{
using (
var assemblyRunner = new XunitTestAssemblyRunnerWithAssemblyFixture(TestAssembly, testCases,
DiagnosticMessageSink, executionMessageSink, executionOptions))
await assemblyRunner.RunAsync();
using var assemblyRunner = new XunitTestAssemblyRunnerWithAssemblyFixture(TestAssembly, testCases,
DiagnosticMessageSink, executionMessageSink, executionOptions);
await assemblyRunner.RunAsync();
}
}

public class XunitTestAssemblyRunnerWithAssemblyFixture : XunitTestAssemblyRunner
public class XunitTestAssemblyRunnerWithAssemblyFixture(ITestAssembly testAssembly,
IEnumerable<IXunitTestCase> testCases,
IMessageSink diagnosticMessageSink,
IMessageSink executionMessageSink,
ITestFrameworkExecutionOptions executionOptions): XunitTestAssemblyRunner(testAssembly, testCases, diagnosticMessageSink, executionMessageSink, executionOptions)
{
readonly Dictionary<Type, object> assemblyFixtureMappings = new Dictionary<Type, object>();

public XunitTestAssemblyRunnerWithAssemblyFixture(ITestAssembly testAssembly,
IEnumerable<IXunitTestCase> testCases,
IMessageSink diagnosticMessageSink,
IMessageSink executionMessageSink,
ITestFrameworkExecutionOptions executionOptions)
: base(testAssembly, testCases, diagnosticMessageSink, executionMessageSink, executionOptions)
{ }
readonly Dictionary<Type, object> assemblyFixtureMappings = new();

protected override async Task AfterTestAssemblyStartingAsync()
{
Expand Down Expand Up @@ -118,24 +99,17 @@ protected override Task<RunSummary> RunTestCollectionAsync(IMessageBus messageBu
=> new XunitTestCollectionRunnerWithAssemblyFixture(assemblyFixtureMappings, testCollection, testCases, DiagnosticMessageSink, messageBus, TestCaseOrderer, new ExceptionAggregator(Aggregator), cancellationTokenSource).RunAsync();
}

public class XunitTestCollectionRunnerWithAssemblyFixture : XunitTestCollectionRunner
public class XunitTestCollectionRunnerWithAssemblyFixture(Dictionary<Type, object> assemblyFixtureMappings,
ITestCollection testCollection,
IEnumerable<IXunitTestCase> testCases,
IMessageSink diagnosticMessageSink,
IMessageBus messageBus,
ITestCaseOrderer testCaseOrderer,
ExceptionAggregator aggregator,
CancellationTokenSource cancellationTokenSource): XunitTestCollectionRunner(testCollection, testCases, diagnosticMessageSink, messageBus, testCaseOrderer, aggregator, cancellationTokenSource)
{
readonly Dictionary<Type, object> assemblyFixtureMappings;
readonly IMessageSink diagnosticMessageSink;

public XunitTestCollectionRunnerWithAssemblyFixture(Dictionary<Type, object> assemblyFixtureMappings,
ITestCollection testCollection,
IEnumerable<IXunitTestCase> testCases,
IMessageSink diagnosticMessageSink,
IMessageBus messageBus,
ITestCaseOrderer testCaseOrderer,
ExceptionAggregator aggregator,
CancellationTokenSource cancellationTokenSource)
: base(testCollection, testCases, diagnosticMessageSink, messageBus, testCaseOrderer, aggregator, cancellationTokenSource)
{
this.assemblyFixtureMappings = assemblyFixtureMappings;
this.diagnosticMessageSink = diagnosticMessageSink;
}
readonly Dictionary<Type, object> assemblyFixtureMappings = assemblyFixtureMappings;
readonly IMessageSink diagnosticMessageSink = diagnosticMessageSink;

protected override Task<RunSummary> RunTestClassAsync(ITestClass testClass, IReflectionTypeInfo @class, IEnumerable<IXunitTestCase> testCases)
{
Expand Down
Loading
Loading